alma icon indicating copy to clipboard operation
alma copied to clipboard

Implement Smalltalk-style method chaining syntax

Open masak opened this issue 7 years ago • 7 comments

Courtesy of @arnsholt. Slightly made-up but complete example:

my employee = {
    name: "Frank",
    age: 44,
    role: "Senior Janitor",
    printName() {
        say(employee.name);
    },
    printAge() {
        say(employee.age);
    },
    printRole() {
        say(employee.role);
    }
};

employee
    ..printName()
    ..printAge()
    ..printRole()
;

That is, even though those printMumble methods don't explicitly return the employee object (or some this equivalent), the .. call syntax allows them to be used as if they did.

Here's a possible implementation:

macro postfix:<..>(object_ast, identifier_ast, parameterlist_ast)
    is parsed(/ ".." <identifier> "(" <parameterlist> ")" / {

    return quasi {
        {{{object_ast}}}.{{{Q::Identifier @ identifier_ast}}({{{Q::ParameterList @ parameterlist_ast}}});
        {{{object_ast}}};
    };
}

masak avatar Feb 02 '17 12:02 masak

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?

arnsholt avatar Feb 02 '17 12:02 arnsholt

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.

arnsholt avatar Feb 02 '17 12:02 arnsholt

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;
};

masak avatar Feb 02 '17 13:02 masak

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.

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.

masak avatar Feb 02 '17 13:02 masak

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

vendethiel avatar Feb 02 '17 13:02 vendethiel

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).

arnsholt avatar Feb 02 '17 13:02 arnsholt

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:

masak avatar May 27 '17 12:05 masak