admin管理员组文章数量:1334370
Dynamic languages allow dispatching with and invoking on values from variables whose values are only known at run-time. Contrasting examples in Perl:
class names
constant
Foo::Bar->some_method Foo::Bar::->some_method 'Foo::Bar'->some_method
These are all identical, except the first one is an edge case. If there's a subroutine defined in scope with that name, the dispatch happens on its return value, which leads to difficult to understand bugs. The quoted versions are always safe.
dynamic
my $class_name = 'Foo::Bar'; $class_name->some_method
method names
constant
Some::Class->foo_bar
dynamic
my $method_name = 'foo_bar'; Some::Class->$method_name
function names
constant
foo_bar; (\&foo_bar)->()
dynamic
my $function_name = 'foo_bar'; (\&$function_name)->()
I wonder, how do languages whose variable names have no sigils (normally, or at all) deal with these problems, specifically how did their language designers disambiguate the following?
- resolving class name
FooBar.some_method
where classFooBar
might be name literal or a variable whose value is a class name - dispatching to
SomeClass.foo_bar
where methodfoo_bar
might be name literal or a variable whose value is a method name - invoking
foo_bar
where the function might be a name literal or a variable whose value is a function
I'm primarily interested in the three languages mentioned in this question's tags, but if you know a different dynamic language with no sigils, you can answer too.
Dynamic languages allow dispatching with and invoking on values from variables whose values are only known at run-time. Contrasting examples in Perl:
class names
constant
Foo::Bar->some_method Foo::Bar::->some_method 'Foo::Bar'->some_method
These are all identical, except the first one is an edge case. If there's a subroutine defined in scope with that name, the dispatch happens on its return value, which leads to difficult to understand bugs. The quoted versions are always safe.
dynamic
my $class_name = 'Foo::Bar'; $class_name->some_method
method names
constant
Some::Class->foo_bar
dynamic
my $method_name = 'foo_bar'; Some::Class->$method_name
function names
constant
foo_bar; (\&foo_bar)->()
dynamic
my $function_name = 'foo_bar'; (\&$function_name)->()
I wonder, how do languages whose variable names have no sigils (normally, or at all) deal with these problems, specifically how did their language designers disambiguate the following?
- resolving class name
FooBar.some_method
where classFooBar
might be name literal or a variable whose value is a class name - dispatching to
SomeClass.foo_bar
where methodfoo_bar
might be name literal or a variable whose value is a method name - invoking
foo_bar
where the function might be a name literal or a variable whose value is a function
I'm primarily interested in the three languages mentioned in this question's tags, but if you know a different dynamic language with no sigils, you can answer too.
Share Improve this question asked Oct 16, 2014 at 10:26 daximdaxim 39.2k4 gold badges69 silver badges133 bronze badges 1- What do you mean by "name literal"? Do you mean a string containing the name? – BrenBarn Commented Oct 22, 2014 at 19:34
5 Answers
Reset to default 5 +500In python and js (my ruby is a bit rusty) it's not possible to use a string in the name context. If you attempt to do so, that would be interpreted as an operation on the string itself:
class_name = 'Foo'
object = class_name()
> TypeError: 'str' object is not callable
The string should first be resolved by looking up in the context/scope dictionary:
class Foo:
....
object = globals()['Foo']()
or
function Foo() ....
object = new window['Foo'];
Python provides both global and local dicts, js only the global one, so there's no way, apart from ubiquitous "eval", to obtain a local value from its name.
The same applies to methods: you look up a method using getattr
(python) or the indirect reference operator [...]
(js):
method_name = 'foo'
method = getattr(some_object, method_name)
method()
Javascript code is slightly more plicated, because, unlike python, the returned method pointer is unbound:
method_name = 'foo'
method = some_object[method_name].bind(some_object)
method()
Without bind
you won't get the correct this
in a method:
var obj = {
xyz: 42,
method: function() {
document.write(this.xyz)
}
}
var method_name = 'method';
var unbound_method = obj[method_name];
unbound_method() // prints undefined
var bound_method = obj[method_name].bind(obj);
bound_method() // prints 42
To put it more formally, the dot operator .
(equivalent to php's ::
and ->
) in python/js requires an identifier on the right, and allows for arbitrary expressions on the left. Since everything is an object, if the left-hand expression returns a string, the dot applies to that string, not to a variable whose name this string happens to be equal to.
(As a side note, it's technically feasible to allow expressions on the right of the dot as well, so that foo.('bar' + 'baz')
would resolve to foo.barbaz
. I'm not aware of languages that support this syntax, but it looks like a nice alternative to getattr
and similar methods).
Similarly, the call operator ()
allows for plex expressions on the left, additionally this expression must resolve to a "callable" object. Therefore, "someString"()
doesn't make sense, since strings are normally not callable (unless you hack them somehow so that they are).
That is, if you have a string the contains a variable name, you have to explicitly resolve it before use. There's no magic in the language that does that for you behind the scenes.
In Ruby, you can always use const_get
and send
:
class FooBar
def some_method
return 42
end
end
class_name = 'FooBar'
puts Module.const_get(class_name).new.some_method
class SomeClass
def foo_bar
return 23
end
end
method_name = 'foo_bar'
puts SomeClass.new.send(method_name)
def foo_bar
return 123
end
function_name = 'foo_bar'
puts send(function_name)
In js, you need a global to do a name lookup:
xyz = 100; // notice: no "var" here – it's a global
var varname = 'xyz';
window[varname]; // 100
... Or you can always use eval
, but it's most probably going to bite you:
var x = eval;
x("var y = 10"); // INDIRECT call to eval
window.y; // 10 (it worked)
eval("var y = 11"); // direct call
window.y; // still 10, direct call in strict mode gets a new context
When trying to get a value from an object, you need to know that JS doesn't respect eta conversion. Let's set up the context to explain.
var object = {
x: 10,
say: function () {
console.log(this.x);
}
}
var method = 'say';
// that version (the "eta abstraction" version):
object.say(); // "this" inside of say is correctly "object"
object[method](); // equivalent
// ... isn't equivalent to this (the "eta reduction" version):
var sayit = object[method];
sayit(); // "this" inside of say is incorrectly "window" (and thus the property x won't exist)
// You can use Function#bind to work around that, but it's only available in ES5-patible browsers (and currently pretty slow on v8/chrome)
var sayit = object[method].bind(object);
sayit(); // "this" inside of say has been forced to "object", and will print 10 correctly
// You can also, of course, close over it:
var sayit = function () { object[method]() };
sayit(); // prints 10
Python does not allow you to treat objects the same as strings containing the names of variables referring to those objects. If obj
is a variable whose value is an object, you can do FooBar.some_method()
. If you have a string "FooBar"
, you have to do something else entirely. Exactly what you do depends on where you expect to find that variable "FooBar"
(i.e., is it a global variable, a local variable, an attribute name, or what). You must look up the name in whatever namespace you think it should be in, and then perform your operation on the resulting object. For instance, if you want to interpret "FooBar"
as a global variable, you can do globals()["FooBar"].some_method()
.
The situation is the same for functions, since functions in Python are just objects like any other. If you have a string "foo_bar"
that you think refers to a function named foo_bar
in the global namespace, you can do globals()["foo_bar"]()
to try to call it.
For methods, the situation is basically the same, except that for methods you always know what namespace you're trying to look up the method name in: it's the namespace of the object you're trying to call the method on. For this you use the getattr
function. If you have a string "method_name"
and want to call the method of that name on FooBar
, you do getattr(FooBar, "method_name")()
.
The getattr
approach can also be used to look up global names in another module's namespace. If you think function_name
refers to a function in another module's global namespace, you can do getattr(other_module, function_name)
.
Here are some examples:
def some_function():
print "I am a function!"
class SomeClass(object):
def some_method(self):
print "I am a method!"
function_name = "some_function"
class_name = "SomeClass"
method_name = "some_method"
some_function() # call the function
globals()[function_name]() # call the function
getattr(some_module, function_name)() # if the function was in another module
SomeClass() # make an instance of the class
globals()[class_name]() # make an instance of the class
getattr(some_module, class_name)() # if the class was in another module
instance = SomeClass()
instance.some_method() # call the method
getattr(instance, method_name)() # call the method
In short, there is no ambiguity, because Python does not allow you to use the same syntax to try to do things with objects and with strings referring to objects. Trying to do something like "obj".method()
directly is unambiguous in Python: "obj"
is a string, so it can only mean you are trying to call the method on the string itself. There is no attempt to implicitly "decode" the string to see if it happens to contain a variable name. Also, there is no conceptual difference between the operations for looking up a class, function, or method, because those are all first-class objects in Python. The procedure is always the same: first, get the namespace where you want to look up the name; then, look it up. Both steps have to be explicit.
It's also worth noting that using this sort of string-based lookup in globals()
is usually considered hackish in Python. Use of getattr
and the like is considered okay only when you're dealing with a highly dynamic data structure (e.g., reading user data from some kind of self-documenting file that tells you what its own fields are called) or some kind of metaprogramming (e.g., a plugin framework). Using something like globals()["some_class"]()
for such simple cases would be considered very unpythonic.
In a nutshell: the designers of these other languages (and all the others that I know of, for that purpose) didn't create this ambiguity, that a string can be interpreted as an entity name, in the first place. Such a conversion is always explicit.
So, there's nothing to disambiguate from.
Java and other static languages offer tools for reflection through the use of runtime type information. You need to reintroduce the type information.
class SomeClass {
int foo(int x) {
return x * 2;
}
public static void reflectionExample()
throws ReflectiveOperationException {
String someMethodName = "foo";
SomeClass obj = new SomeClass();
// You must select the method by both method name and the signature.
// This is because Java supports overloading.
Method method = SomeClass.class.getMethod(
someMethodName, Integer.TYPE
);
// You must cast the object back to the right return type.
// Java will automatically 'box' and 'unbox' the values.
int result = (Integer) method.invoke(obj, 3);
assert result == 6;
}
}
本文标签:
版权声明:本文标题:javascript - How do languages whose variables don't have sigils deal with dynamic dispatchinvocation? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742343550a2457068.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论