是否有用于函数式编程的软件工程方法?

时间:2011-01-31 14:50:30

标签: functional-programming clojure lisp model-driven-development

今天教授的软件工程完全专注于面向对象的编程和面向对象的“自然”的世界观。有一个详细的方法描述了如何将域模型转换为具有多个步骤的类模型以及许多(UML)工件,如用例图或类图。许多程序员已经内化了这种方法,并且很好地了解了如何从头开始设计面向对象的应用程序。

新的炒作是函数式编程,在许多书籍和教程中都有教授。但功能软件工程呢? 在阅读有关Lisp和Clojure的文章时,我发现了两个有趣的陈述:

  1. 功能性程序通常是自下而上而不是自上而下开发('On Lisp',Paul Graham)

  2. 功能程序员使用地图,其中OO程序员使用对象/类('Clojure for Java Programmers',Rich Hickley谈话)。

  3. 那么功能性应用程序的系统化(基于模型的?)设计的方法是什么,即在Lisp或Clojure中?有哪些常见步骤,我使用哪些工件,如何将它们从问题空间映射到解决方案空间?

13 个答案:

答案 0 :(得分:160)

感谢上帝,软件工程人员还没有发现函数式编程。以下是一些相似之处:

  • 许多OO“设计模式”被捕获为高阶函数。例如,访客模式在功能世界中被称为“折叠”(或者如果你是一个尖头理论家,那就是“catamorphism”)。在函数式语言中,数据类型主要是树或元组,并且每种树类型都具有与之相关的自然变形。

    这些高阶函数通常带有某些编程定律,即“自由定理”。

  • 功能程序员使用图表的程度远低于OO程序员。在OO图中表达的大部分内容都表示在 types 或“signatures”中,您应该将其视为“模块类型”。 Haskell也有“类型类”,有点像接口类型。

    那些使用类型的函数式程序员通常认为“一旦你获得了正确的类型;代码实际上就是自己写的。”

    并非所有函数式语言都使用显式类型,但How To Design Programs本书是学习Scheme / Lisp / Clojure的优秀书籍,在很大程度上依赖于与数据类型密切相关的“数据描述”。

  

那么功能性应用程序的系统化(基于模型的?)设计的方法是什么,即在Lisp或Clojure中?

任何基于数据抽象的设计方法都能很好地工作。我碰巧认为,当语言具有明确的类型时,这会更容易,但即使没有,它也能正常工作。关于抽象数据类型设计方法的好书,很容易适应函数式编程,是Barbara Liskov和John Guttag的抽象和规范开发第一个版本。利斯科夫为这项工作赢得了图灵奖。

Lisp独有的另一种设计方法是确定哪些语言扩展在您工作的问题域中有用,然后使用卫生宏将这些结构添加到您的语言中。关于这种设计的一个好地方是Matthew Flatt的文章Creating Languages in Racket。这篇文章可能是付费墙的背后。您还可以通过搜索“特定于域的嵌入式语言”这一术语找到有关此类设计的更多常规材料;除了Matthew Flatt所涵盖的特殊建议和例子,我可能会从Graham的On LispANSI Common Lisp开始。

  

有哪些常见步骤,我使用哪些工件?

常见步骤:

  1. 识别程序中的数据及其上的操作,并定义表示此数据的抽象数据类型。

  2. 识别常见的动作或计算模式,并将它们表示为高阶函数或宏。期望将此步骤作为重构的一部分。

  3. 如果您使用的是键入的函数式语言,请尽早使用类型检查器。如果您使用的是Lisp或Clojure,最佳做法是首先编写函数契约,包括单元测试 - 它是测试驱动开发的最大值。并且您将希望使用已移植到您的平台的任何版本的QuickCheck,在您的情况下,它看起来像是ClojureCheck。它是一个非常强大的库,用于构建使用高阶函数的代码的随机测试。

答案 1 :(得分:44)

对于Clojure,我建议回到旧的关系建模。 Out of the Tarpit是一本鼓舞人心的读物。

答案 2 :(得分:38)

就我个人而言,我发现OO开发中所有常见的良好实践也适用于函数式编程 - 只需稍加调整即可考虑功能性世界观。从方法论的角度来看,你并不需要做任何根本不同的事情。

我的经验来自于近年来从Java搬到Clojure。

一些例子:

  • 了解您的业务领域/数据模型 - 无论您是要设计对象模型还是使用嵌套地图创建功能数据结构,都同样重要。在某些方面,FP可以更容易,因为它鼓励您将数据模型与功能/流程分开考虑,但您仍然必须同时执行这两者。

  • 设计中的服务导向 - 从FP的角度来看实际上非常有效,因为典型的服务实际上只是一个带有一些副作用的函数。我认为在Lisp世界中有时支持的软件开发的“自下而上”观点实际上只是另一种形式的面向服务的API设计原则。

  • 测试驱动开发 - 在FP语言中运行良好,实际上有时甚至更好,因为纯函数非常适合编写清晰,可重复的测试,而无需建立有状态的环境。您可能还想构建单独的测试以检查数据完整性(例如,此映射是否具有我期望的所有键,以平衡在OO语言中类定义将在编译时为您强制执行此操作的事实)。 / p>

  • Prototying / iteration - 与FP一样好用。如果您非常擅长构建工具/ DSL并在REPL中使用它们,您甚至可以与用户进行实时原型设计。

答案 3 :(得分:13)

OO编程将数据与行为紧密结合在一起。功能编程将两者分开。所以你没有类图,但是你有数据结构,特别是你有代数数据类型。可以编写这些类型以非常匹配您的域,包括通过构造消除不可能的值。

所以没有关于它的书籍和书籍,但有一种完善的方法,正如俗话所说,使不可能的价值无法代表。

通过这样做,您可以做出一系列选择,将某些类型的数据表示为函数,相反,将某些函数表示为数据类型的并集,以便您可以获得,例如,序列化,更严格的规范,优化等。

然后,鉴于此,您在adts上编写函数,以便建立某种代数 - 即存在适用于这些函数的固定法则。有些可能是幂等的 - 在多次应用之后也是如此。有些是联想的。有些是传递性的等等。

现在,您拥有一个域,您可以根据良好的法律构建函数。一个简单的嵌入式DSL!

哦,给定属性,你当然可以编写它们的自动随机测试(ala QuickCheck)..而这只是一个开始。

答案 4 :(得分:7)

面向对象的设计与软件工程不同。软件工程与我们如何从需求到工作系统,按时和低缺陷率的整个过程有关。功能编程可能与OO不同,但它不会消除需求,高级和详细设计,验证和测试,软件度量,估计以及所有其他“软件工程”。

此外,功能程序确实展示了模块化和其他结构。您的详细设计必须以该结构中的概念来表达。

答案 5 :(得分:5)

一种方法是在所选择的函数编程语言中创建内部DSL。然后,“模型”是在DSL中表达的一组业务规则。

答案 6 :(得分:5)

请参阅我对另一篇文章的回答:

How does Clojure aproach Separation of Concerns?

我同意有关如何构建使用FP方法的大型应用程序的更多需求(还有更多需要用来记录FP驱动的UI)

答案 7 :(得分:3)

虽然这可能被认为是天真和简单的,但我认为“设计食谱”(Felleisen等人在他们的书HtDP中提倡的一种应用于编程的问题解决的系统方法)将接近于你似乎在寻找。

这里有几个链接:

http://www.northeastern.edu/magazine/0301/programming.html

http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.86.8371

答案 8 :(得分:2)

我发现行为驱动开发非常适合在Clojure和SBCL中快速开发代码。将BDD与函数式语言相结合的真正好处是,我倾向于编写比使用过程语言时通常更精细的粒度单元测试,因为我在将问题分解为更小的功能块方面做得更好。

答案 9 :(得分:2)

我最近发现这本书: Functional and Reactive Domain Modeling

我认为完全符合你的问题。

从书中描述:

  

功能和反应域建模教您如何根据纯函数思考域模型,以及如何构建它们以构建更大的抽象。您将从函数式编程的基础知识开始,逐步进入实现复杂域模型所需的高级概念和模式。该书演示了高级FP模式如代数数据类型,基于类型类的设计以及副作用的隔离如何使您的模型构成可读性和可验证性。

答案 10 :(得分:1)

老实说,如果你想要功能程序的设计配方,请看一下标准的函数库,比如Haskell的Prelude。在FP中,模式通常由更高阶的过程(对函数起作用的函数)本身捕获。因此,如果看到一个模式,通常会创建一个更高阶的函数来捕获该模式。

一个很好的例子是fmap。此函数将函数作为参数,并将其应用于第二个参数的所有“元素”。由于它是Functor类型类的一部分,所以Functor的任何实例(例如列表,图形等等)都可以作为第二个参数传递给此函数。它捕获了将函数应用于第二个参数的每个元素的一般行为。

答案 11 :(得分:1)

与牛津大学(英国)的Richard Bird教授和编程代数小组有关联的“程序计算”/“计算设计”风格,我认为这不是太过牵强。方法

就个人而言,当我喜欢AoP小组制作的作品时,我自己没有以这种方式练习设计的纪律。然而,这是我的缺点,而不是程序计算之一。

答案 12 :(得分:-6)

那么,

通常很多函数式编程语言在很长一段时间内用于“小玩具问题”。

由于OOP由于“状态”而导致“并行编程”出现困难,因此它们现在变得越来越受欢迎。而且有时候功能风格更适合谷歌MapReduce等手头的问题。

我确信,当功能人员碰壁[试图实现大于1.000.000行代码的系统]时,他们中的一些会带来新的软件工程方法,并带有流行语:-)。他们应该回答一个老问题:如何将系统分成几块,以便我们可以一次“咬”每件? [工作迭代,inceremental en evolutionary way]使用功能风格。

  

确定功能样式将影响我们的面向对象   Style.We“仍然”来自功能系统的许多概念并适应   我们的OOP语言。

但功能性程序是否会用于如此庞大的系统?它们会成为主流吗?这就是问题

如果没有实施如此庞大的系统,没有人能够采用现实的方法,让他的手变脏。 首先,你应该弄脏手,然后建议解决方案。解决方案 - 没有“真正的痛苦和污垢”的建议将是“幻想”。