Looking at some scala-docs of my libraries, it appeared to me that there is some unwanted noise from value classes. For example:
implicit class RichInt(val i: Int) extends AnyVal {
def squared = i * i
}
This introduces an unwanted symbol i
:
4.i // arghh....
That stuff appears both in the scala docs and in the IDE auto completion which is really not good.
So... any ideas of how to mitigate this problem? I mean you can use RichInt(val self: Int)
but that doesn't make it any better (4.self
, wth?)
EDIT:
In the following example, does the compiler erase the intermediate object, or not?
import language.implicitConversions
object Definition {
trait IntOps extends Any { def squared: Int }
implicit private class IntOpsImpl(val i: Int) extends AnyVal with IntOps {
def squared = i * i
}
implicit def IntOps(i: Int): IntOps = new IntOpsImpl(i) // optimised or not?
}
object Application {
import Definition._
// 4.i -- forbidden
4.squared
}
Perhaps the problem is the heterogeneous scenarios for which value classes were plotted. From the SIP:
I think there is a difference between the first and the last two. In the first case, the value class itself should be transparent. You wouldn't expect anywhere a type
RichInt
, but you only really operate onInt
. In the second case, e.g.4.meters
, I understand that getting the actual "value" makes sense, hence requiring aval
is ok.This split is again reflected in the definition of a value class:
The latter meaning it has no other fields etc., contradicting No. 1.
With
the only ever place in the SIP where
u
is used, is in example implementations (e.g.def extension$plus($this: Meter, other: Meter) = new Meter($this.underlying + other.underlying)
); and then in intermediate representations, only to be erased again finally:The intermediate representation being accessible for synthetic methods IMO is something that could also be done by the compiler, but should not be visible in the user written code. (I.e., you can use a
val
if you want to access the peer, but don't have to).A possibility is to use a name that is shadowed:
Or
This would still end up in the scala-docs, but at least calling
4.toInt
is neither confusing, no actually triggeringIntOps
.In Scala 2.11 you can make the val private, which fixes this issue:
I'm not sure it's "unwanted noise" as I think you will almost always need to access the underlying values when using your
RichInt
. Consider this:Also, if you had a method that adds two
RichInt
you would need again to access the underlying value:It does introduce noise (note: in 2.10, in 2.11 and beyond you just declare the val private). You don't always want to. But that's the way it is for now.
You can't get around the problem by following the private-value-class pattern because the compiler can't actually see that it's a value class at the end of it, so it goes through the generic route. Here's the bytecode:
See how the first one returns a copy of the class
Definition$IntOps
? It's boxed.But these two patterns work, sort of:
(1) Common name pattern.
Use one of these. Adding
i
as a method is annoying. Addingunderlying
when there is nothing underlying is not nearly so bad--you'll only hit it if you're trying to get the underlying value anyway. And if you keep using the same name over and over:the name collision sorta takes care of your problem anyway. If you really want to, you can create an empty value class that exists only to collide with
repr
.(2) Explicit implicit pattern
Sometimes you internally want your value to be named something shorter or more mnemonic than
repr
orunderlying
without making it available on the original type. One option is to create a forwarding implicit like so:Now you have to call
3.pow.sq
instead of3.sq
--which may be a good way to carve up your namespace!--and you don't have to worry about the namespace pollution beyond the originalrepr
.