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