There are another different between assignment and _let_, a _let_
creates new fresh local variables (binding) while assignment is able
to reuse an existing local variable.
Correct, the more precise analogy is not to _assignment_, but to _local
variable declaration with initialization_ (whose semantics are derived
from assignment.)
In Java, the if statement is used a lot (too much IMO but i don't
think we should fight to change that) so it may make sense to be able
to reuse an existing local variables.
Yes, this has come up before. I agree that there are cases where we
might want this (there's one distinguished case where we almost cannot
avoid this), but in general, I am pretty reluctant to go there -- I
think this is incremental complexity (and encouragement of more
mutability) with not enough commensurate benefit.
## Possible extensions
There are a number of ways we can extend `let` statements to make
it more
useful; these could be added at the same time, or at a later time.
#### What about partial patterns?
There are times when it may be more convenient to use a `let` even
when we know
the pattern is partial. In most cases, we'll still want to
complete abruptly if the
pattern doesn't match, but we may want to control what happens.
For example:
```
let Optional.of(var contents) = optName
else throw new IllegalArgumentException("name is empty");
```
Having an `else` clause allows us to use a partial pattern, which
receives
control if the pattern does not match. The `else` clause could
choose to throw,
but could also choose to `break` or `return` to an enclosing
context, or even
recover by assigning the bindings.
I don't like that because in that case "let pattern else ..." is
equivalent of "if instanceof pattern else ... " with the former being
expression oriented and the later statement oriented.
As i said earlier, i don't think we should fight the fact that Java is
statement oriented by adding expression oriented variations of
existing constructs.
We haven't talked about let expressions yet; this is still a statement.
It's a fair point to say that the above example could be rewritten as an
if-else, and when the else throws unconditionally, we still get the same
scoping. Or that it can be rewritten as
if (!(pattern match))
throw blah
On the other hand, people don't particularly like having to invert the
match like this just to get the scoping they want.
In any case, the real value of the else block is where you want to
continue (and merge the control flow) with default values of the
bindings set in the else clause (next section). Dropping "else" makes
this extremely messy. And once you have else, the rest comes for the ride.
#### What about recovery?
If we're supporting partial patterns, we might want to allow the
`else` clause
to provide defaults for the bindings, rather than throw. We can
make the bindings of the
pattern in the `let` statement be in scope, but definitely
unassigned, in the
`else` clause, which means the `else` clause could initialize them
and continue:
```
let Optional.of(var contents) = optName
else contents = "Unnamed";
```
This allows us to continue, while preserving the invariant that
when the `let`
statement completes normally, all bindings are DA.
It fails if the "then" part or the "else" part need more than one
instruction.
Again, it's statement vs expression.
No, it's still a statement. I don't know where you're getting this
"statement vs expression" thing from?
#### What about guards
If we're supporting partial patterns, we also need to consider the
case where
the pattern matches but we still want to reject the content. This
could of
course be handled by testing and throwing after the `let`
completes, but if we
want to recover via the `else` clause, we might want to handle
this directly.
We've already introduced a means to do this for switch cases -- a
`when` clause
-- and this works equally well in `let`:
```
let Point(var x, var y) = aPoint
when x >= 0 && y >= 0
else { x = y = 0; }
```
It can be re-written using an if instanceof, so i do not think we need
a special syntax
int x, y;
if (!(aPoint instanceof Point(_ASSIGN_ x, _ASSIGN_ y) && x >= 0 && y
>= 0)) {
x = 0;
y = 0;
}
All let statements can be rewritten as instanceof. Are you arguing that
the whole idea is silly?
"Let ... in" is useful but i don't think it's related to the current
proposal, for me it's orthogonal. We can introduce "let ... in"
independently to the pattern assignment idea,
and if the pattern assignment is already in the language, then "let
... in" will support it.
Yes and no. You are correct that we could do either or both
independently. But it's not my job just to design each feature in a
locally optimal way; it's my job to ensure that the features we design
will fit together when they meet up in the future. The fact that the
construct generalizes in this way is an important part of the design
even if we don't plan to do this part now.