“defun defun () 3”是什么意思?

8
在CppCon的一个演讲中(https://www.youtube.com/watch?v=80BZxujhY38,5:00处),赫伯特·萨特暗示defun defun 3存在一些问题。在我谷歌搜索后,仍然不清楚为什么。有人可以详细说明吗?

3
在Common Lisp中,重新定义标准符号如defun会导致未定义的行为。链接的论文是通过打压其他编程语言来提升一种语言的方法,这在编程语言社区经常出现。 - coredump
1
@coredump,这实际上在论文中有提到。 - melpomene
3
看到一篇关于C++抱怨Lisp的论文真是很有趣。在C++中,重载位移操作符来进行I/O似乎是某个人的好主意。 - user5920214
@tfb 没错。'Image' 明显是进行 IO 的最佳方式。 - TamaMcGlinn
@TamaMcGlinn:我不知道那是什么意思。如果你在谈论Lisp中的“quote”:那根本与IO无关。 - user5920214
Ada使用Image属性获取某物的字符串表示形式,然后您将其传递给Text_IO.Put_Line过程。这只是另一个编程语言以明显无理由的方式执行任务的例子。 - TamaMcGlinn
1个回答

5

同一视频的评论中:

Herb Sutter

参见论文P0707(http://wg21.link/p0707),并搜索“defun”。Lisp defun(以及Scheme define)允许您定义函数...但在Lisp和Scheme中,您甚至可以重新定义内置函数和宏,包括defun/define本身,这就是“defun defun” /“define define”的作用。这里有一个相关的StackExchange问题示例:https://emacs.stackexchange.com/questions/375/symbols-value-as-a-variable-is-void-defun-when-reloading-emacs

我不想在C++中做任何类似的事情,在我的提案中也没有这样的东西,您不能更改任何定义(包括在定义后定义此类),您不能接触并影响任何其他人的类型或代码,您唯一能做的就是参与生成仅一次然后不可变的此类定义,您正在编写的类现在很好地局限和界定...但仍然非常强大。

链接的论文中包含以下内容:

5.2.1 其他语言中的问题

在Lisp和相关语言中,程序员可以重新定义其他人的代码,甚至是全局语言设施(例如Lisp中臭名昭著的(defun defun () 3)或Scheme中的(define define () 3))。这是强大的,但是缺乏纪律性(会导致任意全局效果,甚至破坏语言本身),易碎(Lisp使编写“只写不读”代码变得特别容易,这些代码难以审查、阅读和维护),并且导致程序在其组件之间以及与其开发者的环境之间紧密耦合(Lisp使编写的代码的含义取决于本地定制,难以共享,并且当共享时难以与来自具有竞争假设的环境的其他代码组合)。4

脚注说:

4 Lisp的各种化身和分支尝试以各种方式缓解这个问题,但没有实际上去除根本原因:Common Lisp添加了保证包中所有符号COMMON-LISP受到保护,并且不得被用户代码重新定义否则将导致未定义行为的保证;虽然这提供了一些标准设施的保护,但它并没有解决一般问题,因为它仍然允许一个用户代码集合重新定义另一个用户代码集合中的东西。此外,像SBCL这样的实现尝试通过提供“锁定”包的方式来进一步改善问题,以使其内容不能被意外重新定义;然而,即使是SBCL也提供了“解锁”它们的方法。


3
这并不能解决一般性问题,因为它仍然允许一个用户代码集合重新定义另一个用户代码集合中的内容。在Lisp中,这被视为一项有用的功能而不是问题。 - Rainer Joswig
1
@RainerJoswig 同意。这完全取决于你如何设置你的优先级。在这方面,Lisp很像Perl(例如,参见元类中的make_immutable)。 - melpomene
CL有锁,如果选择忽略它们,你就得自己承担后果。在Scheme中,你需要回到R5RS才能将其作为一个问题。从R6RS开始,你可以import标准库并在自己的代码中重新定义它,但你不能影响其他人编写的库。如果你取一个对象并调用该对象上的方法,如果你的类型规范模糊不清,你就不知道你正在调用什么方法。在我看来,这与defun defun完全相同,只是加了类。顺便说一句,我不是Python的粉丝,但他们的monkey patching方式非常好。 - Sylwester
C和C++程序不是通过运行代码来重新定义,而是通过加载目标文件来实现的;然而,这甚至可以在运行时使用共享库完成。ELF共享库允许新加载的对象覆盖可执行文件或先前加载的对象中已满足的链接引用。 - Kaz
1
@melpomene defun 是一个宏。你可以在 C 和 C++ 中重新定义宏:#undef mac #define mac(blah) ...。就像在 Lisp 中一样,这对已经编译的代码没有影响。如果有人在他们的 Lisp 图像中删除了 defun,我仍然可以在我的 Lisp 图像中编译带有 defun 的文件,并给他们加载的已编译模块,因为它们不会扩展我的 defun 宏。 - Kaz
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接