-
Notifications
You must be signed in to change notification settings - Fork 98
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Block syntax for let-binding #494
Comments
As noticed by @litchipi, it may be a bit strange that single let-binding aren't recursive by default, and block let-binding are. That is, turning
into
changes a benign shadowing to infinite recursion. I see three possibilities:
I'm in favor of 2. While I understand that one may want recursive records by default, 1. sounds extreme semantically and totally disallows shadowing altogether. I've never seen a language with such a feature. 3 makes things unexpected, as shown by the example. 2 is consistent, makes recursion explicit for let-binding (which is IMHO sensible, as most of the time you don't write recursive let-binding) while keeping it implicit for records, as we decided to do in #216. |
I'm partial to option (2) as well. |
I'm not a fan of the proposed syntax. The parallel with records is non-obvious. It would be if the braces were there, though they are of course redundant. In records, I can have (for good reason) trailing commas. Here is would look like this:
We could add the braces back, so e.g.
This would have the added benefit of making #494 (comment) a non-issue. But now people will wonder why they can't write
(which incidentally would effectively be the So to rewind, what problem are we trying to solve? The linked standardization meeting notes contains nothing in the way of motivation, and neither does #218, which at this point could probably be closed in favour of this issue. Writing a sequence of |
The rationale isn't to be exactly the same as records. We chose to use the Nix syntax, which was otherwise reasonable and in par with other similar features in other languages, but it wouldn't make sense to keep the
For what it worth, I don't find it any worse or uglier than the possibility of writing
I would say that the main motivation is to write mutually recursive definitions indeed. And it doesn't seem to hurt anyway. It gives aesthetics as a bonus for blocks of definitions, and more generally to convey explicitly that the bindings of the block are independent. As opposed to a sequence of A lot of functional languages have it in some form (Nix, Dhall, OCaml, Haskell, Elm, Clojure, and son on). Although for some reason it's not so used in OCaml indeed (maybe because |
So in short, I think it's premature to be adding sugar like this to the language. I move to close this issue and revisit once the language has stabilized and we have more users and use cases. What do you think? I call it sugar because we already have recursive let-bindings and the let-bindings are destructuring. Together those features are enough to deal with mutual recursion:
It's not the most ergonomic thing, but mutually recursive bindings are not that common, and we get to save a little bit of language syntax estate. Nickel is a configuration language. We don't need perfect ergonomics. We do need a small language that is simple and quick to pick up. Haskell is an example where lots of let-binding styles have proliferated. See #218 for an instance of that. Regarding Nix, TBH the
There is a philosophical fork in the road that I don't think we need to resolve now, but I'm pointing out for the future. Making recursion or lack thereof explicit, and independence between bindings explicit, gives more control to the user but at the cost of a bigger language. We have a choice between, allowing the user to state their intent very precisely, or keeping things simple for the user and trying to recover the same metadata through simple pre-evaluation analyses if/when needed. |
It turns out let-bindings are not recursive currently, see #525. This is a change relative to the very early prototypes for Nickel. Nevertheless, even if let-bindings were to remain non-recursive, if we did had |
I wasn't aware of that. When I started, let-binding weren't recursive. Recursive records weren't implemented. When we added them, @eelco made a case for record being recursive by default in in #83 and #216, and so we did, but there was nothing about let-binding. Back to the original issue, I agree that mutual recursive values are rare enough that it is acceptable to use records + destructuring as long as we have a |
Should we apply the same thinking to the addition for OCaml-like function syntax |
@yannham you led me to the conclusion in #525 that actually, recursion is very much an escape hatch (like you said in #83 already). But then mutual recursion should be even rarer. So going back to my earlier question,
... then this makes the case for let-blocks all the weaker. I'm becoming increasingly convinced that we should punt.
I think so, yes. I don't think that syntactic sugar improving anyone's QOL in any significant way. |
I take that back. Function definition is so pervasive that it ought to be really short. However, we could achieve the same differently: by removing the |
We discussed that option in the meeting, but I recall attendees didn't like it for a bunch of reasons. IIRC the general feeling was that it's just three characters that make it explicit from the beginning that we are parsing a function definition and not something else, both for the user and the parser. Otherwise, in Nix destructuring looks like it faces the same problem (when you parse It is still probably doable by parsing something that looks like an application in a syntax that is a superset of the two, and decide validity once we've reached the end, but it may be painful (without even considering the specific parsing lib we currently use). Some languages choose this path, like ReScript, which dropped the We actually discussed doing something like ReScript (we don't have tuple, so it would not be ambiguous) but that looks odd with respect to ML style application. ReScript just did this to be more JavaScript-like, and also changed the function application in consequence. We also discussed using something shorter like Haskell |
I stand corrected, as ReScript does support destructuring for function arguments. |
Do we have a dedicated issue regarding function syntax? Your thoughts above will be more easily found there than in this issue about blocks of let-bindings. |
Function syntax was part of #207 originally, but hasn't been updated after the standardization meeting discussions. Let me do that. |
FWIW I find issue-specific tickets easier to browse, comment on and interrelate than the original omnibus "dojo" tickets - in case you feel like breaking out some of the discussion into separate tickets. ;) |
My 2 cents on that matter, what I originally understood when taking the feature request was to simply create a syntax sugar at the parser level to make
behave exactly the same as
And I think this syntax sugar makes sense.
And I think it would be better to keep this simple syntax for the simplest use case, and complexify the syntax for more complex features (like mutual recursion), that allows simple project configuration to be written in a simple way, and big projects with complex "programming" needs to still have them. |
Well, if this is just syntactic sugar for declaration sequences, and not specifically to handle mutual recursion more cleanly like @yannham said above, then I don't think we yet have enough experience to guide us. Syntactic sugar is easy to add later when we do have more Nickel code in the wild. Harder to remove if we realize it isn't paying for itself in the context of a minimal language like Nickel. |
Given the conclusion of the previous discussion, let's close this for now. |
Is your feature request related to a problem? Please describe.
Block let-binding are nice to express a series of order-independent bindings. See #218 for more context.
Describe the solution you'd like
As decided during the standardization meeting, have let-binding blocks as in Nix, just replacing semicolon
;
by,
for consistency with the rest of the Nickel syntax.Example:
To be consistent, the inner syntax should be exactly the same as for records, just without the braces. Those let-binding would also be mutually recursive, as records.
Describe alternatives you've considered
See #218 for discussion and alternatives.
The text was updated successfully, but these errors were encountered: