Scala中的真实世界函数式编程

时间:2011-12-11 02:52:51

标签: scala functional-programming design-patterns scalaz

... SOOOO

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

然而,在现实世界的编程方面,我发现自己很难找到这些概念的用法。是的,当然我总是在网上找到一个人使用Monads for IO或镜头在Scala中,但是......还是......

我想要找到的是一种模式的“规定”线。类似的事情:“在这里,你试图解决这个,解决问题的一个好方法是使用镜头这样!”

建议?


更新:沿着这些方向,有一两本书,会很棒(感谢保罗):Examples of GoF Design Patterns in Java's core libraries

5 个答案:

答案 0 :(得分:19)

函数式编程的关键是抽象和抽象的可组合性。 Monads,Arrows,Lenses,这些都是已证明有用的抽象,主要是因为它们是可组合的。你已经要求提供一个规范性的"回答,但我会说不。也许你不相信functional programming matters

我确信StackOverflow上有很多人会非常乐意尝试帮助您解决FP方式的特定问题。有一个东西列表,你想要遍历列表并建立一些结果?使用折叠。想解析XML? hxt使用箭头。和monads?好吧,大量的数据类型都是Monads,所以要了解它们,你会发现很多方法可以操作这些数据类型。但是它很难用空气简单地说出来并说'#34;镜头是正确的方法来做到这一点","幺半群是最好的方法来做到这一点"等等。你会向新手解释一下for循环的用途吗?如果你想[空白],那么使用for循环[以这种方式]。它如此笼统;有很多方法可以使用for循环。这些FP抽象也是如此。

如果你有多年的OOP经验,那么不要忘记你曾经是OOP的新手。学习FP方式需要时间,甚至需要花费更多时间来忘掉一些OOP倾向。给它时间,你会发现功能方法有很多用途。

答案 1 :(得分:12)

我通过 scalaz.Validation a talk back in September专注于monoids和applicative functors / monads的实际应用。我给了同一个演讲at the scala Lift Off的另一个版本,重点更多的是验证。在我开始验证之前,我会先观看第一次谈话,然后跳到第二次谈话(27分钟)。

还有a gist I wrote,其中显示了如何在“实用”应用程序中使用验证。也就是说,如果你正在为夜总会保镖设计软件。

答案 2 :(得分:7)

我认为你可以采取相反的方法,而是在编写一小部分功能时,问问自己这些是否适用:Semigroups,Monoids,Monads,Functors,Lenses,Catamorphisms,Anamorphisms,Arrows ...... A lots这些概念可以以本地方式使用。

一旦你开始走这条路线,你可能会看到无处不在的用途。对我来说,我有点像Semigroups,Monoids,Monads,Functors。所以以回答这个问题How do I populate a list of objects with new values为例。对于提出问题的人来说这是一个真正的用法(一个自我描述的菜鸟)。我试图以一种简单的方式回答,但我必须克制自己不要抓痒“这里有幺半群”。

现在抓它:使用foldMap以及Int和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等实用方法这一事实,那么通过思考这似乎更简单。有趣的是,在查看生成的代码时,我并不完全在考虑monoid方面。

答案 3 :(得分:6)

你可能会喜欢Chris Marshall的this talk。他介绍了几个Scalaz好东西 - 即Monoid和Validation--有很多实际例子。 Ittay Dror写了一篇关于Functor,Applicative Functor和Monad在实践中如何有用的非常容易post的文章。 Eric TorreborreDebasish Gosh的博客上还有大量帖子,内容涵盖了分类结构的用例。

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

答案 4 :(得分:4)

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

为了回答你的问题并解释观点差异,我需要区分“类型类”(幺半群,仿函数,箭头),数学上称为“结构”,以及通用操作或算法(catamorphisms或folds,肛交或展开等)。这两个通常是交互的,因为为特定类型的数据类型定义了许多通用操作。

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

一方面,通用算法不是设计模式,它为您编写的代码提供了结构;它们是您可以直接应用的语言定义的抽象。它们是您已经实现的常用算法的一般描述,但是手动完成。例如,无论何时通过扫描计算列表的最大元素,您都需要对其进行硬编码;当你对元素求和时,你也是这样做的;等等。当您意识到这一点时,您可以通过调用相应的折叠函数来声明您正在执行的操作的本质。这样,您可以保存代码和错误(没有机会出现一个错误),并且您可以省读读者所需的所有代码。

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

最后的建议是,您可能会找到关于这些概念的大部分文档都涉及Haskell,因为这种语言已经存在了很长时间并且以非常优雅的方式支持它们。这里相当推荐的是Learn you a Haskell for Great Good,一个适合初学者的Haskell课程,其中第11章到第14章主要关注某些类型类,以及Typeclassopedia(其中包含具有特定示例的各种文章的链接)。编辑:最后,来自Typeclassopedia的Monoids应用程序示例如下:http://apfelmus.nfshost.com/articles/monoid-fingertree.html。我并不是说Scala的文档很少,只是在Haskell中有更多文档,而Haskell就是这些概念在编程中的应用诞生的地方。