As far as I can tell the clone
method, which is part of Object
has not been discussed in this thread, nor its associated interface Cloneable
. I assume for the short term it would be included in Any
like most of the others. What do you think we should do with it in the longer term? Manage it with a type class?
clone
is a protected method in Object
/AnyRef
, not Any
, so itās not really relevant here.
As far as Iām aware, clone
is sort of ignored by the Java world as well (artima - Josh Bloch on Design), so the answer might be ādo nothingā
By that logic all AnyVal
s are also AnyRef
s because they can be boxed. Iād argue itās not an implementation detail because itās observable. Instead of an implementation detail Iād say boxing is an implicit conversion, just one in Java-land instead of Scala user-land with our implicit
keyword. So yeah IMO Matchable
is clearer than AnyClass
.
Donāt want to get in a big offtopic discussion about this, but how would you as a programmer observe the difference if in a future scala version Int
is always represented by a java.lang.Integer
instance instead of by the more efficient int
where possible? I donāt consider profiling the heap or the runtime performance an answer, unless you think that the scalac optimizer and the JIT should be part of the language specification. You could inspect the Class
instance, but the compiler could still defer a.getClass
to ScalaRunTime.anyValClass
just like it does now when a
is statically known to be Int
.
I donāt think AnyVal
should be matchable. In fact, I donāt think AnyRef
should be either, as that exposes what should be implementation details related to boxing:
@ List(1: Any).map(_.isInstanceOf[AnyRef])
res0: List[Boolean] = List(true)
@ List(1: Any).map(_.isInstanceOf[AnyVal])
cmd1.sc:1: type AnyVal cannot be used in a type pattern or isInstanceOf test
val res1 = List(1:Any).map(_.isInstanceOf[AnyVal])
^
The first line above should not compile any more than the second, because its semantics is just as broken.
This is fine, on the other hand, and Int
can still be made matchable:
@ List(1: Any).map(_.isInstanceOf[Int])
res0: List[Boolean] = List(true)
Matchable
has nothing to do with the target type of the match. It has to with the source type, i.e., the type of the scrutinee.
(That said, we could independently warn off x.isInstanceOf[AnyRef]
as being equivalent to x != null
.)
Since Iām here: I am warming up to the idea of this proposal. I think Matchable
is a good name.
Ah, I thought Iād read somewhere that it also prevented using the type in a type test, which would also make sense.
Iād like to have a way of opting out of traits being usable as sources and being usable as targets of type tests.
Not that itās hugely important, but would it make sense to change the definition of isInstanceOf
to something like:
def isInstanceOf[T <: Matchable]: Boolean
?
How would that be any better than what it is now, i.e., <: Any
?
It would rule out something like this:
object opaques:
opaque type Logarithm = Double
def weirdTest(d: Double) = d.isInstanceOf[Logarithm]
If thatās desirable.
That is already ruled out.
Right. All abstract and opaque types are ruled out?
Yes.
Maybe Iām taking you to literally (accidental habit of mine) but IIUC here are a few examples:
- Array[Int] and Array[Integer] are mutually incompatible
- Patmatāing to AnyRef fails with Int and succeeds for Integer
- Having an AnyRef upper-bound on a method (i.e. def x[A <: AnyRef])
(2) and (3) might sound esoteric but I actually use both approaches quite often in code, usually around performance-related stuff where using a ref and doing ref comparisons is fast and effective, where as a function with the same purpose but for Ints, will get a different implementation with different logic.
If the bounds of isInstanceOf
were adjusted to isInstanceOf[A <: Matchable]
then you could remove whatever code is in the compiler that currently prevents isInstanceOf[OpaqueType]
and get it for free. Not to mention it would be clearer and more accurate to anyone looking at the method.
No, the checks performed on the target type of an instance test are not directly related to that, but the above is a consequence. The checks are that the target type has a stable public erasure, basically. That is more general than just checking non matchable stuff. So we would still have to keep that code.
Also, you still donāt want to be able to type test for an opaque type alias with an upper bound that is Matchable. So we would still have to keep that code.
The rules on the left and in the right have nothing to do with each other. On the left, you want not to break parametricity. On the right, you want a stable, public erasure to be able to perform the type test in the first place. These are orthogonal.
Ah ok cool. I thought it was a nice idea but thanks for clarifying.
@LPTK has a point about non-Matchable
traits though, in particular universal traits. It would make sense to require it. But maybe it could be added later in that case.
New issue with Matchable that hasnāt been discussed so far: