Łukasz Łukasz - 2 months ago 11
Scala Question

aliasing proper type as existential type (why it compiles?)

Why can I do the following:

class A
type M[_] = A


I would expect I can only alias type that expects one type parameter, for example a
List[_]
, but it works also with plain classes.

If I create a method:

def foo(m: M[_]) = m


and call it with wrong parameter:

scala> foo("a")
<console>:15: error: type mismatch;
found : String("a")
required: M[_]
(which expands to) A[]
foo("a")


I get such error. What is the meaning of
A[]
?

Going further, how to explain this:

scala> type M[_, _] = A
<console>:12: error: _ is already defined as type _
type M[_, _] = A


Is there a way to assure that what I put on the right hand side of my alias will be a parametrized type?

Answer

type M[_] = A is the same as type M[X] = A: a constant function on types. M[X] is A whatever X is: M[Int] is A, M[String] is A, M[Any] is A, etc. _ in this case is just an identifier (which explains the error for type M[_, _] as well).

Of course, in def foo(m: M[_]) = m, M[_] is an existential type: M[T] forSome { type T }. I don't know why Scala says it expands to A[] in the error message, though; this may be a bug. You can check it's the same type as A by calling

scala> implicitly[M[_] =:= A]
res0: =:=[A[],A] = <function1>

Is there a way to assure that what I put on the right hand side of my alias will be a parametrized type?

You can declare an abstract member type with higher-kind

trait Foo { type M[_] }

and it can only be implemented by parametrized types:

class Bar1 extends Foo { type M = Int } // fails
class Bar2 extends Foo { type M[X] = List[X] } // works

Of course, as mentioned in the first paragraph, M in type M[X] = Int is parametrized, and I don't think there is a way to rule it out.