背景故事:我在Java中做了很多大型且相对复杂的项目,在嵌入式C编程方面有很多经验。我已经熟悉了scheme和CL语法,并用racket编写了一些简单的程序。
问题:我已经计划了一个相当大的项目,并希望在球拍中做到这一点。我听过很多“如果你”得到“lisp,你将成为一个更好的程序员”等等。但每次我尝试计划或编写一个程序时,我仍然用熟悉的有状态对象与接口“分解”任务。
是否有针对lisp的“设计模式”?如何“获得”lisp-family“mojo”?如何逃避面向对象约束你的思考?如何应用功能强大的宏观设施推动的功能性编程思想?我尝试在github上研究大项目的源代码(例如Light Table)并且更加困惑,而不是开悟。
EDIT1(不那么暧昧的问题):关于这个主题是否有很好的文献,你可以推荐或者是否有用cl / scheme / clojure编写的高质量的开源项目,可以作为一个很好的例子吗?
多年来,一些“范例”已经流行起来:
结构化编程,面向对象,功能等。会有更多。
即使在一种模式不合时宜之后,它仍然可以很好地解决首先使其受欢迎的特定问题。
因此,例如使用OOP进行GUI仍然很自然。 (大多数GUI框架都有一堆由消息/事件修改的状态。)
球拍是多范式的。它有一个 class
系统。我很少用它,
但是当OO方法对问题有意义时,它是可用的。
Common Lisp有多种方法和CLOS。 Clojure有多种方法和Java类互操作。
无论如何,基本的有状态OOP~ =在闭包中改变一个变量:
#lang racket
;; My First Little Object
(define obj
(let ([val #f])
(match-lambda*
[(list) val]
[(list 'double) (set! val (* 2 val))]
[(list v) (set! val v)])))
obj ;#<procedure:obj>
(obj) ;#f
(obj 42)
(obj) ;42
(obj 'double)
(obj) ;84
这是一个伟大的对象系统吗?不。但它可以帮助您看到OOP的本质是使用修改它的函数封装状态。你可以轻松地在Lisp中做到这一点。
我得到的是:我不认为使用Lisp是关于“反OOP”还是“支持功能”。相反,它是一种很好的方式来玩(并在生产中使用)编程的基本构建块。您可以探索不同的范例。您可以尝试诸如“代码是数据,反之亦然”之类的想法。
我不认为Lisp是某种精神体验。至多,它就像Zen,而satori意识到所有这些范例都只是同一枚硬币的不同方面。他们都很精彩,他们都很糟糕。指向解决方案的范例不是解决方案。等等等等等等。 :)
我的实际建议是,听起来你想要完善函数式编程的经验。如果你必须第一次在一个大项目上这样做,这是具有挑战性的。但在这种情况下,尝试将程序分解为“维持状态”与“计算事物”的部分。后者是你可以尝试专注于“更具功能性”的地方。寻找编写纯函数的机会。将它们连在一起。了解如何使用高阶函数。最后,将它们连接到应用程序的其余部分 - 这可以继续是有状态的,OOP和命令式。那是好的,现在,也许永远。
比较OO与Lisp中的编程(以及一般的“功能”编程)的方法是查看每个“范例”对程序员的启用。
这一推理的一个观点是查看数据的表示,OO样式使得扩展数据表示变得更容易,但却使得在数据上添加操作变得更加困难。相比之下,功能样式使添加操作更容易,但更难添加新的数据表示。
具体来说,如果有一个带有OO的Printer接口,则很容易添加一个实现该接口的新HPPrinter类,但是如果要向现有接口添加新方法,则必须编辑实现该接口的每个现有类。 ,如果类定义隐藏在库中,则更难以实现。
相比之下,使用函数样式,函数(而不是类)是代码的单位,因此可以轻松添加新操作(只需编写函数)。但是,每个函数都负责根据输入的类型进行调度,因此添加新的数据表示需要编辑对该类数据进行操作的所有现有函数。
确定哪种样式更适合您的域取决于您是否更有可能添加表示或操作。
这当然是一种高级概括,并且每种风格都已经开发出解决方案来应对所提到的权衡(例如,针对OO的mixins),但我认为它仍然在很大程度上持有。
这是一篇着名的学术论文 25年前就抓住了这个想法。
以下是最近课程的一些注释 (我教过)描述相同的哲学。
(注意