【问题标题】:Scala: deferring a trait method to an implicit class in parent trait's objectScala:将特征方法推迟到父特征对象中的隐式类
【发布时间】:2018-03-21 07:06:30
【问题描述】:

具体来说,我正在尝试使用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)))
  }
}

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

【问题讨论】:

    标签: scala generics functional-programming typeclass implicit


    【解决方案1】:

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

    可以做的是在purefapply方面实现fmap

    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] 原样(或根据需要覆盖 fmap)。你现在有一个不同的问题,因为隐式搜索有点转向,因为Functor[Seq] 实例实际上在object Applicative 中,但是修复隐式解析比强制独立的FunctorApplicative 的一致性要简单实例。在这种情况下,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 ...
    }
    

    仅供参考:我建议使用你的类型类方法

    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]
    

    ApplicativeOps 中有一个flip fapply 可能会很好

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

    【讨论】:

    • 我想做我正在做的事情,基于扩展给定类型类而不更改其源的想法。所以:1) 我无法更改我的Functor 代码,包括删除隐式类。即,如果我得到一个想要扩展的随机类型类怎么办? 2) fmap(或我从给定的类型类继承的任何函数)根据我的类型类的函数可能不太容易定义。
    • 2) 没有实际意义。如果您不能根据子类定义超类的方法,那么您只需将它们保留为抽象并让实现者去做(就像在 Haskell 中,它是 only (可移植的)方式。) . 1)如果你有一些现有的、不可修改的超类实例,唯一明智的做法是定义你的子类实例以推迟到超类(你可以在它自己的帮助类(Helper[F[_]](f: Functor[F]) { ... })中抽象出来,但是 don 't 对子类本身施加该限制)并确保不要同时导入子类和超类实例。
    猜你喜欢
    • 2015-05-26
    • 2017-10-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多