Conversation
|
@yminsky, you wrote in comments to #278:
Easy-peasy, with this proposal: do
if <cond> then
<body>
else if <cond> then
<body>
else if <cond> then
<body>
else
<body>
done |
|
Given that there haven't been any comments at all on this issue, for more than four months, I think a reasonable judgement is to deem that it does not carry sufficient support for merging at present. As such I am going to close it. I have made a couple of people who have thought about the if-then-else issue aware of the proposal in case they wish to come back to it later. |
|
I upvoted this proposal a while ago; I thought it was an obvious syntax simplification. Now I think it hasn't been 'marketed' enough to a broad section of OCamlers. I think it needs more publicity; to that end I suggest we post it with a short description to the usual OCaml fora--Reddit, Discourse, caml-list, maybe the ReasonML Discord. If no one beats me to it, I will start up this 'publicity campaign' in a few hours when I get home. |
|
Did you? |
|
Thanks a lot for this, @yawaramin. Very heart-warming. :-) I'd be happy to get any feedback on this proposal, even the most negative one. Silent treatment hits hard. :-( |
|
The problem with this proposal IMO is that to be worthwhile, it should simplify the syntax, and to do that, it needs to not be backwards compatible. There are 2 basic approaches to solve OCaml's leaky
Adding |
This patch extends the parser with a new kind of expression --
do..done-- which is syntactic sugar for((..): unit).The initiative is largely inspired by PR #278.
Rationale
OCaml's syntax includes a couple of curious constructs:
What makes these constructs curious is the common part of the syntax -- an expression enclosed between
doanddone. This expression is special -- it's computed for effects, and has always typeunit. The constraint is enforced by the type-checker, protecting the user from possible coding mistakes, as expressions of any other type make no sense as the body of the loop. And so, when we see an expression insidedo..done, we immediately know that this expression is, by intent of the author, computed for its side effects.Now, there are more kinds of expressions in OCaml, beside loops and loop bodies, that could be computed for effects. The simplest example is function call, which can have any type, including
unit. But what if we want to suggest to the reader the intention of calling the function merely for its effects, not its value? In other words, how can we say that expression has typeunitsans resorting to the clumsy((..): unit)pattern?One option is to abuse the semicolon operator, the first (left-hand) argument of which is assumed by the compiler to have type
unit(otherwise, a warning is issued). Unfortunately, the right-hand argument can have any type, so I commonly find myself churning out something abominable like this:Or even this (horror!):
But wouldn't the following be nicer and more intuitively suggestive?
And this is what my patch adds -- a new kind of expression wrapper, similar to parentheses and
begin..end, which also checks that the enclosed expression has typeunit. As simple as that.I think that this change is good, because, on one hand, it doesn't break backward compatibility (in comparison to proposals like #715); on the other hand, it doesn't really make the language more complex. On the contrary: it's akin to simplification, as the feature was already there, just bolted to
whileandfor, in an ad hoc way. And now it is generic and universal, applicable in a wide range of situations.For example, PR #278 explored the idea of adding a specialized variant of the
ifconstruct, the purpose of which was to help the user avoid common pitfalls of a single-branchif. Withdo..done, we don't need a separate construct:Even if universal
do..doneis a bit more verbose than "imperative if", in this particular context, I still find it superior, because, in my book, it is simple and composable features that comprise good design in programming languages.Implementation details
Currently,
do..doneis syntactic sugar that's resolved in the parser, as I think it'd be overkill to extend the AST (which would only add noise).A minor LR conflict had to be solved with a %nonassoc constraint.
As a positive side effect of unifying
do..doneinside the parser, loop bodies can now be empty (which equals to()) -- this fixes the old nuisance of being unable to comment out contents ofdo..done(e.g. for debugging purposes). Also, it is now possible to attach attributes solely to thedo..donepart of the loop (as it is possible with the newdo..doneexpression).An illustration of these changes (correct piece of code):
Expectations
I know this is going to be controversial and opinionated, so I'm not spending efforts on writing a proper test or updating the manual. Not yet. The implementation is a working proof-of-concept that should facilitate discussion and criticism. Please question my reasoning behind this design.