有关 Forth
今天晚上继续读 《Masterminds of Programming》,忍不住又翻译了半章关于 Forth 之父的访谈。我以前读过几篇更早时期关于他的访谈,部分了解他的观点。小时候还特别迷 Forth 。这位神叨叨的老头很有意思。
没看过原来的译本,只是自己按自己的理解翻了第 4 章 Forth 的前一半。我也算对 Forth 很有爱的人吧,也还了解 Forth 里诸如 ITC (Indirected-threaded code) 这种术语到底指的什么,不过还是觉得翻译有点吃力。
对 Forth 同样有爱的同学们姑且看之吧。
Forth 语言及语言的设计
你怎样定义 Forth
Chunk: Forth 是一门计算机编程语言,其语法规模简到极致。把参数栈专门独立出来是语言的一大特色,这使得子程序调用非常高效。因此,语言采用后缀表达式(先列参数,再跟操作符),并鼓励把程序高度细分成众多短小的子程序的风格。这些子程序共享栈上参数。
我曾经读过关于 Forth 这个名字的介绍,据说是象征着第四代软件开发。你可以给我们多介绍一些吗?
Chunk: Forth 源于 "Fourth (第四)" 这个词。暗指 “第四代计算机语言”。据我回忆,我跳过了一代。FORTRAN/COBOL 是第一代;Algol/Lisp 是第二代。这些语言都强调语法。语法越详尽,越能检查出错误。但大部分错误是语法错误。我决定将语法元素减到最小,而强调其语义。被加载的 Forth 词就真正的表达其含义。
你把 Forth 当成一个语言工具集。我这样理解这个视角:给出相对其它语言更简单的语法,提供比其它语言所用的更短的词构成一个词汇表的能力。我还漏掉了什么吗?
Chunk Moore: 就是这样。其中关键一点是,我们尽其可能的分解。一段 Forth 程序有大量的小词构成。相对来看,同样的 C 程序用到的词则少的多,而且每个词都要大的多。
我所谓短小的词,是指可以用一行源代码定义出来的词。你可以用刚定义出来的词来定义下一个新词,如此层叠,最终得到数千个词的定义,最终形成了语言。这里面临的挑战是:1) 判断哪些词是有用的。2) 记住所有定义出来的词。我现在正在编写的应用程序有上千个词。所以我写了个工具来搜索这些词,不过你必须先记得有这个词,大概怎么拼才找得出来。
好了,现在有了种完全不同的编程风格,程序员需要一点时间才能适应。我已经看过太多 Forth 程序就像是从 C 程序直译过来的。这样做可不对。正确的做法是从零开始。关于这个工具集还有一个有趣的地方,只要你好好做,定义出来的新词和内核中预定义的词并无不同。它们一样高效,对外的意义也相同。定义你自己词是没有额外开销的。
外部结构看起来是由许多小词组成,这一点是源于 Forth 的实现吗?
Chunk: 这是我们非常高效的子程序调用序列的结果。这里面没有参数传递,因为语言是基于栈的。所有的一切仅仅只是调用和返回。而栈暴露在外。让机器识别的语言编译在一起,进入和退出字程序只需要逐字翻译成一条 call 指令和一条 return 指令。你总可以进一步的达到汇编语言层面,得到等价的指令。你可以定义一个词刚好执行一条真正的机器指令,而不是做一次子程序调用,这样,它可以匹敌任何其它语言的效率,甚至比一些语言更高效。
这样就没有 C 调用的消耗
Chunk: 没错。这赋予了程序员巨大的灵活性。如果你能聪明的分解问题,你不仅仅只是高效的完整任务,还使得完成的过程变得格外易读。
另一方面,如果你做的很糟糕,你最终的代码世界上只有你一个人能解读——连你那全知全晓的经理都看不懂你写的代码。你可以写出天书。所以,这是把双刃剑。你能做的完美无暇,也能弄得污七八糟。
你能说点什么(或展示点代码)让用别的编程语言的开发人员对 Forth 一见钟情吗?
Chunk: 让有经验的程序员对 Forth 感兴趣挺难的。这是因为他已经为他正在使用的语言/操作系统相关的工具学习做了大量的投资。为他的应用程序积累了许多。即使是告诉他们,Forth 会更小,更快,更简单,也及不上把所有东西都重写的代价。一个新手程序员,或是一个没这么干扰的需要写点代码的工程师可能更容易接受一点。嗯,或是有某个有经验的程序员要开个新项目,而这个项目有些新的约束条件,比如我现在在做的多核芯片上做开发。
你提到你见过的大量 Forth 程序看起来像 C 程序。那么你自己怎么设计一个更好的 Forth 程序的?
Chunk: 由底至上。
首先,你估计会有一些 I/O 信号要去产生,好吧,就来产生它们。这样你就写一些代码控制这些信号的产生过程。然后你一点点来,知道最终构建出高层的词。假定你把它起了个名字叫 go ,接下来你敲一下 go ,一切如期发生。
我不信任由顶向下的系统分析方法。他们判断问题是什么,然后再分解,分解出来却很难实现。
领域驱动设计建议以客户的词汇来描述商务逻辑。在创建词库和使用你问题域的术语之间有什么联系?
Chunk: 最好是程序员在开始写代码前了解那个领域。我会和客户沟通。我会倾听他用的词汇,并试着用这些词,这样他也能明白程序在做什么。Forth 因其采用后缀记号法使代码费用易读。
如果我写一个经济有关的应用程序,我可能会用一个叫作 "percent" 词。你可以在代码中写 "2.03 percent" 。这里 percent 的参数就是 2.03 ,一切都和看起来那样自然。
一个从穿孔卡片计算机时代开始的项目居然到了互联网时代的现代计算机上还这么有用。Forth 于 1968 年在 IBM 1130 上设计并运用。它到了 2007 年继续为并行处理所用,这真奇妙。
Chunk: 其间 Forth 也进化了。不过 Forth 可能是最为简洁的计算机语言。它没给程序员附加任何约束。他(她)可以瘦的层次化方式定义一些词精确切合问题的原貌。
你在设计程序时,是否把让程序读起来像英文一样做为一个目标?
Chunk: 在非常高的层次上看,是这样。但英文并非功能性描述的好语言。英语不是设计来干这个的,不过英语和 Forth 有个共同的特性,就是你能定义新词。
你用以前定义好的许多词通过解释的方式来定义新的词汇。对于自然语言来说,有可能不严谨。如果你查字典的话,会发现有些是循环定义,你查不到本质内容。
是不是把注意力转移到一堆词上面,比 C 里出现的那些各种括号来说,Forth 因此能写出更好味道的程序?
Chunk: 希望如此。这使得 Forth 程序员去关注事物的外貌,而不仅仅是其功能。如果你能组织起一系列的词,把它们有序的排列起来,会感觉很好。这正是为什么我开发了 colorForth 。我曾为 Forth 里老的语法烦恼。比如,按现在的方法,你要做注释,就必须用一左括号和一右括号来括起来表示。
我看着所有标点符号说,“好吧,可能还有更好的方式。” 这更好的方式最麻烦的一点是,源代码中每一个词都需要附加一个标签。如果我能忍受这点开销,所有的符号都另人舒服的消失了,取而代之的是,每个词都有了颜色。对我来说,这是个优雅的表达其功能的方式。
我遭到了色盲群体的抨击。他们对我试图把他们排出程序员行列的做法义愤填膺。不过某人最后想了个招,用字体的区分代替颜色的区分,这也是个不错的方法。
关键点在于,每个词有个四位的标签,这能区分出 16 种事物。编译器可以立刻感知它要做什么,而不用从上下文去推断。
第二代和第三代语言都皈依了极简主义,举例来说就是实现了 meta-circular bootstrapping (圆环自举,靠自身把自身运作起来)。Forth 是在对语言的概念定义以及硬件需求量方面的极简主义的最好的例子。这是当年的时代特征或是你做出的跨时代的创举吗?
Chunk: 非也。当时再三考虑的设计目标是尽可能的做一个最小的内核。只预定义最为必要的几个词,然后让程序员再去添加他觉得合适的。主要因素是可移植性。在那个时代,大打的小型计算机,接着又是一大坨微型计算机。而我必须把 Forth 弄到如此之多的机器上去。
我想干这点事能尽量轻松点。我要干的就是弄出一个百来个词的内核,以此能够组成一个,我叫作操作系统,但其实不完全是操作系统的东西,这个东西再给出几百个词为人所用。接下来你就能在这上面做开发了。
我来提供做前两个阶段的工作,让程序员去做第三个。我也经常做应用程序开发程序员。定义我知道的词总是很有必要。头一百个词可能用机器语言或汇编语言定义,至少是直接和特定平台打交道。第二和第三百个词可以是高层次的词,在较低层次最小化机器依赖性。接下来,应用程序就能最大限度的做到机器无关了,这样很容易把程序从一台机器移植到另一台上。
当初你能在第二阶段之上方便的移植吗?
Chunk: 绝对如此。比如我有个文本编辑器,用来编辑源代码的。它总是在各种机器上不需要修改任何地方都能用。
坊间流传着一个传说,每次你看到一台新机器,你就立刻动手把 Forth 移植到上面。是说的这个吗?
Chunk: 没错。实际上对于理解一台机器如何工作;了解那些可以用来更容易实现 Forth 标准包里词的诡异机器特性;这是条最简单的途径。
你是怎样发明 indirect-threaded code 的?
Chunk: 代码是一个很微妙的概念。每个 Forth 词在字典里有一个入口。对于基于直接线索的编码(direct-threaded code ,有译为直接串线编码),遇到引用每个词的位置直接指向要执行的代码。而基于间接线索的编码(indirect-threaded code 有译为间接串线编码) 则指明一个包含了代码所在地址的位置。这使得地址之外的信息能被访问到——比如,一个变量的值。
这可能是最为紧凑的词组织方法了。它可以等价于基于直接线索的编码和基于子程序的编码(subroutine-threaded code ,即类似 C 语言编译成的那种直接调用子程序方式)。当然这个概念和术语在 1970 年时还没有。但对我来说,这是实现各种各样词的最自然方式。
Forth 会如何影响计算机系统的未来走向吗?
Chunk: 这已经在发生了。我在微处理器优化方面干了 25 年,最新近的一个多核芯片的核心是 Forth 计算机。
Forth 提供了些啥?作为一个简单的语言,它使得这样一台简单的计算机:有 256 个字的本地内存;两个下压栈;32 条指令;异步操作;易于和相邻机器通讯;可以很小,且功耗极低。
Forth 鼓励被高度分解的程序。这非常适合在多核芯片上做并行处理。大量小程序鼓励你每一个都深思熟虑的设计。这样最终你可能只需要写 1% 的代码就够了。
只要我听到有人吹嘘代码达到了上百万行,我就知道他们肯定前面理解错问题了。当代没啥问题需要写几百万行代码。要么是程序员太粗心、要么项目经理太混蛋、要么就是为兼容一些不存在的需求。
使用 Forth 对需要小计算机编程是个巨牛叉的策略。别的语言都提供不了相当的模块化能力和扩展性。尤其计算机越来越小,它们之间必须做网络化协作(智能微尘?),这就是未来的环境。
这听起来像 Unix 的重要原则之一:以许多程序,每个只做一件事,相互作用。这依旧是当今最好的设计吗?在一台机器上跑多个程序会被通过网络运行的多个程序取代吗?
Chunk: 让代码跑在多个线程上的这个概念,被 Unix 和别的操作系统所实现。这是并行化处理的先驱。但这里有些重要的差别。
大的计算机承担得起多线程通常会要付出的一些代价。最终弄出个庞大的操作系统。对于并行化处理来说,永远都是计算机越多越好。
在资源一定的情况下,更多的计算机意味着更小的计算机。但很小的计算机是承担不起在大计算机上承担的代价的。
小的计算机的网络化会发生在芯片之上,通过 RF 连接的芯片之间。小的计算机内存也小。操作系统无处容身。计算机必须自治,自己要有能力保持通讯。因此通讯环节必须简单——没有那些煞费苦心的协议。软件也必须紧凑高效。最为理想的应用程序就是 Forth 了。
那些需要数百万行代码筑就的系统将会淡出历史舞台,是它们造就了巨大的中央计算机。分布式技术需要条不同的思路。
一门语言若是设计成支持繁杂的,拘泥于语法条条框框的代码,就会鼓励程序员写出巨大的程序。并以此自得,洋洋得意。没什么压力去寻找紧凑的方案。
以繁杂句法定义出来的语言生成的代码也可以很小,但通常做不到。以语法默示的实现流程导致了笨拙而不那么高效的目标代码。这对于小的计算机来说并不合适。一门良好设计的语言在源码和目标码之间存在一对一的联系。这向程序员昭显了源码如何生成为最终代码。注重性能、减少对文档的需求,使得程序员感到满足。
Forth 设计成对于源代码和二进制输入的目标代码皆很紧凑,这也是嵌入式开发中广泛使用的原因。不过在许多其它领域程序员总有别的理由去用别的语言。是不是说这些语言的设计的某些方面只是增加了原代码或是目标码的开支吗?
Chunk: Forth 的确很紧凑。一个因素在于他的语法量很小。
其它语言看起来是故意的增加一些语法,弄出点冗余,可以帮助语法检查以及错误检测。
Forth 没提供啥机会用来做错误检查,因为它没有冗余信息。这也使得源代码非常紧凑。
我感觉其它语言几乎所有的错误都出在语法上。设计者看起来为程序员犯下编译器就都能找出来的错误创造了条件。这没啥经济价值。这不是为写出正确的代码自找麻烦吗。
比如说类型检查吧。不同的数字类型之间的赋值错误会被侦测到。无意中带来的后果是程序员必须自己来转换类型,有时就是想回避类型检查而得到他们真正想干的事情。
语法导致的另一结果是它必须适应所有的应用程序的意图。这就会越来越复杂。Forth 是一个可扩展的语言。程序员可以创建一些别的语言只能通过编译器的改进才能获得同样性能的结构。而所有的能力不需要一开始就想好提供出来。
Forth 的特征之一是使用后缀操作符。这能简化编译器,从源代码到目标代码给出一对一的关系。程序员对他写的代码的充分理解能增加代码编译后的紧凑程度。
**许多最近的计算机语言(尤其是 Python 和 Ruby)都把可读性引为其关键好处。Forth 在这方面与之相较可以从中学到并保持些什么?Forth 能在可读性方面的定义方面传授给其它语言些什么东西?
Chunk: 计算机语言都宣称要可读。但他们并不可读。或许对懂这门语言的人来说是可读的。但初学者还是稀里糊涂的。
问题就在于晦涩、武断、隐秘的语法中。那些小括号啦,& 符啦,等等。你试着学习它们为啥出现在那里,最终推断,其实没什么好理由。但是你还是要按规则办事。
你无法说这门语言。你必须像 Victor Borgia 那样叽里呱啦的把符号都念出来。
Forth 通过最小化语法来减轻这个问题。它用的哪些个神秘符号,@ 和 ! 可以读成“取”和“存”。这些个使用符号是因为出现的太频繁了。
程序员被鼓励使用自然语言中的词汇。它们不通过标点的间隔组织在一起。若是你选好了词,你就能构造出有意义的句子。实际上有人用 Forth 来写诗。
另一个优势是后缀表示。一个像 “6 英寸”这样的短语能把操作符“英寸”作用于参数 6,这是非常自然的表达方法。非常的可读。
另一方面,程序员的任务就是开发吃一组词来描述问题。这个词典会变得非常大。读者需要了解整个词典使得程序可读。程序员就必须好好的定义词。
总而言之,用任何语言,这些都会影响程序的阅读。
你如何定义你的工作中如何定义成功?
Chunk: 一个优雅的解决方案。
人们并没有用 Forth 编程。Forth 就是一个程序。他们添加新的词创建一个词典来定义问题。当正确的词被定义出来,一切都浑然天成。接下来你就能你可以互动地解决所有相关问题的任何方面。
举个例子:我可以定义一些词来描述一个电路。我以后想把这个电路加到一块芯片里去,显示电路的布局,校验设置规则,模拟跑一下。用来干这些事情的词决定了应用程序的形态。如果精心选择这些词,提供一个紧凑而有效的工具集,然后我就搞定了。
你在哪学会写编译器的?在那个年代每个人都必须去写编译器吗?
Chunk: 哦,我在六十年代去了趟 Stanford ,那里有组研究生正在写一个 ALGOL 编译器——Burroughs 5500 用的版本。当时我想他们不过才三四个人,就三四个人坐在那写一个编译器,我那个内牛满面啊。
我想,“靠,他们要能做,我也能做。”然后我就干了。其实一点也不难。当年写编译器还是有点神秘西西的。
现在其实也还是神秘西西的
Chunk: 嗯,不过已经没那么神秘了。你看现在新语言一个个的冒出来,我不知道算解释型的还是编译型的,不管怎样,有黑客风范的人都想做一个。
操作系统是另一个稀奇古怪的东西。操作系统吓人的复杂,而且完全没用。它是 Bill Gates 成功向世界推销出去的一个光彩夺目的概念。这可能是世界上出现的最大的骗局。
操作系统对你来说做着绝对的无用功。其实你有这样一些东西就够了:一个叫作磁盘驱动程序的子程序,一个叫什么什么通讯支持的子程序,而在现代社会,操作系统啥也没做。实际上,Windows 花了大量时间在包装层上,或是诸如磁盘管理器这样不相干的东西上。你有了上 G 的磁盘,有了上 M 的内存。世界格局发生了变化,使得操作系统不那么有用了。
那设备支持怎么办?
Chunk: 你对每个设备有一个子程序。那是一个库,而不是操作系统。你需要那个就装载哪个。
在工作间隙后,你怎么继续编程?
Chunk: 在被困扰的时候,我不会中断我的编码。我会充满热情的思考问题,做梦都会想着它们。我想这是一个 Forth 的特质:在小段时间(以天计)内全神贯注的解决一个问题。这帮助 Forth 应用程序自然的被分解为一个个子项目。几乎所有的 Forth 代码都简单易读。当我真的要做一些偏激的事情,我会做好注释。好的注释能帮我以后回到问题中,不过重新阅读和理解代码总还是有必要的。
你在设计或编程中犯过最大的错误是什么?你从中学到点什么?
Chunk: 20 多年前,我想为设计 VLSI 芯片开发个工具。我的新电脑上没有 Forth ,因此我就想用另种方案,写机器语言。不是汇编,就是用 16 进制码敲机器指令。
我像我写 Forth 程序那样编写代码,分层次的定义出需要简单的相互有关的词。最终搞定了。我用了这个东西 10 年。但是无法维护,也没有文档。最终,我用 Forth 重写了一遍,这个玩意变得更为小巧而且简单多了。
我的结论是,Forth 比机器语言更高效。一部分源于其交互性,另一部分是因为它的语法。Forth 的一个很漂亮的方面是,数字可以用计算它们的表达式文档化的表达。
Comments
Posted by: 路 | (21) August 30, 2024 08:08 PM
Posted by: 晓峰 | (20) February 28, 2020 10:55 AM
Posted by: hxd | (19) February 22, 2017 09:57 PM
Posted by: Anonymous | (18) April 24, 2016 11:33 PM
Posted by: 成建文 | (17) March 6, 2016 11:06 PM
Posted by: suqin2012@live.cn | (16) October 21, 2015 09:55 PM
Posted by: ear | (15) August 15, 2012 01:15 PM
Posted by: xiaohao | (14) April 5, 2012 05:19 PM
Posted by: wangwangwar | (13) October 21, 2011 01:35 PM
Posted by: tau | (12) May 1, 2011 05:50 AM
Posted by: 狐狸 | (11) March 18, 2011 07:35 PM
Posted by: damnduck | (10) November 29, 2010 04:27 PM
Posted by: hplonline | (9) June 15, 2010 04:14 PM
Posted by: ucmvoliton | (8) June 12, 2010 12:17 PM
Posted by: cloud | (7) June 11, 2010 01:41 PM
Posted by: 减肥饼干 | (6) June 11, 2010 01:15 PM
Posted by: mike | (5) June 11, 2010 11:47 AM
Posted by: xuzhongxing | (4) June 11, 2010 09:07 AM
Posted by: Anonymous | (3) June 11, 2010 07:32 AM
Posted by: yalong | (2) June 11, 2010 07:31 AM
Posted by: Anonymous | (1) June 11, 2010 04:15 AM