2017-10-10 195 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