Reputation: 1094
my problem at its simplest form:
object test {
import scala.language.higherKinds
sealed trait IO[+F[+_], +A] {
def flatMap[G[+_] >: F[_], B](f: A => IO[G, B]): IO[G, B]
}
trait FileOp[+A]
val a: IO[FileOp, Int] = null
val b: IO[FileOp, String] = null
val c: IO[FileOp, String] = a flatMap (i => b)
}
this would give me:
type mismatch
found : test.IO[[+_]test.FileOp[_],String]
required: test.IO[test.FileOp,String]
val c: IO[FileOp, String] = a flatMap (i => b)
^
I'm expecting (in the flatMap call) both the F
and G
equals to FileOp
and B
equals to String
and this is kind of right, except the [+_]
before and [_]
after...
can anyone explain why the return type is not what I expected and how can I fix it~?
p.s. this is closer to what I wanted to express with the trait IO
:
trait ResourceOp[+A]
trait FileOp[+A] extends ResourceOp[A]
trait DBOp[+A] extends ResourceOp[A]
def readFromFile(path: String): IO[FileOp, String] = null
def writeToDB(s: String): IO[DBOp, Int] = null
val combinedOp: IO[ResourceOp, String] = readFromFile("/txt") flatMap writeToDB
Upvotes: 0
Views: 281
Reputation: 11366
In your type expression G[+_] >: F[_]
, because of the _'s you are saying "given types A,B G[A] >: F[B]
" which is not what you mean to say. For a counter-example, you know that Seq[String]
is not a supertype of List[Int]
even though Seq >: List
. Instead you mean that for any given type A
, G[A] >: F[A]
Note that def foo[A >: B]
is equivalent to def foo[A,B](implicit B <:< A)
,
Here is a rearrangement of what you want which is closer to what you are trying to express:
object test {
import scala.language.higherKinds
sealed trait IO[+F[+_], +A] {
def flatMap[G[+_], B](f: A => IO[G, B])(implicit ev: <:<[F[A],G[A]]): IO[G, B]
}
trait ResourceOp[+A]
trait FileOp[+A] extends ResourceOp[A]
trait DBOp[+A] extends ResourceOp[A]
def readFromFile(path: String): IO[FileOp, String] = null
def writeToDB(s: String): IO[DBOp, Int] = null
val combinedOp = readFromFile("/txt").flatMap[ResourceOp,Int](writeToDB)
}
The change I have made is to move the G >: F requirement to be the equivalent implicit parameter. Now, when you compile this you get a better error:
foo.scala:5: error: covariant type A occurs in contravariant position in type <:<[F[A],G[A]] of value ev
def flatMap[G[+_], B](f: A => IO[G, B])(implicit ev: <:<[F[A],G[A]]): IO[G, B]
and when we look up the definition of <:< in Predef.scala, we can verify that on of the A's is, in fact, contravariant:
sealed abstract class <:<[-From, +To] extends (From => To) with Serializable
So I don't believe you are going to get away with this unless you are willing to make ResourceOp invariant in its type parameter. The following will compile:
object test {
import scala.language.higherKinds
sealed trait IO[+F[_], A] {
def flatMap[G[_], B](f: A => IO[G, B])(implicit ev: <:<[F[A],G[A]]): IO[G, B]
}
trait ResourceOp[A]
trait FileOp[A] extends ResourceOp[A]
trait DBOp[A] extends ResourceOp[A]
def readFromFile(path: String): IO[FileOp, String] = null
def writeToDB(s: String): IO[DBOp, Int] = null
val combinedOp = readFromFile("/txt").flatMap[ResourceOp,Int](writeToDB)
}
Upvotes: 2