3

我正在尝试实现一个适用于具有 amapflatMap方法的类型的函数。我已经做到了Traversable,但这不包括FutureOption直接。所以我决定使用我自己的接口,使用类型类:

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,我尝试了一个非常接近 Option 的实现:

  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)会返回 aTraversable而不是特定的 type 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主要是 Functor/Monad,所以如果你敢,你可以使用scalaz来实现它。但是,如果您像我一样想尝试不使用它,这里是解决方案,基于 Kipton Barros 的回答:

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

4

3 回答 3

4

第一个问题是该Traversable map方法的“幕后”发生了很多事情。返回最具体的集合类型需要做一些工作,这就是您需要CanBuildFrom. 第二个问题是Option没有实现Traversable接口,所以它的map方法CanBuildFrom.

这是我能得到的最接近的,

import scala.collection.generic.CanBuildFrom
import collection.TraversableLike

trait CanMap[A, M[_]] {
  def map[B](l: M[A])(f: A => B)(implicit bf: CanBuildFrom[M[A], B, M[B]]): M[B]
}

object Test {

  // ugly hack to work around nonexistent CanBuildFrom for Option
  implicit def optionBuilder[A, B]: CanBuildFrom[Option[A], B, Option[B]] = null

  implicit def canmapopt[A] = new CanMap[A, Option] {
    def map[B](l: Option[A])(f: A => B)(implicit bf: CanBuildFrom[Option[A], B, Option[B]]): Option[B] = l.map(f)
  }

  implicit def canmaptrav[A, M[_]](implicit ev: M[A] => TraversableLike[A, M[A]]) = new CanMap[A, M] {
    def map[B](l: M[A])(f: (A) => B)(implicit bf: CanBuildFrom[M[A], B, M[B]]): M[B] = l.map(f)
  }

  // example usage

  def mapper[A, B, M[_]](l: M[A])(f: A => B)(implicit cm: CanMap[A,M], bf: CanBuildFrom[M[A], B, M[B]]) = {
    cm.map(l)(f)
  }
  mapper(List(1,2,3))(_ + 1)          // List(2,3,4)
  mapper(Some(2): Option[Int])(_ + 1) // Some(3)
  // (cast to Option[Int] is needed to find the canmapopt implicit)
}

顺便说一句,隐式转换TraversableLike使其也适用于数组,

 mapper(Array(1,2,3))(_ + 1)         // Array(2, 3, 4)
于 2013-04-16T02:51:58.317 回答
2

首先,我尝试了您的两次失败的尝试,但并没有从中受益。然后我决定简单一点,做我自己的 CanMap 实现。我最终得到了这个:

  def canmaptrav[A] = new CanMap[A, Traversable]{
    def map[B](l: Traversable[A])(f: A => B): Traversable[B]= l.map(f)
    def flatMap[B](l: Traversable[A])(f: A => Traversable[B]): Traversable[B] = l.flatMap(f)
  }

看起来正是CanMap[_,Option]. 假设您正在寻找的子类型适用于这样的用例:

canmaptrav[Int].map(List(1,2,3,4))(_ + 1)       //> res0: Traversable[Int] = List(2, 3, 4, 5)
canmaptrav[Int].map(Vector(1,2,3,4))(_ + 1)     //> res1: Traversable[Int] = Vector(2, 3, 4, 5)

现在,如果您希望 res1 和 res0 类型成为具体类型(List、Vector),那么该方法将不得不依赖CanBuildFromfrom。

顺便说一句,你知道这CanMap几乎是 Monad 接口,对吧?

于 2013-04-16T02:35:17.103 回答
2

Scalaz已经包含了这些类型类,它们被称为Monadand Functor。一个简短的例子:

// map
def foo[F[_] : Functor](xs: F[Int]) = xs.map(_ + 1)

scala> foo(List(1,2,3))
res2: List[Int] = List(2, 3, 4)

// flatMap
def flatten[M[_] : Monad](xs: M[M[Int]]) = xs.flatMap(identity)

scala> flatten(List(List(1,2,3)))
res3: List[Int] = List(1, 2, 3)

编辑

的仿函数实例Future可能如下所示:

implicit object FutureFunctor extends Functor[Future] {
  def map[A,B](fa: Future[A])(f: A => B) = fa.map(f)
}
于 2013-04-16T09:09:22.453 回答