Amber features 2026
Remi Forax
forax at univ-mlv.fr
Wed Jan 14 08:39:55 UTC 2026
> From: "John Rose" <john.r.rose at oracle.com>
> To: "Brian Goetz" <brian.goetz at oracle.com>, "Archie Cobbs"
> <archie.cobbs at gmail.com>
> Cc: "Gavin Bierman" <gavin.bierman at oracle.com>, "amber-dev"
> <amber-dev at openjdk.org>
> Sent: Wednesday, January 14, 2026 5:15:01 AM
> Subject: Re: Amber features 2026
> On 13 Jan 2026, at 18:33, Brian Goetz wrote:
>> While I totally understand why you would want to do that, I don't see the
>> connection between pattern assignment and extending the type system to permit
>> denotation of quantified or existential types? I don't see any patterns in your
>> example, only type manipulation.
>> What you're saying is that you'd like a conversion from Foo<?> to `\exist T .
>> Foo<T>`. This is understandable, as wildcards are basically existentials
>> already, and is just as safe as the the trick of calling an out-of-line generic
>> method.
>> But I just don't see the connection with pattern assignment? It seems like what
>> you really want is "generic variables", to go along with generic methods and
>> classes?
> I’ve wanted this sort of thing from time to time. I think of the ask
> as "local type variables", where the type variable is local to a block
> (tighter control than local to method or class/interface). But it
> would only be set via inference, not explicitly; in that it would
> be a narrower feature than type variables on methods or classes.
> The two other type variable occurrences (method, class) offer
> explicit specification as well as inference. But there is no
> occasion for explicit specification of a local type. If you
> have a type you want to use explicitly, you just declare the
> local with that type.
> Other kinds of variables (fields, not locals) also do not benefit
> from having their types inferred, for the same reason that the /var/
> keyword is only accepted on locals. (Inferring a type for var on
> an API point is dangerously obscure, not worth the concision.)
> So, local type vars. The form Archie mentions is also the one
> that I have wondered about, these many years.
> var x1a = foo(); // x1a has type inferred from foo()
> <T1> T1 x1b = foo(); // so does x1b; now it is denotable as T1
> T1 x1c; // and now x1c has the same type too
> { x1a = x1b; x1b = x1c; x1c = x1a; } // types are the same
> <K2,V2> Map<K2,V2> x2 = bar(); // capturing g-type structure
> <T3> T3 x3a = baz();
> List<T3> x3b = bat(); // checks result of baz is list of what x3a is
> <T4> T4 x4; // ERROR; must have an initializer to drive inference
> This might allow some new types to become denotable, so maybe
> it’s dangerous. Maybe there is some useful sanitization move
> that could apply to type variables captured this way, as with var.
> But var definitely can capture non-denotables, despite sanitization.
> var o1 = new Object() { int f; };
> o1.f++; // field WHOOZIT.f
> <T2> o2a = new Object() { long f; static int Q; };
> T2 o2b = o2a; // rather odd
> o2a.Q++; // static variable reference (deprecated syntax)
> o2b.Q++; // again?
> T2.Q++; // ERROR; unless T2 is a type alias instead of a type var
> Oddly, the range of variation of such a type var is very narrow,
> since it can be driven only from one use site, the initialization.
> But it’s still a type var, if you squint, since the thing coming
> out of the initializer expression can have type vars mixed into it.
> Feature priority? Very low! It’s a "filling in the corners" move.
> No known important use cases. The use case I had was avoiding
> refactoring to a private generic method, because the body wanted
> to access some local vars in an enclosing scope. In the end,
> I think I just boxed the local vars in an array and moved on
> with the generic helper method. It did make the code more
> obscure, so having a local type var would have been an aid
> in readability. But it’s rare to want to name that type,
> and I can’t recall why I needed to.
> In other words, I am not asking for a JEP or RFE for this.
> Just laying out the case FTR, in case a use comes up later.
> Maybe the balance shifts if/when we get reified generics,
> since then there will be "more to capture".
> There, got it all off my chest. Thanks Archie for fellow-traveling.
> Now, back to 2026.
For fields, i think I would prefer to have a way to denote the type of null and bottom/nothing.
class A {
<T> List<T> list = List.of();
// can be
List<Nothing> list = List.of();
}
Inside a method, you sometime need to access a static field (e.g. you can use it as a JIT "anchor")
void main() {
var o1 = new Object() { int f; static int Q; };
o1.Q++;
// can be
record T1() { static int Q; } // the compiler adds a package private constructor
T1.Q++;
// or
enum T2 { static int Q; } // the compiler adds values()/valueOf()
T2.Q++;
// this does not compile
// the keyword "final" is allowed but "static" is not
static final class T3 { private T3() {} static int Q; }
}
another example is the class initializer idiom
class DBFacade {
public static DB getLazyDB() {
final class DBInit { private DBInit() {} private static final DB INSTANCE = new DB(); }
return DBInit.INSTANCE;
}
}
Here the class DBInit is "static" because it is defined in a static context (inside the static method).
So the two missing pieces are
- being able to denote the type of null and/or nothing,
- being able to declare a local class static *
> — John
Rémi
* also allow private classes in interface which is another hole to plug.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20260114/4ae2c83c/attachment.htm>
More information about the amber-dev
mailing list