我很惊讶地发现 Hacker News 的源代码只是一个包含函数定义的平面列表的大文件。 Git Hub - news.arc
这对于函数式编程来说是典型的吗?在 OOP 项目中很常见的潜在深层目录结构中的大量短文件中包含源代码是否不常见?
FP 中的模块是否与 OOP 中的命名空间相同?
我很惊讶地发现 Hacker News 的源代码只是一个包含函数定义的平面列表的大文件。 Git Hub - news.arc
这对于函数式编程来说是典型的吗?在 OOP 项目中很常见的潜在深层目录结构中的大量短文件中包含源代码是否不常见?
FP 中的模块是否与 OOP 中的命名空间相同?
有许多函数式编程语言 (FPL),它们非常不同。Lisp 方言也是如此(如 Scheme、Common Lisp、Logo、Arc 等)。
它们通常不是围绕类(或类似概念)组织的,并且类通常不与命名空间混为一谈。
在一些面向对象的语言中,程序由许多类组成,类层次结构(或类似的东西)被映射到目录结构,每个类都是一个或多个文件。这导致软件系统由许多文件和一个将这些文件/类作为层次结构浏览的 IDE 组成。(这与最初的 Smalltalk 不同,其中代码由浏览器访问,而不是基于文件检索)。
例如,在 Common Lisp 中,类不是命名空间,方法也不附加到单个类(因为有多种方法)。有一个名为“package”的单独构造,它为 Lisp 符号提供命名空间。一个典型的软件系统由捆绑了几个相关功能的文件组成。通常,更大的功能单元拥有自己的命名空间。
例如,一个图形工具包可能有几个命名空间:ui-backend、ui-user、ui-system、ui-drawing、ui-animation。ui-drawing 命名空间可用于多个文件:ui-draw-2d-objects.lisp、ui-draw-3d-objects.lisp、ui-draw-macros.lisp 等。单个文件 ui-draw-2d-objects.lisp 将捆绑绘制 2d 对象(线、多边形、圆、位图……)所需的所有类、方法和变量。
然后开发系统负责提供导航。但通常导航不是分层的,而是基于搜索和检索符号。那么文件有多大并不重要。更重要的是,文件对正确的功能进行分组并在内部进行组织,以便可以以某种方式识别相关的功能。
例如,如果我想识别所有矩形绘图函数,我会使用 REPL。
在 LispWorks 中,绘图原语位于包“GP”或“GRAPHICS-PORTS”中。然后我可以让 LispWorks 告诉我在包“GP”中包含“draw-rect”的所有符号。
CL-USER 10 > (apropos "draw-rect" "GP")
GRAPHICS-PORTS::%DRAW-RECTANGLE (defined)
GRAPHICS-PORTS::DRAW-RECTANGLE-BOUNDS (defined)
GRAPHICS-PORTS::%DRAW-RECTANGLES (defined)
GRAPHICS-PORTS::DRAW-RECTANGLES-BOUNDS (defined)
GRAPHICS-PORTS:DRAW-RECTANGLES (defined)
GRAPHICS-PORTS:DRAW-RECTANGLE (defined)
上面的列表告诉我,这些符号中的每一个都有定义的功能,带有单个冒号的符号是“导出的”。
然后我可以使用这些符号来查找更多信息:参数列表、源代码、文档等等。Common Lisp 甚至提供了标准函数,如 DOCUMENTATION、DESCRIBE 和 ED。
因此,这里的开发不是基于组织到某个类层次结构的大量小文件,而是作为模块和命名空间的层次结构,每个命名空间捆绑存储在一个或多个文件中的大量功能。然后,IDE 负责支持非分层浏览和搜索。
不,模块化编程在 FP 中也很常见。模块化的相同一般原则也适用。
例如,在 Haskell 中,您可以说
import qualified Parsec as P
它为您提供了命名空间中的 Parsec 解析库P
。
模块和命名空间是否“与 OOP 中的命名空间相同”取决于您的函数式语言和您的 OOP 语言。(ML 模块与其他语言有点不同。)