The first two paragraphs boil down to “it’s difficult to work with user-defined 
numeric types, especially with a tight memory layout”. As you probably know, 
this is a problem we’re already working on solving.

Aside from that, I think the problem you’re reporting is:

"The java.util.ServiceLoader ... requires explicit custom code to verify 
bindings at startup for fail-fast behavior. The API remains an extra invocation 
hurdle with its lookup and instantiation requirements.”

What you’re saying about “right-sizing” the language is absolutely right, but 
for the JDK team to propose a solution to a problem you’ve identified, the 
problem first needs to be understood. Assuming that what I quoted is the 
problem you’ve tried expressing, can you elaborate more on:

1. Requires explicit custom code to verify bindings at startup for fail-fast 
behavior.

2. The API remains an extra invocation hurdle with its lookup and instantiation 
requirements.

The more concrete you are, as in “I tried to do X and this is the code I 
wrote”, the easier it would be to understand. 
I think I know what you mean in #1, but it would be helpful to show what code 
you had to write. In #2, what is exactly the hurdle? E.g. let’s say that the 
hurdle is that the API is verbose. That is only a problem if it has to be used 
lots of times in a program, so what was your program that required so many uses 
of ServiceLoader?

(P.S. “right-sizing” the language doesn’t mean that anything that could be 
improved with a language feature should be because every addition to the 
language complicates it. We like it when a non-trivial language feature can 
solve a big problem or multiple small ones.)

— Ron


> On 28 Jan 2026, at 23:20, Steffen Yount <[email protected]> wrote:
> 
> Hi Ron,
> 
> Thank you for the feedback. It's totally reasonable to push for the
> "why" before getting into the "how."
> 
> The personal problem I encounter is the severe and inconvenient
> extensibility costs of the current language model. When pursuing
> data-oriented design or domain-specific numeric types in Java, I find
> the language's current facilities to be a significant design obstacle.
> These are not just issues I find while building, but constraints that
> fundamentally alter how I consider a project's architecture before a
> single line of code is written.
> 
> I have encountered the following specific friction points:
> 
> The Instantiation Tax: On several occasions, I have turned to Java for
> intensive math calculations. These efforts typically start small but
> eventually outgrow Java's supported numeric abstractions. To move
> beyond small memory footprints and 64-bit representational limits, I
> find I must refactor to contiguous arrays, reusable Flyweight objects,
> and custom math methods just to manage the memory pressure and data
> type limitations. Because I cannot define polymorphic static contracts
> for these types, I am forced to pay an Instantiation Tax—maintaining
> "witness" objects just to access static logic. The ergonomic noise and
> heap-inefficiency of the unwanted object headers are so high that I am
> often discouraged from pursuing my original abstractions entirely.
> While I have used primitive long types as witnesses for static
> overloaded method binding, using the NewType pattern for type-safe
> bindings remains prohibitively expensive; a new NewType class's
> implementation immediately excludes it from participation within the
> language's built-in expression operators. In the aforementioned
> efforts, I have ultimately abandoned Java for C/C++ simply because
> they allowed me to shape the data layout and its static behavior
> without this "abstraction tax."
> 
> The Expression Problem (Post-hoc Abstraction): When I find it
> necessary to treat third-party classes as part of a common
> abstraction—for instance, when attempting to resolve Guice-based
> injection or AOP design issues without the "magic" of runtime
> reflection—the traditional path is the Adapter Pattern. I find this
> route unsustainable; creating a new wrapper class for every instance
> not only fragments object identity but generates significant GC churn.
> I have seen production code with wrapper classes nested eight levels
> deep just to satisfy disparate abstractions. The ability to implement
> type level contracts rather than just instance level contracts, along
> with type level extension methods, would allow us to side-step the
> wrapper classes with implementations that bind to existing types
> without modifying their source code. The lack of them serves as a wall
> preventing me from designing the clean, type-safe, and AOT-friendly
> systems I know are possible elsewhere.
> 
> The ServiceLoader Ceremony: The java.util.ServiceLoader acts like more
> of a library than a language feature. It requires explicit custom code
> to verify bindings at startup for fail-fast behavior. The API remains
> an extra invocation hurdle with its lookup and instantiation
> requirements. A coherent language-integrated, static service interface
> method dispatch and binding would dramatically reduce this ceremony
> and increase utility by moving it from a manual runtime search to a
> link-time certainty.
> 
> My "big picture" problem is that Java’s evolution model currently
> makes it difficult to "grow the language" via libraries that feel
> native and are performan, such as the recent prototype exploration of
> Float16. I believe the language should provide the infrastructure for
> _Static Service Traits_ or otherwise make that kind of library-driven
> growth a standard capability for all developers.
> 
> I feel "corralled" into 1990s instance-based OOP. When I explore
> data-oriented design or high-performance numeric abstractions, the
> features found in my competitors' language tool belts would be
> incredibly useful; without them, I find myself looking at alternate
> language implementations just to avoid Java's structural obstacles.
> 
> Given that Project Amber’s stated mission is to "right-size language
> ceremony" and improve developer productivity, doesn't a proposal that
> eliminates this Instantiation Tax and link-time service ceremony seem
> like a relevant and worthy pursuit?
> 
> -Steffen
> 
> 
> On Wed, Jan 28, 2026 at 7:45 AM Ron Pressler <[email protected]> wrote:
>> 
>> The hardest part in designing and evolving a language is deciding which 
>> problems are important enough to merit a solution in the language and how 
>> their priorities compares to other problems. It’s the hardest part because 
>> the language team are expert at coming up with solutions, but they may not 
>> always know what problems people enoucnter in the field, how frequently they 
>> encounter them, and how they work around them today.
>> 
>> I’m sure there is some problem hidden here and in your previous post, but it 
>> is not articulated well and is hidden in a poposed solution, even though no 
>> solution is even worth exploring before understanding the problem. And so 
>> the best way to get to a solution is for you to focus on the problem and 
>> only on the problem.
>> 
>> What was the problem you *personally* ran into? How bad were its 
>> implications? How did you work around it?
>> 
>> With the hard part done, the JDK team will then be able to assess its 
>> severity and think whether it merits a solution in the JDK, if so, where 
>> (language, libraries, or VM), and how to prioritise it against other 
>> problems worth tackling. Then they’ll be able to propose a solution, and 
>> that’s would be the time to try it out and discuss it.
>> 
>> — Ron
>> 
>> 
>> 
>>> On 28 Jan 2026, at 00:28, Steffen Yount <[email protected]> wrote:
>>> 
>>> The recent thread "Java Language Enhancement: Disallow access to static 
>>> members via object references" highlights a long-standing tension in Java's 
>>> handling of static members. While that thread seeks to further decouple 
>>> instance state from static logic, I would like to propose moving in the 
>>> opposite direction: "doubling down" on Java’s compile-time and link-time 
>>> static polymorphism.
>>> 
>>> By beefing up java.util.ServiceLoader facilities and integrating its 
>>> discovery mechanism directly into the language via Static Service Traits, 
>>> we can facilitate the "Witness Object" paradigm discussed by Brian Goetz's 
>>> "growing the java language" presentation and the algebraic "well-known 
>>> interface" model for custom numeric types (like Float16) proposed  in Joe 
>>> Darcy's "Paths to Support Additional Numeric Types on the Java Platform" 
>>> presentation.
>>> 
>>> == Static Service Traits for Java ==
>>> 
>>> I propose a system of Static Service Traits. I use the term "Trait" 
>>> advisedly: this feature adopts a rigorous Coherence Model (inspired by 
>>> systems like Rust) to ensure that service resolution is not merely a 
>>> dynamic search, but a type-safe, deterministic binding of static 
>>> capabilities to types.
>>> 1. The service Contextual Keyword
>>> We introduce service as a contextual modifier for interface declarations. 
>>> Marking an interface as a service identifies it as a "service type" with a 
>>> contract for static capabilities and a high-performance service provider 
>>> registry.
>>> 
>>> 2. Static Implementations and Extension Methods
>>>    • Static Implementations:
>>>        • In Interface Headers: interface MyTrait implements ServiceX<T>. 
>>> Methods are fulfilled as static.
>>>        • In Class Headers: class MyClass implements static 
>>> Numeric<Float16>. Methods are implemented as static on the class. Existing 
>>> signature rules prevent a method from being both a static and an instance 
>>> implementation simultaneously.
>>>    • Static Extension Methods: Desugared at the call site. 
>>> myInstance.method() becomes MyClass.method(myInstance). Notably, if 
>>> myInstance is null, it desugars to MyClass.method(null) without an 
>>> immediate NullPointerException.
>>>    • Ergonomic Aliases: To simplify signatures, we introduce private nested 
>>> static type aliases This and Super (e.g., static This add(This a, This b)).
>>> 
>>> 3. Operational Mechanics & Link-Time Integration
>>> A ServiceLoader Controller is integrated into the JVM’s class-loading 
>>> pipeline. During class definition, the Controller eagerly extracts all 
>>> relevant metadata to populate the Static Service Provider Registry, 
>>> including:
>>>    • Header-level static implements and implements declarations.
>>>    • Service binding descriptors from module-info.class.
>>>    • META-INF/services/ provider-configuration files.
>>> Hierarchical Precedence Resolution: To ensure deterministic binding, the 
>>> Controller resolves call sites to their most specific service provider via 
>>> a waterfall dispatch model:
>>>    • Tier 1: Type Specialization: Most specific generic match wins, 
>>> applying the same scrutiny and rules currently used for existing static 
>>> overloaded method resolution.
>>>    • Tier 2: Physical Locality: Provider in the same file (.jar/.class) as 
>>> the caller wins.
>>>    • Tier 3: Loader Proximity: Nearest ClassLoader in the delegation path 
>>> wins.
>>>    • Tier 4: Modular Topology: Internal > Explicit > java.base > Transitive 
>>> > Automatic.
>>>    • Tier 5: Sequential Order: Final tie-breaker via Classpath order.
>>> 
>>> 4. Coherence, The Orphan Rule, and Quarantining
>>> To achieve the type-safety of a trait system, we enforce an adapted Orphan 
>>> Rule: A module (or package on the classpath) must own either the service 
>>> interface or the target type to define an implementation.
>>>    • Coherence Enforcement: Violations in modular code trigger a 
>>> LinkageError.
>>>    • Behavioral Continuity: Violations in classpath code trigger a 
>>> load-time warning and the provider is quarantined from the Static Registry. 
>>> To ensure continuity, quarantined providers remain accessible via existing 
>>> java.util.ServiceLoader API calls, protecting legacy iteration-based 
>>> discovery while ensuring the integrity of the new link-time dispatch.
>>> 5. Performance and AOT Considerations
>>> This model transforms ServiceLoader into a link-time resolver. JIT 
>>> compilers can treat service calls as direct invokestatic instructions, 
>>> enabling aggressive optimization. This is highly compatible with Project 
>>> Leyden and GraalVM, as precedence can be "baked" into the binary during AOT 
>>> compilation.
>>> Conclusion
>>> By transitioning ServiceLoader to a link-time resolver, we provide a 
>>> type-safe, high-performance path for algebraic types and witness-based 
>>> generics. This allows Java to "grow" through libraries—fulfilling the goals 
>>> of both Darcy and Goetz—while maintaining the performance and stability 
>>> characteristics of the modern JVM.
>>> 
>>> 
>>> Thoughts?
>> 

Reply via email to