奇怪的flatMap返回类型

时间:2014-01-17 03:52:18

标签: scala functional-programming type-inference

我的问题最简单:

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)
}

这会给我:

type mismatch
found   : test.IO[[+_]test.FileOp[_],String]
required: test.IO[test.FileOp,String]
     val c: IO[FileOp, String] = a flatMap (i => b)
                                   ^

我期待(在flatMap调用中)FG等于FileOpB等于String这是善良的是的,除了[+_]之前和[_]之后......

任何人都可以解释为什么返回类型不是我的预期,我该如何解决它??

P.S。这更接近我想用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

1 个答案:

答案 0 :(得分:2)

在你的类型表达式G[+_] >: F[_]中,由于_是你所说的“给定类型A,B G[A] >: F[B]”,这不是你想说的。对于反例,您知道即使Seq[String]List[Int]也不是Seq >: List的超类型。相反,您的意思是对于任何给定类型AG[A] >: F[A]

请注意,def foo[A >: B]相当于def foo[A,B](implicit B <:< A)

这是你想要的重新排列,它更接近你想要表达的内容:

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)

}

我所做的改变是将G&gt;:F要求移动为等效的隐含参数。现在,当你编译它时,你得到一个更好的错误:

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]

当我们查找&lt;:&lt;的定义时在Predef.scala中,我们可以验证A的实际上是逆变的:

sealed abstract class <:<[-From, +To] extends (From => To) with Serializable

所以我不相信你会逃避这个,除非你愿意在其类型参数中使ResourceOp不变。以下将编译:

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)

}