masak / alma

ALgoloid with MAcros -- a language with Algol-family syntax where macros take center stage
Artistic License 2.0
138 stars 15 forks source link

Implement named arguments, Python-style #299

Open masak opened 6 years ago

masak commented 6 years ago

That is, if you have a function

sub foo(x, y, z) {
    say(`The ${x} to the ${y} to the ${z}`);
}

You could call it as (for example)

foo("x", y="y", z="z");

Python imposes the restriction that a positional argument never come after a named one. I suggest we do the same.

I'm slightly open to the objection that = is not the right syntax there, as it's not Perl-y enough. Perl 6 uses = for parameter defaults, but uses y => "y" or :y("y") for named arguments. I suspect if you'd ask TimToady he'd say that it's "because it's not an assignment". Maybe in 007 y: "y" (like in object properties) would be the closest analogue. But I remain on the fence. (Update: Decided on colon; see below.)

Shout-out to #112 and to the fact that I still haven't really started thinking about how a pluggable signature binder would need to work. 🎩 (Edit: Have now, in #329.)

masak commented 6 years ago

I hadn't realized it before, but you can send keyword arguments to dict in Python, meaning that you can construct dicts using the constructor instead of using the dedicated {} syntax.

The {} syntax still has merit, of course. I don't think we should remove it. But the fact that you can initialize dictionaries that way is in some sense a weak argument against overloading block and dictionary syntax. (On the gripping hand, it's hard to imagine something like dict comprehensions without the dedicated syntax.)

If we decide to allow Dict instances to be created nonempty via the constructor, it would be nice to have a story for how we declare named slurpy parameters.

masak commented 6 years ago

I'm slightly open to the objection that = is not the right syntax there, as it's not Perl-y enough. Perl 6 uses = for parameter defaults, but uses y => "y" or :y("y") for named arguments. I suspect if you'd ask TimToady he'd say that it's "because it's not an assignment". Maybe in 007 y: "y" (like in object properties) would be the closest analogue. But I remain on the fence.

I'm no longer on the fence; we should do it with the colon.

Why? Becuase = already means something in that position: assignment. The way 007 looks today, it'd be confusing to introduce a new conflicting syntax there. Colon, on the other hand, is free (and strangely consistent with the dictionary key/value separator).

It's possible for #279 to come down in such a way that the direct ambiguity goes away, but (a) that is not at all certain, and (b) even if it did, there would still be some sort of mental ambiguity left.

masak commented 5 years ago

(And #279 came down in such a way that it'd be confusing to use = for that.)

masak commented 5 years ago

I already started implementing this in a branch, but I hit an interesting snag, which I'll quickly describe:

Macro calls are calls. As such, they have argument lists. It's in the argument lists we are adding the new named argument syntax. In other words, macro invocations get named arguments too. Fine.

But one big way in which function invocation and macro invocation differ, is that the former takes evaluated arguments (that is, their Qtrees have been interpreted by the runtime), but the latter just wants the dry Qtrees as-is.

The solution is probably (and I don't know if I realized this back then) to separate the "evaluate arguments" logic out to Q.Postfix.Call, where it gets to eval for functions, but not for macros since they will have already been intercepted by the compiler.

Need to try this. A bunch of language changes around constructors are blocking on this feature.