[External] : Re: Record patterns (and beyond): exceptions
Brian Goetz
brian.goetz at oracle.com
Thu Feb 17 14:43:08 UTC 2022
>
> As we look ahead to record patterns, there is a new kind of
> remainder: the "spine" of nested record patterns. This includes
> things like Box(null), Box(novel), Box(Bag(null)),
> Box(Mapping(null, novel)), etc. It should be clear that there is
> no clean extrapolation from what we currently do, to what we
> should do in these cases. But that's OK; both of the existing
> remainder-rejection cases derive from "what does the context
> think" -- switch hates null (so, NPE), and enum switches are a
> thing (which makes ICCE on an enum switch reasonable.) But in the
> general case, we'll want some sort of MatchRemainderException.
>
>
> Nope, it can not be a runtime exception because people will write code
> to catch it and we will have a boat load of subtle bugs because
> exception are side effect so you can see in which order the
> de-constructors or the pattern methods are called. ICCE is fine.
But this clearly does not fall into ICCE. ICCE means, basically, "your
classpath is borked"; that things that were known to be true at compile
time are not true at runtime. (Inconsistent separate compilation is the
most common cause.) But Box(Bag(null)) is not an artifact of
inconsistent separate compilation.
In any case, I am not getting your point about "but people can catch
it." So what? People can catch OOME too, and try to parse the output
of toString() when we tell them not to. But that's no reason to make
all exceptions "OpaqueError". So what is your point here?
>
>
> Note that throwing an exception from remainder is delayed until
> the last possible moment. We could have:
>
> case Box(Bag(var x)): ...
> case Box(var x) when x == null: ...
>
> and the reasonable treatment is to treat Box(Bag(var x)) as not
> matching Box(null), even though it is exhuastive on Box<Bag<?>>),
> and therefore fall into the second case on Box(null). Only when we
> reach the end of the switch, and we haven't matched any cases, do
> we throw MatchRemainderException.
>
>
> I really dislike that idea, it will be a burden in the future each
> time we want to change the implementation.
> I would like the semantics to make no promise about when the error
> will be thrown, the semantics should not be defined if a
> deconstructors/pattern method does a side effect, the same way the
> stream semantics is not defined if the lambda taken as parameter of
> Stream.map() does a side effect.
> I think the parallel between the pattern matching and a stream in term
> of execution semantics is important here. From the outside, those
> things are monads, they should work the same way.
>
>
I think this stems from the same misunderstanding you have about the
boundary between the pattern semantics and the construct semantics. I'm
going to test-drive some adjusted language here.
A total pattern is just that -- it matches everything.
Some patterns are considered exhaustive, but not total. A
deconstruction pattern D(E(total)) is one such example; it is exhaustive
on D, but does not match D(null), because matching the nested E(total)
requires invoking a deconstructor in E, and you can't invoke an instance
member on a null receiver. Still, we consider D(E(total)) exhaustive on
D<E>, which means it is enough to satisfy the type checker that you've
covered everything. Remainder is just the gap between exhaustiveness and
totality.
If we have the following switch:
case D(E(Object o)):
caes D(var x) when x == null:
the semantics of D(E(Object)) are that *it matches all non-null D,
except D(null)*. So throwing when we evaluate the case would be
incorrect; switch asks the pattern "do you match", and the pattern says
"no, I do not." And the semantics of switch, then, say "then I will
keep trying the rest of the cases."
So *when* the error is thrown derives from the semantics of the
construct; switch tries matching with each pattern, until it finds a
match or runs out of patterns. When it runs out of patterns is when it
needs to insert a catch-all to deal with remainder (as we do with enum
switch expressions.)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20220217/be7cdb88/attachment.htm>
More information about the amber-spec-experts
mailing list