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

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

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

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


当前回答

就我个人而言,我发现来自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.

其他回答

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

Clojure如何实现关注点分离?

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

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

这里有一些链接:

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

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

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

此外,函数式程序确实表现出模块化和其他结构。你的详细设计必须根据该结构中的概念来表达。

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

Many OO "design patterns" are captured as higher-order functions. For example, the Visitor pattern is known in the functional world as a "fold" (or if you are a pointy-headed theorist, a "catamorphism"). In functional languages, data types are mostly trees or tuples, and every tree type has a natural catamorphism associated with it. These higher-order functions often come with certain laws of programming, aka "free theorems". Functional programmers use diagrams much less heavily than OO programmers. Much of what is expressed in OO diagrams is instead expressed in types, or in "signatures", which you should think of as "module types". Haskell also has "type classes", which is a bit like an interface type. Those functional programmers who use types generally think that "once you get the types right; the code practically writes itself." Not all functional languages use explicit types, but the How To Design Programs book, an excellent book for learning Scheme/Lisp/Clojure, relies heavily on "data descriptions", which are closely related to types.

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

任何基于数据抽象的设计方法都可以很好地工作。我碰巧认为,当语言有显式类型时,这更容易,但即使没有显式类型,它也可以工作。Barbara Liskov和John Guttag所著的《程序开发中的抽象与规范》(Abstraction and Specification in Program Development)是一本关于抽象数据类型设计方法的好书,很容易适应函数式编程。利斯科夫获得图灵奖的部分原因就是这项工作。

Another design methodology that is unique to Lisp is to decide what language extensions would be useful in the problem domain in which you are working, and then use hygienic macros to add these constructs to your language. A good place to read about this kind of design is Matthew Flatt's article Creating Languages in Racket. The article may be behind a paywall. You can also find more general material on this kind of design by searching for the term "domain-specific embedded language"; for particular advice and examples beyond what Matthew Flatt covers, I would probably start with Graham's On Lisp or perhaps ANSI Common Lisp.

常见的步骤是什么,我使用什么工件?

常见的步骤:

Identify the data in your program and the operations on it, and define an abstract data type representing this data. Identify common actions or patterns of computation, and express them as higher-order functions or macros. Expect to take this step as part of refactoring. If you're using a typed functional language, use the type checker early and often. If you're using Lisp or Clojure, the best practice is to write function contracts first including unit tests—it's test-driven development to the max. And you will want to use whatever version of QuickCheck has been ported to your platform, which in your case looks like it's called ClojureCheck. It's an extremely powerful library for constructing random tests of code that uses higher-order functions.

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