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

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

函数式程序通常是自底向上而不是自顶向下开发的(《论Lisp》,Paul Graham) 函数式程序员使用映射,而oop程序员使用对象/类(《Clojure for Java Programmers》,Rich Hickley谈话)。

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


当前回答

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

Clojure如何实现关注点分离?

我同意需要写更多关于如何构造使用FP方法的大型应用程序的主题(另外需要做更多的工作来记录FP驱动的ui)

其他回答

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

我最近发现了这本书: 功能和反应域建模

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

从书的描述:

Functional and Reactive Domain Modeling teaches you how to think of the domain model in terms of pure functions and how to compose them to build larger abstractions. You will start with the basics of functional programming and gradually progress to the advanced concepts and patterns that you need to know to implement complex domain models. The book demonstrates how advanced FP patterns like algebraic data types, typeclass based design, and isolation of side-effects can make your model compose for readability and verifiability.

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

Clojure如何实现关注点分离?

我同意需要写更多关于如何构造使用FP方法的大型应用程序的主题(另外需要做更多的工作来记录FP驱动的ui)

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

所以没有关于这个问题的书籍,但有一种行之有效的方法,正如俗话所说,让不可能的价值观变得不可代表。

在这样做的过程中,您可以做出一系列选择,将某些类型的数据表示为函数,反之,将某些函数表示为数据类型的联合,这样您就可以获得,例如序列化、更严格的规范、优化等。

然后,考虑到这一点,你在adts上写函数,这样你就建立了某种代数——也就是说,这些函数有固定的定律。有些可能是等幂的,多次应用后是一样的。有些是结合性的。有些是传递性的,等等。

现在你有了一个定义域,在这个定义域上,你有了函数,这些函数根据良好的规律组成。一个简单的嵌入式DSL!

哦,对了,给定属性,你当然可以编写自动随机测试。而这仅仅是个开始。

就我个人而言,我发现来自OO开发的所有常见的良好实践也适用于函数式编程——只是在考虑函数式世界观时做了一些小调整。从方法论的角度来看,您实际上不需要做任何根本不同的事情。

我的经验来自于最近几年从Java转移到Clojure。

一些例子:

Understand your business domain / data model - equally important whether you are going to design an object model or create a functional data structure with nested maps. In some ways, FP can be easier because it encourages you to think about data model separately from functions / processes but you still have to do both. Service orientation in design - actually works very well from a FP perspective, since a typical service is really just a function with some side effects. I think that the "bottom up" view of software development sometimes espoused in the Lisp world is actually just good service-oriented API design principles in another guise. Test Driven Development - works well in FP languages, in fact sometimes even better because pure functions lend themselves extremely well to writing clear, repeatable tests without any need for setting up a stateful environment. You might also want to build separate tests to check data integrity (e.g. does this map have all the keys in it that I expect, to balance the fact that in an OO language the class definition would enforce this for you at compile time). Prototying / iteration - works just as well with FP. You might even be able to prototype live with users if you get very extremely good at building tools / DSL and using them at the REPL.