Hi all,

Value classes are a good fit for small numeric-like types such as
Int16 or Half. One place they still differ from primitives is
construction: a primitive or wrapper can be written as a literal,
but a user-defined value class requires an explicit constructor call:

    Int16 a = new Int16((short) 13);

I read the 2020 thread "Typed variants of primitives" (Stephen
Colebourne / John Rose):

https://mail.openjdk.org/archives/list/[email protected]/thread/I77UXUZ4VQWMWPK3QSSEDWJJS7RJAVCZ/#X4F6C3BG3MD3656Y5WPLENKWPKH3RMGK

There, conversions between user-defined primitive-like types and
int were discussed as reasonable in principle, but requiring
carefully balanced rules.

I also searched valhalla-dev for discussions of
implicit conversions, user-defined conversions, and literal
conversions. I did not find a discussion of a per-constructor
opt-in marker specifically. The closest prior art I know of is
Swift's literal protocols and C++ converting constructors.

SKETCH

The keyword "opt" is a placeholder:

    value class Int16 {
        private final short val;

        opt Int16(short val) {
            this.val = val;
        }
    }

    Int16 a = 13;

The idea is to let a constructor explicitly opt in to a narrowly
defined form of implicit construction, rather than introduce
general user-defined conversions.

COMMON RULES

1. "opt" is allowed only on a constructor with exactly one
   parameter.

2. The class opts in explicitly, so existing constructors and
   code remain unaffected unless the new feature is used.

3. No conversion chaining is permitted. The source expression
   must be compatible with the parameter type under the existing
   assignment-conversion rules, including the constant-expression
   narrowing rule that permits "short s = 13", without requiring
   another conversion after that.

TWO POSSIBLE SCOPES

Option A: Constant expressions only

    Int16 a = 13;       // allowed
    int n = 13;
    Int16 b = n;        // error; use new Int16((short) n)

Advantages:
- Closely follows the existing rule that permits certain constant
  expressions to be assigned to narrower primitive types.
- Allows range checks for representable constants at compile time.
- Introduces fewer questions about overload resolution and
  generics.

Disadvantages:
- Less useful, since variables still require explicit
  construction.
- A relatively narrow feature, with the boundary between eligible
  and ineligible expressions needing careful definition.

Option B: Any expression compatible with the parameter type

    Int16 a = 13;       // allowed
    short s = 12;
    Int16 b = s;        // allowed

Advantages:
- More general and useful beyond literals and constant
  expressions.

Disadvantages:
- Constructor invocation becomes invisible at the use site and
  may throw or be expensive.
- Requires rules for method arguments, return expressions,
  conditional expressions, generics, and overload resolution.
  For example, what should happen when both f(Int16) and f(long)
  are applicable?
- Raises the question of which parameter types may be used with
  "opt". A constructor accepting a List or another reference type
  could perform substantial work implicitly, so restricting the
  feature to primitive parameter types, and perhaps String, might
  be worth considering.

OTHER OPEN QUESTIONS

- Should this be limited to value classes, or should ordinary
  classes be able to opt in as well? For example, could
  BigDecimal x = 13 be supported?
- How should an int constant be handled when the constructor
  parameter is short, compared with the existing special rule
  for assignments such as "short s = 13"?
- Would supporting constant expressions without operators be a
  useful first step, and how would that interact with future
  discussions of operators for user-defined primitive-like types?

ALTERNATIVES

- Static factories such as Int16.of(13).
- Keeping construction explicit.

I understand this is outside the scope of JEP 401 and may belong
with the planned work on conversions for value classes. I also
understand why Java has generally avoided implicit conversions.

I would be interested to hear whether similar approaches have
been considered, which scope, if either, is closer to the Valhalla
team's thinking, and whether this idea is worth developing further.

Thanks,
Saurav Bhattarai

Reply via email to