函数式编程的工程方法?
正如今天所教授的软件工程完全侧重于面向对象的编程和世界的“自然”面向对象视图。 有一个详细的方法论,描述了如何通过几个步骤和许多(UML)构件(如用例图或类图)将领域模型转换为类模型。 许多程序员已经内化了这种方法,并且对如何从头开始设计一个面向对象的应用程序有了一个好主意。
新的宣传是函数式编程,这在许多书籍和教程中都有教导。 但是功能软件工程呢? 在阅读关于Lisp和Clojure时,我发现了两个有趣的陈述:
功能性程序通常是自下而上的,而不是自上而下('On Lisp',Paul Graham)
功能程序员使用OO程序员使用对象/类的地图('Clojure for Java程序员',Rich Hickley谈话)。
那么,一个功能应用系统(基于模型)设计的方法是什么,即在Lisp或Clojure中? 什么是常见步骤,我使用了哪些工件,如何将它们从问题空间映射到解决方案空间?
感谢上帝,软件工程人员还没有发现函数式编程。 以下是一些相似之处:
许多面向对象的“设计模式”被捕获为高阶函数。 例如,访问者模式在功能世界中被称为“折叠”(或者如果你是尖尖的理论家,“变形神色”)。 在函数式语言中,数据类型主要是树或元组,并且每种树类型都有与之相关的自然变形。
这些高阶函数通常伴随着某些编程规则,也就是“自由定理”。
功能程序员使用图表比OO程序员少得多。 OO图中表达的大部分内容都是以类型或“签名”表示,您应将其视为“模块类型”。 Haskell也有“类型类”,它有点像接口类型。
那些使用类型的函数式程序员通常认为“一旦你获得了正确的类型,代码实际上就自己编写了。”
并非所有的功能语言都使用明确的类型,但如何设计程序书籍是一本学习Scheme / Lisp / Clojure的优秀书籍,在很大程度上依赖于与类型密切相关的“数据描述”。
那么,一个功能应用系统(基于模型)设计的方法是什么,即在Lisp或Clojure中?
任何基于数据抽象的设计方法都能很好地工作。 我碰巧认为,当语言具有明确的类型时,这更容易,但即使没有,语言也能正常工作。 一本关于抽象数据类型的设计方法的好书,很容易适用于函数式编程,它是Barbara Liskov和第一版John Guttag在程序开发中的Abstraction and Specification。 Liskov在这项工作中赢得了图灵奖。
Lisp独特的另一种设计方法是确定哪些语言扩展在您工作的问题域中有用,然后使用卫生宏将这些构造添加到您的语言中。 阅读这种设计的好地方是Matthew Flatt的文章“在球拍上制作语言”。 该文章可能位于付费墙后面。 通过搜索术语“特定领域的嵌入式语言”,您也可以在这种设计中找到更多的一般材料; 除了Matthew Flatt涵盖的特定建议和例子之外,我可能会从Graham的On Lisp或ANSI Common Lisp开始。
什么是常用步骤,我使用的是什么工件?
常见步骤:
识别程序中的数据及其中的操作,并定义表示此数据的抽象数据类型。
确定常见的计算行为或模式,并将其表示为高阶函数或宏。 预计将这一步作为重构的一部分。
如果您使用的是键入的功能语言,请尽早使用类型检查器。 如果你使用Lisp或Clojure,最好的做法是首先编写函数合同,包括单元测试 - 它是测试驱动开发到最大值。 你会想要使用任何版本的QuickCheck已经移植到你的平台,在你的情况下,它看起来就是所谓的ClojureCheck。 这是一个非常强大的库,用于构建使用高阶函数的代码的随机测试。
对于Clojure,我建议回到旧的关系建模。 走出Tarpit是一个鼓舞人心的阅读。
就我个人而言,我发现面向对象开发的所有常见良好实践都适用于函数式编程 - 只需稍作调整即可考虑功能性世界观。 从方法学的角度来看,你并不需要做任何根本不同的事情。
我的经验来自近年来从Java转移到Clojure。
一些例子:
了解您的业务领域/数据模型 - 无论您要设计对象模型还是使用嵌套地图创建功能数据结构,同等重要。 在某些方面,FP可能更容易,因为它鼓励您将数据模型与函数/过程分开进行思考,但是您仍然必须同时执行这两个操作。
设计中的服务导向 - 从FP角度看,实际上工作得很好,因为典型的服务实际上只是一种带有一些副作用的功能。 我认为Lisp世界有时支持的软件开发的“自下而上”的观点实际上只是另一个幌子中的良好的面向服务的API设计原则。
测试驱动开发 - 在FP语言中运行良好,事实上有时甚至更好,因为纯函数非常适合编写清晰,可重复的测试,而无需设置有状态的环境。 您可能还想要构建单独的测试来检查数据完整性(例如,此映射是否包含我期望的所有关键字,以平衡在OO语言中类定义将在编译时为您执行此操作的事实)。
Prototying /迭代 - 与FP一样好。 如果您非常擅长构建工具/ DSL并在REPL中使用它们,您甚至可以与用户进行原型开发。