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

时间:2013-03-31 05:44:31

标签: haskell monads category-theory monoids

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

是否有任何简单的例子来说明它们之间的差异 类别,Monoid和Monad?

如果有这些抽象概念的说明,将会非常有用。

2 个答案:

答案 0 :(得分:92)

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

一种看待monad& amp;共

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

  • 类别概括了(.)操作。
  • monoid概括了(++)操作。
  • 仿函数概括了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

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

简而言之,Haskell基础中的类别是表现得像函数的东西,即你可以使用(.)的通用版本一个接一个地放置。

一个Monoid

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

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

幺半群的常见例子包括:

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

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

将此与普通map函数的定义进行对比。

申请编织者

可以将应用仿函数视为具有广义zipWith运算的事物。 Functors一次映射一般结构,但使用Applicative仿函数,您可以将两个或更多结构压缩在一起。对于最简单的示例,您可以使用applicatives将Maybe类型中的两个整数压缩在一起:

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

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

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

将此与通常的zipWith函数对比:

zipWith (+) [1] [2]  

该应用程序适用于各种结构,而不仅仅是列表。此外,pure(<*>)的巧妙诡计概括了压缩与任意数量的参数一起使用。要了解其工作原理,请检查以下类型,同时保持部分应用函数的概念:

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

另请注意fmap(<*>)之间的相似性。

Monad

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

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

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

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

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

答案 1 :(得分:0)

我认为要理解单子,需要使用绑定运算符(>>=)。 受[http://dev.stephendiehl.com/hask/#eightfold-path-to-monad-satori](Don影响的健康状况没有阅读monad教程。)

我的小游戏如下:

1。 Concat getLine和putStrLn

改编自http://www.haskellforall.com/2014/10/how-to-desugar-haskell-code.html

Prelude> f = getLine >>= \a -> putStrLn a
Prelude> f
abc
abc
Prelude>

和签名:

Prelude> :t getLine
getLine :: IO String
Prelude> :t (\a -> putStrLn a)
(\a -> putStrLn a) :: String -> IO ()
Prelude> :t f
f :: IO ()

结果:人们可以看到(>>=) :: Monad m => m a -> (a -> m b) -> m b签名的一部分。

2。 concat也许是

https://wiki.haskell.org/Simple_monad_examples改编

Prelude> g x = if (x == 0) then Nothing else Just (x + 1)
Prelude> Just 0 >>= g
Nothing
Prelude> Just 1 >>= g
Just 2

结果:fail "zero" is Nothing

3。将绑定视为加入计算

...如https://www.slideshare.net/ScottWlaschin/functional-design-patterns-devternity2018

中所述

enter image description here