Strongtalk
Language

Typing Smalltalk without breaking it

Strongtalk's types are optional, written in angle brackets, and checked separately from running the program. Removing every annotation leaves a program that behaves exactly the same.

Types in angle brackets

A type is written after the name it describes: arguments, temporaries, instance variables and block parameters. A method's result type follows a caret.

at: index <Int> put: value <E> ^<E>
    | old <E> |
    old := self at: index.
    ...

Untyped code is simply dynamic: the checker trusts it. You can add types one method at a time, so a large untyped program can be typed gradually.

Protocols, not classes

In most typed object-oriented languages a type is a class. Strongtalk instead separated the two: a type describes the messages an object understands (its protocol), and an object conforms to it by understanding those messages, whatever its class. That fits Smalltalk, where unrelated classes often implement the same messages. Int, Str and Bool are such types.

Self, Instance and unions

WriteMeans
<Self>the receiver's own type, so subclasses get the right result type for free
<Instance>on the class side, an instance of this class (new ^<Instance>)
<Int | Str>either type
<[Int, ^Bool]>a block taking an Int and answering a Boolean
<Array[Str]>a generic class applied to a type
guaranteed <Int> exprtell the checker to trust that expr is an Int

Generics

Collections are generic. A class declares its type parameters with Generic forAll::

Delta define: #OrderedCollection as: (
    Generic forAll: 'E' body: (
        (Class subclassOf: 'SequenceableCollection[E]'
               instanceVariables: 'array <Array[E]> firstIndex <Int> lastIndex <Int>')))

The original also let methods describe how their result type depends on their arguments, with inference clauses written in {where ...}; that is how Int + Int could be an Int while Int + Float is a Float.

What the checker reports

| n <Int> |
n := 'seven'.          "assigning String to 'n', which is declared Integer"
n squared: 3           "Integer does not understand #squared:"

(BankAccount owner: 'Ada') deposit: 'lots'
                       "argument 1 of #deposit:: String is not a Integer"

None of this stops the program from running. In Strongtalk, as in every Smalltalk, the run-time system catches real errors with doesNotUnderstand:. The checker's job is to tell you about them early.

Try it

Open the environment, choose Demos › Type checking tour, put the cursor in an example and press Typecheck.

The type checker in this edition is simpler than the original: it follows the class hierarchy rather than matching protocols structurally, and it uses built-in rules for arithmetic instead of {where ...} clauses.