2017-10-10 210 views
0

具体而言,我试图用Applicative扩展Functor类特定类。Scala:将特征方法推迟到父特征对象中的隐式类

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 S,但它确实应该在我的FunctorOps类中定义的方法相同。我如何以最干净的方式做到这一点?

回答

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]秒。

什么你可以做的就是实现fmappurefapply方面:

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

FYI:我建议讨好你的类型类的方法

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] 

,并可能有一个好的flip fapplyApplicativeOps

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

我想要做的基础上延伸的给定typec的想法我在做什么没有改变它的来源。所以: 1)我不能改变任何关于我的'Functor'的代码,包括删除隐式类。即如果我得到一个我想要扩展的随机类型类型呢? 2)'fmap'(或者我从给定的类型类继承的任何函数)可能不像我的类型类的函数那么容易定义。 – allidoiswin

+1

2)是没有意义的。如果你不能根据子类来定义超类的方法,那么你只需要让它们抽象并让实现者去做就可以了(就像在Haskell中,它是* only *(便携)方式)。 1)如果你有一些现有的,不可修改的超类实例,唯一可行的方法是定义你的子类实例以遵循超类(你可以在它自己的帮助类('Helper [F [_]](f :Functor [F]){...}'),但*不要*对子类本身施加这种限制),并确保不要将子类和超类实例一起导入。 – HTNW