Any assignment statements that occur within the transformation
    block have the following constraint: If the left-hand side of the
    assignment is an unqualified name, that name must be either (i)
    the name of a local component variable, or (ii) the name of a
    local variable that is declared explicitly in the transformation
    block.


And because there's no way to qualify a local variable from the surrounding scope, reassigning such variables is simply impossible within this block. Right?

That's "no great loss" of course, although I'm missing why the restriction is necessary. The notion of variables that (at least in userspeak) are "in scope for reading but not for writing" seems weird; does it have precedent?

There is some precedent with lambdas/inner classes, where you can only access effectively final locals, though that wasn't really in our mind when we crafted this restriction.

The motivation for the restriction is twofold:

 - This is a functional idiom (think "state monad"), side-effecting the environment would be weird.  (Of course, you could launder side-effects through any of the usual means, including probably using a qualified acess (Foo.x = 3; this.y = 4), but you shouldn't.)

 - We intend to extend this to classes in the future.  This idiom is basically "take apart with deconstructor + transform state + reconstruct with constructor".  There's an overload selection problem buried in there, and the names of variables involved in the transform may be important inputs to that selection decision.

We're not sure that we'll want to do overload selection nominally in this manner, but we're not ready to say "we will never be able to"; having this restriction in place keeps the flexibility to do so.


    The transformation block need only express the parts of the state
    being modified. If the transformation block is empty then the
    result of the derived instance creation expression is a copy of
    the value of the origin expression (the expression on the
    left-hand side).


This could be interpreted as saying that in this case the record's constructor isn't even run, which I suspect isn't what you mean, and which could make a difference (if best practices aren't being followed). Do you need to say anything at all about this case?

I interpret this question as "is the result guaranteed to have a distinct identity from the origin expression."  (Obviously, for value types, the answer is "huh, what's identity?")  But we probably do want to say that the constructor is always invoked to produce the result, even if the block is empty; that "copy" is more of an analogy.


Overall, there have been several references here to the record class R, but I would think it's the record /type/ we really need to talk about. That type post-substitution is what determines these variable types, no?

Yes.  It is probably a little more complicated than "the static type of the origin expression is the static type of the with expression", because of, as you say, wildcards (and other weirdo types).  You probably have to do an upward projection on the type of the origin expression, or something like that.

    The use of a derived instance creation expression:
    can be thought of a switch expression:


imho this would be useful to state earlier!

What goes wrong if we think of this feature as /exactly/ desugaring to that switch code?

The set of statements permissible in the two contexts is probably slightly different; you can do a `yield <expression>` in the RHS of a switch case, but not in a reconstruction block.  Probably other subtle reasons too.  Our experience with "specify by syntactic expansion" frequently runs into annoying roadblocks because of things that are expressible in one context but not in the desugared context, or vice versa.

Reply via email to