2014-10-07 91 views
18

我在阅读学习你一个哈斯克尔,我已经介绍了应用程序,现在我在monoids。我对这两种理解都没有问题,尽管我发现应用在实践中很有用,monoid也不是那么完美。所以我想我对Haskell不了解。什么是monoids的实际使用?

首先,谈到Applicative,它创建了一些类似于统一语法的操作来对“容器”执行各种操作。所以我们可以用正常功能对Maybe,列表执行操作,IO(?我应该说的单子我不知道单子还),功能:

λ> :m + Control.Applicative 
λ> (+) <$> (Just 10) <*> (Just 13) 
Just 23 
λ> (+) <$> [1..5] <*> [1..5] 
[2,3,4,5,6,3,4,5,6,7,4,5,6,7,8,5,6,7,8,9,6,7,8,9,10] 
λ> (++) <$> getLine <*> getLine 
one line 
and another one 
"one line and another one" 
λ> (+) <$> (* 7) <*> (+ 7) $ 10 
87 

所以应用性是一种抽象。我认为我们可以没有它,但它有助于清晰地表达一些想法模式,这很好。

现在,让我们来看看Monoid。它也是抽象的,非常简单。但它对我们有帮助吗?从书中的每个例子似乎是显而易见的,有更清晰的方式来做事:

λ> :m + Data.Monoid 
λ> mempty :: [a] 
[] 
λ> [1..3] `mappend` [4..6] 
[1,2,3,4,5,6] 
λ> [1..3] ++ [4..6] 
[1,2,3,4,5,6] 
λ> mconcat [[1,2],[3,6],[9]] 
[1,2,3,6,9] 
λ> concat [[1,2],[3,6],[9]] 
[1,2,3,6,9] 
λ> getProduct $ Product 3 `mappend` Product 9 
27 
λ> 3 * 9 
27 
λ> getProduct $ Product 3 `mappend` Product 4 `mappend` Product 2 
24 
λ> product [3,4,2] 
24 
λ> getSum . mconcat . map Sum $ [1,2,3] 
6 
λ> sum [1..3] 
6 
λ> getAny . mconcat . map Any $ [False, False, False, True] 
True 
λ> or [False, False, False, True] 
True 
λ> getAll . mconcat . map All $ [True, True, True] 
True 
λ> and [True, True, True] 
True 

因此,我们已经注意到了一些模式,并创造了新的类型的类......好吧,我喜欢数学。但从实际角度来看,Monoid有什么意义?它如何帮助我们更好地表达想法?

+2

如果你想深入挖掘一些真正展现monid的力量的高级材料,请查看[finger trees](http://www.cs.ox.ac.uk/ralf.hinze/publications/FingerTrees.pdf )(“Data.Sequence”后面的引擎)。我敢肯定,现在我想不出有些简单的例子。 – luqui 2014-10-07 08:01:16

+4

我建议你通过这篇出色的[Dan piponi's](http://blog.sigfpe.com/2009/01/haskell-monoids-and-their-uses.html)文章。 – Sibi 2014-10-07 08:04:18

+0

相关,但不重复的问题:[Data.Monoid中所有这些新类型包装器的实际价值是什么](http://stackoverflow.com/questions/22080564/whats-the-practical-value-of-all-those- newtype-wrappers-in-data-monoid) – AndrewC 2014-10-07 15:54:36

回答

25

加布里埃尔冈萨雷斯在他的博客写了很多关于你为什么要关心的信息,你真的应该关心。您可以阅读它here(并参见this)。

这是关于可扩展性,架构& API的设计。这个想法是,还有的“传统建筑”,上面写着:

结合了几个组件一起A型的,以产生一个 “网络”或B型“拓扑”

与问题这种设计是,随着程序的扩展,重构时你的地狱也会随之变化。

所以你想改变模块A来改善你的设计或域名,所以你这样做。哦,但现在依赖于A的模块B & C中断了。你修好B,太好了。现在你修复C.现在B再次爆发,因为B还使用了C的一些功能。我可以永远继续下去,如果你曾经使用过OOP,那么你也可以。

再有就是加布里埃尔所说的“哈斯克尔架构”:从它取代部分

结合A型在一起的几个部件产生相同的A型的新 组件,性格区分

这也解决了这个问题,优雅。基本上:不要将模块分层或扩展成专门的模块。
相反,组合。

所以现在鼓励的是,不要说“我有多个X,所以让我们用一个类型来表示他们的联合”,你说“我有多个X,所以我们把它们组合成一个X”。或者用简单的英语说:“让我们首先制作可组合的类型。” (你是否感觉到monoids的潜伏?)。

想象一下,您想为您的网页或应用程序制作一个表格,并且您拥有您创建的模块“个人信息表格”,因为您需要个人信息。后来你发现你也需要“更改图片窗体”,所以很快就写了。现在你说我想合并它们,所以让我们制作一个“个人信息&图片窗体”模块。而在现实生活中可扩展的应用程序,这可以并且确实失去控制。也许不是形式,但证明,你需要撰写和创作,所以你将最终获得“个人信息&更改图片&更改密码&更改状态&管理好友&管理收藏&更改视图设置&请不要延长我了&请&请停止!& STOP !!!!“模块。这不太好,你将不得不在API中管理这种复杂性。哦,如果你想改变任何东西 - 它可能有依赖关系。所以..是啊..欢迎来到地狱。

现在让我们来看看其他的选择,但首先让我们来看看的好处,因为它会引导我们到它:

这些抽象规模无限因为他们总是保持 组合性,因此,我们永远需要将顶层的进一步抽象 分层。这就是为什么你应该学习Haskell的一个原因:你学会如何构建平板体系结构。

听起来不错,所以,不要制作“个人信息表单”/“更改图片表单”模块,请停下来思考一下,如果我们可以在这里制作任何可组合的东西。那么,我们可以制作一个“表格”,对吧?也会更抽象。
然后,为所需要的任何东西构建一个可以合理使用的方法,将它们组合在一起并像其他任何一样获取一个表单。

所以,你不会因为使用两种形式并获得一种形式的关键而再次变得一团乱七八糟的复杂树。所以Form -> Form -> Form。正如您已经清楚看到的那样,此签名是mappend的一个实例。

替代,和传统的架构可能会像a -> b -> c然后c -> d -> e然后......

现在,形式也不算太富有挑战性的;我们面临的挑战是如何在真实世界的应用程序中使用它要做到这一点,只需尽可能地问自己(因为它可以带来回报,正如你所看到的):我怎样才能使这个概念成为可组合的?并且由于monoids是一种简单的方法来实现这一点(我们想要简单)首先问自己:这个概念是一个幺半群?

旁注:谢天谢地,Haskell会非常不鼓励你扩展类型,因为它是一种函数式语言(没有继承)。但是仍然有可能为某种东西创建一个类型,为某种东西创建另一个类型,并且在第三种类型中将两种类型都作为字段。如果这是为了构图 - 看看你是否可以避免它。

+2

+1大文章和答案! – Mark 2014-10-07 15:41:47

+1

您可能还想提及[这篇文章](http://www.haskellforall.com/2014/07/equational-reasoning-at-scale.html),它特别使用'Monoid'作为运行示例。 – 2014-10-07 17:40:51

6

问题的关键在于,当您将Int标记为Product时,您表达了要将整数相乘的意图。并将它们标记为Sum,并加在一起。

然后你可以在两者上使用相同的mconcat。这用于例如在Foldable中,其中一个foldMap表示折叠包含结构的想法,同时以特定monoid的方式组合元素。

9

好吧,我喜欢数学。但是从实际角度来看,Monoid有什么意义呢?它如何帮助我们更好地表达想法?

这是一个API。一个简单的。对于支持类型:具有零元件

  • 具有追加操作
  • 很多类型

    • 支持这些操作。因此,为操作和API命名有助于我们更清楚地了解事实。

      API很好,因为它们让我们重用代码并重用概念。意味着更好,更易维护的代码。

    5

    一个非常简单的例子是foldMap。只是通过插入不同类群这个单一的功能,你可以计算:

    • firstlast元素,
    • sum或元素的product(从这个也是他们的平均等)
    • 检查是否all元件或any具有给定的属性,
    • 计算的最大或最小的元素,
    • 映射元素的集合(如列表,sets,字符串,Text,ByteString或ByteString Builder)并将它们连接在一起 - 它们都是monoid。

    此外,类群是可组合的:如果ab是类群,所以是(a, b)。因此,您可以轻松地在一次传递中计算几个不同的monoidal值(如计算元素平均值时的总和和产品等)。

    虽然你可以做这一切,没有类群,使用foldrfoldl,它更麻烦,也往往事倍功半:例如,如果你有一个平衡二叉树,你想找到它的最小和最大的元素,您不能同时使用foldr(或两者都使用foldl),对于其中一种情况,总是会有O(n),而当使用foldMap与适当的monoids时,它将是O(log n)在两种情况下都是

    而这只是一个单一的功能foldMap!还有许多其他有趣的应用程序。给一个,exponentiation by squaring是一个有效的计算能力的方式。但它并没有与计算能力绑定在一起。你可以实现它的任何monoid,如果它的<>O(1),你有一个有效的计算方法n times x <> ... <> x。突然之间,您可以执行高效的矩阵求幂运算,并且仅用O(log n)乘法计算_。参见times1p半群。请参阅Monoids and Finger Trees

    相关问题