Closed tyomitch closed 3 years ago
You want an indexing pattern :)
(defstruct Point row col)
(let point (Point (row 1) (col 2))
(let [row col] point)
(prn row col) ; prints 1 2
Sorry for the confusion - the pattern-matching chapter in the reference manual could do with a rewrite, if I'm being honest!
May I argue that this syntax is out of line with most other patterns?
After (let (x (y z) "any literal" ..rest) foo)
, the value (x (y z) "any literal" ..rest)
equals foo
, but after (let [row col] point)
, the value [row col]
is nothing like foo
. This is why using #((row row) (col col))
for the pattern felt intuitive.
You're quite right - there are two metaphors that could have been used here. I chose "indexing patterns" over "table-destructuring patterns" for a few reasons:
Unlike many languages (JavaScript, Lua), we have a syntactic distinction between constant table literals #(...)
and the dynamic table constructor (tab ...)
. This could lead to confusion.
The syntax for table literals is very inflexible. It's strictly an unordered list of literal key-value pairs. Indexing patterns are compatible with a richer syntax. For example, they permit predicates to be applied to each sub-pattern:
(let [x : int?, y : int?] pt)
Because table literals are unordered, the execution and printing of table-literal patterns would occur in an unpredictable order.
It would be difficult to improve the syntax for table literals to make them more ergonomic in patterns, because they need to evaluate to a table. We could make #(row col)
syntax sugar for #((row row) (col col))
, but that seems inelegant.
We need the ability to pattern-match on "anything which can be indexed with a symbol" (tables, objects, classes, rdata), so "indexing patterns" felt like the more truthful abstraction.
I agree that the lack of symmetry is frustrating.
Even better if
(let #(row col) point)
could be supported as shorthand for the above.