【问题标题】:Simple examples to illustrate Category, Monoid and Monad?简单的例子来说明 Category、Monoid 和 Monad?
【发布时间】:2013-03-21 12:59:06
【问题描述】:

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

有没有简单的例子来说明两者的区别 类别、Monoid 和 Monad ?

如果有这些抽象概念的插图会很有帮助。

【问题讨论】:

标签: haskell monads category-theory monoids


【解决方案1】:

我认为要理解 monad,需要使用绑定运算符 (>>=)。 受到 [http://dev.stephendiehl.com/hask/#eightfold-path-to-monad-satori](Don't read the monad tutorials.) 的影响。

我的小玩意如下:

1。连接 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 。连接Maybe的

改编自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中所述

【讨论】:

    【解决方案2】:

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

    看待 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
    

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

    简而言之,Haskell 基础中的类别是表现得像函数的东西,也就是说,您可以将一个一个接一个地放在一个通用版本的 (.) 上。

    一个幺半群

    幺半群是具有单位元素和关联运算的集合。这在 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 可用于将总和列表缩减为单个总和,或将嵌套列表缩减为平面列表。将此视为(++)(+) 操作的一种概括,以某种方式“合并”两件事。

    函子

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

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

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

    应用函子

    应用函子可以看作是具有广义zipWith 操作的事物。函子一次映射一个通用结构,但使用 Applicative 函子,您可以将两个或多个结构压缩在一起。举个最简单的例子,你可以使用 applicative 将 Maybe 类型中的两个整数压缩在一起:

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

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

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

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

    zipWith (+) [1] [2]  
    

    applicative 不仅适用于列表,还适用于各种结构。此外,pure(&lt;*&gt;) 的巧妙技巧将压缩推广到使用任意数量的参数。要了解其工作原理,请检查以下类型,同时掌握部分应用函数的概念:

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

    还要注意fmap(&lt;*&gt;) 之间的相似之处。

    单子

    Monad 通常用于对不同的计算上下文进行建模,例如非确定性或副作用计算。由于monad教程已经太多了,我只推荐The best one,而不是再写一个。

    关于普通的列表处理函数,monad 将函数concat :: [[a]] -&gt; [a] 泛化为与列表以外的许多其他类型的结构一起工作。举个简单的例子,一元操作join 可用于展平嵌套的Maybe 值:

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

    这与使用 Monad 作为一种结构化计算方法有何关系?考虑一个玩具示例,您从某个数据库执行两个连续查询。第一个查询返回一些键值,您希望使用它进行另一次查找。这里的问题是第一个值被包裹在Maybe 中,所以你不能直接用它来查询。相反,可能是 Functor,您可以改为 fmap 使用新查询的返回值。这将为您提供两个嵌套的 Maybe 值,如上所示。另一个查询将产生三层Maybes。这将很难编程,但一元 join 为您提供了一种扁平化此结构的方法,并且只需使用单个级别的 Maybes。

    (我想我会在这篇文章有意义之前对其进行大量编辑..)

    【讨论】:

    • mempty = 0 应该是 mempty = Sum 0
    • 我喜欢这个答案,但您可能想指出[] 的标准Applicative 实例不是活泼的实例,而是笛卡尔积。不太有用的 zippy list monad 仅在长度一致的列表上有效,并且其 join 采用对角线。
    • @C.A.McCann Applicative Functors 在另一种意义上是“活泼的”,不是吗?从某种意义上说,他们有一个方法fzip :: (f a, f b) -&gt; f (a,b),将两个fs 合二为一。 (参见stackoverflow.com/a/15211856/849891)。
    • @WillNess:通常,短语“zippy Applicative”表示充当结构交集的实例,匹配来自具有相同位置的两个结构的元素。这种用Applicative 实现的fzip 在其他实例中的行为可能不像zip,明显的例子是默认列表实例,其中fzip 将给出完整的笛卡尔积。
    • @CAMcCann 是的,显然我在那里跳过了“also”这个词,就像“are also ''zippy'' 在另一种意义上...”。
    猜你喜欢
    • 1970-01-01
    • 2017-02-18
    • 1970-01-01
    • 2012-04-02
    • 1970-01-01
    • 1970-01-01
    • 2019-03-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多