applicatives/monads 都保留幺半群结构但方式略有不同的实际含义是什么?

What are the practical implications that both applicatives/monads preserve monoidal structure but in a slightly different way?

我读过这个但不理解类别理论部分。

到目前为止,这是我的推理:当我查看类型时

F (a -> b) -> F a -> F b
(a -> M b) -> M a -> M b

a -> F a
a -> M a

唯一在类型级别类似于幺半群的部分是类型构造函数,即 applicative/monadic 上下文:

// binary operation
F -> F -> F
M -> M -> M

// identity element
F
M

所以我会说 applicatives/monads 就其上下文而言是幺半群的,因为它们将相同类型的两个上下文组合为一个。 pure/return创建了一个最小的上下文,因此我们可以将其视为类似于幺半群的恒等元素的恒等上下文,它创建了一个"most minimal value".

但是,monads/applicatives 在它们的类型参数中不是幺半群的,因为它们包括从 ab 的转换。

我不确定我的推理是否合理。令我感到困惑的是,一方面的幺半群和另一方面的 applicatives/monads 以不同的方式组合事物:

Nothing <> (Just "bar") -- Just "bar"
(++) <$> Nothing <*> (Just "bar") -- Nothing
Nothing >>= (\x -> (Just "bar") >>= (return . (++) x)) -- Nothing

但是,我猜不同的结果值是由于幺半群将表达式解释为普通值,而 applicatives/monads 将表达式解释为计算(在上面的示例中可能会失败)。

现在在前面提到的问答中,单子在内函子类别中是幺半群的,而应用程序是松弛幺半群函子。我不完全理解这一点,但显然应用程序只部分保留了幺半群结构,而单子则完全保留了它。从函数式程序员的角度来看,这种差异的实际含义是什么?

我问这个问题是为了更好地理解 applicative/monads 以及导致不同表达方式的原因。

我们应该弄清楚我们在这里处理的是三个完全不同的幺半群:

  • Haskell 中的幺半群,即 Monoid class 的实例。这些是 值级幺半群 ,即您将 Haskell 值(例如列表)组合在一起。
  • 从数学上讲,应用程序是 monoidal functors,但是幺半群没有任何东西可以写一个 Monoid 实例;在Haskell上下文中,它是由单元类型()和元组构造器(,)构造的类型级幺半群。 (请注意,如果您假装 (Int, (String, Bool))((Int, String), Bool) 是同一类型,则这只是一个幺半群。)
    这些元组在 Applicative class' 方法,但这只是因为它们已经通过柯里化隐藏了。 class 更通用的公式是

    class Functor f => Monoidal f where
      funit ::    ()     -> f  ()
      fzip :: (f a, f b) -> f (a,b)
    

    这是一个很好的练习,可以证明这等同于标准 Applicative class。
    幺半群函子的特征实际上是它保留了这个 (,) monoid 结构同时进行其函子映射,但这与任何特定的 Haskell monoid 并没有太大关系。

  • 单子作为一个半幽默的流行术语一直在流传,单子群属于内函子。但那是 我们正在谈论的另一个幺半群,即仿函数应用程序堆栈的幺半群。与 Applicative 一样,class 的数学公式有点不同:

    class Monoidal m => Monad m where
      pure ::      a  -> m a
      join :: m (m a) -> m a
    

    即这里的幺半群是m.

  • 的一种组合

所以除此之外,当比较涉及 monads/applicatives 的事物与某些特定的 Haskell 幺半群时,您得到不同的行为真的不应该让我们感到惊讶。在某些情况下,两者的行为相同,但这基本上可以归因于使用了一个实例,该实例将更高级别的幺半群结构退化为当与固定的包含类型一起使用时,同构于非-参数 Monoid 实例。