一步一步/深度解释:通过Coroutines的(Co)Yoneda(最好是scala)的力量

时间:2014-06-02 17:54:03

标签: scala haskell category-theory

一些背景代码

/** 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[_], B, A](x: F[B])(f: B => A): Aux[F, A, 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只是从类型 -  即  他们量化/抽象在某些类型构造函数中修复的映射   F和某些类型a,对于返回F [B]或(Co)Yoneda [F,B]的任何类型B. 因此,提供免费的地图融合(?这类似于地图的剪切规则?)。 但我发现Coyoneda是任何类型构造函数F的函子,无论F是Functor,  并且我没有完全掌握。 现在我正处于我试图定义Coroutine类型的情况,  (我正在查看https://www.fpcomplete.com/school/to-infinity-and-beyond/pick-of-the-week/coroutines-for-streaming/part-2-coroutines开始使用的类型)

case class Coroutine[S[_], M[_], R](resume: M[CoroutineState[S, M, R]])

sealed trait CoroutineState[S[_], M[_], R]

  object CoroutineState {
    case class Run[S[_], M[_], R](x: S[Coroutine[S, M, R]]) extends CoroutineState[S, M, 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, M, x]})#l] {
        override def map[A, B](f : A => B) : CoroutineState[S, M, A] => CoroutineState[S, M, B]
        =
        { ??? }
    }
  }

我认为如果我更好地理解Coyoneda,我可以利用它来制作S& M类型构造函数的构造方式很简单,而且我看到Coyoneda可能在定义递归方案中扮演一个角色  因为函子的要求很普遍。

那么我怎样才能使用coyoneda来创建类型构造函数,例如coroutine状态? 或类似Pause仿函数的东西?

1 个答案:

答案 0 :(得分:32)

Yoneda的秘诀在于它"延迟"需要Functor实例。一开始很棘手,因为我们可以在不使用instance 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 a的聪明部分是它应该与f a同构,但是这种同构的见证要求fFunctor }:

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

因此,在Functor f Functor实例的定义过程中,Yoneda实例Yoneda没有吸引人,而是“#{构建fmap本身。在计算上,它还具有将所有(a -> b)转换为具有"延续"函数CoYoneda

相反的情况发生在CoYoneda f。例如,Functor仍为f,无论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只不过是最终延续时的构图。

所以这两种方式都是"忽略"一段fmap的要求,特别是在执行Coroutine时。


现在让我们谈谈这个我认为有Haskell类型的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)

Functor

此实例需要sm类型的Yoneda个实例。我们可以使用CoYonedadata 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,您sm都需要s个实例才能提取mCoroutine中的mapCoYoneda :: (forall a . f a -> g a) -> CoYoneda f a -> CoYoneda g a mapCoYoneda phi (CoYoneda mp fb) = CoYoneda mp (phi fb) 类型。那么重点是什么?

f

好吧,如果我们从gFunctor进行自然转换,然后实例化fromCoYoneda,那么我们可以在最后应用它来提取结果。此结构映射仅应用一次,然后在评估fmap时,整个堆栈的组合Yoneda ped函数将命中结果。


您可能希望使用Monad的另一个原因是即使Yoneda f不是{39},有时也可能获得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) 。例如

Monad YEndo

我们通过将YEndo视为CPS转化Maybe monad来获得s的定义。

如果Coroutine必须保持一般性,那么这种工作显然不是很有用,但如果具体地实例化{{1}}则可能是有益的。这个例子直接来自Edward Kmett的帖子Free Monads for Less 2