Open jneem opened 2 months ago
If you have questions about how GHC compile “binding groups” as they're called there, ask me again at the end of the week. I've spent a lot of time in this code this year (right now I'm drowning in emails, so not the best time).
We've had some discussion of recursive destructuring in #2010 and #1989. Opening this issue for more discussion.
Nickel currently allows recursive bindings in let blocks (opt-in, using the
rec
keyword), but only if all bindings are simple. As soon as destructuring is used, recursive bindings are forbidden. Possible use-cases include recursive references for default arguments:or referring to other bindings in let blocks:
The main difficulty with this feature is that we'd need to figure out the semantics of recursive destructuring in general, and it's possible to come up with pretty complicated examples.
Haskell
I spent a little while exploring how Haskell does recursive let bindings. Their let blocks are recursive and lazy by default, and it seems like they desugar the destructuring assignments into one gigantic
letrec
block. For example, runningghc -ddump-ds-preopt
on an input likegives (after a bit of clean-up, and removing all the error cases in the
case
blocks)I'm not sure how ghc decides to use two letrec blocks instead of one; changing the input around to make it more recursive forces it use only one. I also noticed that ghc likes to produce one tuple binding for each pattern binding in the let block. So rather than recursively desugar
z@(c, d) = ...
likeit first generates a pattern than destructures all the bound variables and returns them as the tuple
(z, c, d)
, and only then does it extract them. Since tuples are lazy, I think these two approaches are equivalent.I'm going to have a look at scala next...