一些背景代码
/** 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仿函数的东西?
答案 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
同构,但是这种同构的见证要求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
因此,在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
此实例需要s
和m
类型的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
进行自然转换,然后实例化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。