Smalltalk: Getting The Message
The Essentials of Message-Oriented Programming with Smalltalk

© Copyright 2007 by Alan L. Lovejoy. Licensing terms at end of document.

About Smalltalk

Smalltalk is a foundational programming language that is based on pervasive message passing, pervasive dynamic and strong typing, pervasive reflection and pervasive object orientation.

Message passing: Almost all computation in Smalltalk happens via the sending of messages. The only way to invoke a method is to send a message—which necessarily involves dynamic binding (by name) of message to method at runtime (and never at compile time.) The internals of an object are not externally accessible, ever—the only way to access or modify an object's internal state is to send it a message. So function and data abstraction are both complete and universal. Pervasive message passing is Smalltalk's most important feature—a point that was lost on most of those who have tried to emulate Smalltalk when designing other programming languages.

Dynamic and strong typing: Although any object can be assigned to any variable, the only way to access or modify the internal state of an object is to send it a message—and the sending of any invalid message is detected and prevented at run time. So, even though Smalltalk's pervasive use of dynamic typing enables the programmer to define highly polymorphic abstractions with an extremely high degree of applicability and reusability, it is impossible to apply a function to a value for which there is no valid, defined behavior.

Reflection: In most programming languages, the specifications of types, classes, functions and subroutines exist only in the source code, and so are not accessible at runtime. But in Smalltalk, all specifications of all program constructs (classes, methods, etc.) are live objects that exist both at compile time and at runtime—and those objects are fully accessible to a running program, and can be queried or modified by sending them messages. So a Smalltalk program can not only fully introspect on itself, it has full power to change itself.

Object-orientation: In Smalltalk, all values are objects—even integers and other numbers, characters, strings, classes and blocks of code. Smalltalk is one of the first object-oriented programming languages. Its design was influenced by Lisp, Logo, Sketchpad, Flex and Simula. Smalltalk was developed as a research project at Xerox PARC in the 1970s by a team whose members included Dr. Alan Kay, Dan Ingalls, Adele Goldberg, Ted Kaehler, Scott Wallace and others.

Warning: Terms such as "object," "class," "type," "method" and hence "object-oriented programming" itself, as used in the context of Smalltalk, do not have the same meanings as they do when used in the context of other programming languages. The term object-oriented programming ("OOP") was coined by Dr. Alan Kay, the inventor of Smalltalk. He intended the term to describe the essential nature of Smalltalk. Unlike Smalltalk, most of the programming languages that market themselves as "object oriented" do not satisfy Dr. Kay's definition of object oriented programming:

"OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things. It can be done in Smalltalk and in LISP. There are possibly other systems in which this is possible, but I'm not aware of them."

The full import of "object-oriented programming" as originally defined by Dr. Kay—and how and why the meaning of "OOP" as it applies to Smalltalk differs from the meaning of "OOP" as commonly understood outside of a Smalltalk context, is fully explained in the sections that follow. In addition, Dr. Kay's article "The Early History of Smalltalk" is highly recommended reading for anyone who wants to gain an even deeper insight into why and how Smalltalk came to be what it is, and why it is so different from the mainstream programming languages.

This is a living document whose contents will be corrected, enhanced, improved and expanded over time. Constructive criticism, suggested rewordings to improve clarity, and requests that additional information be included or that additional topics be covered, are encouraged. Such requests should be sent to the author, Alan L. Lovejoy, at the following e-mail address: smalltalk-tutorial (at) alan-lovejoy (dot) net

Why Smalltalk?

Smalltalk is a worldview, a way of thinking, a completely different programming paradigm. The concepts at the heart of Smalltalk are so profound, and so foundational, that all serious programmers should learn the language[1], even if they don't ever expect to actually use it professionally. Learning C++, Java or C# is not any sort of substitute for learning the real thing. None of those languages satisfy Dr. Kay's definition of OOP.

Smalltalk is considerably simpler than most other languages, both conceptually and syntactically. But Smalltalk is also considerably more powerful and expressive than most other languages, due in part to its greater simplicity, uniformity and symmetry. Smalltalk is comprehensively uniform and consistent, and comprehensively symmetrical—which is an important part of the reason why it can be both simple and powerful at the same time. The flexibility provided by Smalltalk's high degree of symmetry and extreme late binding have proven to help programmer productivity and creativity more than any theoretical benefits that might be derived from the use of static type checking, symmetry-breaking primitive data types or symmetry-breaking syntactic sugar.

Smalltalk is also pervasively dynamic, pervasively reflective, and pervasively open. Its metalevel is completely symmetric with its base level: All the same things can be done at the metalevel as at the base level, using the same syntax.

Stated concisely, the symmetry, the uniformity, the consistency and the simplicity of Smalltalk lie in the following: All values are objects, all behavior is invoked by sending messages, and almost nothing is done by syntax when it can instead be done by sending messages to objects.

But most importantly, Smalltalk is fun. It's fun because its programming tools make use of Smalltalk's superior reflective capabilities to provide a development environment unlike any other. You have to use it to believe it.

Smalltalk is also fun because defining and using domain specific languages isn't an afterthought, it's the only way Smalltalk works at all. As a result, Smalltalk code lets both the reader and the writer focus on the problem domain, using language and notation that is natural to that domain.

And last but not least, Smalltalk is fun because of its exceptional productivity. Unless you are also using Smalltalk, using Lisp (or one of its variants,) or using Erlang (or some reasonably similar language,) it would be wise to avoid challenging a Smalltalk programmer to a coding contest where the winner is the first to finish the assigned task. Unless you choose a language that is optimized for the problem domain of the assigned task, or where there's a library of code available that largely solves the problem, you'll almost certainly lose (e.g., Doing matrix algebra with APL, parsing with Perl, or astronomical calculations using FORTRAN libraries specific to that domain.)

Programmers of the world, throw off your chains. You owe it to yourselves to take a trip to Smalltalk country, where the native optimistic typing paradigm assumes that the programmer is innocent until proven guilty. The mainstream programming languages are too complex, are too static, bind too early, and have too little symmetry. It's time for a "Copernican Revolution," where programmers recognize that the curly-braced languages are based on overly-complex "epicycles," and that there's a much simpler, more effective and less arduous approach.

  1. There are other languages, in addition to Smalltalk, whose ideas are so profound and foundational that all serious programmers should learn them. Some good examples of such languages that most programmers don't already know would be Lisp, Scheme, Haskell and Erlang.

The Smalltalk Computational Model

NOTE: This document mostly limits its description of Smalltalk to ANSI Standard Smalltalk. However, there are many dialects of Smalltalk in the Smalltalk language family (which is similar to the situation with Lisp,) not all of which fully conform to the ANSI standard for Smalltalk, and most of which have features/capabilities that go beyond both the ANSI standard and the description in this document.

Key points

  • In Smalltalk, all values are objects.
  • Almost all computation is done by sending messages to objects.

Messages and Methods

A message can be thought of as a request to invoke or perform a computation. When a message is sent to an object, the receiving object (usually referred to as the "receiver of the message," or simply as "the receiver,") is responsible for deciding how to fulfill the request to invoke (perform) the computation named by the message—or in Smalltalk jargon, how to "respond to the message." An object that receives a message responds to the message by (dynamically, at runtime) finding a method in its method namespace whose name matches that of the message, and then executing that method.

A method is an object that embodies a computational algorithm that, when executed, computes a value—possibly with side effects. To execute a method is to execute the algorithm embodied by the method. Conceptually, a method is analogous to a function (or subroutine), and a message is analogous to a function/subroutine call (a function or subroutine "call" represents a request for it to be executed.) But the analogy between methods and functions (subroutines) is only that—a conceptual analogy—for reasons that are explained below.

The name of a message or method is known as its selector. The selector of a message or method identifies its intentional semantics (logical meaning,) and is what distinguishes one message or method from another. In addition to its selector, a message may have zero or more arguments (parameters.) And a method may accept zero or more arguments—although any particular method requires a particular number of arguments. Each argument in a message becomes an argument to whatever method is executed in response to the message.

A method namespace operates as a mapping (functional predicate) that, for each unique method selector (method name) in its domain, associates that method selector (method name) with a particular method. It is used to lookup a method by its selector (name.)

Dynamic message dispatch is the term used to refer to the process of using a method namespace to (dynamically, at runtime) find the method associated with a particular method selector (name.) It should be noted that Dr. Kay (and Smalltalk) use the term "method" to mean a function or subroutine that is invoked in response to the sending of a message, by means of dynamic message dispatch at run time (and not by static binding at compile time.) This is one of the two differences between a method and a function/subroutine. It's also the essential difference between Smalltalk methods and the functions/subroutines in other programming languages that, in disrepect to those who coined the term, are often referred to as "methods."

Because of dynamic message dispatch, the semantics of sending a message to an object depend absolutely on the object that receives the message. The same message may be interpreted quite differently by different objects, due to the fact that each object may use a different method namespace in order to lookup a method based on the selector of a message, and so may associate a different method with the same message selector. Consequently, a message can be thought of as an abstract function call which will result in the execution of whatever concrete function (subroutine) is chosen by the object that receives the message. The idea is that a message should name the logical function to be computed, but the object receiving the message should decide how best to physically compute the desired result. The universal, pervasive application of this distinction between logical function (semantics) and physical function (implementation strategy) is the central point of object oriented programming, as defined and envisioned by Dr. Kay.

Variables

A variable is a dynamically modifiable association (binding) of either a name or an index to a value. Each distinct variable has exactly one name (or index.) Distinct variables in the same scope must have distinct (different) names (or indices.)

A variable is either a named variable, or it is an indexed variable—one or the other, not both. Named variables associate (bind) a name to a value. Indexed variables (bind) an index (strictly positive integer > 0) to a value. The value associated with ("bound to") a named variable can be accessed by simply using the name of the variable in the code. The value associated with ("bound to") an indexed instance variable can be accessed by sending a message to the object that contains it (see the section on Objects, below.)

Creating a binding of a value to the name of a variable is also called assigning a value to the variable. The same value can be assigned to different variables at the same (or different) times. A variable is initially automatically assigned a special value, known as nil, that is used to indicate that, logically speaking, the variable has no value. An assignment operation (see the section on Smalltalk syntax, below) can be used to assign a value to a variable. A variable can be assigned a new value any number of times.

When assigning a value to a named variable, the variable assignment operation necessarily references the variable's name—but does so with different semantics than applies when the name of a variable is used as a reference to the value of the variable, since the name of a variable to which a value is being assigned refers to the variable itself, and not to the variable's (pre-assignment) value.

Assigning a value to an indexed variable is not done syntactically, but by sending a message to the object that contains it (see the section on Objects, below.)

Objects

An object is a value that can be sent messages, can be assigned as the value of a variable, referenced as the value of a variable, passed as an argument in a message, or returned as the result of sending a message. In Smalltalk, all values are objects—which is not usually the case in other programming languages.

The difference between a value that is an object versus one that is not an object lies in the following:

  1. An object inseperably unifies its value with the behavior that is applicable to its value, and so binds the value it represents to the semantics of that value.
  2. An object fully encapsulates its behavior: The only way to invoke (request the execution of) any of an object's behavior is to send it a message (which results in the execution of one its methods by means of dynamic message dispatch.)
  3. An object fully encapsulates its structure and state, because the only way to navigate an object's structure, or to modify an object's state, is to send it a message.

An object is not a variable, nor is a variable an object. A variable provides a way to reference an object (either by name or by index,) but is distinct from the object it references. Defining a variable does not create an object, nor does creating an object define a variable. However, all objects are either referenced by at least one variable, or by at least one expression that is currently being evaluated, or by at least one thread that has not yet been terminated. Once an object has no remaining references, it will be deallocated by means of automatic garbage collection. The only way to "delete" an object is to remove all references to it.

Mathematically, an object is a node in a directed graph. This also applies to data values in other languages that may be called (for example) "objects," "structures" ("structs," "records,") instances of a data type, or instances of an abstract data type (ADT.) A graph node has zero or more directed arcs. It is a common convention to interpret each directed arc connecting one graph node to another as semantically equivalent to the assertion of a predicate with arity 2, where the directed arc represents the assertion of the predicate, and the two nodes are the arguments of the predicate.

For example, if A and B are nodes in a directed graph, and there is a directed arc from node A to node B whose semantic role is "has-parent," then by convention that situation is understood to be the assertion of the predicate "has-parent(A, B)"—in other words, the assertion that B is the parent of A.

Structurally, an object has zero or more instance variables, each of which may be assigned any value at any time. Of course, since all values are objects, the value of an instance variable is necessarily an object. Mathematically, each instance variable is equivalent to a directed arc, with the object that contains the instance variable and the object that is the value of the instance variable being the two graph nodes connected by the directed arc. It is essentially a universal convention in programming languages that each instance variable (directed arc) is understood to represent the assertion of an arity-2 predicate, where the predicate is identified by the instance variable, and where the two arguments to the predicate are a) the object which contains the instance variable, and b) the value of the instance variable.

For example, if A and B are objects, and object A has an instance variable whose semantic role is "has-parent," then by convention that situation is understood as being semantically equivalent to the assertion of the predicate "has-parent(A, B)"—in other words, the assertion that object B is the parent of object A.

Instance variables are completely private to the objects that contain them. Each instance variable belongs to precisely one object, although different objects may have instance variables with the same name or index. The value of an instance variable can only be accessed or changed by the object to which it belongs. If two objects both have instance variables with the same name or the same index, those instance variables are actually distinct from each other, even though they happen to have the same name or the same index. Within the context of a particular object, each instance variable must either have a unique name or a unique index.

A named instance variable should be given a name that suggests the semantics of the predicate (relation, directed arc) it is intended to model. Operationally, of course, the meaning of the predicate modelled by an instance variable depends only on a) the intentional semantics of the messages that can be sent to the object, and b) the algorithm implemented by the methods that will be executed in response to those messages.

An object with indexed instance variables encapsulates/represents an ordered sequence (ordered collection) of objects as a single value (and hence, as a single object.) For example, an array object encapsulates/represents (as a single value) an ordered sequence of the values (objects) it contains as its elements—and does so by means of indexed instance variables, one for each element in the array. Similarly, a String object encapsulates/represents (as a single value) an ordered sequence of character values—and does so by means of indexed instance variables, one for each character in the String.

An object may have zero or more named instance variables, and/or zero or more indexed instance variables. Indexed instance variables may optionally be restricted so that they can only contain unsigned 8-bit integers, instead of being able to contain any object. Indexed instance variables whose values can only be 8-bit unsigned integers are referred to as byte-valued indexed instance variables. Some dialects also support indexed instance variables whose values are restricted to being 2-byte and/or 4-byte unsigned integer values.

An object can only have one set of indexed instance variables, so it either has no indexed instance variables, or it has indexed instance variables that contain only 8-bit unsigned integers (are "byte-valued,") or it has indexed instance variables that may contain any object. In the case of byte-valued indexed instance variables, the initial value of each byte (8-bit unsigned integer) is zero.

Code Blocks

In addition to methods, there are also objects that represent blocks of code. A code block object is usually just called a "block" or "block closure." A block object represents an anonymous function (or subroutine) which can be invoked by sending a message to the block object. The argument values in the message (which is sent to the block object in order to invoke the function it represents) become the arguments to that function. A block (anonymous function) can have any number of arguments, although each block requires some specific number.

Blocks make it possible to dynamically define and invoke functions that are anonymous (have no name by which to "call" them) because they are objects. The function is referenced dynamically at run time using the identity of the block object, instead of statically by name at compile time.

Code blocks provide Smalltalk with an implementation of the lambda calculus, with full closure semantics.

Blocks are objects, and so can be assigned to any variable, passed as message arguments, and returned as the value of a message send. Blocks are almost always constructed as literal values, using a notation (syntax) unique to Smalltalk.

Blocks are used quite pervasively. Examples: To implement control structures (conventional "if statements" and "loop statements," as well as unconventional ones useful in a particular problem domain,) threads, futures, promises and continuations. They are also used as function-valued parameters to higher-order functions—for example, to apply a function to every member of a collection.

Classes

Every Smalltalk object is an instance of a class. There can be any number of objects which are all instances of the same class. The term instance is often used as a shorthand for the expression "instance of a class." An instance of a class is necessarily an object, and so the term "instance" will sometimes be used as a synonym for the term "object."

The class of an object serves as the single specification of the structure and behavior of the objects that are its instances, and so is a convenient way to specify the common behavior and structure of a set of objects once, so that the behavioral and structural specification of objects need not be repeated whenever two or more objects should have the same behavior and the same structure.

A class defines the structure of its instances because it specifies the set of instance variables they will have. A class defines the behavior of its instances because it specifies and implements the method namespace used by its instances to lookup methods in response to messages.

The methods that a class specifies for use by its instances are known as instance methods. The instance methods specified by a class define which methods will be executed in response to messages sent to its instances—and so define the meaning of those messages when sent to any of its instances. By defining the mapping of messages to methods, a class also defines the operational meaning of the instance variables of its instances.

Note, however, that there are OOP languages that don't use classes. The best known such language is Self, which uses objects and methods, but does not use classes. The fact that an OO language can be implemented without classes is rather inconvenient for those who think classes are central to the definition of OO. The truth is that classes and methods are both simply convenient implementation techniques for that which truly is central to OO: message sending. There is an analogy here with Lisp, where s-expressions are simply a convenient implementation technique for that which is truly central to Lisp: the lambda calculus.

A class is also an object. Like any other object, a class is itself an instance of a class—which is called the metaclass of the class. A class is the sole instance of its metaclass. The metaclass of a class named Foo is always referred to as "Foo class." All metaclasses are instances of the class Metaclass. The class Metaclass is an instance of the metaclass (canonically referred to as) "Metaclass class," which is then (recursively) an instance of Metaclass.

Since a class is an object, and is also an instance of a class (the metaclass of the class,) each class can have its own methods which can be executed by sending messages to the class. In fact, instances of a class are created by sending it messages.

The methods that are used by a class to respond to messages sent to the class itself (not its instances) are known as class methods. Note that the class methods of a class are one and the same as the instance methods defined by its metaclass—and that the "class instance variables" of a class are one and the same as the instance variables defined by its metaclass.

Class Inheritance

One class can inherit from another. A superclass can have any number of subclasses that inherit from it. When a subclass inherits from a superclass, the effect is that a) any and all instance variables that its superclass specifies (or inherits) are added to the set of instance variables the subclass specifies directly (so that each of its instances will have the combined set of intance variables,) and b) any instance methods its superclass defines (or inherits) whose selectors are not the same as those of any of the instance methods that the subclass defines itself, are added to the subclass' method namespace (so that the combined namespace will be used by its instances to lookup methods in response to messages.)

By default, the inheritance relation between a subclass and a superclass is mirrored between the metaclass of the subclass and the metaclass of the superclass, so that if the class Beta inherits from the class Alpha, then Beta class (the metaclass of Beta) also inherits from Alpha class (the metaclass of Alpha.) However, it is not at all required for such to be the case. In fact, for any class that has no superclass (known as a "root class,") the superclass of its metaclass is the class Class (which is a sibling of the class Metaclass, mentioned in the previous section.) Note that this is not only one case where the mirroring of inheritance relationships between classes and their metaclasses does not hold, it is also a case where the superclass of a metaclass is a class, and not a metaclass! Note also that all classes therefore inherit (usually indirectly) from the class Class.

When a class directly defines an instance method itself whose selector is the same as that of some method it would otherwise inherit, the instance method it directly defines is said to override the one defined by its superclass, and the instance method defined by its superclass is said to be overridden. Although a class does not inherit the instance methods it overrides, it is nevertheless possible for any of the instance methods it directly defines to invoke any of the methods it overrides, as though they were instance methods of the class. That can be done by sending a message whose selector is that of the overriden method to a special pseudovariable named "super."

Inheritance Example:

Class Date specifies that its instances have the instance variables "year," "month," and "dayOfMonth." It also specifies a set of instance methods, one of which has the selector (name) "secondsSinceEpoch," which computes the number of seconds between a Date (year, month, dayOfMonth) and the epoch date of some calendrical system (year=1, month=1, dayOfMonth=1.) The number of seconds between the epoch date of a calendrical system and some date (in that same calendrical system) would be computed by using the instance variables year, month and dayOfMonth (of the instance of Date to which the message is sent) to compute the number of days between the epoch date and the date specified by the Date instance, and then converting the number of days so computed into a number of seconds.

Class Timestamp is a subclass of class Date, and so inherits the three instance variables specified by Date, and also inherits any instance methods defined by class Date whose selectors are not identical to any instance methods defined directly by class Timestamp. Timestamp itself defines three additional instance variables: "hour," "minute" and "second." So an instance of class Timestamp has six instance variables: "year," "month," "dayOfMonth," "hour," "minute" and "second."

Class Date defines an instance method named "year" that answers the value of the instance variable named "year" in the instance of Date to which the message "year" is sent. Assuming the class Timestamp does not directly define any method with that same selector, all instances of Timestamp will inherit that method. Consequently, when the message "year" is sent to an instance of Timestamp, the method with selector "year" defined by class Date will execute, returning the value of that Timestamp instance's instance variable "year" as the result of the message send (this behavior is controlled by the way the method "year" is coded, and has nothing to do with the fact that the method selector and instance variable happen to have the same name.)

Class Timestamp also defines a set of additional instance methods, one of which has the selector "secondsSinceEpoch," which is identical to the selector of one of the instance methods defined by class Date. Consequently, Timestamp does not inherit the instance method "secondsSinceEpoch" from its superclass, so when an instance of Timestamp is sent the message "secondsSinceEpoch," the method that executes is the instance method with that selector defined by class Timestamp, and not the one defined by class Date.

Presumably, the implementation of the "secondsSinceEpoch" method as defined by Timestamp not only performs the same computation as does the one defined by class Date (perhaps by sending the message "secondsSinceEpoch" to the pseudovariable "super,") but also computes the number of seconds since midnight represented by the Timestamp instance to which the message is sent (using that instance's instance variables hour, minute and second,) and then adds them to the total number of seconds derived from the count of days since the epoch in order to compute the answer to the message.

The primary purpose, motivation and intent for class inheritance in Smalltalk is to factor out common code into a common class so that it can be defined once, and reused as needed by inheritance. Classes have the same motivation: to factor out common code into a class, so that it does not need to be duplicated among a set of structurally-identical objects. Although it can be used as such (with proper care,) a class is not intended to represent a type as understood and defined by type theory. Nor is a class equivalent to a concept in an ontology. A class may implement behavior in order to model a concept, but at best it is only one possible model or reflection of certain aspects of a concept (there may be others that aren't equivalent.)

Class inheritance in Smalltalk should primarity be based on what you want an object to do, and not necessarily based on what an object is (or represents, or models) in any philosophical sense. There may be very good reasons to use two or more different classes to model (represent) the same concept, in order to optimize the implementation of each class to meet different engineering goals. One might, for example, implement one Point class using cartesian coordinates, and other Point class using polar coordinates, for use in different situations. One might also implement one Vehicle class whose purpose is to draw/animate vehicles on a display screen, and another Vehicle class whose purpose is to model auto insurance coverage. The two "Vehicle" classes could even have the same name, if they reside in different namespaces (although not all Smalltalk dialects support multiple namespaces for classes.)

Variable Name Scoping

When a method executes, it does so in the context of both the object that received the message that resulted in the execution of the method, and also in the context of the class of that object[1]. Firstly, that means that the code of an executing instance method has direct access to any of the instance variables of the message receiver that are either a) defined directly by the class that defines the executing instance method, or b) inherited by the class of the message receiver. Secondly, that means that the code of an executing instance method has direct access to any of the class variables (see below) defined or inherited by the class of the message receiver.

In addition to instance variables, there are also the following types of variables:

  • Local (temporary) variables—accessible only by code within the method or code block wherein they are defined; each invocation of the method or code block (whether by recursion, or by a different thread) uses a different instance of the variable, but uses the same variable name;
  • Formal method arguments—accessible only by code within the method wherein they are defined; each invocation of the method (whether by recursion, or by a different thread) uses a different instance of the formal argument, but uses the same argument name;
  • Formal block arguments—accessible only by code within the code block wherein they are defined; each invocation of the code block (whether by recursion, or by a different thread) uses a different instance of the formal argument, but uses the same argument name;
  • Class instance variables—accessible only by the class itself, although all subclasses each have their own private instances of each variable with the same name; Note that class instance variables are actually just the instance variables specified by the metaclass of a class;
  • Class variables—accessible by all objects that are instances of the class or any of its subclasses, and by the class itself or any of its subclasses;
  • Shared pool variables—accessible by all the classes that import the shared pool, by all subclasses thereof, and by all their instances;
  • Global variables—accessible everywhere;

Global variables have the lowest variable name binding precedence. The definition of any non-global variable will take precedence over ("override") any global variable with the same name. Shared pool variables have lower variable name binding precedence than do class variables, and will be overridden by any class variable defined with the same name. A class variable defined in a subclass has higher variable name binding precedence than one defined with the same name in any superclass. They all have lower variable name binding precedence than any instance variable, local variable or formal argument.

The instance variables defined by a class are in an outer scope with respect to the local variables and formal arguments defined in any instance methods defined by the class. Variables defined in an outer scope have lower name binding precedence than any variable defined with the same name in an inner scope. Although it is legal for a variable in an inner scope to have the same name as any variable in an enclosing (outer) scope, such name conflicts are considered to be a bad programming practice.

Since code block literals can be nested, they define nested variable scopes for local variables, formal method arguments and formal block arguments. Within the method or code block literal in which they are defined, local variables, formal method arguments and formal block arguments take precedence over (i.e., "override") any variable with the same name that is defined:

  • As a global variable;
  • As a shared pool variable;
  • As a class variable;
  • As an instance variable;
  • As a local variable in either the enclosing method or in any any enclosing code block literal;
  • As a formal block argument in the same code block;
  • As a formal method argument of the defining method, or as a formal block argument of any enclosing (outer) code block literal.

Normally, the standard naming conventions prevent any name conflict between local variables, formal arguments or instance variables on the one hand, and class variables, shared pool variables or global variables on the other hand. However, variable naming conventions are not enforced by the compiler, so a naming conflict is theoretically possible (although the author has never seen such, in over 20 years of using the language.)

  1. The fact that an instance method executes in the context of the message receiver (and in the context of the class of the message receiver) is one of the two essential differences between a method and a function (or subroutine.) The other, as explained above, is that a method must be selected for execution by means of dynamic message dispatch in response to the sending of a message.

Variables, Types and Classes

Smalltalk variables have no type constraint syntactically associated with them. Any variable can have any value assigned to it—in other words, the value of a variable can be an object that is an instance of any class (all values are objects.)

It should be noted that a Smalltalk class is not a type, although it implements one (or more.) A Smalltalk type is defined as the power set of messages to which an object can meaningfully respond—a definition that differs non-trivially from that of the term type as commonly used in most other programming languages. Smalltalk essentially uses what has come to be called duck typing. The author likes to call it "optimistic typing."

Different classes can all implement the same Smalltalk ("duck") type—which is a key ingredient to the extreme polymorphic power of Smalltalk. Unlike a class, a Smalltalk type is not an object—although you can define a class whose instances model a type, and can modify Smalltalk at the metalevel so that classes can inform you of their type (or types) using one or more instances of the type-modelling class you've defined.

It is not correct to say that Smalltalk is untyped, or that it is weakly typed. Smalltalk is strongly typed by means of dynamic typing, instead of by means of static typing. Strong typing requires that type errors be detected and prevented—it does not require that type errors must be detected and prevented statically by a compiler, using syntactic analysis of source code.

The class (and hence the type) of a Smalltalk object is encapsulated as part of the value (internal structure) of an object. The type (or types) of an object is an intrinsic part of the value of an object, and is exclusively determined by the class of the object, and not at all by what variable happens to reference it. To make either the class or the type of an object in any way dependent on the variable that references it is a violation of the encapsulation principle, with consequences that are every bit as serious as any other failure to observe good information hiding protocol. In spite of that hard fact, it is a common feature of "OO" languages to actually allow the exact same "message" to have different semantics (behavior,) when sent to the exact same object, depending on the static type constraint of the variable which references the object to which the message is sent. That interpretation of "OO" and "message" statically binds the meaning of a "message" to the variable used to reference an object, and prevents the object itself from having full control over the meaning of the messages it is sent. An object that does not fully control the semantics of the messages it is sent does not fully encapsulate its behavior—hence the violation of encapsulation.

The class (and hence the type) of a value is an essential property of both the input and output data used and produced by a computational process. In other words, the class (and hence the type or types) of objects are just as much a part of the values to be computed dynamically at run time as is any other data involved in a computation. Attempting to statically restrict the set of values that may be involved in a computation, based on syntactic constraints enforced at compile time, not only artificially and unneccesarily reduces the set of valid computations that can be performed, but makes the compiler responsible for a task that should rightfully be done by the programmer.

If the programmer can be trusted to use and produce the right values in the code he writes, then he can be trusted to use and produce values of the right types—since the type of a value is no different than any other aspect of a value. Conversely, if the compiler can rightfully be assigned the task of determining which types are or are not valid, why isn't it also assigned the task of determining the validity of all other aspects of the values to be assigned to variables? Of course, even if the compiler were assigned all such responsibility, the additional constraints that would need to be provided in the code, so that the compiler could enforce those constraints on all aspects of all values, would still have to written by a programmer. There's a reason that imperative code is not written that way: There usually isn't enough information when imperative code is compiled to make such decisions—especially not optimal ones! And that's one reason why mainstream (imperative) languages with static typing tacitly admit the error in their design by permitting programmers to "cast" values from one type to another at run time, thus circumventing the static type system.

Without static type checking, some typing errors will only be detected at runtime, and not at compile time. But catching all type errors at compile time has a cost (relative to relying solely on dynamic typing,) and the cost is not worth the benefit. From an engineering perspective, dynamic typing is good enough. The fact that some academics think otherwise is nothing new.

There are two self-consistent prgramming paradigms: 1) Imperative programming, where program semantics emerge operationally as a result of behavior that is explicitly specified using imperative commands; and 2) declarative programming, where program behavior emerges operationally as a result of semantics that is explicitly specified using the rules of logic. Static type checking is a poor attempt to mix the two paradigms. Instead of trying to compute correct behavior from explicitly specified semantics, it merely tries to prove whether explicitly specified behavior conforms to constraints on the usage of types and function calls—something that it can only do by preventing correct behavior from being specified, simply because it can't prove that the usage constraints won't be violated dynamically. The "impedence mismatch" caused by static type checking, which is a natural consequence of mixing the two basic programming paradigms, imposes costs that are greater than the benefits.

In a system where behavior is explicitly specified using imperative statements, guaranteeing the semantics of a message (function call) is something that no system of static type constraint checking can provide, period. It's the implementation of the methods (functions) that operationally distinguish the semantics of "fire an employee" from that of "fire a gun." A static type checking system can guarantee that the function "fire" is applicable to an Employee or to a Gun, and can prevent the assignment of a Gun object to a variable whose static type constraint says "Employee." But it cannot guarantee what the semantics of the function (or method) that responds to the message "fire" might be—which is why it's essentially useless, from an engineering perspective.

When strong typing is provided by dynamic typing, the presence of static typing causes more damage than benefit. In practice dynamic typing is a better engineering solution. That's a conclusion that many Smalltalk programmers don't believe initially, but come to appreciate with ever greater certainty the longer they use the language. That's why, in spite of how easy it is to define objects that model or represent types, and how easy it would be to modify the Smalltalk compiler to add static type checking, and despite the fact that serious efforts to do one and/or the other of those things have been expended more than once, neither static type checking, nor reifying types as objects, have ever really caught on in the Smalltalk programming community[1].

  1. Note: Squeak Smalltalk has recently added Traits as part of its standard library, which in some ways are analogous to reified types (as defined above)—but the motivation for Traits is not to enable the enforcement of static type constraints at compile time, but rather as a more elegant way to accomplish the same goals that motivate multiple inheritance.

Modules/Packages

Unfortunately, there is a complete lack of standardization for providing or dealing with modules/packages in Smalltalk. Some dialects provide very strong, comprehensive support for modules/packages (including versioning and distributed access by programming teams,) and other dialects provide little or nothing in this regard. Some dialects provide a robust implementation of multiple, shareable namespaces, others don't. The only commonality is that, when either modules/packages or namespaces are provided, they are implemented as reified objects, in the same way that classes and methods are implemented as reified objects.

The Smalltalk Runtime/Development Environment

Smalltalk is a complete system and environment, in which there are only objects that send messages to each other. The objects live in what is called an image, which is a combination of an address space, a process (in the Unix sense) and a persistent object store. When the image is persisted, it's not just the objects that are stored, but the computational process itself. "Saving" or "snapshotting" a Smalltalk "image" is analagous to "hibernating" an operating system, so that when it is restarted, all the same threads are running, all the same windows are open, and the computational process resumes right where it was suspended, with no data lost.

In the case of Smalltalk implementations that descend from the original Smalltalk-80 system developed at Xerox, each Smalltalk image represents a continuation of a process (in the Unix sense) that began to run in 1976, and has been executing (albeit with intermittent pauses) ever since then.

Smalltalk is much more similar to an operating system than it is to a conventional programming language. Just like an operating system, a running Smalltalk image is not any single "program." It is a set of programs, each of which can be started and stopped independently of each other, and without any need to restart the image itself.

In a Smalltalk image, there is no distinction between "run time" and "compile time." For example, while the system ("image") is running:

  • Methods can be added to or removed from existing classes;
  • Existing methods can be modified;
  • Classes can be added to or removed from the system;
  • Classes can be renamed;
  • Classes can have their superclass changed;
  • Classes can have new instance variables added, or have their existing instance variables removed or renamed.

All of the above code changes can be safely performed even when there are instances of the affected classes, even when there are multiple threads, and even when a code debugger is being used to step through a thread. For example, you can modify the code of a method while stepping through its execution using the Debugger.

Conversely, while the programmer is working on the code, the classes and methods that have so far been defined are live objects to which he can send messages. The programmer can select a section of the source code of a method and execute it (or even use the debugger to step through it message-send by message send) from within the Smalltalk text editor. The code is alive.

A Smalltalk class is an object that knows its superclass and its subclasses. It knows its instance varaibles and its methods. A Smalltalk method is an object that knows its class, its selector (method name,) its formal method arguments, its local variables, its external references and its bytecodes. A method also knows its source code—or more precisely, it knows how to store and retrieve its source code from the source code repository associated with the Smalltalk image in which it lives.

The full and complete encapsulation by class and method objects of their entire state—including their source code—is the essential feature that makes the Smalltalk development environment so much more powerful than just about any other. The fact that other systems rely on files, and not on objects that exist at runtime, as the "store of record" for the specifications of the classes and methods they define is a major reason why their development environments cannot match the power of Smalltalk's development environment. Files are static and passive. Objects can be active and dynamic, and can actively and dynamically ensure that invariants are maintained—which is exactly what Smalltalk class and method objects do.

Smalltalk Syntax: By Explanation and Example

Like Lisp, and unlike most programming languages, Smalltalk is defined more by its standard library than by its syntax. All computation in Smalltalk is performed by one of four operations:

  1. Assignment of a value to a variable;
  2. Returning a value from a method (function);
  3. Testing whether two objects have the same identity;
  4. Sending a message to an object.

Other than assignment of values to variables, returning values from methods, and object identity testing, almost all computation in Smalltalk consists of sending messages to objects. There is no syntax for control structures (looping, if-then-else, etc,) no syntax for object instantiation (e.g., "new Object()",) and no syntax for defining classes, modules or packages. Control flow is effected by sending messages to objects. New objects are created by sending messages to objects. Methods, classes, modules and packages are all created by sending messages to objects. In fact, methods, classes, modules and packages are all themselves objects. [How all those things can be done by sending messages, without any special syntax, is demonstrated throughout the following examples.]


The examples that follow are grouped thematically into major and minor sections. Each minor section begins first with a thematic title, followed immediately by a parenthetical comment delimited between square brackets (and in a smaller font) where the number(s) of the production rule(s) that is (are) being demonstrated in the example(s) in that section are listed. For example, the "[Production rule: 13]" at the beginning of the section that deals with pseudo-variables indicates that production rule 13 is being demonstrated and explained in that section. The complete set of numbered production rules that specify the grammar (syntax) of Smalltalk are presented at the end of this document.

Lexical Tokens and Literal Values

Lexical tokens are usually handled by a lexical analyzer, and not by a parser. This section describes all Smalltalk lexical tokens and literal values—most of which are handled by the Smalltalk lexical analyzer.

Whitespace and Code Comments: [Production rules: 7, 8] Generally speaking, whitespace is any sequence of characters that are typographically displayed as blanks. In Smalltalk, whitespace can be any consecutive sequence of one or more space, carriage return, linefeed or horizontal tab characters. It can also be any code comment. Or it can be any combination of both. That is either identical, or very similar, to the syntax of "whitespace" in most programming languages.

Whitespace can optionally be used to separate one lexical token from another. In some cases, whitespace is absolutely required for that purpose. If you bear in mind that the production rules handled by the lexical analyzer take precedence over those handled by the parser, it should be evident when it is necessary to use whitespace to separate lexical tokens from each other in order to prevent syntactic ambiguity.

As is generally the case in any programming language, a code comment is an annotation to the code of a method or block literal. Comments in the code are usually used to document or explain the intent or purpose of the code, or explain why the code is written in a particular way. Other than its function as a token separator, a code comment has no effect on the meaning of the code itself. A code comment is delimited between two double quote characters, as in the following example:

sum := 3 + 4. "This is a comment."
product := "This is another comment" 3 * 4.

Many other programming languages use the double quote character to delimit String literals. Smalltalk doesn't. Smalltalk is not the only language that uses the double-quote character to delmit comments. English does the same—as do many natural languages.

Identifiers: [Production rule: 10] Semantically, an identifier serves the same function as it does in any other programming language: It's a name, or part of a name. In Smalltalk, it defines all or part of the syntax of variables, formal arguments, messages and Symbol literals (see below.)

The syntax of a Smalltalk identifier is quite similar to that used by most other programming languages: An idenfitier must begin with a character that is either a letter (A-Z | a-z) or the underscore character ("_"); after the first character, there may optionally be any number of characters, each of which must be one of a) a letter (A-Z | a-z),   b) a digit (0-9),   or c) the underscore character ("_").

Examples:

x"An identifier may be a single letter"
door3"Identifiers with multiple characters can have digits in any position other than the first"
the_end"An underscore character may occur in any position"
_privateName"An identifier may begin with an uderscore"

By very strong Smalltalk convention, camel casing is used instead of underscore characters in order to separate one word from another in an identifier that happens to correspond to a compound word or phrase in English. The underscore character is mostly used when an identifier needs to match a name used by an external variable, data type or function[1]. The underscore may also be (rarely) used in a message/method selector to mark it as private to the system.

  1. The underscore character was not originally legal as part of an identifier. The syntax of identifiers was changed by the ANSI Standard for Smalltalk (1998) to also allow the underscore character—partly in order to make it easier for Smalltalk to interoperate with external systems and languages—many of which, by convention, prefer to use underscores instead of camel-casing.

Constants: [Production rule: 12] A constant is a named reference to a value, such that the binding of the name to its value is immutable There are three pre-defined constants in Smalltalk: nil, false and true. The constant nil references the sole instance of the class UndefinedObject, and is used as a metavalue that signifies either "there is no value" or "there is no value defined." The constant false references the sole instance of the class False, and represents the Boolean truth value false. The constant true references the sole instance of the class True, and represents the Boolean truth value true.

NOTE: Due to the fact that nil, false and true are all syntactically-valid identifiers, they have the effect of pre-empting the definition by programmers of any identifiers having the same names as any of nil, false or true. They are reserved identifiers that have been predefined by the system so that their binding to their value is immutable.

Pseudo Variables: [Production rule: 13] A pseudo-variable is a system-defined variable whose value can only be assigned by the Smalltalk runtime environment itself, and not by the programmer. There are two ANSI-Smalltalk pseudo-variables: self and super. Many dialects of Smalltalk, including the two most widely used (VisualWorks and Squeak,) also define the pseudo-variable thisContext (as defined by the original Smalltalk-80 implementation of Smalltalk—which served as the de facto standard definition of Smalltalk before ANSI produced an official standard.)

The pseudo-variables self and super both always refer to the object that is the receiver of the message whose sending initiated the execution of any method that references either pseudo-variable. When a message is sent to self, dynamic message dispatch works as it does when a message is sent to any other object. But when a message is sent to super, dynamic message dispatch will not result in the execution of any method that is directly defined as an instance method by the class of the object receiving the message. So sending a message to super can only result in the execution of an instance method defined by some superclass of the class of the object that receives the message. Messages are usually sent to super in order to execute an instance method defined in a superclass that has been overriden (in the class of the receiver) by another method with the same selector.

If defined, the pseudo-variable thisContext references an object that either is or represents the activation frame on the call stack of the current thread's execution context for the method in which thisContext is referenced.

NOTE: Due to the fact that self, super and thisContext are all syntactically-valid identifiers, they have the effect of pre-empting the definition by programmers of any identifiers having the same names as any of self, super and thisContext. They are reserved identifiers that are defined and (re)initialized by the system as required. Their values cannot be (re)assigned by the programmer.

Keywords: [Production rule: 17] A keyword is a Smalltalk syntactical form that generally has no analog in other programming languages. Smalltalk code uses keywords quite pervasively—and gets substantial benefits from their use, as will be made clear in subsequent sections. The fact that there are no syntactic analogs of keywords in most other programming languages is by far the most important factor that makes Smalltalk code seem so syntactically impenetrable to most programmers. To solve that problem, read on.

Syntactically, a keyword is an identifier followed by a colon character (":").

Examples:

x:"A keyword may be a single letter followed by a colon"
door3:"Keywords with multiple characters can have digits in any position other than the first"
the_end:"An underscore character may occur in any position"
_privateMessage:"A keyword may begin with an uderscore"

A keyword forms all or part of a keyword selector. A keyword selector is a syntacical construct for representing message and method selectors (names) that need to be combined with one or more arguments (apart from the receiver of the message)—as is more fully discussed in the section (below) dealing with keyword message selectors. Keywords are also used as part of the syntax of Symbol literals (which are explained below.)

Binary Selector Characters: [Production rule: 19] A binary selector character is a punctuation character that can be used to compose message selectors that correspond to the tokens commonly used in programming languages for representing mathematical (and other) operators. Examples of such "operator tokens" include +, -, *, / and <=. Most, but not all, punctutation characters are binary selector characters. Here's the complete list of Smalltalk's binary selector characters:

~ ! @ % & * - + = | \ < > , ? /"A whitespace-separated list of characters"

There are also punctuation characters that cannot be used to compose message selectors:

( ) [ ] { } ` ' " . # ^ $"A whitespace-separated list of characters"

Integer Literals: [Production rule: 21] An integer literal specifies a value (and hence, an object) that represents an integer (a whole number that may be positive or negative.) Decimal integer literals are specified as they would be in most other programming languages—except that there are no length constraints. You can specify an integer literal with as many digits as you might ever need. Nor is there is ever any need to specify whether an integer literal is short, long or any other length. The syntactical representation is actually very similar to that of math or natural language—except you can't use a comma (as you might in North America) or a period (as you might in Europe) to separate thousands, millions, billions and so on.

Since class Integer inherits from class Number, integer values are all polymorphically type-compatible with general Smalltalk Number protocol.

Examples (decimal notation):

52"The integer fifty two"
371993326789901217467999448150835200000000"The factorial of 36"
-451"Negative integer literals must be (immediately) preceded by a minus sign"

Integer literals can also be specified in any base from 2 to 36. The format is to first specify the base (as a base 10 decimal literal,) then the letter "r" (lowercase,) followed by the digits of the number according to the specified base.

Examples (radix notation):

2r101"The number 5 in base 2 (1 * 2 squared + 1 = 5)"
3r200"The number 18 in base 3 (2 * 3 squared = 18)"
8r70"The number 56 in base 8 (7 * 8**1 = 56)"
16rFF"The number 255 in base 16 (15 * 16**1 + 15 = 255)"

For non-decimal digits greater than 9, use the alphabetic characters A-Z, where A has the decimal value 10, B has the decimal value 11, and Z has the decimal value 35. The lower case letters a-z may also be used, with the same decimal values as the corresponding upper case characters.

Scaled Decimal Literals: [Production rule: 26] A scaled decimal is a value (and hence, an object) that provides a precise representation of decimal fractions with some minimum number of fractional digits. A scaled decimal would typically be used to represent monetary/financial values, which need to have unlimited precision to the left of the decimal point, and need some minimum guaranteed precision to the right of the decimal point. Typically, implementations in Smalltalk actually provide unlimited precision on the right side of the decimal point, and not just on the left side—although the ANSI Standard does not require that. The minimum precision (whether implied, or explicitly specified) is used to limit the number of digits to the right of the decimal point that will appear when a scaled decimal value is converted to a textual representation.

Since class ScaledDecimal inherits from class Number, scaled decinal values are all polymorphically type-compatible with general Smalltalk Number protocol.

Syntactically, a scaled decimal is an optionally-negative decimal integer literal, optionally followed by a decimal point and another decimal integer literal, with all the preceding followed (non-optionally) by the letter "s" (lower case,) optionally followed by another decimal integer literal. The initial decimal integer literal represents the non-fractional part of the number. If present, the decimal point and following decimal integer literal represent the fractional part of the value. The lower-case "s" must immediately follow; its presence is essential in order to syntactically mark the literal as a scaled decimal value. The decimal integer literal that may optionally follow the "s" specifies the minimum decimal precision of the scaled decimal value (this is also known as the scale of the number—hence the "s".) If the minimum precision (scale) is not specified, it defaults to the number of decimal digits present in the literal (to the right of the decimal point, before the "s.")

Examples:

1s"A scaled decimal with the numeric value 1, with a scale (minimum precision) that defaults to 0 decimal places"
99.99s2"A scaled decimal for 99 and 99/100ths, with a scale (minimum presion) explicitly specied as 2 decimal places"
-654.052s"A scaled decimal for -654 and 52/1000ths, with a scale (minimum precision) that defaults to 3 decimal places"

Floating Point Literals: [Production rule: 27] A floating point literal is a value (and hence, an object) that approximates a real number. The syntax is either identical, or very similar, to that of most other programming languages. The ANSI Standard for Smalltalk provides syntax for specifying floating point literals with any one of three different precisions (single-precision, double-precision and quad-precision,) but does not require that a conforming Smalltalk implementation actually provide more than one precision (although the syntax for literals of all three precisions must be supported, with any literals for unsupported precisions auto-converted to one of the supported precisions.)

Since the various floating-point number classes all inherit from class Number, all floating point values are all polymorphically type-compatible with general Smalltalk Number protocol.

Syntactically, a floating point literal is an optionally-negative decimal integer literal (representing the non-fractional part of the number,) (non-optionally) followed either by a) a decimal point and another decimal integer literal (representing the fractional part of the value, a.k.a. the mantissa,) which is then optionally followed by an exponent specification, or b) an exponent specification. An exponent specification starts with one of three lower-case lettters: "e" (single-precision,) "d" (double-precision) or "q" (quad-precision,) optionally followed by an optionally negative decimal integer literal (specifying the value of the exponent—which specifies the power of ten by which the number to the left of the exponent specification is multiplied to obtain the value of the literal.) If no exponent specification is provided, then the precision defaults to single-precision, and the exponent defaults to zero. If no exponent is specified, then the exponent defaults to 0.

Examples:

3.14"A floating point literal which, by default, is single-precision"
3.14q"A floating point literal explicitly-specified to be quad-precision"
1e100"A single-precision floating point value that represents 1 * 10**100"
-3.141592654d"A floating point literal explicitly-specified to be double-precision"
31415.92654q-4"A quad-precision floating-point value that represents 31415.92654 * 10**-4 (i.e., 3.141592654)]"

Character Literals: [Production rule: 29] A character literal specifies a character value. Like all other values in Smalltalk, a character value is an object. Syntactically, a character literal begins with the dollar-sign character ("$"), followed immediately by the character itself.

Examples:

$x"The character x (lower case)"
$#"The `pound-sign` or `hash` character (`#`)"
$ "A space character (a blank)"
$$"The dollar-sign character (`$`)"

String Literals: [Production rule: 30] A string literal specifies a value that is an instance of the class String (or perhaps an instance of a dialect-specific subclass of String.) An instance of String (or of a subclass of String) represents a sequence of characters as a single value (and hence, as a single object.) There may be any number of String instances which all contain the same sequence of characters. Generally, the characters contained in a String instance can be changed dynamically—although some dialects make string literals immutable (the ability to make specific objects immutable—which is not the same thing as the ability to make all instances of a class immutable—is not supported by all dialects.)

Since class String inherits (indirectly) from class Collection, String instances are all polymorphically type-compatible with general Smalltalk collection protocol.

Syntactically, string literals are delimited between single-quotes ('). Many other languages use double quotes (") for the same purpose. Smalltalk doesn't—it uses the double-quote to delimit comments. To embed a single-quote within a string literal, use two single-quotes right next to each other.

Examples:

'x'"A literal for a string containing the single character $x"
'This is a string literal'"A self-describing string literal"
'Smalltalk doesn''t use the double-quote to delimit a string literal'"To embed a single-quote within a string literal, use two single-quotes right next to each other"
''"A string literal for an empty string value (object)"

Symbol Literals: [Production rule: 33] A symbol literal specifies a value that is an instance of the class Symbol (or perhaps an instance of a dialect-specific subclass of Symbol.) Conceptually, a Symbol is an immutable String (sequence of characters encapsulated as a single value.) There is only one Symbol instance for each distinct sequence of characters.

Symbols are frequently used to represent reified variable names, class names and message/method selectors—and that use is supported at the deepest levels of the system.

Since class Symbol inherits (indirectly) from class Collection, Symbol instances are all polymorphically type-compatible with general Smalltalk collection protocol.

Syntactically, a symbol literal starts with[1] a pound (hash) sign character (#), followed by one of four different syntactic patterns:

  1. An identifier ("identifier symbol syntax");
  2. A sequence of one or more keywords ("keyword symbol syntax");
  3. A sequence with any one, or with any two, characters from the set of binary selector characters ("binary selector syntax");
  4. A String literal. ("string literal symbol syntax")

Examples:

#x"A Symbol literal containing the single character $x (using identifier syntax)"
#at:put:"A Symbol literal that uses keyword syntax"
#value"A Symbol literal encapsulating the sequence of characters $v, $a, $l, $u and $e (using identifier syntax)"
#<="A Symbol literal that uses binary selector syntax"
#'This is a Symbol'"A Symbol literal that uses String literal syntax"

  1. When a symbol literal is specified as part of an array literal (see below,) and the symbol literal doesn't use String literal symbol syntax, then the leading pound sign (#) may optionally be omitted.

Array Literals: [Production rule: 34] An array literal specifies a sequence of values, encapsulated by an object that is an instance either of the class Array or of the class ByteArray—depending on the specific syntax used. The elements contained in an Array may each be of a different class (or type.) In contrast, the element values contained in a ByteArray must all be unsigned integers in the range 0-255.

Since class Array and class ByteArray both inherit (indirectly) from class Collection, instances of both classes are all polymorphically type-compatible with general Smalltalk collection protocol.

The syntax of ByteArray literals and Array literals is very similar, differing only in a) the tokens used to delimit (enclose) the elements of the array, and b) the literals that may legally be listed as elements of the array literal. In either case, the first token of an array literal must be the pound-sign character ($#). Immediately following the pound-sign character ($#) must occur the left-side array delimiter, followed by a list of literal values separated from each other by whitespace, followed by the right-side array delimiter. General object-valued array literals use the left paren, $(, as the left-side delimiter, and the right paren, $), as the right-side delimiter. Byte-valued array literals use the left square bracket, $[, as the left-side delimiter, and the right square bracket, $], as the right-side delimiter.

As mentioned above, ByteArray literals can only contain unsigned integer literal in the range from 0 to 255. General object-valued array literals may contain any literal other than a code block literal (see below.)

In the case of Symbol literals and array literals that are contained in an enclosing array literal, the leading pound-sign token (#), that is otherwise required, may optionally be omitted by the inner Symbol literal or inner array literal. Omitting the pound sign token usually doesn't change the value of the literal value in any way—except for any of three special Symbol literals: If the leading pound-sign is omitted from any of the Symbol literals in the set {#nil, #true, #false}, then those specific literals will be interpreted by the lexical analyzer as one of the pre-defined constants, and not as Symbol literals at all.

Examples:

#[2 4 8 16 32]"A ByteArray literal whose elements are the unsigned byte values 2, 4, 8, 16 and 32"
#(22 'hello' 3.14 nil #value $x)"An array literal containing 6 elements, each of a different type"
#()"An empty Array (it has no elements)"
#('hello' world)"An array literal whose first element is the String 'hello'
and whose second element is the Symbol #world"
#(('hello' 'world') #(hello world))"An array literal whose two elements are themselves array literals"
#(#('hello' 'world') (hello world))"Like the above—but swapping which inner array literal does or doesn't omit the #-token"
#(true false)"An Array whose two elements are the Boolean values true and false"
#(#true #false)"An Array whose two elements are the Symbols #true and #false"
#(#nil #value)"An Array whose two elements are the Symbols #nil and #value"
#(nil value)"An Array whose two elements are the metavalue nil and the Symbol #value"

Array/ByteArray instances are the canonical mechanism in Smalltalk for representing the very same array abstraction found in most other programming languages. But unlike the case in most other programming languages, and of course with the exception of the syntax for array literals, arrays in Smalltalk are implemented as part of the class library—and even were they not, they could be implemented by programmers themselves, without any special-case magic from the compiler or run-time system. Programmers can define their own implementations of array-like abstractions, should they so desire.

Code Block Literals: [Production rule: 41] A code block is a value (and hence, an object) that represents an anonymous function (or subroutine) which can be invoked by sending a message to the code block. A code block literal is usually referred to as simply a "block," instead of either of the more formally-correct terms block literal or code block literal. Since all values in Smalltalk are objects, and all objects are first-class values, a block can be assigned to a variable, passed in as an argument to a method, or returned as the result of an expression. Blocks can be sent the same messages, and respond to those message in the same way, regardless of whether they are referenced as literals, via named variables, or as an intermediate result value in an expression evaluation.

A block literal defines a function that has no name. Just like a function definition, a block literal does not, by itself, result in the execution of the algorithm it defines. Blocks only execute the function they embody when they are sent certain messages; not all messages sent to a block will cause the block to perform the computation it specifies.

Although Smalltalk's block literal syntax is at least as simple as the syntax for anonymous functions in other languages (and often simpler,) it is also quite different than that used by other languages. And block literals appear pervasively in Smalltalk code—which is one reason that programmers unfamiliar with Smalltalk syntax typically find Smalltalk code hard to decipher (although Smalltalk's keyword-based message syntax is more of a problem in that regard.) Another issue is that not all programming languages have any support for anonymous functions as first-class values (or at all)—although there are some widely-used languages that do: Lisp has full and complete support for anonymous function literals and the lambda calculus. Python and Ruby provide them. Java has anonymous classes, which are partially analogous—but the syntax is both complicated, and very different than that of Smalltalk. C# supports anonymous function literals with what it calls "delegates" (although the syntax is rather awkward.)

Syntactically, the specification of a block literal can be as simple as enclosing code between the two block literal delimiters, which are the single characters $[ and $] (expressed as Smalltalk character literals.) The character $[ (left square bracket) is the left-side block delimiter, and the character $] (right square bracket) is the right-side block delimiter. The following are examples of a few zero-argument block literals:

[3 + 4] A block literal defining an anonymous function whose algorithm computes the sum of 3 and 4.
[1 * 2 * 3 * 4 * 5] A block literal defining an anonymous function whose algorithm computes the factorial of 5.
[] A block literal defining an anonymous function whose algorithm computes nothing at all.

In the examples in this section, code that forms the body of block literals is presented without having first demonstrated or explained the grammatical production rules that govern Smalltalk expressions or statements. However, the examples in this section use Smalltalk syntax for the body of a block literal which is not only identical to that used in most programming languages, but is either identical, or very similar, to the notational conventions of everyday arithmetic. It should be noted, however, that the syntax for the code that forms the body of a block literal is the same as that which forms the body of a method—the expression syntax is the same, the statement syntax is the same, and the syntax for declaring temporary (local) variables is the same. What differs between the syntax of block literals and methods is the way formal arguments are declared, and the fact that, unlike block literals, methods don't use either a beginning or ending delimiter token (how that can be will also be explained later.)

Since a block is a function, it computes and returns a value as its result. Normally, the result returned from the evaluation of a block is the value of the last expression or statement in the body of the block. One exception to that is a block that has neither any code nor any arguments: An "empty," no-argument blocks evaluates to nil. An "empty" block that has arguments evaluates to the actual value passed in as the last argument.

The syntax for sending messages to zero-argument blocks so that they will execute the function they specify will be shown later.

Blocks can be defined to have any number of arguments. The declaration of a formal block argument begins with the character $: (a colon,) followed immediately by an identifier. The set of formal block argument declarations for a block are specified as a whitespace-separated list at the beginning of the block (after the left-side block delimiter.) The list of formal block argument declarations is terminated by a $| (bar) character—which is required, if there are any formal block argument declarations, but is not permitted otherwise.

The syntax for referencing the value of a formal block argument is simply to use the name of the same identifier used in its declaration—without the leading $: (colon) character. The $: (colon) at the beginning of each formal block argument declaration can be thought of as a token meaning "declare a formal block argument bound to the following identifier."

Examples:

[:x | x * x] A one-argument block, whose formal argument is x, and which computes the square of x
[:a :b | a < b] A two-argument block, whose formal arguments are a and b, and which answers whether a is less than b
[:x :m :b | m * x + b] A three-argument block, whose formal arguments are x, m and b, and which computes the y-coordinate of a point on a line whose x-coordinate is x, for a line whose slope is m and whose y-intercept is b.

Formal block arguments are not variablesassignment operations cannot be used to reassign (rebind) the value of formal block arguments.

For each of the three block literals from the preceding example, we show below equivalent notation, using the standard lambda calculus, and also using a notation (invented just for this example) that will hopfully resemble a syntax which is more familiar to a wider audience:

[:x | x * x] λx. (x * x)function f(x) is {return x * x;}
[:a :b | a < b] λ a b. (a < b) function g(a, b) is {return a < b;}
[:x :m :b | m * x + b] λ x m b. (m * x + b) function h(x, m, b) is {return m * x + b;}

The following three examples demonstrate the effect of sending invocation messages with message arguments to three different blocks—one example for the one-argument case, one example for the two-argument case, and one example for the three-argument case. Not shown is the actual syntax for sending invocation messages to blocks, which will be covered later:

Block Literal Invocation Message Argument(s) Result of Invocation
[:x | x * x] 6  =>   6 * 6   => 36;
When the block invocation message is sent to the block with the message argument 6, it responds by evaluating its function with the value of the formal argment (named x) set to the same value as the message argument (which is 6,) so that it evaluates the formula 6 * 6, and then answers the result of that computation (which is 36) as the result of the message send.
[:a :b | a < b] 3, 4  =>   3 < 4   =>   true;
When the block invocation message is sent to the block with the message arguments 3 and 4, it responds by evaluating its function with the value of the formal argments (named a and b) set to the same values as the message arguments (3 and 4, respectively,) so that it evaluates the expression 3 < 4, and then answers the result of that computation (which is true) as the result of the message send.
[:x :m :b | m * x + b] 33, 1.5, -12  =>   (1.5 * 33 + -12)   =>   37.5;
When the block invocation message is sent to the block with the message arguments 33, 1.5 and -12, it responds by evaluating its function with the value of the formal argments (named x, m and b) set to the same values as the message arguments (33, 1.5 and -12, respectively,) so that it evaluates the expression 1.5 * 33 + -12, and then answers the result of that computation (which is 37.5) as the result of the message send.

Executable Code

Examples of executable code have already been shown in the previous section, wherein the definition and usage of code block literal (usually referred to simply as "blocks") was demonstrated and explained. Block literals are used pervasively in Smalltalk code, so understanding the syntax and semantics of blocks is an important part of understanding code written in the language. However, those previous examples were limited to the minimum necessary usage of executable code in order to demonstrate and explain the syntax and semantics of block literals, and relied on the fact that Smalltalk's syntax for relatively simple arithmetic expressions is either identical, or very similar, to that of arithmetic notation, and/or that of most other programming languages.

What has not yet been fully demonstrated and discussed is the syntax for sending messages. Since almost all computation in Smalltalk is done by sending messages, the syntax used to do that is central to the syntax of executable Smalltalk code. The next four sections fully demonstrate and explain Smalltalk message sending syntax.

And now a word of "warning" to those readers who don't already know Smalltalk (I expect that there will be those who are already proficient at Smalltalk who will also read this document): If you read much further, you will be "taking the red pill" or "jumping down the rabbit hole" (whichever metaphor you prefer.) Just like learning how to see a stereogram, once you learn how to "read the code," there's no going back.

Messages: [Production rule: 53] A message is a request to perform a computation, and answer the result of that computation. To that extent, it is analogous to a function call. Like a function call, a message send a) may have arguments, and b) always has a return value. But a message send differs from a function call in that a) a message, when sent to one object, may result in the execution of a different method (function) than when that same message is sent to some other object, and b) the method (function) that actually executes in response to a message does so in the context of the object to which the message was sent—which means that the method (function) has access to the receiver and its internal structure (state.) Also, since the execution of a method may have side-effects, a message send may not striclty satisfy the semantics of a function call, as that term is used in mathematics.

A message send has three components:

  1. The object to which the message is sent—also known as the message receiver;
  2. The selector of the message—which is the unique identifier ("name") of the message;
  3. The arguments of the message—if any.

Although the semantics of sending a message is more fully discussed above, in the section that discusses the Smalltalk computational model, there is one point about the semantics of sending messages in Smalltalk that belongs here, because it concerns itself with the relation between the syntax and the semantics of sending messages: Conceptually, although a message receiver is not an argument of the message it is sent, it is an argument of the implied higher-order function responsible for the dynamic message dispatch operation that finds and executes a method as a function of both the message receiver and the message selector. In other words, sending a message to a receiver is conceputally equivalent to invoking a sendMessage function, with the message receiver, the message selector and the message arguments as its input parameters:

sendMessage(messageReceiver, messageSelector, messageArguments).

The sendMessage() function call—which performs dynamic message dispatch—is implied (and not explicitly specified) by the syntax of an object-oriented programming language. This is analogous to the syntax/semantics of function calls in most modern programming languages, which—unlike some early programming languages—do not use a "CALL" keyword or operator in order to invoke a function (although the "call subroutine" instruction does need to explicitly exist at the level of machine code.) In modern programming language syntax, the practice is to call a function by just referencing its name and listing its arguments. Similarly, the syntax of a Smalltalk message send specifies only the receiver, the message selector and the message arguments (if any.) The fact that the operation is a message send, and not a function call, is fully implied by the fact that Smalltalk methods can only be executed by sending a message—and also by the fact that almost everything in Smalltalk is done by sending messages.

Syntactically, there are three different types of Smalltalk messages: Unary messages, binary messages and keyword messages. Unary messages are used to send messages that have no arguments. Keyword messages are used to send messages having one or more arguments. Binary messages—which have one argment exactly—are used when the message selector is composed of punctuation characters, such as the characters usually used in other programming languages for the arithmetic operators +, -, * and /.

All message sends have the message receiver as an operand, in addition to the message arguments (if any.) The receiver of a message is always the leftmost operand in a message send. The message is specified to the right of the receiver, with only whitespace used to separate one from the other. The syntax for specifying a message selector (and any arguments) vary among the three message types.

Binary Messages: [Production rule: 48] The selector of a binary message is composed of either one or two binary selector characters. Although the complete list of binary selector characters was specified previously, here it is again for easy reference:

~ ! @ % & * - + = | \ < > , ? /"A whitespace-separated list of characters"

Syntactically, a binary message send starts with the receiver of the message, followed by the binary message selector, followed by the message argument (a binary message always has one argument.) Examples of binary message sends are shown in the following examples:

3 - 4 Receiver = 3
Selector = #-
Argument = 4
"Subtract 4 from 3"
=> -1
3 / 4 Receiver = 3
Selector = #/
Argument = 4
"Answers a Fraction whose numerator is 3 and whose denominator is 4"
3.0 / 4 Receiver = 3.0
Selector = #/
Argument = 4
"Floating-point division"
=> 0.75
10 // 4 Receiver = 10
Selector = #//
Argument = 4
"Integer division"
=> 2
10 \\ 3 Receiver = 10
Selector = #\\
Argument = 3
"Division modulo 3"
=> 1
'abc' = 'abc' Receiver = 'abc'
Selector = #=
Argument = 'abc'
"Answers whether the String literals 'abc' and 'abc' have the same value"
=> true
'abc' ~= 'abc' Receiver = 'abc'
Selector = #~=
Argument = 'abc'
"Answers whether the String literals 'abc" and 'abc' have different values"
=> false
42 > 42 Receiver = 42
Selector = #>
Argument = 42
"Answers whether 42 is greater than 42"
=> false
42 <= 42 Receiver = 42
Selector = #<=
Argument = 42
"Answers whether 42 is less than or equal to 42"
=> true
'con', 'cat' Receiver = 'con'
Selector = #,
Argument = 'cat'
=> 'concat'
#surname -> 'Flintstone' Receiver = #surname
Selector = #->
Argument = 'Flintstone'
"Answers an Association whose key is #surname and whose value is 'Flintstone'."
3 @ 4 Receiver = 3
Selector = #@
Argument = 4
"Answers a Point whose x-coordinate is 3 and whose y-coordinate is 4"

With two exceptions, binary message selectors are not operators! Their meaning is not defined by the syntax of Smalltalk. For all unary message selctors and all keyword message selectors, and for all but two binary message selectors, it is the object that receives a message that operationally defines the meaning of whatever messages it receives, as a result of the behavior that it exhibits in response to a message. And the behavior that an object exhibits is defined by its class. Which means that, with the exception of two special binary message selectors, the semantics of message selectors is defined by Smalltalk's class library, and not by its syntax. That's true of all the binary messages shown in the examples above.

The two binary message selectors which actually are operators, and whose semantics actually are defined by the syntax of Smalltalk, are the two pseudo-message selectors #== (the equal sign repreated twice) and #~~ (the tilde character repeated twice.) Those two "message selectors" syntactically look and act like binary message selectors, but are actually operators, and not really message selectors at all. Both the #== operator and the #~~ operator have two operands, one on the left and one on the right (just like a binary message selector.) The #== operator tests whether both its operands are the same, identical object. The #~~ operator tests whether both its operands are not the same, identical object. They are object identity tests—each with inverse semantics from the other. Two objects have the same identity (are identical) if they occupy the same storage location in memory—which is a far stronger notion of "equality" than simply having the same value. For example, two different String objects may contain the same sequence of characters—and so be "equal" to each other, in that they have the same value—and yet be different, distinct objects stored at different locations in memory. In the case of String objects (for example,) that distinction may be rather important, since String objects are mutable. Two different String objects that are equal one moment (have the same value) may not be equal the next—if one or both of them should happen to have any characters changed.

The default meaning of the binary message selector #= is that it tests the message receiver against the message argument for equality of value (which is a weaker equality test than the one performed by the #== operator.) The default meaning of the binary message selector #~= is that it tests the message receiver against the message argument for non-equality of value (which is a weaker equality test than the one performed by the #~~ operator.) It is usually best to use #= instead of #==, and #~= instead of #~~.

Since, generally speaking, binary message selectors are not operators, the Smalltalk compiler does not know the semantics of a binary message selector. The compiler cannot simply assume that the selector #+ has the semantics of addition, or that the selector #* has the semantics of multiplication. Therefore, the compiler cannot apply operator precedence rules to binary message selectors—they aren't operators. So the compiler parses expressions and emits code so that binary message sends are evaluated form left to right in all cases (unless parens are used to create a nested expression requiring a different order of expression evaluation,) as demonstrated by the following examples:

7 + 3 * 2 ...evaluates to 20, not 42
6 - 2 / 4 ...evaluates to 1, not the Fraction 11/2
12 - (2 * 5) ...evaluates to 2 (parentheses can be used to change the order in which messages are sent)

Finally, it should be noted that a method with any binary message selector can be added to any class—including any class in the standard library, such as String, Collection or Object. For example, any class can have a method whose selector is #+ (a plus sign,) or #* (an asterisk,) or #< (a left angle bracket.) Although the standard Smalltalk concatenation message selector is a comma (#,), a programmer can easily add a method to SequenceableCollection with the selector #+ (plus sign) that also does concatenation, so that Strings (or any other SequenceableCollections, such as Arrays) could then be concatenated using #+, in addition to using #, (comma):

'hello' + ' world' => 'hello world' (assumes addition of #+ method to SequenceableCollection)
#(1 2) + #(3) => #(1 2 3) (assumes addition of #+ method to SequenceableCollection)

Were selectors such as #+, #<= or $| operators, the general ability to define methods with such selectors in any class would constitute what in other languages is called "operator overloading." Although the effect is the same, it would not be correct to apply that term to the syntax of Smalltalk, because selectors such as #+, #* and #= are not operators! To put it another way: Programmer-definable binary message selectors give Smalltalk all the benefits of operator overloading, without actually having operators (execpt as noted above.)

Unary Messages: [Production rule: 45] A unary message has no message arguments. The selector of a unary message is syntactically an identifier. As is the case for all message sends, a unary message is sent (syntactically) by writing the message after the message receiver—with only whitespace as separation, without any other syntactic decoration (i.e., no periods or any other punctuation is used between the receiver and the message.)

Although it has no message arguments, a unary message send has one operand: the message receiver. The following are some examples of sending unary messages to receivers:

1 negated=> -1
0.5 reciprocal=> 2.0
1.9 rounded=> 2
9 sqrt=> 3
'hello' size=> 5
'72' asNumber=> 72
72 printString=> '72'
(3 > 4) not=> true
Object superclass=> nil "The class Object is a root class that has no superclass"
#(2 $y 3.14d) class=> Array
#(2 $y 3.14d) copy=> #(2 $y 3.14d) "Answers a copy"

Keyword Messages: [Production rule: 52] A keyword message has one or more message arguments. The selector of a keyword message is syntactically a sequence of one or more keywords; a keyword is an identifier followed by a colon character, without any whitespace in between.

There is one message argument per keyword, and one keyword per message argument. A keyword message begins with the first keyword of its selector, which must be followed by the first message argument. That pattern is repeated for each keyword and message argument in the selector: The ith keyword is followed by the ith message argument, such that the message arguments are interleaved in between the keywords, in order, until every pair of keyword and message argument has been written. Note that the colon character at the end of each keyword makes it easy to differentiate the keywords from the message argument that follows each keyword..

As is the case for all message sends, a keyword message is sent (syntactically) by writing the message after the message receiver—with only whitespace as separation, without any other syntactic decoration (i.e., no periods or any other punctuation is used between the receiver and the message.)

In addition to the message arguments, a keyword message send has one additional operand: the message receiver. The following are some examples of sending keyword messages to receivers:

12 quo: 5 Receiver = 12
Selector = #quo:
Argument = 5
"12 div 5 (integer division)"
=> 2
12 rem: 7 Receiver = 12
Selector = #rem:
Argument = 7
"12 mod 7"
=> 5
5 bitAnd: 2 Receiver = 5
Selector = #bitAnd:
Argument = 2
"2r101 bitAnd: 2r10"
=> 0
5 bitOr: 2 Receiver = 5
Selector = #bitOr:
Argument = 2
"2r101 bitOr: 2r10"
=> 7
'hello' at: 1 Receiver = 'hello'
Selector = #at:
Argument = 1
=> $h
#(#xyz 'fred' 7.99s) at: 3 Receiver = #(#xyz 'fred' 7.99s)
Selector = #at:
Argument = 3
=> 7.99s
'abc' collect: [:char | char asUppercase] Receiver = 'abc'
Selector = #collect:
Argument = [:char | char asUppercase]
=> 'ABC'
#(1 2 3 4 5) select: [:n | n odd] Receiver = #(1 2 3 4 5)
Selector = #select:
Argument = [:n | n odd]
=> #(1 3 5)
aView displayOn: aWindow at: aPoint Receiver = aView
Selector = #displayOn:at:
Arguments = (aWindow, aPoint)
"A view is asked to display itself on a window at a point"
3 < 4 ifTrue: ['con', 'cat'] ifFalse: [3 + 4] Receiver = true
Selector = #ifTrue:ifFalse:
Arguments = (['con', 'cat'], [3 + 4])
=> 'concat'
3 > 4 ifTrue: ['con', 'cat'] ifFalse: [3 + 4] Receiver = false
Selector = #ifTrue:ifFalse:
Arguments = (['con', 'cat'], [3 + 4])
=> 7
DateAndTime
    year: 2008
    month: 9