为什么Kleisli不是Monoid的一个例子?

时间:2015-10-04 16:39:57

标签: haskell monads monoids kleisli

如果你想附加两个类型的函数(a - > m b),所以只得到一个同一类型的函数追加两个结果,你可以使用Kleisli这样做:

instance (Monad m, Monoid b) => Monoid (Kleisli m a b) where
    mempty = Kleisli (\_ -> return mempty)
    mappend k1 k2 =
        Kleisli g
            where
                g x = do
                    r1 <- runKleisli k1 x
                    r2 <- runKleisli k2 x
                    return (r1 <> r2)

但是,目前Control.Arrow中没有定义此类实例。 通常,在Haskell,我怀疑有一个很好的理由,但找不到哪一个。

注意

这个问题与this one非常相似。但是,对于Monoid,我没有看到定义实例的方法,例如:

instance (Monad m, Monoid b) => Monoid (a -> m b) where
    [...]

因为已有一个实例:

instance Monoid b => Monoid (a -> b) where
    [...]

1 个答案:

答案 0 :(得分:26)

在图书馆设计业务中,我们在这里面临一个选择点,我们选择在集体政策(或缺乏集体政策)中不完全一致。

Monoid(或Monad)类型构造函数的

Applicative个实例可以通过多种方式实现。逐点提升始终可用,但我们没有定义

instance (Applicative f, Monoid x) => Monoid (f x) {- not really -} where
  mempty         = pure mempty
  mappend fa fb  = mappend <$> fa <*> fb

请注意,instance Monoid (a -> b)只是一个逐点提升,因此只要(a -> m b)的幺半群实例为{{1}上的幺半群逐点提升,就会发生m b的逐点提升。 }}

我们一般不做逐点提升,不仅因为它会阻止其载体碰巧应用类型的其他b实例,而且因为Monoid的结构通常被认为更多比f重要。一个关键的例子是 free monoid,更好地称为x,它是[x] Monoid[],而不是(++)逐点提升。幺半群结构来自列表包装,而不是来自包裹的元素。

我的首选经验确实是优先考虑类型构造函数中固有的幺半群结构,而不是点状提升,或类型的特定实例的幺半群结构,如a -> a的组合monoid。这些可以并且确实得到newtype包装。

关于Monoid (m x)是否应该与MonadPlus m同时存在(并且与Alternative类似)的争论突然爆发。我的感觉是唯一的好MonadPlus实例是Monoid实例的副本,但其他实例不同。尽管如此,图书馆在这个问题上仍然不一致,特别是在这个问题上(很多读者都会看到我的这个古老的虫子来了)......

... Maybe的monoid实例,它忽略了我们经常使用Maybe来模拟可能的失败这一事实,而是观察到在额外元素中卡住的相同数据类型的想法可以用于给半群一个中性元素,如果它还没有。这两种结构产生了同构类型,但它们在概念上并不相同。 (编辑更糟糕的是,当只需要Monoid时,这个想法很难实现,为实例提供了Semigroup约束。我希望看到{{ 1}} - 已实现扩展到 - Semigroup的想法,但<{1}}的不是。)

特别回到Monoid,我们有三个明显的候选实例:

  1. Maybe Kleisli和Kleisli撰写
  2. Monoid (Kleisli m a a)提升returnMonadPlus m => Monoid (Kleisli m a b)逐点mzero
  3. mplus->的幺半群结构提升为Monoid b => Monoid (Kleisli m a b),然后b
  4. 我希望没有做出任何选择,只是因为不清楚做出哪个选择。我对此犹豫不决,但我的投票结果为2,优先考虑m来自->的结构的结构。