如何使用约束构造Applicative实例(类似于使用ContT构造Monad实例)

时间:2014-01-13 19:15:22

标签: haskell type-constraints applicative

This question涉及从monad中构建正确的Monad实例,但仅限于某些约束 - 例如Set。诀窍是将其包装到ContT中,这会将约束推迟到包装/展开其值。

现在我想对Applicative做同样的事情。特别是,我有一个Applicative实例,其 pure 具有类型类约束。有没有类似的技巧如何构建有效的Applicative实例?

(是否有“所有应用函子的母亲”和there is for monads一样?)

3 个答案:

答案 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)