这个问题涉及Monad
从 monad 构造一个适当的实例,但仅在某些约束下 - 例如Set
。诀窍是将它包装成ContT
,这会将约束推迟到包装/展开其值。
现在我想对Applicative
s 做同样的事情。特别是,我有一个Applicative
实例,其pure具有类型类约束。有没有类似的技巧如何构造一个有效的Applicative
实例?
(是否有“所有应用函子之母”,就像单子一样?)
最一致的可用方式可能是从 开始Category
,对对象有限制是很自然的:对象!
class Category k where
type Object k :: * -> Constraint
id :: Object k a => k a a
(.) :: (Object k a, Object k b, Object k c)
=> k b c -> k a b -> k a c
然后我们定义函子,类似于Edward 的做法
class (Category r, Category t) => Functor f r t | f r -> t, f t -> r where
fmap :: (Object r a, Object t (f a), Object r b, Object t (f b))
=> r a b -> t (f a) (f b)
所有这些都运行良好,并且在constrained-categories 库中实现,这 -真丢人!– 仍然不在 Hackage 上。
Applicative
不幸的是,做起来不那么简单。在数学上,这些是幺半群函子,所以我们首先需要 幺半群类别。categories
有那个类,但它不适用于基于约束的版本,因为我们的对象总是*
带有约束的任何类型。所以我所做的就是组成一个Curry
类,这与这个很接近。
然后,我们可以做Monoidal
仿函数:
class (Functor f r t, Curry r, Curry t) => Monoidal f r t where
pure :: (Object r a, Object t (f a)) => a `t` f a
fzipWith :: (PairObject r a b, Object r c, PairObject t (f a) (f b), Object t (f c))
=> r (a, b) c -> t (f a, f b) (f c)
这实际上等价于Applicative
我们有适当的封闭笛卡尔类别。在受限类别版本中,不幸的是,签名看起来非常糟糕:
(<*>) :: ( Applicative f r t
, MorphObject r a b, Object r (r a b)
, MorphObject t (f a) (f b), Object t (t (f a) (f b)), Object t (f (r a b))
, PairObject r (r a b) a, PairObject t (f (r a b)) (f a)
, Object r a, Object r b, Object t (f a), Object t (f b))
=> f (r a b) `t` t (f a) (f b)
尽管如此,它确实有效——对于不受约束的情况,呵呵!我还没有找到一种方便的方法来使用它与非平凡的约束。
我不确定“受限应用”的概念是否独特,因为不同的表示不是同构的。这里所说的至少在某种程度上类似于 Codensity 。这个想法是有一个“自由函子”和一个单元
{-# LANGUAGE TypeFamilies, ConstraintKinds, ExistentialQuantification #-}
import GHC.Prim (Constraint)
import Control.Applicative
class RFunctor f where
type C f :: * -> Constraint
rfmap :: C f b => (a -> b) -> f a -> f b
class RFunctor f => RApplicative f where
rpure :: C f a => a -> f a
rzip :: f a -> f b -> f (a,b)
data UAp f a
= Pure a
| forall b. Embed (f b) (b -> a)
toUAp :: C f a => f a -> UAp f a
toUAp x = Embed x id
fromUAp :: (RApplicative f, C f a) => UAp f a -> f a
fromUAp (Pure x) = rpure x
fromUAp (Embed x f) = rfmap f x
zipUAp :: RApplicative f => UAp f a -> UAp f b -> UAp f (a,b)
zipUAp (Pure a) (Pure b) = Pure (a,b)
zipUAp (Pure a) (Embed b f) = Embed b (\x -> (a,f x))
zipUAp (Embed a f) (Pure b) = Embed a (\x -> (f x,b))
zipUAp (Embed a f) (Embed b g) = Embed (rzip a b) (\(x,y) -> (f x,g y))
instance Functor (UAp f) where
fmap f (Pure a) = Pure (f a)
fmap f (Embed a g) = Embed a (f . g)
instance RApplicative f => Applicative (UAp f) where
pure = Pure
af <*> ax = fmap (\(f,x) -> f x) $ zipUAp af ax
编辑:修复了一些错误。这就是您在发布前不编译时会发生的情况。
因为每个 Monad 都是 Functor,所以您可以使用相同的 ContT 技巧。
pure
变成return
fmap f x
变成x >>= (return . f)