简体   繁体   English

Scala中的实际函数式编程

[英]Real World Functional Programming in Scala

Soooo... 真是的

Semigroups, Monoids, Monads, Functors, Lenses, Catamorphisms, Anamorphisms, Arrows... These all sound good, and after an exercise or two (or ten), you can grasp their essence. 半群,Monoids,Monads,Functor,Lenses,Catamorphisms,Anamorphisms,Arrows ...听起来都很不错,经过一两次或十次练习后,您可以掌握它们的本质。 And with Scalaz , you get them for free... 有了Scalaz ,您可以免费获得它们...

However, in terms of real-world programming, I find myself struggling to find usages to these notions. 但是,就实际编程而言,我发现自己正在努力寻找这些概念的用法。 Yes, of course I always find someone on the web using Monads for IO or Lenses in Scala, but... still... 是的,我当然总是在网上使用Scala的Monads for IO或Lenses在网上找到某个人,但是...仍然...

What I am trying to find is something along the "prescriptive" lines of a pattern. 我试图找到的是某种模式的“说明性”方面的东西。 Something like: "here, you are trying to solves this , and one good way to solve it is by using lenses this way !" 就像这样:“在这里,您正在尝试解决此问题 ,而解决此问题的一种好方法是通过这种方式使用镜片!”

Suggestions? 有什么建议吗?


Update: Something along these lines, with a book or two, would be great (thanks Paul): Examples of GoF Design Patterns in Java's core libraries 更新:遵循这些原则,只要有一两本书,就很好了(感谢Paul): Java核心库中的GoF设计模式示例

The key to functional programming is abstraction, and composability of abstractions. 函数式编程的关键是抽象和抽象的可组合性。 Monads, Arrows, Lenses, these are all abstractions which have proven themselves useful, mostly because they are composable. Monad,Arrows,Lenses都是已被证明自己有用的抽象,主要是因为它们是可组合的。 You've asked for a "prescriptive" answer, but I'm going to say no. 您已要求“说明性”答案,但我要说不。 Perhaps you're not convinced that functional programming matters ? 也许您不相信函数式编程很重要

I'm sure plenty of people on StackOverflow would be more than happy to try and help you solve a specific problem the FP way. 我敢肯定,StackOverflow上的很多人都非常乐于尝试并帮助您以FP方式解决特定问题。 Have a list of stuff and you want to traverse the list and build up some result? 有东西的清单,您想遍历清单并建立一些结果吗? Use a fold. 使用折叠。 Want to parse XML? 要解析XML吗? hxt uses arrows for that. hxt为此使用箭头。 And monads? 和单子? Well, tons of data types turn out to be Monads, so learn about them and you'll discover a wealth of ways you can manipulate these data types. 好吧,大量的数据类型竟然是Monad,因此了解它们,您将发现处理这些数据类型的丰富方式。 But its kind of hard to just pull examples out of thin air and say "lenses are the Right Way to do this", "monoids are the best way to do that", etc. How would you explain to a newbie what the use of a for loop is? 但是,很难仅凭空拿出示例并说“镜头是实现此目标的正确方法”,“仿制眼镜是实现此目标的最佳方法”,等等。您将如何向新手解释for循环是? If you want to [blank], then use a for loop [in this way]. 如果要[空白],请使用[以这种方式] for循环。 It's so general; 太一般了; there are tons of ways to use a for loop. 有很多使用for循环的方法。 The same goes for these FP abstractions. 这些FP抽象也是如此。

If you have many years of OOP experience, then don't forget you were once a newbie at OOP. 如果您有多年的OOP经验,那么不要忘记您曾经是OOP的新手。 It takes time to learn the FP way, and even more time to unlearn some OOP tendencies. 学习FP方式需要花费时间,甚至需要花更多的时间来学习一些OOP趋势。 Give it time and you will find plenty of uses for a Functional approach. 给它时间,您会发现功能方法有很多用途。

I gave a talk back in September focused on the practical application of monoids and applicative functors/monads via scalaz.Validation . 在9月份做了一次演讲,重点是通过scalaz.Validation的monoid和应用函子/单子的实际应用。 I gave another version of the same talk at the scala Lift Off , where the emphasis was more on the validation. 在scala Lift Off上做了同样演讲的另一个版本,重点是验证。 I would watch the first talk until I start on validations and then skip to the second talk (27 minutes in). 在开始验证之前,我会看第一个演讲,然后跳到第二个演讲(27分钟)。

There's also a gist I wrote which shows how you might use Validation in a "practical" application. 我写的要点还显示了如何在“实际”应用程序中使用Validation That is, if you are designing software for nightclub bouncers. 也就是说,如果您正在设计夜总会蹦蹦跳的软件。

I think you can take the reverse approach and instead when writing a small piece of functionality, ask yourself whether any of those would apply: Semigroups, Monoids, Monads, Functors, Lenses, Catamorphisms, Anamorphisms, Arrows... A lots of those concepts can be used in a local way. 我认为您可以采用相反的方法,而在编写一小部分功能时,请问自己是否适用以下任何一种功能:半群,Monoids,Monads,Functors,Lenses,Catamorphisms,Anamorphisms,Arrows ...这些概念很多可以以本地方式使用。

Once you start down that route, you may see usage everywhere. 一旦沿着那条路线走,您可能会在各处看到使用情况。 For me, I sort of get Semigroups, Monoids, Monads, Functors. 对我来说,我得到了半群,Monoids,Monads,Functors。 So take the example of answering this question How do I populate a list of objects with new values . 因此,以回答此问题的示例为例。 如何使用新值填充对象列表 It's a real usage for the person asking the question (a self described noob). 对于问这个问题的人(自我描述的菜鸟),这是一种实际用法。 I am trying to answer in a simple way but I have to refrain myself from scratching the itch "there are monoids in here". 我试图以一种简单的方式回答,但我必须避免挠痒痒,“这里有类人猿”。

Scratching it now: using foldMap and the fact that Int and List are monoids and that the monoid property is preserved when dealing with tuple, maps and options: 现在foldMap它:使用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)) })

But I don't come to that result by thinking I will use hard core functional programming . 但是我并没有想到我将使用硬核函数编程来得到这个结果。 It comes more naturally by thinking this seems simpler if I compose those monoids combined with the fact that scalaz has utility methods like foldMap . 它通过思考,这似乎更简单,如果我撰写这些类群与scalaz具有实用的方法,如事实结合来得更自然foldMap Interestingly when looking at the resulting code it's not obvious that I'm totally thinking in terms of monoid. 有趣的是,在查看生成的代码时,我并没有完全按照monoid进行思考。

You might like this talk by Chris Marshall. 您可能喜欢克里斯·马歇尔(Chris Marshall)的演讲 He covers a couple of Scalaz goodies - namely Monoid and Validation - with many practical examples. 他介绍了许多Scalaz的好东西-Monoid和Validation-并包含许多实际示例。 Ittay Dror has written a very accessible post on how Functor, Applicative Functor, and Monad can be useful in practice. Ittay Dror写了一篇非常容易理解的文章 ,介绍了Functor,Applicative Functor和Monad如何在实践中有用。 Eric Torreborre and Debasish Gosh 's blogs also have a bunch of posts covering use cases for categorical constructs. Eric TorreborreDebasish Gosh的博客中也有很多帖子,涵盖了分类构造的用例。

This answer just lists a few links instead of providing some real substance here. 该答案仅列出了一些链接,而不是在此处提供实际内容。 (Too lazy to write.) Hope you find it helpful anyway. (太懒了写。)希望您无论如何都对您有所帮助。

I understand your situation, but you will find that to learn functional programming you will need to adjust your point of view to the documentation you find, instead of the other way around. 我了解您的情况,但是您会发现,要学习函数式编程,您需要将您的观点调整为找到的文档,而不是相反。 Luckily in Scala you have the possibility of becoming a functional programmer gradually. 幸运的是,在Scala中,您有可能逐渐成为一名函数式程序员。

To answer your questions and explain the point-of-view difference, I need to distinguish between "type classes" (monoids, functors, arrows), mathematically called "structures", and generic operations or algorithms (catamorphisms or folds, anamorphisms or unfolds, etc.). 为了回答您的问题并解释观点的差异,我需要区分“类型类”(类,类,函子,箭头),数学上称为“结构”的类和通用运算或算法(变形或折叠,变形或展开)等)。 These two often interact, since many generic operations are defined for specific classes of data types. 由于为特定类型的数据类型定义了许多通用操作,因此这两个经常交互。

You look for prescriptive answers similar to design patterns: when does this concept apply? 您在寻找类似于设计模式的说明性答案:该概念何时适用? The truth is that you have surely seen the prescriptive answers, and they are simply the definitions of the different concepts. 事实是,您肯定已经看到了规定性的答案,它们只是不同概念的定义。 The problem (for you) is that those answers are intrinsically different from design patterns, but it is so for good reasons. (对您而言)问题是这些答案与设计模式本质上是不同的,但这是有充分理由的。

On the one hand, generic algorithms are not design patterns, which suggest a structure for the code you write; 一方面,通用算法不是设计模式,而是为您编写的代码提供了一种结构。 they are abstractions defined in the language which you can directly apply. 它们是用您可以直接应用的语言定义的抽象。 They are general descriptions for common algorithms which you already implement today, but by hand. 它们是您已经实现但现在已经实现的常见算法的一般描述。 For instance, whenever you are computing the maximum element of a list by scanning it, you are hardcoding a fold; 例如,每当您通过扫描来计算列表的最大元素时,就在硬编码折叠。 when you sum elements, you are doing the same; 当您对元素求和时,您正在做同样的事情; and so on. 等等。 When you recognize that, you can declare the essence of the operation you are performing by calling the appropriate fold function. 当您意识到这一点时,可以通过调用适当的fold函数来声明所执行操作的本质。 This way, you save code and bugs (no opportunity for off-by-one errors), and you save the reader the effort to read all the needed code. 这样,您可以节省代码和错误(没有机会解决一次性错误),并且使阅读者无需费力即可读取所有所需的代码。

On the other hand, structures concern not the goal you have in mind but properties of the entities you are modeling. 另一方面,结构与您要考虑的目标无关,而与您要建模的实体的属性有关。 They are more useful for bottom-up software construction, rather than top-down: when defining your data, you can declare that it is a eg a monoid. 它们对于自下而上的软件结构比自上而下的结构更有用:定义数据时,您可以声明它是一个monoid。 Later, when processing your data, you have the opportunity to use operations on eg monoids to implement your processing. 稍后,在处理数据时,您将有机会使用对monoid的操作来实现处理。 In some cases it is useful to strive to express your algorithm in terms of the predefined ones. 在某些情况下,努力根据预定义的算法来表达算法很有用。 For instance, very often if you need to reduce a tree to a single value, a fold can do most or all of what you need. 例如,很多时候,如果您需要将树缩小为单个值,则折叠可以完成大部分或全部所需的操作。 Of course, you can also declare that your data type is a monoid when you need a generic algorithm on monoids; 当然,当您需要针对单节的通用算法时,也可以声明您的数据类型为单节。 but the earlier you notice that, the earlier you can start reusing generic algorithms for monoids. 但是您越早注意到,就可以越早开始将通用算法用于monoid。

Last advice is that probably most of the documentation you will find about these concepts concerns Haskell, because this language has been around for much more time and supports them in a quite elegant way. 最后的建议是,您可能会发现关于这些概念的大多数文档都与Haskell有关,因为这种语言已经存在了很多时间,并且以一种非常优雅的方式为它们提供了支持。 Quite recommended here are Learn you a Haskell for Great Good , a Haskell course for beginners, where among others chapters 11 to 14 focus on some type classes, and Typeclassopedia (which contains links to various articles with specific examples). 这里推荐的是为您学习Haskell for Great Good ,这是针对初学者的Haskell课程,其中第11章至第14章着重于某些类型类,以及Typeclassopedia (其中包含各种带有特定示例的文章的链接)。 EDIT: Finally, an example of applications of Monoids, taken from Typeclassopedia, is here: http://apfelmus.nfshost.com/articles/monoid-fingertree.html . 编辑:最后,取自Typeclassopedia的Monoids应用程序示例在此处: http ://apfelmus.nfshost.com/articles/monoid-fingertree.html。 I'm not saying there is little documentation for Scala, just that there is more in Haskell, and Haskell is where the application of these concepts to programming was born. 我并不是说Scala的文档很少,只是Haskell中有更多文档,而Haskell正是这些概念在编程中的应用诞生的地方。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM