简单的例子来说明Category,Monoid和Monad?

Zna*_*atz 49 monads haskell category-theory monoids

我对这三个概念感到非常困惑.

有没有简单的例子来说明Category,Monoid和Monad之间的区别?

如果有这些抽象概念的说明,将会非常有帮助.

ale*_*tor 93

这可能不是您正在寻找的答案,但无论如何你去了:

看着monads&co的一种非常歪曲的方式.

查看这些抽象概念的一种方法是将它们与基本概念相链接,例如普通列表处理操作.然后,你可以这么说

  • 类别概括了(.)操作.
  • 幺半群概括了(++)操作.
  • 仿函数概括了该map操作.
  • 应用函子概括了zip(或zipWith)操作.
  • monad概括了concat操作.

一个类别

类别由一组(或一类)对象和一组箭头组成,每个对象连接两个对象.此外,对于每个对象,应该有一个标识箭头将此对象连接到自身.此外,如果有一个箭头(f)以对象结束,而另一个(g)从同一个对象开始,则应该还有一个复合箭头g . f.

在Haskell中,这被建模为一个类型类,它将Haskell类型的类别表示为对象.

 class Category cat where
  id :: cat a a
  (.) :: cat b c -> cat a b -> cat a c
Run Code Online (Sandbox Code Playgroud)

类别的基本示例是函数.每个函数都连接两种类型,对于所有类型,都有id :: a -> a将类型(和值)连接到自身的函数.功能的组成是普通的功能组合.

简而言之,Haskell基类中的类别就像函数一样,即你可以用一个通用版本来一个接一个地放置(.).

一个幺半群

monoid是具有单位元素和关联操作的集合.这在Haskell中建模为:

class Monoid a where
  mempty  :: a
  mappend :: a -> a -> a
Run Code Online (Sandbox Code Playgroud)

幺半群的常见例子包括:

  • 整数集,元素0和操作(+).
  • 一组正整数,元素1和操作(*).
  • 所有列表,空列表[]和操作的集合(++).

这些在Haskell中建模为

newtype Sum a = Sum {getSum :: a}
instance (Num a) => Monoid (Sum a) where
  mempty  = Sum 0
  mappend (Sum a) (Sum b) = Sum (a + b)  

instance Monoid [a] where
  mempty = []
  mappend = (++)
Run Code Online (Sandbox Code Playgroud)

Monoids用于"组合"和积累东西.例如,函数mconcat :: Monoid a => [a] -> a可用于将总和列表减少为单个总和,或将嵌套列表减少为平面列表.将此视为一种概括(++)(+)操作,以某种方式"合并"两件事.

一个Functor

Haskell中的仿函数是一个非常直接概括操作的东西map :: (a->b) -> [a] -> [b].它不是映射到列表,而是映射到某些结构,例如列表,二叉树甚至IO操作.函数的建模如下:

class Functor f where
  fmap :: (a->b) -> f a -> f b
Run Code Online (Sandbox Code Playgroud)

将其与正常map函数的定义进行对比.

应用程序编写器

应用仿函数可以看作具有广义zipWith操作的东西.Functors一次映射一般结构,但使用Applicative仿函数,您可以将两个或更多结构压缩在一起.对于最简单的示例,您可以使用applicatives将Maybe类型中的两个整数压缩在一起:

pure (+) <*> Just 1 <*> Just 2  -- gives Just 3
Run Code Online (Sandbox Code Playgroud)

请注意,结构可能会影响结果,例如:

pure (+) <*> Nothing <*> Just 2  -- gives Nothing
Run Code Online (Sandbox Code Playgroud)

与通常的zipWith功能相比:

zipWith (+) [1] [2]  
Run Code Online (Sandbox Code Playgroud)

应用程序适用于各种结构,而不仅仅是列表.另外,聪明的诡计pure(<*>)概括使用压缩来处理任意数量的参数.要了解其工作原理,请检查以下类型,同时保留部分应用函数的概念:

instance (Functor f) => Applicative f where
  pure  :: a -> f a
  (<*>) :: f (a -> b) -> f a -> f b
Run Code Online (Sandbox Code Playgroud)

还要注意fmap和之间的相似性(<*>).

Monad

Monad通常用于模拟不同的计算上下文,例如非确定性或副作用计算.由于已经有太多的monad教程,我只会推荐最好的一个,而不是写另一个.

关于普通列表处理函数,monad将函数概括为concat :: [[a]] -> [a]与列表之外的许多其他种类的结构一起使用.举个简单的例子,monadic操作join可以用来展平嵌套Maybe值:

join (Just (Just 42)) -- gives Just 42
join (Just (Nothing)) -- gives Nothing
Run Code Online (Sandbox Code Playgroud)

这与使用Monads作为构造计算的方法有何关系?考虑一个玩具示例,您可以从某个数据库中连续执行两个查询.第一个查询返回一些键值,您希望使用该值进行另一次查找.这里的问题是第一个值被包装在内部Maybe,因此您无法直接查询.相反,也许是a Functor,您可以fmap使用新查询返回值.这将为您提供两个嵌套Maybe值,如上所述.另一个查询将导致三层Maybes.这对于编程来说是非常困难的,但是monadic join为你提供了一种扁平化这种结构的方法,并且只需要一个单独的Maybes 级别.

(我想在有任何意义之前我会很多时间编辑这篇文章..)

  • 我喜欢这个答案,但你可能想指出`[]`的标准`Applicative`实例不是zippy,而是笛卡尔积.不太有用的zippy列表monad仅在长度一致的列表上有效,并且它的`join`是对角线. (5认同)