Open masak opened 7 years ago
Looks good to me. Only quibble I have is on the macro implementation: if object_ast is something that performs a destructive operation (say, getting the next item off a queue or something), wouldn't the macro end up performing many of them, rather than just one?
Oh, another thing. In ST invocant m1; m2; m3
has the return value of m3
, not the invocant itself which is what we get with this macro.
Only quibble I have is on the macro implementation: if object_ast is something that performs a destructive operation (say, getting the next item off a queue or something), wouldn't the macro end up performing many of them, rather than just one?
Yes, you're right. More generally, with quasis in macros, whenever one is using the same value multiple times, one always wants to capture, with no exception that I've found so far. (Later edit: This is now discussed in #234 and #479.)
Ok, here's a new implementation that does that:
return quasi {
my object = {{{object_ast}}};
object.{{{Q::Identifier @ identifier_ast}}({{{Q::ParameterList @ parameterlist_ast}}});
object;
};
Oh, another thing. In ST
invocant m1; m2; m3
has the return value ofm3
, not the invocant itself which is what we get with this macro.
Hm, you're right. The last method shouldn't return the invocant, because that's clearly useless, just like it's useful in earlier places in the chain.
Let's see... postfixes nest like this: ((employee..printName())..printAge())..printRole()
. The corresponding macros get called innermost-first, that is, from left to right. Which means that what a postfix:<..>
macro should do is not codegen its own method call, but rather tweak the previous method call, if any, to return the invocant.
Under that scheme, ..printRole()
which is called last ends up tweaking ..printAge()
, but nothing ends up tweaking ..printRole()
so it ends up returning what it usually does (None
).
Yes, this scheme is more ambitious, but I like it better, I think.
Hm, you're right. The last method shouldn't return the invocant, because that's clearly useless, just like it's useful in earlier places in the chain.
Yup. In ST, if that's actually what you want, you can just call the identity method, i.e. invocant m1; m2; yourself
Yeah, for maximum manipulexity you want to be able to choose between the invocant and the returned value. In ST it's solved by the chaining operator returning the last return value and having a method that just returns the invocant (called yourself
actually, not self
. self
is the reserved word containing the invocant of a method).
Smalltalk's syntax is different than the one I propose in OP. It uses semicolons and periods.
I think I might have gotten the ..
syntax from LiveScript cascades. Who knows how long ago I read that. :smile:
String s = (new StringBuffer()
..add('Jenny ')
..add('I ')
..add('got ')
..add('your ')
..add('number')
).toString();
Additionally, as this example shows, the entire cascade does return the original invocant (in Dart).
I was going to write something about how the postfix ..
syntax clearly clashes with the infix ..
range syntax. (According to the current/growing spec, range ..
is a built-in syntax... although this might change before the dust settles; it's clearly a candidate for throwing out into a module.) But then I thought, everything's fair if you predeclare, and the world is clearly large enough for two different features with conflicting syntax. Something similar has been discussed earlier in the issues with postfix ++
for destructive increment vs infix ++
for nondestructive array concatenation.
Courtesy of @arnsholt. Slightly made-up but complete example:
That is, even though those
printMumble
methods don't explicitly return theemployee
object (or somethis
equivalent), the..
call syntax allows them to be used as if they did.Here's a possible implementation: