This question涉及从monad中构建正确的Monad
实例,但仅限于某些约束 - 例如Set
。诀窍是将其包装到ContT
中,这会将约束推迟到包装/展开其值。
现在我想对Applicative
做同样的事情。特别是,我有一个Applicative
实例,其 pure 具有类型类约束。有没有类似的技巧如何构建有效的Applicative
实例?
(是否有“所有应用函子的母亲”和there is for monads一样?)
答案 0 :(得分:5)
可能最一致的方法是从Category
开始,对对象进行限制很自然: Object !
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
然后我们定义类似于how Edward does it
的仿函数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 library中实现,其中 - 羞辱我! - 仍然不在Hackage上。
遗憾的是,{p>Applicative
不那么简单。 Mathematically, these are monoidal functors,所以we first need monoidal类别。 categories
has that class,但它不适用于基于约束的版本,因为我们的对象总是带有约束的*
种类。所以我所做的是构成一个Curry
class,这类似于近似。
然后,我们可以Monoidal
functors:
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)
尽管如此,它确实有效 - 对于无约束的情况,呃!我还没有找到一种方便的方法来使用它与非平凡的约束。
但同样,Applicative
相当于Monoidal
,可以在the Set
example中演示使用。
答案 1 :(得分:4)
我不确定“限制性应用”的概念是否独特,因为不同的呈现不是同构的。这就是说,这是一个至少在某种程度上与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
编辑:修正了一些错误。这就是在发布之前没有编译时会发生的情况。
答案 2 :(得分:0)
因为每个Monad都是Functor,所以你可以使用相同的ContT技巧。
pure
变为return
fmap f x
变为x >>= (return . f)