Question

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
Était-ce utile?

La solution

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)

}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top