How Do Callable Fields Work?
In other words, how do we distinguish between a method that takes an argument, and a method that doesn't take an argument but returns a callable object?
The scenario is this:
class Foo this() this items = Array of(123) bar() items(123) end
The body of bar
method could be interpreted to mean two different things:
- Invoke the
items
method, passing in123
. - Look up the field
items
(which is done using a getter method that takes no argument) and then callcall
on it.
The second is the intended interpretation in this case. Unfortunately, a
strictly Io-style syntax cannot support that. (Io doesn't have callables. You
always invoke a named method. Getting an item from a list is list at(123)
.)
The way Python and C# handle this is with properties. A property is not a method, and the distinction is known at runtime. This means we can disambiguate the two scenarios above like this:
look up the member "items" if it's a property invoke the property (with no argument, of course) send a "call" message to the result, passing in the argument (123 here) else invoke the method end
Before, we tried to treat all zero-argument methods implicitly as properties. Unfortunately, we'll have to make an interpreter-visible distinction so that it can behave differently depending on whether or not a member is a property.
While having explicit support for properties feels a bit gross, it does make some things more consistent. Before this, implicitly calling a callable did work if it was in a local variable or a dynamic (not declared and hence wrapped in a getter) field. That's because the interpreter knew it was looking at a local variable or field and therefore it couldn't be a method invocation, so it would implicitly add the call. Now it can apply the same logic to declared fields and other getters.
On the user side, there are two questions:
- How do I declare a property?
- Which things should be zero-argument methods, and which should be properties?
Scala's answer to 2 is that functions with side-effects should have an explicit
()
and others should not. Io assumes that all zero-argument functions should
omit the ()
. I lean towards that simply because it's shorter. Magpie is
different enough that user's shouldn't expect a ()
-less method to always be
"field-like".
So the answer to 2 is "all zero-argument methods should be properties". Which lets us neatly answer 1 by automatically creating a property if the function has no named parameters.
This lets us come full-circle and actually eliminate properties as something tangible the user needs to think about. Instead, we get back to our original scenario. The way we disambiguate it is:
look up the method "items" if it has no named parameters invoke it with no argument send a "call" message to the result, passing in the argument (123 here) else invoke the method with the argument end