It's not clear to me why Elm uses `let`, instead of simply scoping definitions to the expression below them.
With `let`:
foo =
let
a = 1
b = 2
in
a + b
Scoping definitions to the expression below them:
I understand that each function must contain a single expression. In Elm, although they contain expressions, definitions are not expressions.
Visualized:
foo =
a + 2 <- EXPRESSION
foo =
a = 1 <- DEFINITION SCOPED TO THE a + 2 EXPRESSION
a + 2
Another way to demonstrate scope is:
would become (parenthesis to demonstrate scope):
It seems to me that `let` and `in` are unnecessary and verbose. Put another way, I think few people would agree that requiring a keyword before variable assignment `set a = 1` would be a good idea. The `=` makes the intent explicit. Likewise, indentation—or parenthesis—could make scopes explicit, and `let` and `in` unnecessary.
Some have argued that without `let`, we could not have arbitrarily nested scopes. I don't have significant experience with Elm, but I would guess that nesting `let`s today is pretty big code smell. Instead of nesting `let`s to reuse variable names, developers should either pick more descriptive variable names, or abstract into a function.
This could—of course—apply anywhere an expression is expected:
True ->
x = 0
y = 0
(x, y)
...
@rtfeldman on the Slack pointed out that this syntax is more diff friendly:
if I write a view function like
view model =
div []
[ ... lots of other stuff ]
and then I want to introduce a nested constant like so:
view model =
let
foo = ...
in
div []
[ ... lots of other stuff ]
the fact that I indented the final expression makes the VCS diff explode
this happens to me all the time, and it's pretty annoying
with [this] idea it wouldn't happen anymore
Lastly, here's elm-todomvc with scoped definitions, courtesy of @rtfeldman again: