About Smalltalk
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.
- 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.
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.
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.
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.)
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:
- 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.
- 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.)
- 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.
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.
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.
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.)
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.)
- 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.
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].
- 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.
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.
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.
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:
- Assignment of a value to a variable;
- Returning a value from a method (function);
- Testing whether two objects have the same identity;
- 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 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.
- 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:
- An identifier ("identifier symbol syntax");
- A sequence of one or more keywords ("keyword symbol syntax");
- A sequence with any one, or with any two, characters from the set of binary selector characters ("binary selector
syntax");
- 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" |
- 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 variables—assignment 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:
- The object to which the message is sent—also known as the message receiver;
- The selector of the message—which is the unique identifier ("name") of the message;
- 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
| |