我首先描述思考方式,然后是我的问题。我使用Functor定义作为示例。它是在scalaz中定义的,因此我将其用作一个小变化的例子,使其不那么复杂。
这是i
模块定义,我放在Functor
文件中:
Functor.scala
现在我想为精确类型定义模块的一些含义,例如trait Functor[F[_]] {
def map[A,B](fa: F[A])(f: A => B): F[B]
}
。另外,我想定义一个通用List
函数,它隐式接受fmap
。最好将它们定义为尽可能接近Functor
特征。我认为Scala中最好的地方(考虑#1)是一个伴侣对象。以下是同一Functor
文件中的定义:
Functor.scala
我可以在客户端代码中隐式使用object Functor {
def fmap[F[_], A,B](as:F[A])(f:A=>B)
(implicit ff:Functor[F]):F[B] =
ff.map(as)(f)
implicit val listFunctor = new Functor[List] {
def map[A,B](as: List[A])(f: A => B): List[B] = as map f
}
}
和fmap
,例如:
Functor
通常,当我们定义具有函数/函数集的模块时,我们还希望丰富现有类型,以便能够将我们的新函数用作这些现有类型的API的一部分。在Scala中,我们可以通过隐式转换实现它。这是一个import com.savdev.NewLibrary._
val r = fmap(List(1,2))(_.toString)
最终类,允许从某些泛型类型转换为FunctorOps
:
Functor
我将函数命名为final class FunctorOps[F[_], A](self: F[A])(implicit ff:Functor[F]){
def qmap[B](f:A=>B):F[B] = ff.map(self)(f)
}
,fmap
,以避免错误地使用针对不同类型存在的qmap
函数。
现在我想要将隐式转换器定义为map
。作为我想到的第一个选择 - 使用伴侣对象。但是看一下我发现的scalaz代码,他们主要使用它的特征:
FunctorOps
有特征我可以编写它们并用一个词导入。这是一个特征的例子,但我可以扩展它们中的很多:
trait ToFunctorOps {
implicit def ToFunctorOps[F[_],A](v: F[A])(implicit F0: Functor[F]) =
new FunctorOps[F,A](v)
}
现在在客户端代码中,我只能导入object NewLibrary extends ToFunctorOps
,这比导入每个随播广告对象要容易得多:
NewLibrary
比较选项,特征与伴侣对象,我发现使用特征是一种更灵活的方式。首先,因为我可以组成它们。也许我想念一些事情,我的结论是错误的。所以问题是: