35

呜呜……

Semigroups、Monoids、Monads、Functors、Lenses、Catamorphisms、Anamorphisms、Arrows……这些听起来都不错,经过一两次(或十次)练习,你就能掌握它们的本质。并且Scalaz,您可以免费获得它们...

然而,就实际编程而言,我发现自己很难找到这些概念的用法。是的,当然我总是在网上找到有人在 Scala 中使用 Monads for IO 或 Lenses,但是......仍然......

我试图找到的是沿着模式的“规定”线的东西。类似于:“在这里,您正在尝试解决这个问题,而解决这个问题的一个好方法就是以这种方式使用镜头!”

建议?


更新:这些方面的东西,一两本书,会很棒(感谢 Paul):Java 核心库中的 GoF 设计模式示例

4

5 回答 5

19

函数式编程的关键是抽象和抽象的可组合性。Monads、Arrows、Lenses,这些都是已经证明自己有用的抽象,主要是因为它们是可组合的。你已经要求一个“规定性”的答案,但我会说不。也许您不相信函数式编程很重要

我敢肯定 StackOverflow 上的很多人会非常乐意尝试以 FP 方式帮助您解决特定问题。有一个东西列表,你想遍历列表并建立一些结果?使用折叠。想要解析 XML?hxt 为此使用箭头。还有单子?好吧,大量的数据类型变成了 Monad,所以了解它们,你会发现很多可以操作这些数据类型的方法。但是很难凭空举出例子并说“镜头是做到这一点的正确方法”,“monoids是做到这一点的最佳方法”等等。你将如何向新手解释用途一个for循环是?如果要[空白],则使用 for 循环[以这种方式]。太笼统了;有很多方法可以使用 for 循环。

如果您有多年的 OOP 经验,那么不要忘记您曾经是 OOP 的新手。学习 FP 方式需要时间,而忘记一些 OOP 倾向则需要更多时间。给它时间,你会发现函数式方法有很多用途。

于 2011-12-11T07:55:47.133 回答
12

在 9 月份进行了一次演讲,重点是通过scalaz.Validation 对 monoids和 applicative functors/monads 的实际应用。我在 scala Lift Off 上发表了同一个演讲的另一个版本,重点更多地放在了验证上。我会看第一个演讲,直到我开始验证,然后跳到第二个演讲(27 分钟后)。

我还写了一个要点,它展示了如何在“实际”应用程序中使用验证。也就是说,如果您正在为夜总会保镖设计软件。

于 2011-12-11T17:44:53.913 回答
7

我认为你可以采取相反的方法,而不是在编写一小部分功能时,问问自己这些是否适用:半群、Monoids、Monads、Functors、Lenses、Catamorphisms、Anamorphisms、Arrows……很多这样的概念可以在本地使用。

一旦你开始沿着这条路线走下去,你可能会在任何地方看到使用情况。对我来说,我得到了 Semigroups、Monoids、Monads、Functors。因此,以回答这个问题为例,如何使用新值填充对象列表。对于提出问题的人(自我描述的菜鸟)来说,这是一个真正的用法。我试图以一种简单的方式回答,但我必须克制自己不要抓痒“这里有幺半群”。

现在开始讨论:使用foldMapInt 和 List 是 monoid 并且在处理元组、映射和选项时保留 monoid 属性的事实:

// using scalaz
listVar.sliding(2).toList.foldMap{
  case List(prev, i) => Some(Map(i -> (1, Some(List(math.abs(i - prev))))))
  case List(i) => Some(Map(i -> (1, None)))
  case _ => None
}.map(_.mapValues{ case (count, gaps) => (count, gaps.map(_.min)) })

但我并没有通过认为我将使用硬核函数式编程来得出这个结果。如果我组合这些幺半群并结合 scalaz 具有诸如foldMap. 有趣的是,在查看生成的代码时,我完全在考虑幺半群方面并不明显。

于 2011-12-11T15:55:09.140 回答
6

你可能会喜欢Chris Marshall 的这次演讲。他介绍了一些 Scalaz 的好东西——即 Monoid 和 Validation——以及许多实际示例。Ittay Dror 写了一篇非常容易理解的文章,介绍了 Functor、Applicative Functor 和 Monad 如何在实践中发挥作用。Eric TorreborreDebasish Gosh的博客也有很多文章涵盖了分类构造的用例。

这个答案只是列出了一些链接,而不是在这里提供一些真实的内容。(懒得写了。)希望你觉得它有帮助。

于 2011-12-11T16:07:09.133 回答
4

我了解您的情况,但是您会发现要学习函数式编程,您需要根据找到的文档调整您的观点,而不是相反。幸运的是,在 Scala 中,您有可能逐渐成为一名函数式程序员。

为了回答您的问题并解释观点差异,我需要区分“类型类”(monoids、functors、arrows)、数学上称为“结构”和通用操作或算法(catamorphisms 或 fold、anamorphisms 或展开, ETC。)。这两者经常交互,因为许多通用操作是为特定类的数据类型定义的。

您寻找类似于设计模式的规定性答案:这个概念何时适用?事实是,您肯定已经看到了规范性的答案,它们只是不同概念的定义。问题(对你来说)是这些答案本质上与设计模式不同,但这是有充分理由的。

一方面,通用算法不是设计模式,它为你编写的代码提供了一种结构;它们是用您可以直接应用的语言定义的抽象。它们是您今天已经实现但手动实现的常见算法的一般描述。例如,每当您通过扫描来计算列表的最大元素时,您就是在硬编码折叠;当你对元素求和时,你也在做同样的事情;等等。当您认识到这一点时,您可以通过调用适当的折叠函数来声明您正在执行的操作的本质。这样,您可以节省代码和错误(没有机会出现一次错误),并且您可以节省读者阅读所有需要的代码的精力。

另一方面,结构与您心中的目标无关,而是您正在建模的实体的属性。它们对于自下而上的软件构建比自上而下更有用:在定义数据时,您可以声明它是一个例如幺半群。稍后,在处理您的数据时,您有机会使用对例如 monoids 的操作来实现您的处理。在某些情况下,努力根据预定义的算法来表达你的算法是有用的。例如,通常如果您需要将树简化为单个值,折叠可以完成您需要的大部分或全部操作。当然,当你需要一个关于幺半群的泛型算法时,你也可以声明你的数据类型是一个幺半群;但是您越早注意到这一点,您就越早可以开始重用幺半群的通用算法。

最后一条建议是,您将找到的关于这些概念的大多数文档可能都与 Haskell 相关,因为这种语言已经存在了很长时间,并且以一种非常优雅的方式支持它们。这里非常推荐的是Learn a Haskell for Great Good,一门面向初学者的 Haskell 课程,其中第 11 章到第 14 章重点介绍了一些类型类,以及Typeclassopedia(其中包含指向各种文章和具体示例的链接)。编辑:最后,来自 Typeclassopedia 的 Monoids 应用示例在这里:http ://apfelmus.nfshost.com/articles/monoid-fingertree.html. 我并不是说 Scala 的文档很少,只是在 Haskell 中有更多的文档,而 Haskell 是这些概念在编程中的应用诞生的地方。

于 2011-12-19T01:22:04.650 回答