我已经读过Q&A,但不理解类别理论的一部分。
到目前为止,这是我的推理:当我查看类型时
F (a -> b) -> F a -> F b
(a -> M b) -> M a -> M b
a -> F a
a -> M a
在类型级别上唯一类似于一个monoid的部分是类型构造函数,即应用/单调上下文:
// binary operation
F -> F -> F
M -> M -> M
// identity element
F
M
因此,我认为应用程序/单子在上下文方面是单调的,因为它们将相同类型的两个上下文组合为一个。pure
/ return
创建了一个最小的上下文,因此我们可以将其视为与一个monoid的标识元素相似的身份上下文,从而创建一个“最大的最小值”。
但是,单子/应用程序的类型参数不是单项式的,因为它们包括从a
到b
的转换。
我不确定我的推理是否有意义。让我感到困惑的是,一方面的类人独具一格的东西,另一方面,应用性/单子独一的结合方式则不同:
Nothing <> (Just "bar") -- Just "bar"
(++) <$> Nothing <*> (Just "bar") -- Nothing
Nothing >>= (\x -> (Just "bar") >>= (return . (++) x)) -- Nothing
但是,我猜想不同的结果值是由于等分体将表达式解释为普通值,而应用程序/ monads将表达式解释为计算(在上面的示例中可能会失败)。
[在前面的问答中,现在指出单子在内爆函子的类别中是单半体的,而施用物是松散的单半子函子。我不完全理解,但显然应用程序仅部分保留了单面体结构,而单子结构则完全保留了它。从功能程序员的角度来看,这种差异的实际含义是什么?
我问这个问题是为了更好地理解应用性/单语,以及引起不同表达的原因。
我们应该整理一下,我们在这里处理三个截然不同的半身像:
Monoid
类的实例。这些是value-level monoids,即您将Haskell值(例如列表)组合在一起。从数学上讲,应用程序是monoidal functors,但对于等腰线而言,您无法编写Monoid
实例;在Haskell上下文中,它是由单元类型()
和元组构造函数(,)
构造的type-level monoid。 (请注意,如果您假装(Int, (String, Bool))
和((Int, String), Bool)
是相同的类型,则这只是一个monoid。)这些元组在Applicative
类的方法中不是直接可见的,但这仅仅是因为它们已被隐藏通过咖喱。该类的更笼统的表述是
class Functor f => Monoidal f where
funit :: () -> f ()
fzip :: (f a, f b) -> f (a,b)
证明这等效于标准Applicative
类是一个很好的练习。 Monoidal函子的特征实际上在于它在执行其函子映射时保留了此(,)
monoid结构,但实际上与任何特定的Haskell monoid并没有多大关系。
Monad,作为一个半幽默的嗡嗡声不停地飘浮,是monofoids in endofunctors。但是,这是我们正在讨论的另一个monoid,即函子应用程序堆栈的monoid。与Applicative
一样,该类的数学公式也有所不同:class Monoidal m => Monad m where
pure :: a -> m a
join :: m (m a) -> m a
即这里的类人动物是。因此,在将涉及monads / applicatives的对象与某些特定Haskell monoid进行比较时,您会得到不同的行为,这真的不足为奇。在某些情况下,两者的行为相同,但基本上可以归结为使用了一个实例,该实例将更高级的单曲面结构退化为某种东西,当与固定的包含类型一起使用时,该东西同构为非参数m
应用的一种composition
Monoid
实例。