2014-01-17 133 views
0

我的問題,在其最簡單的形式:奇怪flatMap返回類型

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

這會給我:G

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

我期待(在flatMap調用)兩個F和等於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 

回答

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>: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] 

,當我們仰望的<定義:<在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) 

} 
+0

感謝在'摹指出的問題[A]>:F [B]',真正幫助和前〜 我從來沒有想到這一點,但我不熟悉如何隱含<:<'工作... 是否與'def flatMap [G [+ T]>:F [T],B](f:A => IO [G,B]) :IO [G,B]'? – Chris