Smalltalk
编程范型面向对象
設計者艾伦·凯Dan Ingalls英语Dan IngallsAdele Goldberg英语Adele Goldberg (computer scientist)
實作者艾伦·凯Dan Ingalls英语Dan IngallsAdele Goldberg英语Adele Goldberg (computer scientist)、Ted Kaehler、Diana Merry英语Diana Merry、Scott Wallace、彼得·多伊奇Xerox PARC
发行时间1972年,​49年前​(1972
(開發始於1969年)
穩定版本
Smalltalk-80版本2
(1983年,​38年前​(1983
型態系統動態
作用域词法 (静态)
系统平台Xerox Alto (74181英语74181)[1][2]
作業系統跨平台
主要實作產品
Amber英语Amber Smalltalk, Dolphin Smalltalk英语Dolphin Smalltalk, GemStone/S英语Gemstone (database), GNU Smalltalk英语GNU Smalltalk, Pharo, Smalltalk/X, Squeak, VA Smalltalk英语IBM VisualAge, VisualWorks英语VisualWorks
啟發語言
Lisp,[3] Simula,[3] Euler英语Euler (programming language),[3] IMP英语IMP (programming language),[3] Planner英语Planner (programming language),[3] Logo[4],Sketchpad,[3] ARPAnet,[3] Burroughs B5000英语Burroughs large systems[3]
影響語言
AppleScript, CLOS, Dart, Dylan, Erlang, Etoys英语Etoys (programming language), Falcon, Go, Groovy, Io, Ioke, Java, Lasso英语Lasso (programming language), Logtalk英语Logtalk, Newspeak英语Newspeak (programming language), NewtonScript, Object REXX英语Object REXX, Objective-C, PHP 5, Perl 6, Python, Ruby, Scala, Scratch, Self

Smalltalk是一种动态类型反射式面向对象编程语言。Smalltalk由艾伦·凯、Dan Ingalls、Ted Kaehler、Adele Goldberg等于1970年代初在Xerox PARC开始开发。

Smalltalk对其它众多的程序设计语言的产生起到了极大的推动作用,主要有:Objective-CJavaPythonRuby[5]等。1990年代的许多软件开发思想得利于Smalltalk,例如设计模式敏捷编程代码重构[6]等。

概述

Smalltalk和许多程序设计语言不同,它不仅仅是一门语言。下面从几个不同的角度来解释Smalltalk。

  • 一种面向对象的程序设计语言:它是一种面向对象的语言,包含语言的语法和语义。一些编译器可以透过Smalltalk源程序产生可执行文件。这些编译器通常产生一种能在

    最早的Smalltalk原型由艾伦·凯于1970年代初提出。(来自Simula 67)、海龟绘图英语Turtle graphics(来自MITLOGO)以及图形用户界面等概念的有机组合,构成了Smalltalk的最初的蓝图[4]

    在1971年到1975年之间,艾伦·凯在Xerox PARC的小组设计并实现了第一个真正的Smalltalk语言系统,編譯器由Dan Ingalls負責主要實作。这个系统被称为Smalltalk-71與Smalltalk-72,具有以下几个技术创新:

    • 语言完全基于消息交换Simula 67的类的概念。
    • 语言没有固定的语法,语法分析由类本身完成。

    开发环境的革新相当迅速。虽然当时的位图显示器十分昂贵,但是艾伦·凯却说服了PARC让他使用这些位图显示器,这使得艾伦·凯和他的小组能够实现不同大小和字体的文字,使用多窗口环境,以及一些对图像处理的高端支持。Smalltalk-72影响了演员模型的发展。

    在1975到1976年间,艾伦·凯小组认识到应当对执行效率和规模进行优化。于是他们在许多重要方面重新设计了Smalltalk系统,被称为Smalltalk-76,它在语言上:

    • 引入了继承和子类的概念。
    • 确定了语言的语法,这使得编译器能够产生高效、可执行、精炼的二进制代码。
    • 拉里·泰斯勒设计了浏览器,这极大地提高了Smalltalk程序员的编程效率。

    前述的所有Smalltalk系统都是在特殊的硬件上实现的,直到1977年至1978年,Bruce Horn和Ted Kaehler把Smalltalk-76移植到由Intel 8086处理器和自定显示器所组成的硬件环境(被称为NoteTaker)上。虽然这种硬件环境只生产了10台,但是它证明了在通常的处理器上实现Smalltalk的可能性。

    在1979至1980年,部分受NoteTaker项目的影响,Smalltalk小组的注意力转移到Smalltalk的销售可行性上。小组设计并实现了新一代的Smalltalk系统,这次修改的目标着重于在标准硬件上的移植性等方面,被称为Smalltalk-80,它包括:

    • 采取ASCII码集,摒弃了原先在Smalltalk-72和Smalltalk-76中使用的特殊字符。
    • 取消了原始方法直接存取内存的能力。取而代之的是引入一系列的原始方法提供相应的功能。
    • 引入了元类的概念。
    • 引入MVC(模型-视图-控制器)系统以方便交互式应用软件的开发。

    Smalltalk-80是在PARC之外能获得到的第一个语言变体,最初作为Smalltalk-80版本1,给与了少数公司(惠普苹果公司泰克DEC)和大学(UC Berkeley),用于同行评审和在它们自己的平台上实现。后来在1983年普遍可获得的实现,叫做Smalltalk-80版本2,作为映像(具有对象定义的独立于平台的文件)和虚拟机规定而发行。ANSI Smalltalk自从1998年成为标准的语言参考[7]

    两个当前流行的Smalltalk实现变体是这些最初Smalltalk-80映像的后代。Squeak开源实现,经由Apple Smalltalk派生自Smalltalk-80版本1。VisualWorks英语VisualWorks经由Smalltalk-80 2.5和ObjectWorks(二者都是ParcPlace Systems的产品,它是为了将Smalltalk推向市场而成立的Xerox PARC分拆公司),派生自Smalltalk-80版本2。

    面向对象编程

    Smalltalk-80例子代码的类层级和与之并行的元类层级的示意图。其中的rProtoObjectcClassmcMetaclass。蓝色连接表示实例关系,绿色连接表示继承关系。

    如同其他面向对象语言,Smalltalk-80(而非Smalltalk-72)的中心概念是“对象” 。一个对象总是一个“”的一个“实例英语Instance (computer science)”。类是描述它们的实例的属性和行为的“蓝图”。例如,一个GUI窗口类可以声明窗口有属性比如标签、位置和窗口是否可见。类还可以声明实例们支持操作比如打开、关闭、移动和隐藏。每个特定窗口对象对这些属性都有自己的值,它们每个都能进行它的类定义的操作。

    Smalltalk对象确切的可以做三件事:

    1. 持有状态(引用到其他对象)。
    2. 接收消息自本身或其他对象。
    3. 在处理一个消息的过程中,发送消息至本身或其他对象。

    一个对象持有的状态总是私有于这个对象。其他对象只能通过发动请求(消息)至这个对象,来让它做出查询或变更这个状态。任何消息可以发送给任何对象:当接收到一个消息的时候,接收者确定这个消息是否合适。Alan Kay评论说,尽管关注于对象,消息才是Smalltalk中最重要的概念:“最大的想法是消息传递,它是Smalltalk/Squeak核心的全部意义所在(它是我们在Xerox PARC阶段从未真正完成的某种东西)。”[8]

    不同于多数其他语言,Smalltalk对象可以在系统运行的同时进行修改。现场编码和飞速应用补丁是Smalltalk的主导编程方法论,并且是它高效的主要原因。

    Smalltalk是“纯”面向对象编程语言,这意味着,不像C++Java,在作为对象的值和作为基本类型的值之间没有区别。在Smalltalk中,基本值比如整数、布尔值和字符也是对象,在它们也是相应类的实例,和发送消息来调用它们上的运算的意义上。编程者可以改变或扩展(通过子类)实现基本值的类,使得新行为可以定义到它们的实例,例如要实现一个的控制结构,甚至使得它们现存行为改变。这个事实被总结成常听到的一句短语:“在Smalltalk中所有东西都是对象”,它可以更精确的表达为:“所有的值都是对象”,因为变量不是。

    因为所有的值都是对象,也是对象。每个类都是这个类的元类的一个实例。元类依次也是对象,并且都是叫做Metaclass的类的实例。代码块是Smalltalk表达匿名函数的方法,也是对象[9]

    反射

    反射是一个计算机科学术语,适用于有能力检查它们自己的结构的软件程序,例如检查它们的分析树或输入和输出参数的数据类型。反射是动态、交互式语言比如Smalltalk和Lisp的一个特征。具有反射的交互式程序(要么解释的要么编译的)维护所有内存内对象的状态,包括代码对象自身,这是在解析/编译期间生成的,并且是在编程上可访问和修改的。

    反射也是Smalltalk这种有元模型的语言的一个特征。元模型是描述这个语言的模型,开发者可以使用元模型来做事,比如游历、检查和修改一个对象的分析树,或找到特定种类的结构的所有实例(例如在元模型中Method类的所有实例)。

    Smalltalk-80是完全的反射式系统,用Smalltalk-80语言实现。Smalltalk-80提供了结构性和计算性反射二者。Smalltalk是其结构由Smalltalk-80对象定义的结构性反射式系统。定义这个系统的类和方法也是对象,并且完全是它们所有助力定义的系统的一部份。Smalltalk编译器将文本源代码编译成方法对象,典型是的CompiledMethod的实例。通过把它们存储入一个类的方法字典而增加到这个类。类层级的定义类的那部份可以向系统增加新类。系统是由运行中的Smalltalk-80代码扩展的,它建立或定义类和方法。Smalltalk-80系统是个现场(living)系统,承载在运行时间扩展自身的能力。

    因为类是对象,可以向它们提问比如:“你实现了哪些方法?”或“你定义了什么字段/槽/实例变量?”。所以通过向系统中的任何对象应用普通的代码,对象可以轻易的检查、复制、(去)序列化,诸如此类[10]

    Smalltalk-80还提供计算性反射,有能力观察系统的计算状态。在派生自最初Smalltalk-80的语言中,方法的当前活动可以访问为通过伪变量命名的一个对象,它是作为六个保留字之一的thisContext。通过发送消息至thisContext,可以向方法活动提问比如:“谁给你发送了这个消息?”。这些设施使得有可能实现协程或类似Prolog的回溯而不需要修改虚拟机。异常系统也是使用这个设施实现的。这个设施更有趣的用法之一是在Seaside英语Seaside (software) web框架之中,它通过为每个编辑的页面存储续体,并在它们之间切换来导航一个web站点,缓解了编程者处理Web浏览器的返回按钮的复杂性。使用Seaside编程web服务器可以使用更常规的编程风格来完成[11]

    Smalltalk如何使用反射的一个例子是处理错误的机制。当一个对象被发送了一个它没有实现的消息的时候,虚拟机发送给这个对象doesNotUnderstand:消息,具有这个消息的实化作为实际参数。这个消息(它是另一个对象,是Message的实例),包含这个消息的选择器和它的实际参数的一个Array。在交互式Smalltalk系统中,doesNotUnderstand:的缺省实现是打开一个错误窗口(一个Notifier)向用户报告错误。通过它和反射设施,用户可以检查错误在其中发生的上下文,重新定义犯错的代码,并继续,这一切都在这个系统之中,使用Smalltalk-80的反射设施[12][13]

    通过建立只理解(实现)doesNotUnderstand:的一个类,可以建立通过它的doesNotUnderstand:方法能拦截发送给它的任何消息的一个实例。这种实例可以叫做透明代理[14]。可以使用这种代理来实现很多设施,比如分布式Smalltalk,这里的消息在多个Smalltalk系统之间交换,数据库接口,这里的对象被透明的从数据库中排除错误,promise等。分布式Smalltalk的设计影响了如CORBA这样的系统。

    语法

    Smalltalk-80语法是相当极简主义的,只基于了一小把的声明和保留字。事实上,Smalltalk中只保留了六个“关键字”:truefalsenilselfsuperthisContext。它们的准确术语是“伪变量”,是服从变量标识符规则但指示了编程者所不能变更的绑定。truefalsenil伪变量是单例实例。selfsuper,在响应一个消息而激活的方法中,指称这个消息的接收者;但是发送给super的消息,在这个方法的定义类的超类中查找方法,而非这个接收者的类中,这允许子类中的方法调用在超类中的同名方法。thisContext指称当前的活跃记录。唯一内建的语言构造是消息发送、赋值、方法返回和某些对象的文字语法。从它最初作为给所有年龄儿童的语言开始,标准的Smalltalk语法以更像英语而非主流编码语言的方式使用标点符号。语言余下部份,包括用于条件求值和迭代的控制结构,都由标准Smalltalk类库实现在内建构造之上。(出于性能上的原因,实现可以识别并特殊处理某些这种消息;但是,这只是优化而非硬性规定入语言语法的。)

    谚语“Smalltalk语法适合一张明信片”,提及的是Ralph Johnson英语Ralph Johnson (computer scientist)的一个代码片段,展示了方法的所有基本标准语法元素[15]

    exampleWithNumber: x
        | y |
        true & false not & (nil isNil) ifFalse: [self halt].
        y := self size + super size.
        #($a #a 'a' 1 1.0)
            do: [ :each |
                Transcript show: (each class name);
                           show: ' '].
        ^x < y
    

    文字

    下列例子诠释了最常用的对象,可以在Smalltalk-80方法中被写为文字值。

    数,下列是某些可能例子:

    42
    -42
    123.45
    1.2345e2
    2r10010010
    16rA000
    

    最后两个项目分别是二进制和十六进制数。在r前的数是底数或基数。基数不必须是二的幂;例如f36rSMALLTALK是一个有效的数值,等价于十进制的80738163270632

    字符书写时带有前导的美元符:

    $A
    

    字符串是包围在单引号内的字符序列:

    'Hello, world!'
    

    要在一个字符串中包括一个引号,使用另一个引号来转义:

    'I said, ''Hello, world!'' to them.'
    

    双引号不需要转义,因为单引号界定字符串:

    'I said, "Hello, world!" to them.'
    

    两个相等的字符串(字符串相等,如果它们包含完全相同的字符)可以是驻留在内存不同位置中的不同对象。除了字符串,Smalltalk有叫做符号(Symbol)的字符序列对象的类。符号保证是唯一的,没有是不同对象的两个相等的符号。因此,符号非常易于比较并经常用于语言制品,比如消息选择器(见后)。

    符号被写为#跟随着字符串文字英语string literal。比如:

    #'foo'
    

    如果一个序列不好括空白或标点字符,还可以写为:

    #foo
    

    数组:

    #(1 2 3 4)
    

    定义了四个整数的一个数组。

    很多实现支持下列ByteArray的文字语法:

    #[1 2 3 4]
    

    定义了四个整数的ByteArray

    最后却重要的是,块(匿名函数文字):

    [... 一些smalltalk代码 ...]
    

    块在下文中进一步详细解释。

    很多Smalltalk方言为其他对象实现了额外的语法,但是上述的是所有方言都本质上支持的。

    变量声明

    在Smalltalk中通常使用的两种变量,是实例变量和临时变量。其他变量和有关术语依赖于特定实现,例如VisualWorks英语VisualWorks有类共享变量和名字空间共享变量,而Squeak和很多其他实现有类变量、池变量和全局变量。

    在Smalltalk中临时变量声明是在方法(见后)内声明的变量。它们声明在方法的顶部,作为由竖杠包围的空格分隔的名字。例如:

    | index |
    

    声明一个临时变量名叫index,可以包含初始值nil

    多个变量可以在一组竖杠内声明:

    | index vowels |
    

    声明了两个变量:indexvowels。所有变量都给初始化。除了字符串的所有变量之外,所有变量都初始化为nil,它被初始化为null字符或初始化为0ByteArray

    赋值

    变量通过:=语法来指定一个值。比如:

    vowels := 'aeiou'
    

    指定字符串'aeiou'至前面声明的vowels变量。这个字符串是个对象(在单引号之间的字符序列是文字字符串的语法),在编译时间由编译器创建。

    在最初的Parc Place映像中,下划线(_)的字形是为左向箭头出现的()(就像1963年版本ASCII代码中那样)。Smalltalk最初接受左向箭头作为唯一的赋值算符。一些现代代码仍然包含充当赋值的下划线,让人会想起这种最初的用法。多数现代的Smalltalk实现接受要么下划线,要么冒号等号语法。

    消息

    消息是Smalltalk中最基础的语言构造。所有控制结构都实现为消息发送。Smalltalk缺省的采用动态分派单一分派策略(相对于其他一些面向对象语言使用的多分派)。

    下列例子是发送消息factorial至数值42

    42 factorial
    

    在这种情况下,42叫做这个消息的“接收者”,而factorial是消息的选择器。接收者通过返回一个值来相应这个消息(这个情形中是42的阶乘)。同其他事物一样,消息的结果可以赋值给一个变量:

    aRatherBigNumber := 42 factorial
    

    上面的factorial是“一元”消息,因为只涉及了一个对象,即接收者。消息可以承载额外的对象作为实际参数,比如:

    2 raisedTo: 4
    

    在这个表达式中,涉及了两个变量:2作为接收者而4作为消息的实际参数。消息结果,或用Smalltalk的说法,答案被认定为16。这种消息叫做“关键字”消息。消息可以有多个实际参数,使用如下语法:

    'hello world' indexOf: $o startingAt: 6
    

    它的答案是在接收者字符串中字符o的索引,从索引6开始查找。这个消息的选择器是indexOf:startingAt:,构成自两个部份或关键字。

    这种关键字和实际参数的交织意图改进代码的可读性,因为实际参数由前导于它们的关键字来解释。例如,要建立一个矩形的表达式使用C++或Java类语法可以写为:

    new Rectangle(100, 200);
    

    不清楚这些实际参数分别是什么。与之相反,在Smalltalk中,这个代码可以写为:

    Rectangle width: 100 height: 200
    

    这个情况下接收者是Rectangle类,答案是这个类的具有指定宽度和高度的一个实例。

    最后,多数特殊(非字母)字符可以被用作所谓的“二元消息”。这些允许了数学和逻辑算符以传统形式书写:

    3 + 4
    

    它发送消息+给接收者3,具有4作为实际参数传递(答案将是7)。类似的:

    3 > 4
    

    将消息>发送给3具有实际参数4(答案将是false)。

    注意,Smalltalk-80语言自身不包含着这些算符的含义。上述的结果都只是这些消息的接收者(这里是数值实例)为了响应消息+>而定义并返回的。

    这个机制的副作用是运算符重载。消息>可以被其他对象所理解,允许使用形如a > b的表达式来比较它们。

    表达式

    一个表达式可以包括多次消息发送。在这个情况下,表达式依据一个简单的优先级次序来分析。一元消息有最高的优先级,随后是二元消息,最后是关键字消息。例如:

    3 factorial + 4 factorial between: 10 and: 100
    

    被求值如下:

    1. 3接收消息factorial并回答6
    2. 4接收消息factorial并回答24
    3. 6接收消息+具有24作为实际参数并回答30
    4. 30接收消息between:and:具有10100作为实际参数并回答true

    最后的消息发送的答案是整个表达式的结果。

    在需要的时候使用圆括号可以改变求值的次序。例如:

    (3 factorial + 4) factorial between: 10 and: 100
    

    将改变表达式含义,首先计算3 factorial + 4产生10。接着10接收第二个factorial消息,产生36288003628800接着接收between:and:,回答false

    注意因为二元消息的含义不是硬性规定入Smalltalk-80语法的,它们全部都被认为有相等的优先级并简单的从左至右来求值。因此,使用二元消息的Smalltalk表达式的含义可能不同于传统释义:

    3 + 4 * 5
    

    被求值为(3 + 4) * 5,产生35。要得到预期答案23,必须使用圆括号来显式的定义运算次序:

    3 + (4 * 5)
    

    一元消息可以一个接一个的写成方法链英语method chaining

    3 factorial factorial log
    

    它发送factorial3,接着factorial到这个结果(6),接着log到这个结果(720),产生结果2.85733

    在下列(假想)例子中书写了一序列的表达式,每个都用点号分隔。这个例子首先建立类Window的一个新实例,存储它在一个变量中,接着发送两个消息给它。

     | window |
      window := Window new.
      window label: 'Hello'.
      window open
    

    如果向上述例子这样一序列消息都发送给相同的接收者,它们也可以写为方法级联英语method cascading,具有用分号分隔的单独消息:

      Window new
        label: 'Hello';
        open
    

    这种将前面例子的重新为一个单一表达式,避免了对将新窗口存储在临时变量的需要。依据平常的优先级规则,一元消息new首先发送,接着label:open被发送到new的答案。

    代码块

    代码块(匿名函数)可以被表达一个文字值(它是一个对象,因为所有值都是对象)。这是通过方括号达成的:

    [ :params | <消息表达式> ]
    

    这里的:params是代码可以接受的形式参数的列表。这意味着Smalltalk代码:

    [:x | x + 1]
    

    可以理解为:

     :

    或用lambda项表达为:

     :

    而如下:

    [:x | x + 1] value: 3
    

    可以被求值为:

    或用lambda项表达为:

    结果的块对象可以形成一个闭包:它可以在任何时间访问它外围的词法作用域内的变量。块是第一类对象

    块可以通过发送给它们value消息来执行(存在复合变体来给这个块的提供形式参数,比如value:value:valueWithArguments:)。

    块的文字表示是一种创新,它一方面允许特定代码有更重大的可读性;它允许涉及迭代的算法一更清晰和简洁的方式编码。典型的在某些语言中使用循环写成的代码可以在Smalltalk中使用块简洁的书写,有时在单一一行之内。更重要的方面是,块允许控制结构使用消息来表达并具有多态,因为块推延了计算并可以使用多态来选择替代者。所以在Smalltalk中if-then-else被书写和实现为:

    expr ifTrue: [ expr为真时 求值的语句 ] ifFalse: [ expr为假时 求值的语句 ]
    

    再比如选择:

    positiveAmounts := allAmounts select: [:anAmount | anAmount isPositive]
    

    注意这与函数式编程有关,这里的计算模式被抽象成了高阶函数。例如,在一个搜集上的消息select:等价于在一个适当的函数对象上的高阶函数filter[16]

    控制结构

    在Smalltalk中控制结构没有特殊的语法。它们转而实现为发送到对象上的消息。例如,条件执行被实现为发送消息ifTrue:到一个布尔对象,传递一个代码块作为实际参数,它被执行当且仅当布尔接收者为真。有下列代码演示:

    result := a > b
        ifTrue:[ 'greater' ]
        ifFalse:[ 'less or equal' ]
    

    块也被用来实现用户定义控制结构、枚举器、访问器、异常处理、可插拔表现器和很多其他模式。例如:

    | aString vowels |
    aString := 'This is a string'.
    vowels := aString select: [:aCharacter | aCharacter isVowel].
    

    在最后一行,字符串被发送了消息select:具有一个代码块文字作为实际参数。代码块文字将被用作一个谓词函数,它回答true,当且仅当这个字符串的一个元素,应当被包括在满足一个测试的字符搜集之中,这个测试由作为给select:消息的实际参数的代码块来表示。

    字符串对象通过它的成员来响应select:消息(通过向自身发送消息do:),将它包含的每个字符作为实际参数来求值选择块(aBlock)一次。在进行求值的时候(被发送了消息value: each),选择块(由形式参数aBlock来引用,并定义为块文字[:aCharacter | aCharacter isVowel]),回答一个布尔值,它接着向它发送ifTrue:。如果这个布尔值是对象true, 这个字符被增加到要返回的字符串。因为select:方法被定义在抽象类Collection中,它可以如下这样使用:

    | rectangles aPoint collisions |
    rectangles := OrderedCollection
      with: (Rectangle left: 0 right: 10 top: 100 bottom: 200)
      with: (Rectangle left: 10 right: 10 top: 110 bottom: 210).
    aPoint := Point x: 20 y: 20.
    collisions := rectangles select: [:aRect | aRect containsPoint: aPoint].
    

    异常处理机制使用块作为处理器(类似于CLOS风格异常处理):

    [
      some operation
    ] on:Error do:[:ex |
      handler-code
      ex return
    ]
    

    异常处理器的ex实际参数提供到挂起运算的状态的访问(栈帧、行号、接收者和实际参数等),并且还被用来控制计算怎样继续(通过发送ex proceedex rejectex restartex return之一)。

    下面是个平凡的类定义[17]

    Object subclass: #MessagePublisher
        instanceVariableNames: ''
        classVariableNames: ''
        poolDictionaries: ''
        category: 'Smalltalk Examples'
    

    多数这种定义经常由环境来填充。注意这是给Object类的一个消息,用来建立叫做MessagePublisher的一个子类。换句话说,在Smalltalk中类是第一类对象,它可以就像任何其他对象一样接收消息并可以在执行时间动态的创建。

    方法

    当一个对象接收到一个消息的时候,调用匹配这个消息名字的一个方法。下列代码定义一个方法publish,并且这个定义将在这个对象收到publish消息的时候发生。

    publish
        Transcript show: 'Hello World!'
    

    下列方法演示了接收多个实际参数并返回一个值:

    quadMultiply: i1 and: i2
        "这个方法将给定的两个数相乘并对结果乘以4."
        | mul |
        mul := i1 * i2.
        ^mul * 4
    

    这个方法的名字是#quadMultiply:and:。返回值通过^算符来指定。

    注意对象负责在运行时间动态的确定执行哪个方法来响应一个消息,尽管在很多语言中,这可能是(有时或总是)在编译时间静态确定的。

    实例化类

    下列例子代码:

    MessagePublisher new
    

    建立(并返回)MessagePublisher类的一个新实例。这典型的赋值到一个变量:

    publisher := MessagePublisher new
    

    但是也有可能发送一个消息到一个临时的、匿名对象:

    MessagePublisher new publish
    

    Hello World例子

    Hello world程序实质上被所有计算机语言的课本用作要学习的第一个程序,它展示了这个语言的最基本语法和环境。对于Smalltalk,这个程序可极其简单的书写。下列代码中,消息show:被发送给对象Transcript,具有字符串文字'Hello, world!'作为它的实际参数。show:方法的调用导致它的实际参数(字符串文字'Hello, world!')被显示在transcript(终端)窗口:

    Transcript show: 'Hello, world!'.
    

    注意Transcript窗口需要打开来看到这个例子的结果。

    基于映像的持久存储

    多数流行的编程系统,将静态的程序代码(以类定义、函数或过程的形式),分离于动态的或运行时间的程序状态(比如对象或其他形式的程序数据)。它们在程序启动的时候装载程序代码,而任何先前的程序状态必须从配置文件或其他数据源显式的重新建立。程序(和编程者)未显式保存的设置,在每次重启时都必须再次设立。传统的程序在每次程序保存一个文件、退出和重载的时候,还失去很多有用的文档信息。这会失去细节比如回退历史或光标位置。基于映像的系统不会因为计算机关闭或OS更新而强制失去所有这些东西。

    但是很多Smalltalk系统,不区分程序数据(对象)和代码(类)。事实上,类也是对象。因此,多数Smalltalk系统存储整个程序状态(包括类和非类对象二者)在一个映像英语system image文件之中。这个映像可以接着由Smalltalk虚拟机装载,将类Smalltalk系统恢复成先前的状态[18]。这是受到了FLEX的启发,它是Alan Kay创建的语言并描述于他的科学硕士毕业论文中[19]

    Smalltalk映像类似于(可重启的)核心转储,并可以提供与核心转储相同的功能,比如延迟或远程调试,具有对出错时刻的程序状态的完全访问。将应用代码建模为某种形式的数据的其他语言比如Lisp,也经常使用基于映像的持久存储。这种持久存储的方法对于快速开发是强力的,因为所有开发信息(比如程序的解析树)都保存而利用于调试。但是它作为一个真实的持久存储机制也有一个严重的缺点。首先,开发者可能经常想要隐藏实现细节,并使它们在运行时间不可获得。出于法律和维护的原因,允许任何人在运行时间修改程序,对于在运行时间环境不暴露源代码的编译后的系统,不可避免的介入复杂性和潜在的错误。其次,尽管持久存储机制易于使用,它缺乏多数多用户系统需要的真正持久存储能力。最明显的是进行同多个用户并行访问相同的数据库的事务[20]

    实现列表

    OpenSmaltalk

    OpenSmaltalk VM(OS VM)是Smalltalk运行时环境的著名实现,很多现代Smalltalk VM基于或派生自它[21]。OS VM自身是从一组Smalltalk源代码文件(它们叫做VMMaker)转译成原生C语言源代码(通过使用叫做Slang的转译器[22][23]),它依次再针对特定平台和硬件架构来编译,实际上确使Smalltalk映像的跨平台执行。源代码可以在GitHub上获得并在MIT许可证下发布。OS VM的知名派生者有:

    参见