When trying to use higher kinded existentials in Scala I run into the following problem:
trait A[H[_]]
trait Test {
val l: A[List]
// [error] type mismatch;
// [error] found : A[List]
// [error] required: A[_[_] <: Any]
// [error] Note: List <: Any, but trait A is invariant in type H.
// [error] You may wish to define H as +H instead. (SLS 4.5)
val x: A[B] forSome { type B[_] } = l
}
Adding a covariant annotation to H as the compiler suggests works. Is there a way to work around this if I don't want H to be covariant?
A slight variation of the example gives a more helpful error message:
scala> l: (A[B] forSome { type B[_] })
<console>:10: error: type mismatch;
found : A[List]
required: A[_[_] <: Any]
Note: List <: Any, but trait A is invariant in type H.
You may wish to define H as +H instead. (SLS 4.5)
l: (A[B] forSome { type B[_] })
^
<console>:10: error: can't existentially abstract over parameterized type B
l: (A[B] forSome { type B[_] })
^
Looking for this error brings us to a TODO in the compiler.
Since existential types are going to disappear, per Odersky's email, I don't think this limitation will be fixed. However, Martin Odersky's email also reminds us that existential types are equivalent to abstract types. Hence, the above example can be encoded as follows:
scala> trait A { type H[_] }
defined trait A
scala> val l: A {type H[X] = List[X]} = null
l: A{type H[X] = List[X]} = null
scala> l: A
res0: A = null
Type application is syntactically ugly, but turning a value into an existential becomes trivial (also to implement in a compiler, which is part of Odersky's point).
What's useful to encode existentials is that type members don't have to be instantiated ever. So, to encode A[_] we can write:
scala> class A { type T }
defined class A
scala> new A
res1: A = A#3a7049a6
What's confusing here is that this does not work for objects:
scala> object B { type T }
<console>:8: error: only classes can have declared but undefined members
object B { type T }
^
But I recently got that accepted as a bug — see here for a pull request clarifying the spec (approved by Adriaan Moors), and here for my bug report and one-line fix to the compiler (still waiting for review).
Related
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?
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.
I am having trouble resolving a scala.reflect.Manifest for a type with a type member.
For example,
import scala.reflect.Manifest
trait Foo {
type T
}
trait Bar[T]
object Main extends App {
val barM: Manifest[Bar[Int]] =
implicitly[Manifest[Bar[Int]]]
val fooM: Manifest[Foo{type T = Int}] =
implicitly[Manifest[Foo{type T = Int}]]
}
The above code does not compile, giving the following error.
Foo.scala:15: error: type mismatch;
found : scala.reflect.Manifest[Foo]
required: scala.reflect.Manifest[Foo{type T = Int}]
Note: Foo >: Foo{type T = Int}, but trait Manifest is invariant in type T.
You may wish to investigate a wildcard type such as `_ >: Foo{type T = Int}`. (SLS 3.2.10)
implicitly[Manifest[Foo{type T = Int}]]
^
one error found
But the barM declaration works just fine.
I am aware that type members are not quite type parameters, but I am definitely not up on all the subtleties.
How can one go about resolving a Manifest for a type with a concrete type member?
Structural types are not supported by Manifest. SI-4252 (Manifest of structural types) is labelled as "won't fix", and it is recommended that you use TypeTag instead. Manifest is also likely to be deprecated soon.
scala> import scala.reflect.runtime.universe.TypeTag
import scala.reflect.runtime.universe.TypeTag
scala> implicitly[TypeTag[Foo { type T = Int} ]]
res18: reflect.runtime.universe.TypeTag[Foo{type T = Int}] = TypeTag[Foo{type T = Int}]
You might also be able to work around this with a type alias, but it's hard to tell without a use-case.
scala> type FooAux[T] = Foo { type T }
defined type alias FooAux
// Compiles, but is it useful?
scala> implicitly[Manifest[FooAux[Int]]]
res19: scala.reflect.Manifest[FooAux[Int]] = Foo
TL;DR: It appears that type parameters of type aliases (e.g. type T[X<:Serializable]) do not enforce their constraints when referenced as variables, parameters and perhaps other cases. Case classes, however, do enforce the bounds correctly for their parameters.
Consider a type alias designed to represent a subset of generic type. For example, let us say I want a type for lists of Serializable things:
scala> type SerializableList[T <: Serializable] = List[T]
defined type alias SerializableList
Now say that I want a case class with a parameter of these things:
scala> case class NetworkDataCC(things: SerializableList[_])
<console>:9: error: type arguments [_$1] do not conform to type SerializableList's type parameter bounds [T <: Serializable]
case class NetworkDataCC(things: SerializableList[_])
Well, that doesn't work. Scala (annoyingly) does not carry the parameter bounds with the type, but it's easy to fix:
scala> case class NetworkDataCC(things: SerializableList[_ <: Serializable])
defined class NetworkDataCC
Alright. Looks good. Now, what if I want just a regular class with those things, but I again forget to explicitly declare the type bounds. I expect an error:
scala> class NetworkData(val things: SerializableList[_])
defined class NetworkData
Oh, wait. No error... huh.
So, now I can do this?
scala> new NetworkData(List(1))
res3: NetworkData = NetworkData#e344ad3
Well, that seems quite broken. The case class, works fine of course (because the restrictions were declared):
scala> NetworkDataCC(List(1))
<console>:11: error: type mismatch;
found : Int(1)
required: Serializable
NetworkDataCC(List(1))
In my project, I am making use of reflection to generate some metadata about my classes. The metadata for the non-case-class shows a lack of bounds on things:
scala> classOf[NetworkData].getDeclaredFields()(0).getGenericType
res0: java.lang.reflect.Type = scala.collection.immutable.List<?>
Whereas the case class is correct:
scala> classOf[NetworkDataCC].getDeclaredFields()(0).getGenericType
res1: java.lang.reflect.Type = scala.collection.immutable.List<? extends scala.Serializable>
I wasn't able to find any bugs in the scala compiler bug tracker for this. Am I misunderstanding how these bounds should be used?
Scala's underscore is not equivalent to SerializableList[X forSome {type X}] by default:
scala> def aa(a: SerializableList[_]) = a
aa: (a: SerializableList[_])List[Any]
scala> def aa(a: SerializableList[X forSome {type X}]) = a
<console>:11: error: type arguments [X forSome { type X }] do not conform to type SerializableList's type parameter bounds [T <: Serializable]
def aa(a: SerializableList[X forSome {type X}]) = a
^
scala> class NetworkDataCC(things: SerializableList[X forSome {type X}])
<console>:11: error: type arguments [X forSome { type X }] do not conform to typ
e SerializableList's type parameter bounds [T <: Serializable]
class NetworkDataCC(things: SerializableList[X forSome {type X}])
It is equivalent to
scala> def aa(a: SerializableList[X] forSome {type X} ) = a
aa: (a: SerializableList[_])List[Any]
So such "unbounded" behavior is fine. Check out this answer: https://stackoverflow.com/a/15204140/1809978
Case classes seem to have additional type restrictions (due to this bug, which affects unapply method, automatically generated for case class).
If you want to have "unbounded" existential type in case class, just specify higher-order type explicitly:
scala> case class NetworkDataCC[SerializableList[_]](things: SerializableList[_])
warning: there were 2 feature warning(s); re-run with -feature for details
defined class NetworkDataCC
scala> NetworkDataCC(List(1))
res5: NetworkDataCC[List] = NetworkDataCC(List(1))
Or even:
scala> type SLL = SerializableList[_]
defined type alias SLL
scala> case class NetworkDataDD(things: SLL)
defined class NetworkDataDD
So this is definitely a bug regarding that you can workaround it with semantically equivalent type alias (see SI-8997)
Suppose I have:
class Bounded[A] {
type apply[C <: A] = C
}
This compiles:
implicitly[Bounded[Any]#apply[String] =:= String]
This fails:
type Str = Bounded[Any]#apply[String]
...with:
[error] /home/grant/Workspace/scunits/test/src/main/scala/Box.scala:37: type arguments[String] do not conform to type apply's type parameter bounds [C <: A]
[error] type Str = Bounded[Any]#apply[String]
[error] ^
I tried using abstract types instead of type parameters, with the same result. The only work-around I found was to instantiate the type. This compiles:
val boundedAny = new Bounded[Any]
type Str2 = boundedAny.apply[String]
Unfortunately I'm working with phantom types which don't have run time instances, often for performance reasons.
Why does Scala produce a compile error here? Is there a better work-around?
Thanks for any help.
Update: In addition to the workaround below, I needed a way to override types with abstract type bounds. I did this like so:
object Test {
class AbstractBounded[A] {
type apply[C <: A] <: A
class Workaround[C <: A] {
type go = apply[C]
}
}
class Bounded[A] extends AbstractBounded[A] {
type apply[C <: A] = C
}
type Str = Bounded[Any]#Workaround[String]#go
}
How about:
scala> class Bounded[A] { class i[C <: A]{ type apply = C}}
defined class Bounded
scala> type TTT = Bounded[Any]#i[String]#apply
defined type alias TTT
scala> implicitly[TTT =:= String]
res4: =:=[TTT,String] = <function1>
Scala forgot to lookup generic (or another "abstract" type) before binding parameter to type alias. Given that =:= works fine - seems like a bug for me. Maybe implicits are resolving on another compilation level or just before this check.
I am confused by the generic subtyping.
In Java, if type A is a subtype of B, generic type C<A> and C<B> are invariant. For instance, ArrayList<Base> is not a subtype of ArrayList<Derived>.
However, in Scala, generic type C<A> and C<B> are covariant if type A is a subtype of B. So what's the property of generic class in Scala has but not in Java?
Firstly note that variance is a property of generic type parameters, not of the parameterized types themselves.
Secondly: you are wrong about scala - type parameters are invariant by default. Let us investigate!
Java
Java has use-site variance annotations. That is, you can declare methods like this:
boolean addAll(Collection<? extends T> c);
There is, however, one form of "parameterized type" (I use the term loosely) in which the type parameters are covariant: Java Arrays! (This is actually insane because Arrays are mutable and hence it is easy to circumvent the type system). Consider the following:
public static void subvert(Object[] arr) { arr[0] = "Oh Noes!"; }
And then:
Integer[] arr = new Integer[1];
subvert(arr); //this call is allowed as arrays are covariant
Integer i = arr[0];
A good interview question this one: what happens?
Scala
In Scala, you have declaration-site variance. That is, the variance of a type parameter is declared alongside the parameter (using the annotations + and -):
trait Function1[-I, +O]
This says that the trait Function1 has two type parameters, I and O which are contra- and co-variant respectively. If no +/- annotation is declared, then the type parameter is invariant. For example, Set is invariant in its type parameter:
scala> def foo(set: Set[Any]) = ()
foo: (set: Set[Any])Unit
scala> Set(1)
res4: scala.collection.immutable.Set[Int] = Set(1)
scala> foo(res4)
<console>:10: error: type mismatch;
found : scala.collection.immutable.Set[Int]
required: Set[Any]
Note: Int <: Any, but trait Set is invariant in type A.
You may wish to investigate a wildcard type such as `_ <: Any`. (SLS 3.2.10)
foo(res4)
^
List is however, declared as being covariant:
scala> def bar(list: List[Any]) = ()
bar: (list: List[Any])Unit
scala> List(1)
res6: List[Int] = List(1)
scala> bar(res6)
Why not ask the compiler?
Another way of demonstrating this is to ask the compiler directly for subtype-evidence:
scala> class Cov[+A]
defined class Cov
scala> implicitly[Cov[Int] <:< Cov[Any]]
res8: <:<[Cov[Int],Cov[Any]] = <function1>
But with an invariant type parameter
scala> class Inv[A]
defined class Inv
scala> implicitly[Inv[Int] <:< Inv[Any]]
<console>:9: error: Cannot prove that Inv[Int] <:< Inv[Any].
implicitly[Inv[Int] <:< Inv[Any]]
^
Lastly, contravariance:
scala> class Con[-A]
defined class Con
scala> implicitly[Con[Any] <:< Con[Int]]
res10: <:<[Con[Any],Con[Int]] = <function1>
See also identifier <:<