从我编程生涯的第一天开始,我就开始使用面向对象编程。但是,我对学习其他范式很感兴趣(我在这里多次说过是件好事,但我没有时间去做)。我想我不仅准备好了,而且有时间,所以我将开始使用 F# 进行函数式编程。
但是,我不确定如何构建更少的设计应用程序。我习惯于 OO 编程中的每个文件一个类和类名词/功能动词的想法。您如何设计和构建功能性应用程序?
从我编程生涯的第一天开始,我就开始使用面向对象编程。但是,我对学习其他范式很感兴趣(我在这里多次说过是件好事,但我没有时间去做)。我想我不仅准备好了,而且有时间,所以我将开始使用 F# 进行函数式编程。
但是,我不确定如何构建更少的设计应用程序。我习惯于 OO 编程中的每个文件一个类和类名词/功能动词的想法。您如何设计和构建功能性应用程序?
您可能想查看我最近的一篇博客文章:函数式编程如何影响您的代码结构?
在较高层次上,OO 设计方法对于构建 F# 程序仍然非常有用,但是当您深入到较低层次时,您会发现这种方法被打破(规则的更多例外)。在物理级别上,“每个文件一个类”并非在所有情况下都有效,因为需要在同一个文件中定义相互递归的类型(type Class1 = ...和Class2 = ...),并且您的一些代码可能驻留在未绑定到特定类的“免费”函数中(这是 F#“模块”的优点)。F# 中的文件排序约束也将迫使您批判性地思考程序中类型之间的依赖关系;这是一把双刃剑,因为解开高级依赖关系可能需要更多的工作/思考,但会产生以始终使它们易于接近的方式组织的程序(因为最原始的实体总是排在第一位,你可以始终从“从上到下”阅读程序并逐一介绍新事物,而不是仅仅开始寻找一个充满代码文件的目录而不知道“从哪里开始”)。
How to Design Programs就是这样(冗长乏味,使用 Scheme 而不是 F#,但原则仍然适用)。简而言之,您的代码反映了您的数据类型;这个想法可以追溯到老式的“结构化编程”,只有函数式编程更明确,并且具有更高级的数据类型。
鉴于现代函数式语言(即不是 lisps)默认使用早期绑定的多态函数(有效地),并且面向对象只是安排具有多态函数的一种特殊方式,如果你知道如何设计适当封装的类。
Lisp 使用后期绑定来实现类似的效果。老实说,除了没有明确声明类型的结构外,没有太大区别。
如果您已经使用 C++ 模板函数进行了广泛的编程,那么您可能已经有了一个想法。
在任何情况下,答案都是小“类”,而不是修改内部状态,您必须返回具有不同状态的新版本。
F# 为大规模结构化编程(例如接口)提供了传统的 OO 方法,并没有尝试提供在 OCaml 之类的语言中开创的实验方法(例如函子)。
因此,F# 程序的大规模结构化本质上与 C# 程序相同。
函数式编程肯定是一种不同的范式。也许最简单的理解它的方法是坚持使用流程图来布置设计。每个函数都是不同的,没有继承,没有多态,不同。数据在函数之间传递,以进行删除、更新、插入和创建新数据。
关于构建功能程序:
OO 语言用类来构造代码,而函数式语言用模块来构造代码。对象包含状态和方法,模块包含数据类型和函数。在这两种情况下,结构单元将数据类型与相关行为组合在一起。两种范式都有用于构建和实施抽象障碍的工具。
我强烈建议您选择一种您熟悉的函数式编程语言(F#、OCaml、Haskell 或 Scheme),并仔细研究其标准库的结构。
例如,将 OCaml Stack模块与.NET 中的System.Collections.Generic.Stack或 Java 中的类似集合进行比较。
这完全是关于纯函数以及如何组合它们以构建更大的抽象。这实际上是一个难题,需要强大的数学背景。幸运的是,有几种模式可以进行深入的正式和实践研究。关于函数式和反应式域建模Debasish Ghosh 进一步探讨了这个主题,并将几个应用纯函数式模式的实际场景放在一起:
函数式和反应式域建模教你如何从纯函数的角度来思考域模型,以及如何组合它们以构建更大的抽象。您将从函数式编程的基础知识开始,逐步学习实现复杂领域模型所需了解的高级概念和模式。这本书展示了高级 FP 模式(如代数数据类型、基于类型类的设计和副作用的隔离)如何使您的模型组合起来具有可读性和可验证性。