Open masak opened 6 years ago
Oh, and I wanted to include in OP how this is another beautiful example of same old expressions, new evaluation semantics. There's a kind of "show, don't tell" going on where just building the structure of the object you want to destructure ends up being (sometimes) clearer and more descriptive than specifying paths for how to get down to the data.
In other words, we're re-using people's prior knowledge of (possibly nested) dictionary and array literals to show which values we're initializing our declared variables with. It "looks right", but the semantics has been all switched out.
Since the variables we introduce belong to the surrounding "parent language", we might even possibly consider this a slang — one that is confusingly similar to object literals in the parent language. (One unusual thing about this slang, if it is one, is that the "holes" taking us back to the parent language have no syntactic markers like {{{ }}}
in quasiquotes or ${ }
in interpolating strings. Instead they are marked "by position".)
Relatedly, it'd be really cool (but above and beyond this issue) if more things than arrays and dictionaries could participate in destructuring. Ideally it should be an open set of "container types" (sets, for example) that could register somehow against a destructuring protocol. Ah, dreams. We're not in want of ambition, that's for sure.
I should probably mention tuples here (with the ( )
syntax) as something I'd want to pattern-match on.
Amusingly, I think
my (foo) = tuple;
would be unambiguous even without the usual (unesthetic) final (foo,)
comma, because in this context parentheses do not indicate grouping, and so that syntax is not already claimed.
Unfortunately, it falls to the language designer to decide whether to accept the (foo)
syntax out of kindness/dwimminess, or to forbid it in the name of consistency.
If we implement #308, we could even think of allowing constructors in destructuring:
my Complex(re, im) = cpx; # assigns re and im
But that'd also be a "stretch goal", not least because there'd have to be a clear path to seeing that the parameters sent into an object correspond in an invertible way to the properties assigned in the object. Maybe it can only work for properties that are auto-assigned from constructor parameters, or something.
The thing that says that variable declarations and parameter declarations should preferably be equally powerful and sort of "the same" feature in a language seems to be referred to as Tennent's Correspondence Principle, surrounded by a miasma of controversy over exactly what it means and how far it extends.
Right now I think the compiler checks at parse-time whether the thing on the left of an assignment is something we can assign to. We still like to do that statically, of course, but interestingly, arrays and dicts become things that we can assign to once one flips on destructuring. So maybe this should be a protocol of some sort, too, where you'd register arrays and dicts to be "honorary lvalues". In fact, maybe that protocol can be the whole destructuring mechanism? You just specify the macro that should take over when an array literal or a dict literal (respectively) is detected on the left side?
I'm not sure I get that last message.
This means
my a = [1, 2, 3];
a = [3, 2, 1];
should actually replace at indices? How do you replace a
's value then?
Would there be a syntax like a[*] = [3,2,1];
?
No, no. Not planning to mess with a normal variable assignment like a = [1, 2, 3]
. Don't worry.
My whole point was that (a) the compiler can't be over-eager when disqualifying things it doesn't recognize as valid assignment targets (like array and dict literals with variables in them), (b) possibly the extension point you'd use from the destructuring-syntax module to register those as new allowed assignment targets, would also be the place where you'd specify (in a macro, most likely) what their semantics should be.
Allowing destructuring in variable declarations and routine parameters might feel like two distinct tasks, but I'd say the would largely use the same underlying logic, so...
Let's make this really concrete. Something like
would compile down to something like
Specifically,
someDict
is allowed to have more keys that we don't capturesomeDict["baz"]
is allowed to have more elements that we don't captureI'm undecided on whether structures having fewer keys/elements ought to fail hard (with an exception) or soft (with
None
, possibly recursively). Leaning towards the former, because if we end up using destructuring also incase
statements, the destructuring needs to somehow signal failure up to thecase
statement, and an exception would be a good way to do that.To me, the absolute measure of success of this as a macro would be if it interoperated well with #199. I think what we're looking at is a "binding instance" abstraction that's perhaps a bit ill-exposed right now in the language. (The closest we have is
Q::Declaration
, but even that's not quite it, since it's also consumed by function statements and the like.)