我想在scalaz庫中使用scala中的monad做些事情,並且在使用子類型工作時遇到了一些麻煩。Scala子類型+隱式轉換問題
我已經開始定義自己的monad。讓它爲簡單起見身份單子:
import scalaz._
import Scalaz._
class Id[+A] (val value : A) { }
implicit object IdMonad extends Monad[Id] {
override def pure[A](a : => A) = new Id(a)
override def bind[A, B](a : Id[A], f : A => Id[B]) = f(a.value)
}
接下來,我有一些額外的功能擴展它:
class ExtendedId[A] (value : A, val printer : A => String) extends Id[A](value) { }
有了這個額外的功能,ExtendedId
不是一個單子了。
現在我想用ExtendedId[A]
類型的對象作爲Id[A]
:
def increment1(v : ExtendedId[Int]) : Id[Int] = {
for(v <- v) yield v + 1;
// ^
// error: could not find implicit value for parameter t: scalaz.Functor[test.package.ExtendedId]
}
注意,我瞭解到,自從ExtendedId
不是一個單子,最好我能得到作爲輸出Id[Int]
,我那好吧!但不幸的是,該代碼仍然無法編譯。
然而,這一個作用:
def asId[A](a : ExtendedId[A]) : Id[A] = a
def increment2(v : ExtendedId[Int]) {
for(v <- asId(v)) yield v + 1;
}
這裏,asId
功能的確沒有什麼比上溯造型它的參數從ExtendedId[A]
到Id[A]
更多。它似乎應該是完全多餘的,但事實並非如此。
這是怎麼發生的?確實存在從Id[A]
到包含map
的對象的隱式轉換,並且顯然確實存在從ExtendedId[A]
到Id[A]
的簡單隱式轉換。那麼,爲什麼編譯器無法合併它們?
由於'ExtendedId'不是一個monad,我無法定義'Monad [ExtendedId]'''''ExtendedId''沒有有用的'bind'實現('printer'值使它不可能)。我能做的是將'ExtendedId [A]'強制轉換爲'Id [A]',但我不明白編譯器爲什麼不這樣做。我的意思是,上傳是所有隱式轉換的母體,並且它不是隱式執行的。 – Rotsor 2011-05-23 10:56:00
註釋'[A <:Id]'似乎無效,因爲'Id'類型構造函數需要參數。並且'[A [_] <:Id [_]]'抱怨某些涉及類型A的「非法循環引用」。 – Rotsor 2011-05-23 11:02:21
@Rostor你說得對,我弄亂了我的類型參數。我編輯了我的答案。正如我寫的,編譯器不會像你所描述的那樣轉換事物,因爲「Monad」不是協變的;即「Monad」的定義明確指出,在實踐中,即使「B <:A」,「Monad [A]」也不是「Monad [B]」。如果明確演員,編譯器,你知道它對你來說是安全的 - 但它不是在斯卡拉茲單子的一般情況。 – 2011-05-23 11:36:34