简体   繁体   English

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

[英]Scala: deferring a trait method to an implicit class in parent trait's object

Specifically, I'm trying to extend my Functor typeclass with Applicative . 具体来说,我正在尝试通过Applicative扩展我的Functor类型类。

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)))
  }
}

The gist of it is I have to implement fmap for all Applicative s, but it should really be the same method as defined in my FunctorOps class. 要点是我必须为所有Applicative s实现fmap ,但实际上它应该与FunctorOps类中定义的方法相同。 How do I do this in the cleanest way possible? 如何以最干净的方式做到这一点?

You got the Applicative[F] <: Functor[F] part right, but you should really think about what that means. 您正确使用了Applicative[F] <: Functor[F]部分,但您应该真正考虑一下这是什么意思。 It means that an instance of Applicative[F] also provides the methods for Functor[F] . 这意味着Applicative[F]的实例提供了Functor[F]的方法。 That is, you can't have both implicit val listFunctor: Functor[List]; implicit val listApplicative: Applicative[List] 也就是说,您不能同时拥有两个implicit val listFunctor: Functor[List]; implicit val listApplicative: Applicative[List] implicit val listFunctor: Functor[List]; implicit val listApplicative: Applicative[List] , because then the compiler is confused when you ask for implicit param: Functor[List] . implicit val listFunctor: Functor[List]; implicit val listApplicative: Applicative[List] ,因为当您要求implicit param: Functor[List]时,编译器会感到困惑。 You should only have the latter. 您应该只有后者。 What you're trying to do is then nonsensical, because you're defining the Functor instance for F in terms of itself (because the Applicative[F] should be the Functor[F] ), and you end up with two Functor[Seq] s regardless. 然后,您尝试做的事情是荒谬的,因为您是根据F本身定义FFunctor实例的(因为Applicative[F]应该 Functor[F] ),最后得到两个Functor[Seq]不管。

What you can do is implement fmap in terms of pure and fapply : 可以做的是落实fmap来讲purefapply

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]
}

Then remove the Functor[Seq] instance and keep your Applicative[Seq] the way it is (or override fmap if you want). 然后删除Functor[Seq]实例,并保持Applicative[Seq]的状态fmap (或根据需要覆盖fmap )。 You have a different problem now, being that implicit search gets a bit turned around, as the Functor[Seq] instance is actually in object Applicative , but fixing implicit resolution is less onerous than enforcing the consistency of separate Functor and Applicative instances. 现在您遇到了另一个问题,因为Functor[Seq]实例实际上在object Applicative ,所以隐式搜索需要解决一些问题,但是解决隐式分辨率要比强制执行单独的FunctorApplicative实例的一致性少。 In this case, where Seq is a type whose companion object you cannot control, cats , at least, does something like 在这种情况下,如果Seq是您无法控制其伴随对象的类型,则cats至少会执行以下操作

package instances {
  trait SeqInstances {
    implicit val seqFunctor: Functor[Seq] = ???
  }
  package object seq extends SeqInstances
  package object all extends SeqInstances
                        with AAAInstances
                        with BBBInstances
                        with ...
}

FYI: I suggest currying your typeclass methods 仅供参考:我建议使用您的类型类方法

def fmap[A, B](r: F[A])(f: A => B): F[B]
def fapply[A, B](r: F[A])(f: F[A => B]): F[B]

and it may be nice to have a flip fapply in ApplicativeOps ApplicativeOps进行flip fapply可能会很好

def ylppaf[I, B](f: F[I])(implicit ev: A =:= (I => B))
  : F[B] = F.fapply(a.fmap(ev))(f)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM