【问题标题】:Real World Functional Programming in ScalaScala中的真实世界函数式编程
【发布时间】:2011-12-11 02:52:50
【问题描述】:

太好了……

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

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

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

建议?


更新:类似的东西,一两本书,会很棒(感谢 Paul):Examples of GoF Design Patterns in Java's core libraries

【问题讨论】:

  • 一种模式可以弥补语言的不足;根据定义,丰富的抽象支持与这种情况相反。这些是用于以定义的方式构建程序的工具 - 关键是您认识到您将编写的过程代码对应于您具有功能支持的处理模式,然后您只需要编写实质性部分,而不是控制代码。
  • 您可能对这个类似的问题感兴趣:How to learn Haskell
  • 建议通常是从向你学习一个 haskell 序列开始:Functor、Applicative、Monoid、Monad,然后是箭头和拉链。请记住,每个函数只有 1 或 2 个重要函数:fmap 代表 Functor,pure 和 (apply) 代表 Applicative;为 Monad 绑定。

标签: scala functional-programming design-patterns scalaz


【解决方案1】:

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

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

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

【讨论】:

  • 虽然您的一般观点很好,但处理 Scala 解决的实际问题的具体示例的 SO 问题的链接与提问者的相关性要高得多。人们想要通过实例来学习非常普遍;他们必须这样做是正确的。
  • @DonalFellows - 很公平。我对 SO 上的 Scala 问题不够熟悉,无法编写这样的列表;我当然会支持这样做的人。
  • 我认为这并不能真正回答这个问题。我不明白为什么不能“凭空”为 FP 提取示例,但不能为 OO(典型的介绍包含任意数量的示例)。我确实对这些抽象将有用的断言(太常见,但不普遍)感到有点沮丧,但没有提供实例证明。
  • @Paul - 假设有人问了同样的问题,但是关于 OO。您真的可以将Design Patterns 之类的内容提炼成一个答案吗?也许Typeclassopedia(警告:Haskell)会是一本有用的读物​​,但我很难想象这个问题的完整答案打包成 SO 格式。
  • 我想我可以毫不费力地提供一个总结和一些示例指针,是的。事实上,有人已经完成了相当出色的工作:stackoverflow.com/questions/1673841/…
【解决方案2】:

我通过 scalaz.Validationa talk back in September 提供了专注于幺半群和应用函子/单子的实际应用。我给出了同一个演讲的另一个版本at the scala Lift Off,重点更多地放在了验证上。我会观看第一次演讲,直到我开始验证,然后跳到第二次演讲(27 分钟后)。

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

【讨论】:

    【解决方案3】:

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

    一旦您沿着这条路线开始,您可能会在任何地方看到使用情况。对我来说,我得到了 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 这样的实用方法这一事实,它会变得更自然。有趣的是,在查看生成的代码时,我并没有完全考虑到幺半群。

    【讨论】:

      【解决方案4】:

      您可能会喜欢 Chris Marshall 的 this talk。他介绍了一些 Scalaz 的好东西——即 Monoid 和 Validation——以及许多实际示例。 Ittay Dror 写了一篇非常容易理解的post,介绍了 Functor、Applicative Functor 和 Monad 如何在实践中发挥作用。 Eric TorreborreDebasish Gosh 的博客也有很多关于分类结构用例的帖子。

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

      【讨论】:

        【解决方案5】:

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

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

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

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

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

        最后的建议是,您将找到的关于这些概念的大部分文档可能都与 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 正是这些概念在编程中的应用诞生的地方。

        【讨论】:

          猜你喜欢
          • 2010-11-09
          • 1970-01-01
          • 2011-04-11
          • 1970-01-01
          • 1970-01-01
          • 2010-09-13
          • 2011-07-03
          • 1970-01-01
          • 2013-11-19
          相关资源
          最近更新 更多