dark
dark copied to clipboard
Add tuple `let` destructuring
We want to support code like: let (myString, myInt) = ("str", 6), where the user can destructure the tuple into values on the right-hand-side.
This is a prototype of an interaction model for this:
'let ' =>
let |___ = ___
( =>
let (|) = ___
myString =>
let (myString|) = ___
, =>
let (myString, |___) = ___
'myInt) = ' =>
let (myString, myInt) = |___
( =>
let (myString, myInt) = (|)
'"str", ' =>
let (myString, myInt) = ("str", |___)
5) =>
let (myString, myInt) = ("str", 5)|
I've been thinking through how this will look, and suspect @pbiggar you've had some ideas around this.
here is the current definition for how let works.
| ELet of id * string * Expr * Expr
The main question I'm thinking through is:
should let (a, b) = (1, 2) be powered by the existing Pattern structure, or with a new structure?
A 'new structure' could look like this:
type LetPattern =
| Simple of string
| Constructor of ...
| Tuple of Expr * Expr * List<Expr>
| ...
...
| ELet of id * LetPattern * Expr * Expr
...
with the current Pattern would be renamed to MatchPattern, and LetPattern.
Looking at prior art, F#'s docs seem to imply that there's only one "Pattern" concept, and splitting "let patterns" from "match patterns" isn't done.
Using the existing Pattern structure as is could look just like:
| ELet of id * Pattern * Expr * Expr
, with existing Dark code migrated (?) to just use a Variable pattern.
Many of F#'s Patterns are non-exhaustive (e.g. a :: b on a list), and these result in compile-time errors there.
Ideally, I think, we avoid that, favoring the inability to represent illegal ASTs, but I'm a bit torn - introducing a new concept may be overkill here?
WDYT?
I have a vague intuition that re-using Pattern would be easier technically, but could see that being entirely wrong once the work starts going. So may or may not be a useful metric. I'm leaning towards adding a new type of pattern (LetPattern), so I can start with just a Simple case and expand cleanly from there.
A (bad?) alternative to all of this would be to not worry about other use cases, and define an ELetTuple alongside our ELet.
I would lean heavily towards the LetPattern idea. Agreed we want to avoid "compile-time" errors, and it seems error prone to have a sort of half pattern (we actually have the same issue with pipes, and it's caused nothing but problems)
Do you agree that a rename of Pattern to MatchPattern is appropriate?
If so,
- should existing
Patterncases be renamed fromPStringtoMPString? - and
LetPatterncases then taking the form (let patterns then taking the formLPVariable,LPTuple, etc.)? - or maybe this is time to remove our pattern of these prefixes - I believe we've noted that intention elsewhere?
One useful thing about the prefix pattern is that finding 'usages of the string pattern' is much easier with
PString, over simply usingString, so I'm a bit hesitant there.
Sounds good. I'm happy to keep the prefixes, agreed it's kinda useful.
A more detailed spec is being worked on here: https://docs.google.com/document/d/10mr811ONuUSm6Q1egfv9dGtpN7QdHwhM96uyW8w0140/edit#
Done