Scala:将特征方法推迟到父特征对象中的隐式类

时间:2017-10-10 01:05:02

标签: scala generics functional-programming typeclass implicit

具体来说,我尝试使用Functor扩展我的Applicative类型类。

trait Functor[F[_]] {
  def fmap[A, B](r: F[A], f: A => B): F[B]
}

object Functor {
  implicit class FunctorOps[A, F[_]: Functor](xs: F[A]) {
    def fmap[B](f: A => B): F[B] = implicitly[Functor[F]].fmap(xs, f)
  }

  implicit def SeqFunctor: Functor[Seq] = new Functor[Seq] {
    def fmap[A, B](r: Seq[A], f: A => B) = r map f
  }
}

trait Applicative[F[_]] extends Functor[F] {
// What I want to do, but this *does not* work.
  def fmap[A, B](r: F[A], f: A => B): F[B] = Functor.FunctorOps[A, F](r).fmap(f)

  def pure[A](x: A): F[A]
  def fapply[A, B](r: F[A], f: F[A => B]): F[B]
}

object Applicative {
  implicit class ApplicativeOps[A, F[_]](a: F[A])(implicit F: Applicative[F]) {
    def fapply[B](f: F[A => B]): F[B] = F.fapply(a, f)
  }

  implicit def SeqApplicative: Applicative[Seq] = new Applicative[Seq] {
    def pure[A](x: A) = Seq(x)
    def fapply[A, B](xs: Seq[A], fs: Seq[A => B]): Seq[B] = xs.flatMap(x => fs.map(_(x)))
  }
}

它的要点是我必须为所有fmap实现Applicative,但它应该与我的FunctorOps类中定义的方法相同。我该如何以最干净的方式做到这一点?

1 个答案:

答案 0 :(得分:1)

你有Applicative[F] <: Functor[F]部分,但你应该考虑这意味着什么。这意味着Applicative[F] 的实例也为Functor[F]提供了方法。也就是说,你不能同时拥有implicit val listFunctor: Functor[List]; implicit val listApplicative: Applicative[List],因为当你要求implicit param: Functor[List]时,编译器会感到困惑。你应该只有后者。您尝试做的事情是荒谬的,因为您根据自身定义了Functor F个实例(因为Applicative[F] Functor[F]),无论如何最终只有两个Functor[Seq]

fmap

然后移除pure个实例并保持fapply的实际状态(或覆盖trait Applicative[F[_]] extends Functor[F] { override def fmap[A, B](r: F[A], f: A => B): F[B] = fapply(r, pure(f)) def pure[A](x: A): F[A] def fapply[A, B](r: F[A], f: F[A => B]): F[B] } ,如果您愿意)。你现在有一个不同的问题,因为隐式搜索有点转过来,因为Functor[Seq]实例实际上在Applicative[Seq],但修复隐式解决方案比强制执行单独{{1}的一致性更麻烦}和fmap个实例。在这种情况下,Functor[Seq]是一种您无法控制其伴随对象的类型,至少cats会执行类似

的操作
object Applicative

仅供参考:我建议讨论你的类型类方法

Functor

Applicative

中加入Seq可能会很不错
package instances {
  trait SeqInstances {
    implicit val seqFunctor: Functor[Seq] = ???
  }
  package object seq extends SeqInstances
  package object all extends SeqInstances
                        with AAAInstances
                        with BBBInstances
                        with ...
}