我正在读" 了解你一个哈斯克尔",我已经涵盖了应用程序,现在我正在使用幺半群.我对这两者都没有问题,虽然我发现在实践中有用,而monoid却不是这样.所以我想我对Haskell一无所知.
首先,Applicative
它说,它创建了类似于统一语法的东西来对'容器'执行各种操作.所以我们可以使用普通函数对Maybe
列表执行操作IO
(我应该说monad吗?我还不知道monad),函数:
?> :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
Run Code Online (Sandbox Code Playgroud)
因此,applicative是一种抽象.我想我们可以没有它,但它有助于清楚地表达一些想法模式,这很好.
现在,让我们来看看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
Run Code Online (Sandbox Code Playgroud)
所以我们注意到了一些模式并创建了新的类型......好吧,我喜欢数学.但从实际角度来看,有什么意义Monoid
呢?它如何帮助我们更好地表达想法?
Mas*_*tic 29
Gabriel Gonzalez在他的博客中写了很多关于你为什么要关心的信息,你真的应该关心.你可以在这里阅读(也看到这个).
它涉及API的可扩展性,架构和设计.我们的想法是,"传统架构"说:
将A类的几个组件组合在一起以生成B类型的"网络"或"拓扑"
这种设计的问题在于,当你的程序扩展时,你重构时的地狱也是如此.
因此,您希望更改模块A以改进您的设计或域,这样就可以了.哦,但现在依赖A的模块B&C破了.你修好了B,太棒了.现在你修复C.现在B再次破坏,因为B也使用了C的一些功能.我可以永远继续下去,如果你曾经使用OOP - 你也可以.
然后是Gabriel所说的"Haskell架构":
将几种类型的组分组合在一起以产生相同类型A的新组分,其特征与其取代基部分无法区分
这也很好地解决了这个问题.基本上:不要将模块分层或扩展以制作专用模块.
相反,结合起来.
所以现在,鼓励的是,不要说"我有多个X,所以让我们用一种类型来代表他们的联盟",你说"我有多个X,所以让我们将它们组合成一个X".或者用简单的英语:"让我们首先制作可组合的类型." (你觉得幺半群潜伏了吗?).
想象一下,您想为您的网页或应用程序制作一个表单,并且您拥有您创建的模块"个人信息表单",因为您需要个人信息.后来你发现你还需要"更改图片表格",所以很快写下来了.现在你说我想把它们结合起来,所以让我们制作一个"个人信息和图片表"模块.在现实生活中,可扩展的应用程序可以而且确实失控.可能没有表格,但要展示,你需要撰写和撰写,这样你最终会得到"个人信息和更改图片和更改密码和更改状态和管理朋友和管理愿望清单和更改视图设置,请不要再延伸我了&请,请停止!停止!!!!" 模块.这不是很好,您将不得不在API中管理这种复杂性.哦,如果你想改变什么 - 它可能有依赖.所以..是啊..欢迎来到地狱.
现在让我们看看另一个选项,但首先让我们看看它的好处,因为它将引导我们:
这些抽象无限扩展,因为它们始终保持可组合性,因此我们永远不需要在顶部进行进一步的抽象分层.这就是你应该学习Haskell的一个原因:你学习如何构建平面架构.
听起来不错,所以,不要制作"个人信息表"/"更改图片表"模块,停下来想想我们是否可以在这里做任何可组合的事情.好吧,我们可以制作一个"表格"吧?也会更抽象.
然后,有意义的是为你想要的一切构建一个,将它们组合在一起并获得一个形式,就像任何其他形式一样.
所以,你不会再得到一个混乱的复杂树了,因为关键是你采取了两种形式并得到一种形式.所以Form -> Form -> Form
.正如您已经清楚看到的那样,这个签名就是一个例子mappend
.
另一种选择,和传统的架构可能会看起来像a -> b -> c
,然后c -> d -> e
,然后......
现在,凭借形式,它并不那么具有挑战性; 挑战是在现实世界的应用程序中使用它.要做到这一点,只要尽可能多地问自己(因为它可以得到回报,正如你所看到的):我怎样才能使这个概念成为可组合的?因为幺半群是如此简单的实现方法(我们想要简单)首先问问自己:这个概念是如何成为一个幺半群?
旁注:值得庆幸的是,Haskell非常不鼓励你扩展类型,因为它是一种函数式语言(没有继承).但它仍然可以为某些东西制作类型,为某些东西制作另一种类型,而在第三种类型中则将这两种类型都作为字段.如果这是作曲 - 看看你是否可以避免它.
Don*_*art 10
好吧,我喜欢数学.但从实际角度来看,Monoid有什么意义呢?它如何帮助我们更好地表达想法?
这是一个API.一个简单的.对于支持的类型:
许多类型都支持这些操作.因此,拥有操作名称和API有助于我们更清楚地了解事实.
API很好,因为它们让我们重用代码并重用概念.意味着更好,更易于维护的代码.
一个非常简单的例子是foldMap
.只需在此单个函数中插入不同的幺半群,就可以计算:
此外,幺是可组合的:如果a
和b
是幺,所以是(a, b)
.因此,您可以在一次通过中轻松计算几个不同的幺半群值(如计算元素平均值时的总和和乘积等).
虽然你可以在没有幺半群的情况下完成所有这些操作,但使用foldr
或者foldl
更麻烦且效率通常更低:例如,如果你有一个平衡的二叉树并且想要找到它的最小和最大元素,你就不能同时做到这两点对于其中一种情况,有效地foldr
(或两者都有foldl
),一种情况总是为O(n),而当使用foldMap
适当的幺半群时,在两种情况下都是O(log n).
而这只是一个功能foldMap
!还有许多其他有趣的应用程序.举一个,通过平方取幂是计算能力的有效方法.但它实际上与计算能力无关.您可以实现它的任何幺,如果它<>
是O(1) ,你必须计算的有效方法ñ -times x <> ... <> x
.突然之间,您可以进行有效的矩阵求幂,并使用O(log n)乘法计算第n个Fibonacci数.见times1p在半群.