Custom AnyVal types in Scala 3 / Dotty

Hi,

Maybe this was discussed before, sorry if I’m reviving an old topic …

In Scala 3 I was expecting custom AnyVal types to be dropped / at least deprecated, due to the introduction of opaque type aliases.

AnyVal was introduced in SIP-15 with the purpose of having classes that “can get completely inlined, so operations on these classes have zero overhead compared to external methods”.

However, historically AnyVal has had serious limitations and bugs. I remember issues that were not fixed in Scala 2, because it’s too hard and because AnyVal is discouraged anyway. And now we have opaque types aliases, achievable in Scala 2 too, and which are better, superseding AnyVal for the need of compile-time types.

If AnyVal in its current form isn’t at least deprecated, I think we have 2 issues:

  1. How do we explain to beginners, or to ourselves, when to use AnyVal and when to use “opaque type aliases”?
  • In Scala 2 there’s at least the use-case of defining zero-overhead extension methods, but that use-case is now gone too;
  1. What will happen when the JVM implements actual value types, whose semantics will probably be different?
  • Are we hoping that we can modify the semantics of AnyVal to fit such value types?

Thanks,

Value classes are left as they are, for two reasons:

  • Opaque types are not a 1-1 replacement for them. There are tradeoffs. For instance, value classes give you customized toString and equals methods, and behave like normal classes for pattern matching, something that opaque types cannot do by design. That’s precisely why value classes need to be boxed in some situations.

  • Value classes might become more general and useful when Valhalla has landed. Once Valhalla is part of JDK we have to support value classes as part of the interop story.

So, until we know for sure what will happen with Valhalla, we feel it is not a good use of anyone’s time to remove value classes now only to bring them back in slightly modified form later. So in a way, value classes are in statis. No work is being done on them right now, but they might be brought back to life in the future.

6 Likes

Thanks for the answer.

On the boxing of AnyVal, in some cases, I think that’s precisely why they can be problematic.

For equality, would have been cool if Scala could’ve used the Equiv type class, when defined for an opaque type. Not sure what problems this would bring, maybe it isn’t a great idea.

But one has to wonder — if you need boxing, or custom equals or hash functions, maybe defining a regular case class is a better idea.


On being able to support Valhalla’s value types via AnyVal, if Valhalla comes to fruition … I agree that it would be great to be able to do that.

However, would it have compatible semantics? I mean, if such JVM value types exist, then the type name might be featured in the bytecode, being known at runtime, even in cases in which our current AnyVal does not box. In which case the current behavior of AnyVal would have to change in a backwards incompatible way.

I might be speaking nonsense, sorry if that’s the case.

And you probably considered this already, so if AnyVal could support JVM’s value types, then that’s reason enough for them to stay in the language.

I answered similar questions in Synthesize constructor for opaque types

I think AnyVals can be potentially useful. I can store them using custom array based collection classes, found via implicits that only store the underlying backing values, but then if I want a collection of the super trait of the the AnyVal class then the automatic boxing is what you want? Sometimes boxing is unnecessary and carries no useful runtime type information, but sometimes it is necessary and does carry useful runtime type information.

Personally I’d love it if I could provide scalac a flag to get it to show me warnings when it’s boxes. That way I could either modify to code to avoid boxing, or accept boxing by adding something @noWarn or @unchecked.

7 Likes

David - thanks for bringing this up. I need this feature. Please - I could use it tonight!

Thanks,

David

1 Like

I feel this still will be a leaky abstraction, as it’s still possible to cast opaque types to Any and still some Java api accept any where they shouldn’t (e.g. java.util.Map#containsKey)

No worries! And you saying thanks makes me feel silly because I’ve thought this for years but for some reason never even voiced it. :sweat_smile: I’ll at least raise it now as an issue:

3 Likes