The problem is that if we run that code with the new version of Point (the one
with 3 components),
newPoint.z is not equals to point.z but to 0, so once there is a 'with'
somewhere, there is no backward compatibility anymore.
Yes, in that case, we experience something like "decapitation" of
On 6/12/2022 12:21 PM, fo...@univ-mlv.fr wrote:
*From: *"Brian Goetz"
*To: *"Remi Forax"
*Cc: *"amber-spec-experts"
*Sent: *Saturday, June 11, 2022 8:16:26 PM
*Su
information
from one of the enclosing `contents` variables.
On 6/10/2022 11:25 AM, Brian Goetz wrote:
About the declaration of local variables, in Java, there is no
hiding/shadowing between local variables, so a code like this is
rejected ? Or do we introduce a special rule for the hiding of
implicit
erate the 2^n combinations.)
On 6/10/2022 8:44 AM, Brian Goetz wrote:
In
https://github.com/openjdk/amber-docs/blob/master/eg-drafts/reconstruction-records-and-classes.md
we explore a generalized mechanism for `with` expressions, such as:
Point shadowPos = shape.position() with { x
The block is also special because there is an implicit return at the
end ? so i believe "return" should be disallowed inside the block, right ?
That's a good question; we can go in multiple directions with this. One
would be to simply interpret "return" as "return from the enclosing
method
In
https://github.com/openjdk/amber-docs/blob/master/eg-drafts/reconstruction-records-and-classes.md
we explore a generalized mechanism for `with` expressions, such as:
Point shadowPos = shape.position() with { x = 0 }
The document evaluates a general mechanism involving matched pairs of
I wonder how we would try to explain 'case Integer x, null, Double x'... (Does
'x' get bound to 'null'? How?)
Your suggestion to always put the null first probably helps.
Oh, I guess I missed your point here, thinking that P and Q were
constants.
Your comment implies that the two rules that restrict usage of
patterns—can't fall through past one, and can't combine one (via ',')
with most other labels—could be relaxed slightly in the case of
patterns that hav
In this framing, the restrictions about sets of elements in a single label
don't apply, because we're talking about two different labels. But we have
rules to prevent various abuses. Examples:
case 23: case Pattern: // illegal before and now, due to fallthrough Pattern
rule
Ideally, the
For me, there is a difference between a binding and a local variable,
is that bindings are the one declared inside a pattern and a local
variables is how a binding is transformed to be usable inside the boby
if the pattern match.
Our first (wrong) inclination was to treat pattern variables a
ot;annotation merging" problem, which is why annotations are
not inherited in the first place.)
I don't have an answer here, but I'm going to think about the various
issues and try to capture them in more detail before proposing an answer.
On 5/31/2022 10:49 AM, Brian Goetz wrote:
Erm... I actually thought that it was your idea to allow the 'final'
modifier on patterns. This change was introduced in Java 16 (when
patterns for instanceof were finalized). Here's the initial e-mail
from you (item 2):
https://mail.openjdk.java.net/pipermail/amber-spec-experts/2020-August/00
The problem is not at callee site, as you said you have deconstructor binding
like you have constructor parameter, the problem is at callsite, when you have
a Type Pattern, a type pattern does not declare a type that can be used at
compile time but a class that is used at runtime (to do the ins
The problem is that what you propose is a leaky abstraction, because
pattern matching works on classes and not on types, so it's not a
reverse link.
("Leaky abstraction" is sort of an inflammatory term.)
What I think you're getting at is that some objects will have state that
you can "put
Thank you so much for catching this.
On 5/30/2022 12:36 PM, Tagir Valeev wrote:
Hello!
I'm reading the spec draft near "14.30.1 Kinds of Patterns" [1] and I
wonder how the variable declared as named record pattern differs from
the variable declared in the type test pattern
Assuming record Poin
First, i've overlook the importance of the record pattern as a check
of the shape of the data.
Then if we say that data are more important than code and that the aim
of the pattern matching is to detect changes of the shapes of the data,
it changes the usefulness of some features/patterns.
Indeed, this is a big part of the motivation. And it's not just pattern
matching; its the combination of records (representing data as data),
sealed classes (the other half of algebraic data types, enabling richer
data-as-data descriptions), and pattern matching (ad-hoc polymorphism,
great for
- *Refined type checking for GADTs. *Given a hierarchy like:
sealed interface Node { }
record IntNode(int i) implements Node { }
record FloatNode(float f) implements Node { }
we currently cannot type-check programs like:
Node twice(Node n) {
return switch (n) {
AM, Remi Forax wrote:
----
*From: *"Brian Goetz"
*To: *"amber-spec-experts"
*Sent: *Wednesday, May 18, 2022 9:18:01 PM
*Subject: *Pattern matching: next steps after JEP 405
JEP 405 has been proposed to target
I'm sorry, I have no idea what argument you are trying to make. Start
from the beginning.
On 5/20/2022 1:27 AM, fo...@univ-mlv.fr wrote:
*From: *"Brian Goetz"
*To: *"Remi Forax"
Or maybe you mean something else; if so, please share!
The current proposal is more about matching and extracting the first
arguments
It is really about matching *the whole array*. Pattern matching is
about destructuring. Arrays are part of the language. They have
structure. We
When you have a type pattern X in a middle of a pattern *and* you have
conversions, then there is an ambiguity,
does instanceof Box(X x) means
Box(var v) && v instanceof X x
or
Box(var v) && X x = (X) v;
This is not an ambiguity in the language, it is confusion on the part of
the reade
We may want to extract sub-parts of the array / collections by
example, and i would prefer to have the same semantics and a similar
syntax.
This is pretty vague, so I'll have to guess about what you might mean.
Maybe you mean: "I want to match a list if it contains the a subsequence
that ma
Inference is also something we will need for pattern assignment
Box<>(var s) = box;
Yes, it would work the same in all pattern contexts -- instanceof as
well. Every pattern context has a match target whose static type is known.
- *Array patterns. * The semantics of array patte
JEP 405 has been proposed to target for 19. But, it has some loose ends
that I'd like to refine before it eventually becomes a final feature.
These include:
- *Inference for record patterns. *Right now, we make you spell out
the type parameters for a generic record pattern, such as:
c
The accessor throws an exception and with the semantics you propose it
will happily be wrapped by as many MatchExceptions as possible.
But this class is already so deeply questionable, because accessors
should not throw exceptions, that we've lost the game before we
started. Whether the e
Let's imagine that dtor D throws. The wrapping happens when a
dtor/accessor is invoked _implicitly_ as a result of evaluating a
pattern match. In both cases, we will wrap the thrown exception
and throw MatchException. In this way, both instanceof and switch
are "clients of
We’ve already asked one of the questions on side effects (though not sure we
agreed on the answer): what if the dtor throws? The working story is that the
exception is wrapped in a MatchException. (I know you don’t like this, but
let’s not rehash the same arguments.)
Wrapping exceptions into
"case Foo fooButNull" is equivalent to "case null" but with a binding
typed as Foo that's why i ask if it should even compile,
the compiler should ask for an explicit "case null".
It may be "equivalent" in our eyes, but the language doesn't currently
incorporate nullity into the type system.
With the currently specified semantics, the second pattern is dead,
because switches will only match null at the top level with a case
null. This was an accommodation to clarify that that the null-hostility
of switch is a property of switch, not patterns, and make it more clear
when switch wil
Yes, this is something we have to get “on the record”.
Record patterns are a special case of deconstruction patterns; in general, we
will invoke the deconstructor (which is some sort of imperative code) as part
of the match, which may have side-effects or throw exceptions. With records,
we g
This is correct; I agree this is “not quite where we want to be yet”, but the
path to get there is not obvious, which is why we haven’t proposed anything
more than we have.
At some level (though this isn’t the whole story), the “null pattern” is in the
same limbo as constant patterns. Consta
> On Apr 15, 2022, at 10:10 PM, Guy Steele wrote:
>
> That said, I am always (or at least now) a bit leery of language designers
> motivating a new language feature by pointing out that it would make a
> compiler easier to write. As I have learned the hard way on more than one
> language proje
Also, the following would be an error, even though the two are naturally
dual:
record Foo(int x) { }
Foo f = new Foo(aShort);
if (f instanceof Foo(short x)) { ... } // would be an error without
`short x` applicable to int
On 4/15/2022 6:25 PM, Brian Goetz wrote:
Can you
Can you provides examples of such refactorings ?
Refactoring
int x = aShort;
foo(x, x);
to
let int x = aShort
in foo(x, x);
* asking if something fits in the range of a byte or int; doing this
by hand is annoying and error-prone
* asking if casting from long to int would produce truncation; doing
this by hand is annoying and error-prone
Here’s some real code I wrote recently that would benefit dramaticall
We characterize patterns by their /applicability/ (static type
checking), /unconditionality/ (can matching be determined without a
dynamic check, akin to the difference between a static and dynamic
cast), and /behavior/ (under what conditions does it match, and what
bindings do we get.)
And, why would we not want duality with:
record R(short s) { }
...
new R(x)
because new R(x) is alone while case R(...) is part of a larger set of
patterns/sub-pattern of the pattern matching, if for each
pattern/sub-pattern, we need a double-entry table to u
We already discussed those rules when we discuss instanceof, it means
that "x instanceof primitive" has different meaning depending on the
type of x
No, it does not. It means "does x match the pattern P" everywhere. It
is pattern P is that has different meanings depending on type. This may
There's another, probably stronger, reason why primitive patterns
supporting widening, narrowing, boxing, unboxing, etc, are basically a
forced move, besides alignment with `let` statements, discussed earlier:
There is another constraint on primitive type patterns: the let/bind
statement comin
http://cr.openjdk.java.net/~gbierman/PatternSwitchPlusRecordPatterns/PatternSwitchPlusRecordPatterns-20220407/specs/patterns-switch-jls.html
Comments welcome!
The execution of an exhaustive|switch|can fail with a linkage error
(an|IncompatibleClassChangeError|is thrown) if it encounters a
(int x) to Box(null), it will not NPE, it will
just not match, and we'll go on to the next case. If all cases don't
match, then the switch will throw ME, which is a failure of
*exhaustiveness*, not a failure in *pattern matching*.
Does this change your first statement?
On Wed, M
ernal
implementation" and a method is often API-defining, it might be
reasonable to allow implementation details to leak into lambda
definitions if it makes them more convenient to write, while keeping
the more formal separation of implementation and API for method
parameters.
Yes
. If these
methods throw an exception, execution of the pattern matching construct
may fail with `MatchException`.
On 3/30/2022 2:43 PM, Dan Heidinga wrote:
On Wed, Mar 30, 2022 at 2:38 PM Brian Goetz wrote:
Another way to think about this is:
- If any of the code that the user actually
GADTs -- sealed families whose permitted subtypes specialize the type
variables of the base class -- pose some interesting challenges for
pattern matching.
(Remi: this is a big, complex area. Off-the-cuff "this is wrong" or
"you should X instead" replies are not helpful. If in doubt, ask
qu
Yes, and this is a special case of a more general thing -- that while
pattern declarations may have a lot in common with methods, they are not
"just methods with multiple return" (e.g., they have a different set of
characteristics at the declaration, they are intrinsically conditional,
they are
Another way to think about this is:
- If any of the code that the user actually wrote (the RHS of case
clauses, or guards on case labels) throws, then the switch throws that
- If any of the machinery of the switch dispatch throws, it throws
MatchException.
On 3/30/2022 2:12 PM, Dan Heidinga
It seems that what you are saying is that you think an Exception is
better than an Error.
Not exactly; what I'm saying is that the attempt to separate stray nulls
from separate compilation issues here seems like a heroic effort for low
value, and I'd rather have one channel for "exhaustive
It's a little like calling a method, but a little not like it too. For
example, when you match on a record pattern:
case Point(var x, var y): ...
what may happen is *either* you will invoke a user-written deconstructor
pattern, *or* we will test if you are a Point with `instanceof`, and
t
For when the static world and the dynamic world disagree, i think your
analysis has miss an important question, switching on an enum throw an
ICCE very late when we discover an unknown value, but in the case of a
sealed type,
Actually, I thought about that quite a bit before proposing this
We should have wrapped this up a while ago, so I apologize for the late
notice, but we really have to wrap up exceptions thrown from pattern
contexts (today, switch) when an exhaustive context encounters a
remainder. I think there's really one one sane choice, and the only
thing to discuss is
It's not by name. I don't know where you got this idea.
I think i understand the underlying semantics of the syntax, i'm not
100% confident.
It's always OK to ask questions if you are not 100% sure! In fact, its
generally better to do so.
The problem with the proposed syntax is t
The mangling has to be stable across compilations with respect to
any source- and binary-compatible changes to the pattern
declaration. One mangling that works quite well is to use the
"symbolic-freedom encoding" of the erasure of the pattern
descriptor. Because the erasur
1/ conceptually there is a mismatch, the syntax introduce names for
the bindings, but they have no names at that point, bindings only have
names AFTER the pattern matching succeed.
I think you have missed the point here. The names serve the
implementation of the pattern, not the interface
lared patterns together; trying to design dtor patterns in a
vacuum misses a number of considerations.)
I'll respond to your other points separately.
On 3/29/2022 6:19 PM, Remi Forax wrote:
*From: *"Brian
Time to take a peek ahead at _declared patterns_. Declared patterns
come in three varieties -- deconstruction patterns, static patterns, and
instance patterns (corresponding to constructors, static methods, and
instance methods.) I'm going to start with deconstruction patterns, but
the basic
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_ (who
We still have a lot of work to do on the current round of pattern
matching (record patterns), but let's take a quick peek down the road.
Pattern assignment is a sensible next building block, not only because
it is directly useful, but also because it will be required for
_declaring_ deconstruc
ault pattern is not defined.
Rémi
----
*From: *"Brian Goetz"
*To: *"amber-spec-experts"
*Sent: *Thursday, March 24, 2022 6:39:21 PM
*Subject: *Pattern coverage
I've put a document at
http:/
I've put a document at
http://cr.openjdk.java.net/~briangoetz/eg-attachments/Coverage.pdf
which outlines a formal model for pattern coverage, including record
patterns and the effects of sealing. This refines the work we did
earlier. The document may be a bit rough so please let me know if y
On 3/16/2022 4:34 PM, fo...@univ-mlv.fr wrote:
- Original Message -
From: "Brian Goetz"
To: "Remi Forax" , "amber-spec-experts"
Sent: Wednesday, March 16, 2022 5:41:49 PM
Subject: Re: Record pattern, the runtime side
It works in 3 steps:
Step 1,
It works in 3 steps:
Step 1, at compile time, the compiler takes all the patterns and creates a tree
of pattern from the list of patterns,
pattern that starts with the same prefix are merged together.
We can "normalize" a complex pattern into a sequence of simpler
conditionals. For exampl
Given a record R, and a record pattern R(P*), where P* is a list of
nested patterns of the same arity as R's components, then
x matches R(P*)
iff
x instanceof R
&& R(var alpha*) // always true, just binds
&& \forall i alpha_i matches P_i
If P* is empty, the last clause is vac
And in the future, when we have templated classes, some carriers may well
become specializations of arity-indexed base classes (CarrierTuple1,
CarrierTuple2, etc), where the VM takes responsibility for nasty things
like when to unload specializations.
On Mar 9, 2022, at 12:23 PM, John Rose
mai
Also, i wonder if the external Carrier API should have a way to wrap an
existing record class to see it as a Carrier, so the destructuring pattern will
behave the same way with a record or with the result of a de-constructor.
Having records be their own carrier is an optimization we anticipate w
>>
>> The minimal constraint is that the return type of the constructor MH is the
>> same type as the argument type of the component MHs.
>
> Agreed. The types should match but they shouldn't be considered part
> of the api. I don't think (correct me if I'm wrong) that we want them
> to "escap
What i was proposing is for switch to cram "not match" and the index of the
matching case into one int because using -1 seems natural and it will work well
with the tableswitch.
There’s two levels here, and I think part of the confusion with regard to
pattern translation is we’re talking at di
The minimal constraint is that the return type of the constructor MH is the
same type as the argument type of the component MHs. It would seem to me that
preserving stronger types here dynamically gives MH combinators more room to
optimize?
> On Mar 8, 2022, at 4:25 PM, Dan Heidinga wrote:
>
Adding more information,
we want the carrier to be a primitive type (to be able to optimize it
away), which means that we can not use null to represent "do_not_match",
we have to have a flag inside the carrier for that.
The alternate approach is to use a .ref class for partial patterns
(usi
So, I think the main thing we can control about the story is the
terminology. I think part of what people find confusing is the use of
the term "total", since that's a math-y term, and also it collides
with "exhaustive", which is similar but not entirely coincident.
One concept we might w
gir Valeev
On Sat, Mar 5, 2022 at 12:36 AM Brian Goetz wrote:
The following was received on the -comments list.
Summary: "Can we please have block expressions, you're almost there with the
yielding block in switch expressions."
My observations: There was some discussion arou
The following was received on the -comments list.
Summary: "Can we please have block expressions, you're almost there with
the yielding block in switch expressions."
My observations: There was some discussion around the time we did switch
expressions about whether we wanted a general-purpose
On 3/4/2022 5:37 AM, fo...@univ-mlv.fr wrote:
*From: *"Brian Goetz"
*To: *"Remi Forax"
*Cc: *"amber-spec-experts"
*Sent: *Friday, March 4, 2022 3:23:58 AM
*Subject:
Let
This is where it gets ugly. Let has no opinions about null, but
the game here is to pretend it does. So in a let statement:
let P = e
- Evaluate e
- If e is null
- If P is a covering pattern, its binding is bound to null;
- else we throw
Either way, we don't need to mutate or replace carriers.
You want the same carrier for the whole pattern matching:
I think you're going about this backwards. You seem to have a clear
picture of how pattern matching "should" be translated. If so, you
should share! Maybe your way i
t, or Character, and the value of
the constant
expression is representable in the type byte, short, or char respectively.
On 3/3/2022 10:17 AM, Dan Heidinga wrote:
On Wed, Mar 2, 2022 at 3:13 PM Brian Goetz wrote:
On 3/2/2022 1:43 PM, Dan Heidinga wrote:
Making the pattern match compatible
Given the misconceptions about totality and whether "pattern matching
means the same thing in all contexts", it is pretty clear that the story
we're telling is not evoking the right concepts. It is important not
only to have the right answer, but also to have the story that helps
people unders
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
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
I'm in agreement on not adding new contexts but I had the opposite
impression here. Doesn't "having it do range checking" require a new
context as this is different from what assignment contexts allow
today? Or is it the case that regular, non-match assignment must be
total with no left over
Thanks Jim.
As background, (some form of) this code originated in a prototype for
pattern matching, where we needed a carrier for a tuple (T, U, V) to
carry the results of a match from a deconstruction pattern (or other
declared pattern) on the stack as a return value. We didn't want to
spin
On 3/2/2022 1:43 PM, Dan Heidinga wrote:
Making the pattern match compatible with assignment conversions makes
sense to me and follows a similar rationale to that used with
MethodHandle::asType following the JLS 5.3 invocation conversions.
Though with MHs we had the ability to add additional
On 3/2/2022 2:36 PM, fo...@univ-mlv.fr wrote:
There are two ways to express "match non null Integer + unboxing",
this one
Integer value = ...
switch(value) {
case Integer(int i) -> ...
}
And we already agree that we want that syntax.
Wait, what? The above is not yet on the t
This is a valid generalized preference (and surely no one is going to
say "no, I prefer to play to our weaknesses.") But at root, I think
what you are saying is that you would prefer that pattern matching
simply be a much smaller and less fundamental feature than what is being
discussed here.
Now, what if instead of Object, we start with Long?
Long l = 0L
if (l instanceof byte b) { ... }
First, applicability: does Long unbox to a primitive type that can
be narrowed to byte? Yes! Long unboxes to long, and long can be
narrowed to byte.
Then: mat
So *of course* there's an obvious definition of how `int x`
matches against Integer, and its not a question of whether we
"define" it that way, its a question of whether we expose the
obvious meaning, or suppress it. I think the arguments in favor
of suppression are pretty
Let me put the central question here in the spotlight.
Boxing and unboxing
Suppose our match target is a box type, such as:
record IntegerBox(Integer i) { }
Clearly we can match it with:
case IntegerBox(Integer i):
We could stop here, and say the pattern `int i` is not applic
Nope,
i'm saying that inside a pattern if we let the unboxing to be possible
with the semantics that if the value is null, instead of throwing a
NPE if it does not match, we are introducing the equivalent of the
null-safe operator of Groovy (the elvis operator), something we
should not do
Relationship with assignment context
That's a huge leap, let's take a step back.
I see two questions that should be answered first.
1) do we really want pattern in case of assignment/declaration to
support assignment conversions ?
2) do we want patterns used by the switch or instan
As a consequence of doing record patterns, we also grapple with
primitive type patterns. Until now, we've only supported reference type
patterns, which are simple:
- A reference type pattern `T t` is applicable to a match target of
type M if M can be cast to T without an unchecked warning.
t/002458.html
I think we should prune this sub-thread and give other folks a chance to
reply to the main points.
On 2/18/2022 10:07 AM, fo...@univ-mlv.fr wrote:
----
*From: *"Brian Goetz"
*To: *"Re
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 o
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
As we move towards the next deliverable -- record patterns -- we have
two new questions regarding exceptions to answer.
Questions
1. When a dtor throws an exception. (You might think we can kick this
down the road, since records automatically acquire a synthetic dtor, and
users can't w
(For now, the best we can do is noisy
warnings.)
On 2/16/2022 11:00 AM, Remi Forax wrote:
----
*From: *"Brian Goetz"
*To: *"amber-spec-experts"
*Sent: *Wednesday, February 16, 2022 4:49:19
has tried it, or we got away with it...
On 1/25/2022 2:46 PM, Brian Goetz wrote:
We’ve previewed patterns in switch for two rounds, and have received some
feedback. Overall, things work quite well, but there were a few items which
received some nontrivial feedback, and I’m prepared to suggest
For me, && is more natural than "when" because i've written more
switch that uses && than "when".
And don't forget that unlike most of the code, with pattern matching
the number of characters does matter, this is more similar to
lambdas, if what you write is too verbose, you will not write it.
OK, I'll make you a deal: I'll answer your question about let/bind,
under the condition that we not divert the discussion on that right now
-- there'll be a proper writeup soon. The answer here is entirely for
context.
If you don't agree, stop reading now :)
On 2/15/2022 5:58 PM, Remi Forax
Not sure it's a no-brainer.
The question is more a question of consistency. There are two
consistencies and we have to choose one, either switch never allows
null by default and users have to opt-in with case null or we want
patterns to behave the same way if they are declared at top-level or
We're preparing a third preview of type patterns in switch. Normally we
would release after a second preview, but (a) we're about to get record
patterns, which may disclose additional issues with switch, so best to
keep it open for at least another round, and (b) we're proposing some
nontrivia
1 - 100 of 1014 matches
Mail list logo