For the pattern matching,
we also need a 'with' method, that return a method handle that
takes a carrier and a value and return a new carrier with the
component value updated.
It is not clear to me why we "need" this. Rather than jumping
right to "Here is the solution", can you instead try to shine some
light on the problem you are trying to solve?
When you have nested record patterns, each of these patterns
contribute to introduce bindings, so when executing the code of the
pattern matching, the code that match a nested pattern needs to add
values into the carrier object. Given that the Carrier API is non
mutable, we need the equivalent of a functional setter, a wither.
I don't think we need to do this.
Recall what nested patterns means: if R(T t) is a record, and Q is a
pattern that is not total, then
x matches R(Q)
means
x matches R(T t) && t matches Q
So if we have
record R(S s) { }
record S(int a, int b) { }
then
case R(S(var a, var b))
operates by matching the target to R, deconstructing with the R
deconstructor, which yields a carrier of shape (S). Then we further
match the first component of this carrier to the S deconstructor, which
yields a carrier of shape (II). No mutation needed.
Note that this unrolling can happen in one of two ways:
- The compiler just unrolls it doing plain vanilla compiler stuff
- A pattern runtime has a nesting combinator that takes a pattern
description for an outer and an inner pattern, which when evaluated with
R and S, yields a carrier of shape (R;S;II), the compiler evaluates this
nesting combinator with condy, and uses that to do the match.
Either way, we don't need to mutate or replace carriers.