Reducing verbosity for generics
Sam Carlberg
sam at slfc.dev
Sun Jun 9 00:20:39 UTC 2024
I developed and maintain a units of measurement library. The target audience is educational - predominantly high school students with minimal experience with Java or programming in general - so its API must be accessible, readable, and type safe for anyone to use it. Otherwise they would just use a raw double and try to keep track of unit conversions themselves (usually in code comments, and often incorrectly - thus the library).
The core part of the library is a Unit class with a recursive self type to make unit interactions and conversions type safe, and a generic Measure record that holds values in terms of an arbitrary unit. Unit is subclassed for concrete unit types (think distance, time, temperature, and so on) and also mathematical types representing quotient and product types (think velocity, area, or force). The self type is necessary to allow methods in the unit class to have a factory method for returning measurements in terms of itself, and for providing dimensional analysis in the measure record, but causes myriad problems with type inheritance more than one level deep.
This class setup works very well for modeling units and their interactions. A velocity can be represented as Quotient<Distance, Time>; force can be represented (rather verbosely) as Product<Mass, Quotient<Quotient<Distance, Time>, Time>>. The problem is that the type compositions only represent the underlying building blocks; they completely fail to express what the results of the compositions actually mean. Imagine a new programmer given the choice between representing torque as Product<Product<Mass, Quotient<Quotient<Distance, Time>, Time>>, Distance> with the type-safe unit API or with a raw double - they'd (rightly) dismiss the unit outright as being too complex and just use a raw double, trying their best to handle unit safety on their own. Even though the library can express unit types correctly and exhaustively, it cannot do so ergonomically, and so the users who would most benefit from unit safety would never use it.
However, nobody would run screaming from a type simply named Torque. But here we run into limitations in the type system: var only applies to local variables, not fields, and explicit types may sometimes still be desired for pedagogy; simple subclassing fails due to the recursive self type not being inherited more than one level down; "bubbling up" the self type for subclasses to specify pollutes any usages of the intermediate types; wrappers have a different type signature, making them incompatible with the types they wrap; and flattening the hierarchy to avoid subclassing results in frustrating edge cases like Velocity and Quotient<Distance, Time> being incompatible types even though they both represent the same thing.
A way to define type aliases is the most obvious solution (but perhaps not the best):
- We can define direct, expressive type names for units without needing to create specialized subclasses
- Because there's no inheritance, the recursive self-type problem disappears
- Aliases would mean Velocity and Quotient<Distance, Time> are exactly the same type, and would therefore always be two-way compatible
I'm not proposing any particular syntax, or even to add C#-style using or Kotlin's typealias. Just that a way of declaring in code that velocity is shorthand for Quotient<Distance, Time>, or that Torque is shorthand for that monstrous set of nested generics, would neatly resolve all the tradeoffs between verbosity and type safety. I figured amber is a good place to bring this up, given the project's focus on improving developer ergonomics and onboarding of new programmers.
~ Sam
ps: I have made significant simplifications to my examples for the sake of brevity. I'd be happy to clarify anything or share code samples if it would be useful
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20240609/22d36510/attachment.htm>
More information about the amber-dev
mailing list