Given Parent and Result Algebraic Data Types:
sealed trait Parent
case object Kid extends Parent
sealed trait Result[A <: Parent]
case class ResultImpl[A <: Parent](x: A) extends Result[A]
I then wrote:
def general[A <: Parent, F[_]](x: A, f: A => F[A]): F[A] =
f(x)
However, I'm not sure how to call general to get an output of type, ResultImpl[Kid.type].
I attempted:
scala> general(Kid, ResultImpl.apply)
<console>:19: error: inferred kinds of the type arguments (Kid.type,ResultImpl) do not conform to the expected kinds of the type parameters (type A,type F).
ResultImpl's type parameters do not match type F's expected parameters:
type A's bounds <: Parent are stricter than type _'s declared bounds >: Nothing <: Any
general(Kid, ResultImpl.apply)
^
<console>:19: error: type mismatch;
found : Kid.type
required: A
general(Kid, ResultImpl.apply)
^
<console>:19: error: type mismatch;
found : Nothing => ResultImpl[Nothing]
required: A => F[A]
general(Kid, ResultImpl.apply)
^
How can I do that with the general function?
You need to specify that your _ is <: Parent as well, because A is. Also you might want to split arguments into two groups for the compiler to infer A correctly:
def general[A <: Parent, F[_ <: Parent]](x: A)(f: A => F[A]): F[A] =
f(x)
Then the next works as expected:
general(Kid)(ResultImpl(_))
Related
I have an extential type of tuple, and I want to pass it to a generic function (I use ClassTag in this example but it is a custom type class with invariant type parameter):
type TC = (ClassTag[T], Option[T]) forSome {type T}
def foo[T](ct: ClassTag[T], o: Option[T]) = {}
val tc: TC = (classTag[String], Option[String](null))
foo(tc._1, tc._2)
This gives me error:
error: type mismatch;
found : scala.reflect.ClassTag[T]
required: scala.reflect.ClassTag[Any]
Note: T <: Any, but trait ClassTag is invariant in type T.
You may wish to investigate a wildcard type such as _ <: Any. (SLS 3.2.10)
foo(tc._1, tc._2)
I wanna ensure the type of two parameters ct and o uses the same parameter type T and I thought extential type should ensure this, but it does not seem to work.
However if I don't use tuple and only use ClassTag, it works fine:
type TC = ClassTag[T] forSome {type T}
def foo[T](ct: ClassTag[T]) = {}
val tc: TC = classTag[String]
foo(tc)
So the previous error of ClassTag invariance does not make sense.
Why does it not work when I use a tuple? How can I make it work?
For val tc: TC = classTag[String], Option[String](null)) tc._1 has type ClassTag[_] (which is different from any ClassTag[T] e.g. ClassTag[Any] because ClassTag is invariant), tc._2 has type Option[_], which is the same as Option[Any] because Option is covariant
implicitly[Option[_] =:= Option[Any]]
implicitly[Option[Any] =:= Option[_]]
An existential type 𝑇 forSome { 𝑄 } where 𝑄 contains a clause type 𝑡[tps]>:𝐿<:𝑈 is equivalent to the type 𝑇′ forSome { 𝑄 } where 𝑇′ results from 𝑇 by replacing every covariant occurrence of 𝑡 in 𝑇 by 𝑈 and by replacing every contravariant occurrence of 𝑡 in 𝑇 by 𝐿.
https://www.scala-lang.org/files/archive/spec/2.13/03-types.html#existential-types
So in
def foo[T](ct: ClassTag[T], o: Option[T]) = {}
foo(tc._1, tc._2)
we have the type mismatch. Any and _ (aka arbitrary T) are different types.
Try to use universal quantification rather than existential
type TC[T] = (ClassTag[T], Option[T])
def foo[T](ct: ClassTag[T], o: Option[T]) = {}
val tc: TC[String] = (classTag[String], Option[String](null))
foo(tc._1, tc._2)
Given:
class Invar[T]
trait ExtendsAnyref extends AnyRef
def f(a: Invar[ExtendsAnyref]) = {}
The following is erroneous
scala> val x: Function1[Invar[_ <: AnyRef], Unit] = f
<console>:13: error: type mismatch;
found : Invar[ExtendsAnyref] => Unit
required: Invar[_ <: AnyRef] => Unit
val x: Function1[Invar[_ <: AnyRef], Unit] = f
^
Why?
I understand that in Scala, generic types have by
default nonvariant subtyping. Thus, in the context of this example, instances of Invar with different type parameters would never be in a subtype relationship with each other. So an Invar[ExtendsAnyref] would not be usable as a Invar[AnyRef].
But I am confused about the meaning of _ <: AnyRef which I understood to mean "some type below AnyRef in the type hierarchy." ExtendsAnyref is some type below AnyRef in the type hierarchy, so I would expect Invar[ExtendsAnyref] to conform to Invar[_ <: AnyRef].
I understand that function objects are contravariant in their input-parameter types, but since I use Invar[_ <: AnyRef] rather than Invar[AnyRef] I understood, apparently incorrectly, the use of the upper bounds would have the meaning "Invar parameterized with Anyref or any extension thereof."
What am I missing?
When you write
val x: Function1[Invar[_ <: AnyRef], Unit] = ...
it means x must accept any Invar[_ <: AnyRef]. That is, it must accept Invar[AnyRef], Invar[String], etc. f obviously doesn't: it only accepts Invar[ExtendsAnyref].
In other words, you need to combine your last two paragraphs: because functions are contravariant in argument types, for Function1[Invar[ExtendsAnyref], Unit] to conform to Function1[Invar[_ <: AnyRef], Unit] you'd need Invar[_ <: AnyRef] to conform to Invar[ExtendsAnyref], not vice versa.
If you
want a function that takes Invar parameterized with any subclass of AnyRef
this can be written as Function1[Invar[A], Unit] forSome { type A <: AnyRef }. However, I don't believe there is anything useful you could do with an object of this type, because 1) the only thing you can do with a function is to apply it to an argument, but 2) you don't know what arguments this function accepts.
trait Account[T <: Account[T]]
case class BrokerAccount(total:BigDecimal) extends Account[BrokerAccount]
case class SavingsAccount(total:BigDecimal) extends Account[SavingsAccount]
Below function declaration and invocation works fine.
def foo1( xs: Array[T forSome { type T <: Account[T] }]):Array[T forSome { type T <: Account[T] }] = xs
foo1(Array(BrokerAccount(100),SavingsAccount(50)))
But below invocation gives compilation error.
def foo2( xs: List[T forSome { type T <: Account[T] }]):List[T forSome { type T <: Account[T] }] = xs
foo2(List(BrokerAccount(100),SavingsAccount(50)))
Error
Main.scala:14: error: type mismatch;
found : List[Product with Serializable with Main.Account[_ >: Main.SavingsAccount with Main.BrokerAccount <: Product with Serializable with Main.Account[_ >: Main.SavingsAccount with Main.BrokerAccount <: Product with Serializable]]]
required: List[T forSome { type T <: Main.Account[T] }] foo2(List(BrokerAccount(100),SavingsAccount(50)))
Can someone please explain me why compilation error occur in later case?
The key to the problem is variance - you're trying to return a contravariant value in covariant position (function return type).
Despite List type is covariant in its argument (trait List[+A]), this essentially means its values are contravariant (can be assigned to a List of supertypes):
val listOfSupers: List[_ >: Account[_]] = List(BrokerAccount(100), SavingsAccount(50))
What you're trying to return from the function foo2 is a complete contrary - List[_ <: Account[_]], thus the compiler error.
If instead of List you use Set there, which is invariant in its type parameter just like Array, everything will work fine.
On Scala 2.10.4, given the following trait and case classes:
scala> trait Parent
defined trait Parent
scala> case class Girl() extends Parent
defined class Girl
scala> case class Boy() extends Parent
defined class Boy
I'm trying to define a method, f, that produces a Set[Class[A]] where A's type is A <: Parent.
scala> def f[A <: Parent]: Set[Class[A]] = Set[Class[A]](classOf[Boy], classOf[Girl])
<console>:12: error: type mismatch;
found : Class[Boy](classOf[$Boy])
required: Class[A]
def f[A <: Parent]: Set[Class[A]] = Set[Class[A]](classOf[Boy], classOf[Girl])
^
<console>:12: error: type mismatch;
found : Class[Girl](classOf[$Girl])
required: Class[A]
def f[A <: Parent]: Set[Class[A]] = Set[Class[A]](classOf[Boy], classOf[Girl])
But, I can make it work if I use, what I believe is the "wildcard":
scala> def g[A <: Parent]: Set[Class[_]] = Set[Class[_]](classOf[Boy], classOf[Girl])
g: [A <: Parent]=> Set[Class[_]]
And it works:
scala> g
res5: Set[Class[_]] = Set(class Boy, class Girl)
Why did the first approach fail, but the second succeeded? Lastly, is there any risk (to type safety) using Class[_] in the above definition of g?
The problem is that Class[A] is invariant in its type parameter. So Class[Boy] is not a Class[Parent]. The compiler will warn you of this if you set an explicit return type of Set[Class[Parent]].
scala> def f[_ <: Parent]: Set[Class[Parent]] = Set(classOf[Boy], classOf[Girl])
<console>:24: error: type mismatch;
found : Class[Boy](classOf[$Boy])
required: Class[Parent]
Note: Boy <: Parent, but Java-defined class Class is invariant in type T.
You may wish to investigate a wildcard type such as `_ <: Parent`. (SLS 3.2.10)
def f[_ <: Parent]: Set[Class[Parent]] = Set(classOf[Boy], classOf[Girl])
The following method uses existential types, which essentially means you don't care what the type is. _ is a completely unbound, and the type parameter A is superfluous.
def g[A <: Parent]: Set[Class[_]] = Set[Class[_]](classOf[Boy], classOf[Girl])
You might as well write:
def g: Set[Class[_]] = Set[Class[_]](classOf[Boy], classOf[Girl])
Lastly, is there any risk (to type safety) using Class[_] in the above definition of g?
Type safety has pretty much gone out the window at this point, because _ can be anything. Boy, Girl, Parent, Any, Toaster, ...
I think the best you can hope for is to heed the warning and get something like this:
def g: Set[Class[_ <: Parent]] = Set(classOf[Boy], classOf[Girl])
This will at least ensure that you have a Set with elements bounded above by Parent. Exactly what you intend to do with it, I don't know.
I am trying to create a map wrapper in Scala 2.9.2 with values that have a particular higher kinded type, and am wrestling with the type system. Here is a cut down version of the code to illustrate the problem:
trait A
trait B[C] {
def c: C
}
trait E[C <: B[C], D <: A]
case class MyMap[M <: A, L <: B[L], N[L, M]](map: Map[M, N[L, M]])
object MyMap {
def empty[M <: A, L <: B[L], N[L, M]] = MyMap(Map.empty[M, N[L, M]])
}
val myMap = MyMap.empty[A, T forSome { type T <: B[T] }, E]
when I try and compile this, the last statement fails with a compiler error, indicating that I am not matching type bounds. However to me it looks like I am, and perhaps that where I have N[L, M] and previously L <: B[L], it is not inferring that the L in N[L, M] is the same L <: B[L], and likewise for M. Error is as follows:
kinds of the type arguments (A,T forSome { type T <: B[T] },E) do not conform to the expected kinds of the type parameters (type M,type L,type N). E's type parameters do not match type N's expected parameters: type C's bounds >: Nothing <: B[C] are stricter than type L's declared bounds >: Nothing <: Any, type D's bounds >: Nothing <: A are stricter than type M's declared bounds >: Nothing <: Any
val myMap = MyMap.empty[A, T forSome { type T <: B[T] }, E]
Any advice gratefully received.
Thanks -
There is one problem with the second parameter and one with the third. I don't know about the second parameter, I'm not sure what may be allowed here with existentia. So this is just about the problem with the third parameter.
A bit of simpler code with the same error :
class A {}
class C[X <: A] {}
def f[X[_]] = 12
f[List]
res1: Int12
f[C]
error: kinds of the type arguments (C) do not conform to
the expected kinds of the type parameters (type X).
C's type parameters do not match type X's expected parameters:
type X's bounds >: Nothing <: A are stricter
than type _'s declared bounds >: Nothing < : Any
f[C]
^
Quite simply, method empty expects as third type parameter a generic type with two parameters and no restrictions. In the body of empty, you are allowed to write N[Int, String] or whatever. Type E, which has some constraints, is not compatible with this (note: I find writing N[L,M] rather than N[_, _], with L and M the name of the previous type parameters a bit misleading. Or maybe it suggests you don't really want a higher order type parameter).
If you write in the code above
def g[X[_ <: A]] = 13
then calling g[C] is ok (g[List] ok too, as it should, nothing wrong can happen there).
Similarly, your code would work (provided you pass a suitable second parameter) if empty was
Map.empty[M <: A, L <: B[L], N[X <: B[X], Y <: A]]