一步一步/深层解释:(Co)Yoneda(优选在scala)通过协同的力量
一些背景代码
/** FunctorStr: ∑ F[-]. (∏ A B. (A -> B) -> F[A] -> F[B]) */ trait FunctorStr[F[_]] { self => def map[A,B](f: A => B): F[A] => F[B] } trait Yoneda[F[_],A] { yo => def apply[B](f: A => B): F[B] def run: F[A] = yo(x => x) def map[B](f: A => B): Yoneda[F,B] = new Yoneda[F,B] { def apply[X](g: B => X) = yo(f andThen g) } } object Yoneda { implicit def yonedafunctor[F[_]]: FunctorStr[({ type l[x] = Yoneda[F,x] })#l] = new FunctorStr[({ type l[x] = Yoneda[F,x] })#l] { def map[A,B](f: A => B): Yoneda[F,A] => Yoneda[F,B] = _ map f } def apply[F[_]: FunctorStr,X](x: F[X]): Yoneda[F,X] = new Yoneda[F,X] { def apply[Y](f: X => Y) = Functor[F].map(f) apply x } } trait Coyoneda[F[_],A] { co => type I def fi: F[I] def k: I => A final def map[B](f: A => B): Coyoneda.Aux[F,B,I] = Coyoneda(fi)(f compose k) } object Coyoneda { type Aux[F[_],A,B] = Coyoneda[F,A] { type I = B } def apply[F[_],A](x: F[B])(f: B => A): Aux[F,B] = new Coyoneda[F,A] { type I = B val fi = x val k = f } implicit def coyonedaFunctor[F[_]]: FunctorStr[({ type l[x] = Coyoneda[F,x] })#l] = new CoyonedaFunctor[F] {} trait CoyonedaFunctor[F[_]] extends FunctorStr[({type l[x] = Coyoneda[F,x]})#l] { override def map[A,B](f: A => B): Coyoneda[F,A] => Coyoneda[F,B] = x => apply(x.fi)(f compose x.k) } def liftCoyoneda[T[_],A](x: T[A]): Coyoneda[T,A] = apply(x)(a => a) } 现在我以为我理解yoneda和coyoneda有点只是从类型 – case class Coroutine[S[_],M[_],R](resume: M[CoroutineState[S,M,R]]) sealed trait CoroutineState[S[_],R] object CoroutineState { case class Run[S[_],R](x: S[Coroutine[S,R]]) extends CoroutineState[S,R] case class Done[R](x: R) extends CoroutineState[Nothing,Nothing,R] class CoroutineStateFunctor[S[_],M[_]](F: FunctorStr[S]) extends FunctorStr[({ type l[x] = CoroutineState[S,x]})#l] { override def map[A,B](f : A => B) : CoroutineState[S,A] => CoroutineState[S,B] = { ??? } } } 我认为如果我更好地理解Coyoneda,我可以利用它来做S& M类型构造函数方法容易,加上我看到Coyoneda可能在定义递归方案中发挥作用 所以我怎么能使用coyoneda来使类型构造函数像例如协同状态? 解决方法
Yoneda的秘密是它“稍稍”推迟Functor实例的需要。这是棘手的第一,因为我们可以定义实例Functor(Yoenda f),而不使用f的Functor实例。
newtype Yoneda f a = Yoneda { runYoneda :: forall b . (a -> b) -> f b } instance Functor (Yoneda f) where fmap f y = Yoneda (ab -> runYoneda y (ab . f)) 但是关于Yoneda的聪明的部分是它应该是同构的,但是这个同构的见证要求f是一个Functor: toYoneda :: Functor f => f a -> Yoneda f a toYoneda fa = Yoneda (f -> fmap f fa) fromYoneda :: Yoneda f a -> f a fromYoneda y = runYoneda y id 因此,在Yoneda的Functor实例的定义期间,不是对Functor实例呼吁f,它对Yoneda本身的构造获得“deferred”。在计算上,它还具有将所有fmaps变成具有“延续”函数(a→b)的组合的好的属性。 相反发生在CoYoneda。例如,无论f是否,CoYoneda f仍然是一个Functor data CoYoneda f a = forall b . CoYoneda (b -> a) (f b) instance Functor (CoYoneda f) where fmap f (CoYoneda mp fb) = CoYoneda (f . mp) fb 然而现在当我们构造同构的见证时,Functor实例被要求在另一边,当降低CoYoenda f a到f a: toCoYoneda :: f a -> CoYoneda f a toCoYoneda fa = CoYoneda id fa fromCoYoneda :: Functor f => CoYoneda f a -> f a fromCoYoneda (CoYoneda mp fb) = fmap mp fb 我们再次注意到fmap的属性,只是组成沿着最终的延续。 所以这两种都是一种“忽略”Functor需求一段时间,特别是在执行fmaps时。 现在让我们谈谈这个我认为有一个Haskell类型的Coroutine data Coroutine s m r = Coroutine { resume :: m (St s m r) } data St s m r = Run (s (Coroutine s m r)) | Done r instance (Functor s,Functor m) => Functor (Coroutine s m) where fmap f = Coroutine . fmap (fmap f) . resume instance (Functor s,Functor m) => Functor (St s m) where fmap f (Done r) = Done (f r) fmap f (Run s ) = Run (fmap (fmap f) s) 这个实例需要对s和m类型的Functor实例。我们可以使用Yoneda或CoYoneda来消除它们吗?基本上自动: data Coroutine s m r = Coroutine { resume :: CoYoneda m (St s m r) } data St s m r = Run (CoYoneda s (Coroutine s m r)) | Done r instance Functor (Coroutine s m) where fmap f = Coroutine . fmap (fmap f) . resume instance Functor (St s m) where fmap f (Done r) = Done (f r) fmap f (Run s ) = Run (fmap (fmap f) s) 但现在,由于我使用CoYoneda,你将需要Functor实例为s和m,以提取s和m类型从您的Coroutine。那么有什么意义呢? mapCoYoneda :: (forall a . f a -> g a) -> CoYoneda f a -> CoYoneda g a mapCoYoneda phi (CoYoneda mp fb) = CoYoneda mp (phi fb) 好吧,如果我们有一个从我们的f到g的自然转换,它实例化Functor,然后我们可以应用它在结尾以提取我们的结果。这个结构映射将仅应用一次,然后,在从CoYoneda评估时,构成的fmapped函数的整个栈将命中结果。 你可能想玩Yoneda的另一个原因是,有时可能得到Monad实例为Yoneda f即使当f甚至不是一个Functor。例如 newtype Endo a = Endo { appEndo :: a -> a } -- YEndo ~ Yoneda Endo data YEndo a = YEndo { yEndo :: (a -> b) -> (b -> b) } instance Functor YEndo where fmap f y = YEndo (ab -> yEndo y (ab . f)) instance Monad YEndo where return a = YEndo (ab _ -> ab a) y >>= f = YEndo (ab b -> yEndo y (a -> yEndo (f a) ab b) b) 其中我们通过将YEndo视为CPS转换的Maybe monad来获得Monad YEndo的定义。 这种工作显然不是有用的,如果s必须离开一般,但如果实例化Coroutine具体的话可能是有益的。这个例子直接从爱德华Kmett的帖子Free Monads for Less 2。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |