2017-10-10 1 views
0

구체적으로 타입 클래스를 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))) 
    } 
} 

그것의 요점은 내가 모든 Applicative들에 대한 fmap을 구현해야하지만, 내 FunctorOps 클래스에 정의 된대로 정말 같은 방법으로해야합니다. 어떻게하면 가능한 한 가장 깨끗한 방법으로이 작업을 수행 할 수 있습니까?

답변

1

당신은 Applicative[F] <: Functor[F] 부분을 가지고 있지만 그게 무슨 뜻인지 정말로 생각해야합니다. 이는 Applicative[F]의 인스턴스가 Functor[F]에 대한 방법을 제공한다는 것을 의미합니다. 즉, implicit param: Functor[List]을 요구할 때 컴파일러가 혼란 스럽기 때문에 implicit val listFunctor: Functor[List]; implicit val listApplicative: Applicative[List]을 둘 다 가질 수 없습니다. 후자 만 있어야합니다. 당신이 그 자체의 관점에서 FFunctor 인스턴스를 정의하고 있기 때문에 당신이 할하려는 것은, 다음 무의미합니다 (Applicative[F]Functor[F]을해야하기 때문에)와 상관없이 두 Functor[Seq]의와 끝까지.

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을 무시) : 당신이 입니다 purefapply의 측면에서 fmap을 구현 할 수

. Functor[Seq] 인스턴스가 실제로 object Applicative에 있기 때문에 암시 적 검색이 다소 돌아서는 있지만 다른 암시 적 검색은 달라 지지만 암시 적 해결책을 고치는 것은 별도의 FunctorApplicative 인스턴스의 일관성을 적용하는 것보다 덜 부담 스럽습니다. 내가

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] 

당신의 typeclass 방법 무두질 제안하고있을 수 있습니다 : 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 ... 
} 

같은 것을 수행 가지고 좋은 내가 주어진 typec을 확장하는 아이디어를 기반으로하고있어 일을 할 flip fapply

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

ApplicativeOps에 그것의 근원을 바꾸기없이 lass. 그래서 : 1) 암시적인 클래스를 제거하는 것을 포함하여, 나의'Functor' 코드에 대해서는 아무것도 변경할 수 없습니다. 즉, 내가 확장하고 싶은 임의의 typeclass를 얻으면? 2)'fmap' (또는 내가 주어진 typeclass로부터 상속 받고있는 함수)은 제 typeclass 함수의 관점에서 정의하기가 쉽지 않을 수 있습니다. – allidoiswin

+1

2)가 아닙니다. 수퍼 클래스의 메소드를 서브 클래스의 관점에서 정의 할 수 없다면, 추상화 된 상태로두고 구현자를 얻으십시오 (하스켈처럼, 오직 유일한 (휴대용) 방법입니다). 1) 기존의 수정 불가능한 수퍼 클래스 인스턴스가있는 경우 슈퍼 클래스로 연기하도록 서브 클래스 인스턴스를 정의해야합니다 (자신의 도우미 클래스에서이를 멀리 추상화 할 수 있습니다 ('Helper [F [_]] (f : Functor [F]) {...}'), 서브 클래스 자체에 그 제한을 부과하지 않습니다.) 서브 클래스와 수퍼 클래스 인스턴스를 함께 가져 오지 않도록하십시오. – HTNW