我想實現一個功能,可以在類型有map
和flatMap
方法。我已經制作了Traversable
,但這不包括Future
和Option
。所以我決定去與我自己的接口,使用類型類:Typeclass和斯卡拉集合接口
trait CanMap[A, M[_]] {
def map[B](l: M[A])(f: A => B): M[B]
def flatMap[B](l: M[A])(f: A => M[B]): M[B]
}
我已經實現了這個爲Option
:
implicit def canmapopt[A] = new CanMap[A, Option] {
def map[B](l: Option[A])(f: A => B): Option[B] = l.map(f)
def flatMap[B](l: Option[A])(f: A => Option[B]): Option[B] = l.flatMap(f)
}
而這一次的作品非常好。現在,我想要實現它的Traversable
任何亞型,我想非常接近的一個選項的實現:
implicit def canmaptrav[A, B, T[B] <: Traversable[B]] = new CanMap[A, T] {
def map[B](l: T[A])(f: A => B): T[B] = l.map(f)
def flatMap[B](l: T[A])(f: A => T[B]): T[B] = l.flatMap(f)
}
,但我得到的錯誤:
type mismatch; found : Traversable[B] required: T[B] Note: implicit method canmaptrav is not applicable here because it comes after the application point and it lacks an explicit result type
爲l.map
返回類型。我不明白爲什麼l.map(f)
會返回一個Traversable
而不是特定類型T[B]
。所以我試圖在上下文中明確地放置正確類型的CanBuildFrom:
implicit def canmaptrav[A, B, T[B] <: Traversable[B]](implicit cbf: CanBuildFrom[T[A], B, T[B]]) = new CanMap[A, T] {
def map[B](l: T[A])(f: A => B): T[B] = l.map(f)
def flatMap[B](l: T[A])(f: A => T[B]): T[B] = l.flatMap(f)
}
錯誤仍然存在。
任何想法,我哪裏出錯了?這可能很明顯,但我對我猜測的泛型類型簽名感到困惑。
更新:解決方案
首先,作爲回答指出,CanMap
主要是一個函子/單子,所以如果你敢,你可以使用scalaz來實現這一點。但是,如果你像我一樣,想嘗試沒有它,這裏是解決方案的基礎上,通過Kipton巴羅斯答案:
trait CanMap[A, B, M[_]] {
def map(l: M[A])(f: A => B): M[B]
def flatMap(l: M[A])(f: A => M[B]): M[B]
}
implicit def canmapopt[A, B] = new CanMap[A, B, Option] {
def map(l: Option[A])(f: A => B): Option[B] = l.map(f)
def flatMap(l: Option[A])(f: A => Option[B]): Option[B] = l.flatMap(f)
}
implicit def canmaptrav[A, B, M[+_]](implicit bf: CanBuildFrom[M[A], B, M[B]], ev: M[A] => TraversableLike[A, M[A]], eb: M[B] => TraversableLike[B, M[B]]) = new CanMap[A, B, M] {
def map(l: M[A])(f: (A) => B): M[B] = l.map(f)
def flatMap(l: M[A])(f: A => M[B]): M[B] = l.flatMap[B, M[B]] { (a: A) =>
f(a)
}
}
訣竅是使用隱式轉換M[A] => TraversableLike[A, M[A]]
,而不是試圖亞型Traversable
。
優秀! 'TraversableLike'隱式轉換很酷:) 我不得不適應你的解決方案,將'CanBuildFrom'作爲一個隱式移動到'canmaptrav',這樣它就不會「毒化」'canmapopt'。爲了能夠實現'flatMap',我還必須將另一個隱含的'CanBuildFrom'添加到'''M [B]'到'canmaptrav'中。 'B'現在是CanMap [A,B,M [_]]中的一個參數,不再是函數。 – Mortimer
很高興你找到了解決方案。在CanMap中包含'B'類型可以簡化很多事情,但缺點是每個CanMap對象只映射到一個特定的類型。我看着斯卡拉斯。它們完全避免了'CanBuildFrom',它具有上升(更簡單)和負面影響(目標類型不特定;例如不會自動工作)。 –
我真的沒有'B'的問題必須是一個特定的類型,因爲它也是提供CanMap的含義的參數。所以它根據我在哪裏使用它而通過scala動態生成。 – Mortimer