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

我对这三个概念非常困惑。

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

如果有这些抽象概念的例证,这将是非常有帮助的。

这可能不是你正在寻找的答案,但在这里你去:

一个非常扭曲的方式来看monads&co。

看这种抽象概念的一种方法是将它们与基本概念(如普通列表处理操作)联系起来。 那么,你可以这样说,

  • 一个类别概括(.)操作。
  • 一个monoid推广(++)操作。
  • 函子概括了map操作。
  • 应用函子概括了zip (或zipWith )操作。
  • monad概括了concat操作。

一个类别

一个类别由一组(或一类)对象和一堆箭头组成,每个箭头连接两个对象。 另外,对于每个对象,应该有一个标识箭头连接这个对象到自己。 此外,如果有一个箭头( f )结束于一个对象,另一个( g )从同一个对象开始,那么也应该有一个称为g . f的复合箭头g . f g . f

在Haskell中,这被build模为一个typestypes,它将Haskelltypes表示为对象。

  class Category cat where id :: cat aa (.) :: cat bc -> cat ab -> cat ac 

一个类别的基本例子是function。 每个函数都连接两种types,对于所有types,都有函数id :: a -> a ,将types(和值)连接到自身。 function的组成是普通的function组成。

简而言之,Haskell库中的类别就像函数一样 ,也就是说你可以用(.)的通用版本一个接一个。

一个Monoid

幺半群是具有单位元素和关联操作的集合。 这在Haskell中被模拟为:

 class Monoid a where mempty :: a mappend :: a -> a -> a 

monoids常见的例子包括:

  • 整数集合,元素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 = (++) 

幺半群是用来“结合”和积累的东西。 例如,可以使用函数mconcat :: Monoid a => [a] -> a来将总和列表减less为单一和,或将嵌套列表减less到平面列表中。 把这看作是(++)(+)操作的一种泛化,它以某种方式“合并”了两件事物。

一个Functor

Haskell中的一个函子是一个直接推广map :: (a->b) -> [a] -> [b] 。 它不是映射到列表上,而是映射到某个结构上 ,如列表,二叉树甚至IO操作。 仿函数是这样build模的:

 class Functor f where fmap :: (a->b) -> fa -> fb 

将此与法线map函数的定义进行对比。

一个适用的函子

应用函子可以看作是带有广义zipWith操作的东西。 函子映射在一般的结构上,但是用一个应用函数,你可以将两个或多个结构压缩在一起。 举个最简单的例子,你可以使用Applicatives将Maybetypes中的两个整数压缩在一起:

 pure (+) <*> Just 1 <*> Just 2 -- gives Just 3 

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

 pure (+) <*> Nothing <*> Just 2 -- gives Nothing 

将其与通常的zipWith函数进行比较:

 zipWith (+) [1] [2] 

而不仅仅是列表,适用于各种结构的应用程序。 另外,使用pure(<*>)的巧妙技巧概括了压缩以处理任意数量的参数。 要看看它是如何工作的,请检查以下types,同时保留部分应用函数的概念:

 instance (Functor f) => Applicative f where pure :: a -> fa (<*>) :: f (a -> b) -> fa -> fb 

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

Monad

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

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

 join (Just (Just 42)) -- gives Just 42 join (Just (Nothing)) -- gives Nothing 

这与使用Monad作为构造计算的手段有什么关系? 考虑一个玩具的例子,你从某个数据库连续进行两次查询。 第一个查询返回一些关键值,您希望进行另一个查询。 这里的问题是,第一个值被封装在Maybe ,所以你不能直接查询。 相反,也许是一个Functor ,你可以用新的查询来fmap返回值。 这会给你两个像上面嵌套Maybe值。 另一个查询会导致Maybe的三个层次。 这将是很难编程,但一元join给你一个扁平化这个结构的方法,只用一个单一的水平。

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