前言

前些时候,我使用 SqueakJS 读了一篇用动态媒介写的文章

近期继续借助 SqueakJS 挖掘 Squeak 社区里尘封的宝藏。最近挖掘到的一个宝藏是Squeak News, 这是第一份致力于 Squeak 的杂志,一份动态杂志。

杂志主编是 Tansel Ersavas。Tansel Ersavas 现居悉尼,是一位复杂系统/深度学习专家,也是一位热情的 Squeak 爱好者,他为 Squeak 写了很多有趣的文章,接下来的这篇文章便由他撰写。

以下内容翻译自 Tansel Ersavas 的《How to Begin Squeak》,原文刊登在 Squeak News第二期

译文

我的动机

我想写关于 Squeak 的文章已有很长时间。其原因是: 我相信 Squeak 注定会成为世界上最强大的软件开发环境。在很多意义上,它已经是了。我可以写其他编程语言,但它们都没有让我兴奋,更不用说吸引我在它们上面开发系统了。Squeak 是一个例外。它如此清新,即使只是玩玩,也让我觉得自己变年轻了。

现在我终于有机会写点东西了。我已经签署了一份合同,要写一本关于 Squeak 的书。这些文章的详细版本将在剑桥大学出版社发行的书中出现,希望在 2002 年 5 月左右。感谢我的编辑 Lothlorien Homet 的慷慨,允许我在 Squeak News 电子杂志上刊登一些章节的浓缩版。这本书将有一个电子版伴随着它,就像你现在看到的网页一样,由 Squeak News 提供。

我为什么喜欢 Squeak?嗯,有一些实实在在的东西,如复杂的图形用户界面(GUI)、集成开发环境(IDE)、声音、音乐和其他多媒体支持、网络和互联网支持,以及一个可供研究和修改的虚拟机。它是完全免费和开源的,许可条款非常宽松,这是一个很大的激励因素。还有一些动机,如 Squeak 是一个非常便携的系统,能够在各种硬件和平台上运行,包括大多数主流操作系统;它甚至已经成功地被移植到没有操作系统的裸芯片上;它可以在小型手持设备上运行,也可以在有大量内存和磁盘空间的大型系统上运行。

但在所有这些原因之上,还有一种难以言表的情感,我不会试图用 “Squeak 是用爱创造的” 这样的话语来解释,尽管我相信它正是如此。我希望你跟随这趟旅程,自己去寻找答案。

目标受众

如果你是作为一个初学者学习 Squeak,这个系列应该对你有用。我希望达到的一个目标是,让这个系列能被初学者接受,甚至是那些想了解面向对象编程,或刚入门编程的绝对初学者。这个系列不是一个传统的 “初学者” 系列,我相信使用 Squeak 可以让用户快速从初级水平发展到高级(相比于大多数其他开发环境和语言所要求的时间而言)。如果你是一个绝对的初学者,在阅读这些文章时,如果遇到完全不明白的部分,请继续保持阅读,之后你会弄明白它们。

如果你熟悉其他语言,关于如何快速学习 Squeak 的关键信息就藏在这篇文章中,尽管根据经验,我知道可能需要一段时间来适应。

如果你熟悉 Smalltalk 的其他方言,那么你就不会在这里发现很多惊喜。但我还是希望你能关注整个系列的内容。

虽然这些文章不能为有经验的 Squeak 程序员提供太多的学习方法,但我还是建议有经验的 Squeaks 阅读这些文章,希望你们可以给我宝贵的反馈。我也相信这将是一个有趣的阅读,可以对选择 Squeak 起到积极的促进作用。你甚至可能从未来的文章中概述的某些高级概念中获益,并从前面的文章中获得一些教学想法。最重要的是,如果没有所有读者(从初学者到大师)的反馈,这个系列将是不完整的。

这篇文章是为整个系列做的思想准备工作。由于这个原因,我将集中讨论一件事,虽然我冒着成为一个大嘴巴的风险,但我相信这对学习 Squeak 是相当必要的。因此,我在这一期的重点是,给你一个关于如何接近 Squeak 的建议。

在下一篇文章中,我将介绍 Squeak 中使用的某些面向对象的基本概念和 Squeak 的介绍。稍后我们将讨论用户交互、图形、声音和音乐、网络、Squeak 内部、元对象、元 Squeak、系统交付以及更多。

如何接近 Squeak

这里有一个关键,可以大大加快你学习 Squeak 的速度:

即使你有其他编程语言的经验,也要把 Squeak 看成是非常不同的东西。

这听起来好像是一个过于简单的建议,像是一个愚蠢的说法,让人无法立即接受,但如果你坚持下去,迟早会知道我这句话的意思。

在过去的十年中,在我努力向组织和个人介绍 Smalltalk/Squeak 的过程中,我注意到,对这些系统感到最困难的人恰是那些来自现有计算背景的人,他们对计算是什么有着僵化的想法。他们试图根据自己以前的经验来看待 Squeak。在 Squeak 中,有很多东西似乎与当前的许多编程语言和系统相对应,这些可能有助于你快速入门。 然而这并不意味着 Squeak 只是又一个新的编程语言。Smalltalk 语言家族和 Squeak 是突破性的,开发者需要清楚地了解这些差异,以便在突破性项目中使用 Squeak 作为突破性的开发环境。

Edward B. Lindeman 在他 1978 年的作品 《用未来时态思考问题(Thinking in Future Tense)》 中这样描述突破性的时刻:

生命中最充实的时刻之一发生在那一瞬间,熟悉的事物突然转变为全新的耀眼光环。这些突破太少见了,少见多于常见;而我们大部分时间都陷在平凡和琐碎中,令人震惊的是:看似平凡和琐碎的东西正是发现的素材。唯一的区别是我们的视角,我们准备以一种全新的方式将碎片拼凑在一起,并看到仅在片刻之前出现的阴影的模式。

从这个不同的视角看待 Squeak 对于理解它是至关重要的。Squeak 和它的直系祖先与大多数其他编程语言的区别就像飞机与汽车的区别一样。

Squeak 初体验

想象一下,你人生中第一次看到一架小型螺旋桨飞机。在此之前,你只熟悉汽车,你不知道什么是飞机。你对这台机器的最初反应可能是:“看起来多么有趣的交通工具!” 当你走进飞机时,你对仪器和控制装置的反应可能是:“为什么它如此不同?”

想象一下,乘坐飞机去购物中心。那将是一种怎样的体验啊,一个大而笨拙的野兽跑在道路,两边都粘着巨大的拍打物,噪音大,耗油多,而且你可能无法克服你必须用脚来操纵它的事实!你认为你会走进飞机吗?你会再次踏上飞机去任何地方吗? 我非常怀疑。问题是,你会错过整个飞行体验。

然而,如果没有适当的指导,很多 Squeak 用户的经历可能正是这样!多么花哨的陌生环境啊! 只有那些学习如何使用 Squeak 并认识到 Squeak 的不同之处的人,才能享受到 Squeak 的最大好处。

对于已经在使用其他编程语言的人,我觉得我有责任在这里真诚地警告你们。一旦你掌握了 Squeak,再回到你以前的编程语言中去,可能就不是一个快乐的经历了。这个旅程可能会让你付出沉重的代价,因为你的观点会对你目前的编程语言和环境产生不利的影响。但我坚信,对于那些耐心学习 Squeak 的人来说,你手中的力量将达到一个新的高度,这将使你在开发系统方面的熟练程度提升到你从未想过的水平。

什么是 Squeak?

什么是 Squeak ?Squeak 是一个创造性的开发环境。 它是 Smalltalk 的一个全新实现。Squeak 的创造者之一,Dan Ingalls 说 Squeak 项目的目的是 “为每个人的创造性精神提供计算机支持”。Squeak 旨在迎合那些将计算机视作帮助拓展思维的工具的人。Squeak 是 25 年来思想和经验的结晶。

Squeak 背后的隐喻

Squeak 的背后是一群才华横溢的人,他们中的许多人来自最初的 Xerox PARC 小组,该小组创造了第一台个人电脑,第一个图形用户界面,并发明了许多用于现代电脑和软件的概念。在以后的文章中,我们将看看 Squeak 的历史,这对理解 Squeak 背后的愿景很重要。简而言之,Squeak 的早期祖先从 Smalltalk 72 开始,不仅受到 Dynabook 的影响,也受到 ARPANET 和分布式计算概念的影响。这个系统和它的后继者是如此彻底地领先于他们的时代,以至于我们花了四分之一世纪以上的时间才了解它们。

Squeak 为我们提供了一种描述计算机中系统的方法。它还允许我们以一种定义明确的方式与计算机互动。在 1981 年发表在 Byte 杂志 Smalltalk 特刊上的 “Smalltalk 背后的设计原则” 一文中,Dan Ingalls 将其称为 “描述的语言” 和 “交互的语言”。 在以后的文章中,我将重点讨论这些内容。

在 Smalltalk 的发展过程中,某些原则适用于 Squeak。Dan Ingalls 在 Byte 文章中所说的这些原则有以下几点:

可理解性原则:

如果一个系统要为创造精神服务,它必须能被个体完全理解。

Squeak 无疑是为数不多的能让一个人完全理解的系统之一。当然,Squeak 有很多东西,要想获得高水平的熟练度,就必须花很多时间(比如几年)在 Squeak 上,但这是可能的。Squeak 最初可能会让人不知所措,这是因为 Squeak 提供了一个 “白盒” 方法,在这里所有的东西都是可见的,同时也允许 “黑盒分析”,在这里你可以看到从他们的界面点到其他对象的一切。你可以使用黑盒方法快速学习 Squeak,做出简单的项目,你也可以选择使用白盒方法花费大量时间深入了解系统,在白盒方法中,每一行代码都可以供你检查,你可以停止运行的系统,检查在任何时间点发生了什么。你现在就可以中断阅读这本电子杂志并浏览正在发生的事情,然而我现在还不打算向你展示如何做。

良好设计的原则

Ingalls 指出:

用户和系统某些部分之间存在的任何障碍, 最终都会成为创造性表达的障碍。系统中任何无法更改或通用性不足的部分都可能是障碍。如果系统的一部分工作方式与所有其他部分不同,该部分将需要付出更多的努力来掌握。

因此,就可以推断出良好设计的原则:

系统应以最少的不可变零件组成;这些部分应尽可能通用;系统的所有部分都应聚拢在一个统一的框架中。

在 Squeak 的背景下,Ingalls 指出该语言的目的是:

提供一个交流的框架

这种对语言的描述超越了大多数计算机语言。Squeak 语言的目的是描述两个智能生物之间的交流。两个交流的人和一个人与一台计算机之间的差别应该是最小的。

为了实现人和计算机之间的无缝交流,Squeak 使用了一个 “统一隐喻”:

应该围绕一种强大的隐喻设计一种语言,该隐喻可以统一应用于所有领域。

Squeak 中的这种隐喻是基于两个结构: 对象和消息。Ingalls 对这些描述如下:

对象:计算机语言应支持对象的概念,并提供一种统一的方式来引用其宇宙中的对象。 消息:应将计算视为对象的固有功能,可以通过发送消息统一调用它们。

例如,在 Squeak 中,你在屏幕(原文右边的配图)上看到的一切就是一个对象。Byte 杂志封面图片就是一个对象。我们可以通过按下下面的按钮向该对象发送一条信息:

send a message to the picture on the right (译者注: 按下原文的按钮,图像会震动)

用对象和消息进行计算的概念是一个非常有力的隐喻。Alan Kay 在一次采访中描述了这个隐喻:

现在我们不再只有一台计算机,而是有小的、自主的、像细胞一样的实体,通过消息相互交流。在某种程度上,我们有许多小计算机在相互交流。

一旦我们理解了这个简单但非常强大的隐喻,就可以更容易地理解 Squeak。

绕道而行: 冯-诺伊曼架构

为了理解这个隐喻与当前的计算范式有什么不同,我们需要了解当前主导编程的范式。

几乎所有现有的编程系统都深受当前计算机架构的影响,我们通常将其称为 “冯-诺依曼架构”。 这个架构随着 ENIAC 的发展过程开始演变。ENIAC 是由 J.Presper Eckert 和 John Mauchly 在 1943 年至 1946 年间在宾夕法尼亚大学设计的。冯-诺伊曼博士是这个项目的顾问。他知道当时正在建造的另一台名为 EDVAC 的计算机的缺点,他给 ENIAC 团队写了一份备忘录,解释了 EDVAC 的缺陷,并为 ENIAC 提出了一个架构。这份写于 1945 年的备忘录 “关于 EDVAC 的报告初稿” 成为一篇传奇性的论文,影响了大多数现代计算机。建造 ENIAC 的动机是因为美国陆军需要一些东西来进行高要求计算,如弹道计算。这些计算是由被称为 “计算机” 的人进行的。“计算机” 使用机械计算器按照一步步的指示进行这些计算。这项工作很繁琐,错误的代价太大。ENIAC 最重要的设计目标之一是使这些人类 “计算机” 的工作自动化。

冯-诺伊曼对 ENIAC 和随后的计算机的设计做出了重大贡献。他的想法的核心是将人类 “计算机” 自动化,用中央控制单元(CCU)取代她/他,用中央算术逻辑单元(ALU)取代机械计算器。 CCU 和 ALU 组成了 “中央处理单元”(CPU)。人类操作者能记在脑海中的有限数量的数字和事实用 “寄存器” 表示。纸张成为 “存储器”。 与外界的联系是通过一个输入和一个输出单元实现的。

冯-诺伊曼的想法之一是 “存储程序” 的概念,但未能被纳入 ENIAC。这是在后来的计算机中引入的最重要的进步之一。存储程序的概念使 “软件” 的概念成为实际的解决方案,而不是用 “硬接线” 的指令序列来为机器编程。有了 “软件”,计算机将加载不同的指令集以执行不同的任务。

这种简单的架构现在被称为冯-诺依曼架构。无论多么先进,大多数现代计算机的核心仍然使用相同或类似的概念。

请花点时间把鼠标移到右边的图上,指着一些区域(不需要点击),看看解释。 (译者注: 原图是一个 Morph 交互应用,以下用视频展示)

冯-诺伊曼架构是一个非常简单和优雅的架构,其目的是:做重复的繁琐计算,可以用简单的一系列命令来定义。

冯-诺依曼架构有五个关键概念:

  1. 中央控制单元。这个单元是系统的核心。在最简单的意义上,它基本上是人类处理器的自动化。
  2. 算术-逻辑单元。该单元处理算术和逻辑操作。中央控制单元利用该单元获取指令并处理计算。这两个单元集成在一起,形成中央处理单元。
  3. 内存。该单元是人类处理器读取数据并将中间和最终结果写入的纸张的替代品。它还用于存储被称为 “程序” 的命令集。
  4. 输入和输出单元。这些单元用于将初始命令和数据输入存储器,并将结果从存储器返回到外部世界。
  5. 顺序可执行的 “存储程序” 的概念。这些只需加载一个新程序就能替换的程序对 ENIAC 的后继者的成功至关重要。

在这种结构中,一条指令是由一个操作码(简称 op-code)和零个或多个操作数(operands)组成。操作码是告诉 CPU 要执行什么的命令,而操作数则告诉数据在哪里。CPU 只是遵循一系列指令,从内存中逐一取回,就像人类计算机遵循纸张上的一系列指令一样。CPU 确定需要什么类型的操作,然后继续获取操作数,或完成操作所需的数据。有时,这些数据隐含在操作代码中,例如它存在于 CPU 有限的寄存器中。最后 CPU 完成指令并继续执行下一条指令。

另外两种类型的指令让 CPU 拥有一套完整的指令,形成一个完整的计算模型。这些是条件指令和分支指令。条件指令用于比较操作的结果,并根据比较的结果采取行动。分支指令用于改变流程并跳到另一个位置,并在新的位置执行一组新的指令。

使用这些指令的程序是 “例程”(routines) 或 “过程”(procedures) 的形式。 CPU 从内存中获取指令,并将其应用于数据,这些数据必须存储在内存的其他地方,因为指令必须一个接一个地连续执行。

冯-诺依曼架构的一个主要改进是引入了子程序调用。这些调用使一个被称为子程序的小段程序可以从主程序的不同位置被使用。

此架构的重点是过程(procedure),唯一发生的事情是 CPU 将过程应用于驻留在内存中其他位置的被动数据。 这是当今占主导地位的软件范式“面向过程的编程”的基础。

冯-诺伊曼架构及其编程风格最初用于简单的重复计算,然后人们开始开发越来越复杂的程序,以至于机器语言和汇编程序都不够用。更高级的语言开始出现,使这个架构可以做得更多。

大多数高级语言将基于命令的编程扩展到更高的层次,将许多指令组合成允许做更多事情的命令。这些命令是以命令式句子的形式出现的。增强的条件语句和循环可以很容易地转化为比较和跳跃(jump)指令。参数化的函数和子程序调用允许从程序的不同部分重复使用程序。

面向过程的范式是冯诺依曼风格指令与这些高级语言的编程技术之间对应的直接结果。从 Fortran 开始的大多数高级语言本质上都致力于:

  • 提高命令级别以简化学习和用更少的命令执行更多操作
  • 从底层硬件的微小细节中获得一定程度的独立性

这些早期的语言都没有试图改变范式,相反,它们是范式的反映。

当冯-诺伊曼意识到人们想要开发什么类型的系统时,他做了两件事:

  • 他警告不要将这种架构用于更通用的问题(其中大多数警告从未对主流文献提出过)
  • 他开始设计另一种与最初的计算机完全不同的计算机

这位 20 世纪杰出塑造者的最后努力可以在他的最后一本(未完成的)著作《计算机与大脑》中找到。

结构对人类思维的影响

在《第五项修炼(The Fifth Discipline)》中,Peter Senge 总结了结构对人类思维的三个后果。

  1. 结构影响行为
  2. 人类系统中的结构是微妙的
  3. 杠杆作用往往来自于新的思维方式

不管我们是否注意到,目前占主导地位的架构影响着我们的编程方式。只要我们把对软件的思考限制在现有硬件的范围内,我们就无法开发出能够利用更好的范式的系统。冯-诺依曼架构充斥着现有的大多数已知的编程语言。这方面很少有例外。

在为数不多的开发主流计算风格之外的语言和架构的尝试中,最重要的尝试来自 XEROX-PARC 的 Smalltalk 72。正如我之前提到的,Smalltalk 72 不仅受到 Dynabook 的影响,而且还受到 ARPANET 和分布式计算概念的影响。这种遗产在一定程度上贯穿了 Squeak 的所有祖先,并使其成为与当今许多其他编程系统明显不同的系统。

与那些为冯-诺依曼式计算机编写的程序相比,Squeak 有什么不同?让我们听听最初的 Smalltalk 背后的策划者的意见。

今天的大多数编程语言都是作为 1950 年代的硬件级概念的符号化方式开发的。这种方法导致了两种被动的构件:数据结构,或惰性的建筑材料,以及程序,或操作数据的逐步配方。基于这些概念的语言(如 BASIC、FORTRAN、ALGOL 和 APL)以严格的顺序方式遵循其描述。… 随着越来越复杂的系统被尝试,需要精心组合的程序,让整个系统工作的难度也呈几何级数增长。尽管大多数程序员仍在学习数据过程语言,但现在人们已经普遍认识到了它们的不足。一个更有希望的方法是设计出具有更大通用性的构建模块。数据和程序都被 “活动(activities)” 这个单一的概念所取代,这些实体在被发送适当的消息时表现出行为。在这样一种语言中,没有名词和动词 –Alan Kay 《微电子学和个人计算机》 1978

Alan Kay 在这篇文章中所描述的是一种深刻的开发系统的新方法。这种方法在几个不同的方面与主流的范式不同:

在面向过程的范式中,唯一的对象(CPU)从内存中获取一组顺序的指令。在这种环境下,编程是一个从程序员到 CPU 的独白。此外,我们假设有一个单一的实体(CPU)对程序进行中央控制。这个实体一个接一个地获取命令,执行它们,并根据命令的类型决定将控制更改为程序的另一部分。以这种方式开发的程序本质上是一个串行程序。

另一方面,Squeak 的工作方式与 Alan Kay 在上述文章中描述的一样。在 Squeak 中,所有操作都是由对象向其他对象发送消息来完成的。接收方对象可以自由地以任何方式解释这些消息;它甚至可以丢弃该消息。在这种环境下,一个对象甚至可以查询另一个对象,以了解它是否可以回答某个消息。这种方法与冯-诺伊曼指令处理有根本的不同。在 Squeak 方法中,每个对象都明确选择并寻址将处理消息的目标单元。理论上,其他对象单元可以用硬件实现,也可以用软件模拟,可以与发送者对象存在于同一环境中,或者可以只是驻留在某个其他系统中的另一个对象的外观。这种区别是 Squeak 和其他大多数编程环境之间最重要的区别之一。

一个 Squeak 系统由许多对象组成,每个对象都可以被看作是一个封装单元。你可以把一个 Squeak 对象看作是一个黑盒子。这个黑盒子有一个接口。你只能通过它的接口与这个对象进行交互。这种交互是通过向这个对象发送消息来实现的。收到消息的对象会对其进行处理,并回复发送者。从某种意义上说,Squeak 系统是由许多具有独特协议的模拟处理器组成的。这些对象中的每一个都能够向其他对象发送消息。在这种环境中编程是许多对象的互动和协作。

Squeak 消息发送是 Squeak 中所有计算的基础。这个结构显式地寻址消息的接收者。消息包含一个 “消息选择器” 或一个消息标识符,以及对处理该消息所需的其他对象的引用。

这种方法与经典的冯-诺依曼式编程有很大不同:

  1. (冯-诺依曼式编程)命令隐含地寻址一个对象 “CPU”,而 Squeak 则明确地寻址每个对象,向它们发送信息。Squeak 没有一个隐含的全控制对象的概念。
  2. 对 CPU 的命令是固定的。这些命令是在 CPU 的硬件实现时确定的。高高级语言再次具有在编译器设计时确定的相应高级命令。这些语言的用户不能修改这些编译器。Squeak 没有命令集的概念。对象的接口基本上是由定义它们的对象(它们的类)决定的。系统对象和用户对象之间没有区别。
  3. 基于 CPU 的编程是基于 CPU 可以理解的 “命令” 或指令的概念。这种基于命令的架构指定了 “操作符”,它们作用于 “操作数” 或被动数据。Squeak 中没有运算符。操作符是对 CPU 的命令或一系列命令,它们被暗示为由 CPU 处理。编译器知道这些操作数。它处理这些操作数的所有方面,包括它们的优先级。Squeak 不知道操作数的情况。它只是把它们委托给相应的对象来处理。它不知道也不关心某些操作数是否应该在其他操作数之前应用。Squeak 唯一知道的是消息优先级和极少数结构体。这在熟悉其他语言的人中是一个常见的混乱来源。
  4. Squeak 的语法没有控制流语句的概念,如条件或循环。这些都是委托对象的职责。传统语言中的条件和循环语句假定有一个中央和序列化的程序控制。

Squeak 是如何将冯-诺依曼架构的影响降到最低的?

Squeak 通过拒绝了解这个架构来尽量减少它的影响。Squeak 之所以能将自己与底层架构隔离开来,是通过利用 “虚拟机(VM)“来实现的。 虚拟机是一种在现实中不存在的机器,在各种环境中被模拟为一种软件抽象。理论上,它也可以作为一个真实的机器来实现,但由于各种原因,这样做并不实际,至少现在是这样。

虚拟机的实现是否使 Squeak 不受架构的影响?我不能说它完全如此。尽管这些因素在一定程度上影响了 Squeak,但这种影响通常只限于 Squeak 的虚拟机的实现。另一方面,Squeak 的虚拟机在很大程度上受到当前架构的影响,因为它必须在该架构上正常工作,而且必须工作得很好,才能使整个 Squeak 系统在它上面无缝运行。

右边是一张解释 Squeak 架构的图片。通过在上面移动你的鼠标来玩它,尽可能多地了解它。下个月我们将更详细地解释这张图片,并开始学习 Squeak 的工作原理。

欢迎来到 Squeak!