Smalltalk Zero Syntax

Smalltalk Zero Syntax
Photo by Hannah Olinger / Unsplash

Strings
Numbers

blocks:
  [ ...code... ]
  [ -> return type | ...code... ]
  [ arg: type -> return type | ...code... ]
  [ arg1: type1, arg2: type2, ... -> return type | ...code... ]
  [ arg1: type1 = default1, arg2: type2 = default2, ... -> return type | ...code... ]

method:
  [ signature | -> return type | ...code... ]
  [ signature | arg types -> return type | ...code... ]

method-signature:
  [ signature | -> return type ]
  [ signature | arg types -> return type ]

closure:
  a block with bonus arguments that start with ~ to indicate they are closed over from the outside context

variables:
  my-var: a-type
  my-var: a-type = rcvr make-a-thing
  my-var = rcvr make-a-thing

null-type:
  ø

sub-expression:
  ( method-call )

array literal:
  ( a, )
  ( a, b, c, d, ... )
  ( type | )
  ( type | a, b, c, d, ... )
  ( a
    b
    c )
  ( type |
    a
    b
    c )

map literal:
  ( a: b, )
  ( a: b, c: d, e: f )
  ( key type, value type | )
  ( key type, value type | a: b, c: d, e: f, ... )
  ( a: b
    c: d )
  ( key type, value type |
    a: b
    c: d )

tuple:
  ( &person, )
  ( &person, string, number, boolean, ... )

structure:
  ( name: string, )
  ( name: string, number-of-arms: int, ... )

class definition:
  person = (
    name: string,

    #scope: package
    number-of-arms: int,

    #scope: private
    height: distance, )

parameteric class definition:
  person = (
    @of: (arms-class: class,)
    nuber-of-arms: arms-class )

class implementing a trait definition:
  person = (
    #traits: (must-have-name,)
    name: string )

trait definition:
  writable-t = (
    #methods: (
      [ writeable write: data | writable-t, array -> ø ], ) )

parametric trait definition:
  writable-t = (
    @of: (buffer-class: class,)
    #methods: (
      [ writeable write: data | writable-t, array -> ø ], ) )

enum:
  directions = ( uint | north = 1, east, south, west )

unions:
  class-a + class-b

combined classes:
  combined-class = class-a * class-b

message sends:
  jane make-some-noise
  jane meet: sam
  jane meet: sam at: the-park
  jane + sam
  !jane

message chaining:
  name = 'World'
  stdout
    print: 'Hello ',
    print: name,
    newline

object declaration:
  foo = (a, b, c: string)
  
  lots-of-foo: foo = (
    {a:'a1',b:'b1',c:'c1'}
    {a:'a2',b:'b2',c:'c2'}
    {a:'a3',b:'b3',c:'c3'})
    
  lots-of-foo = (foo|
    {a:'a1',b:'b1',c:'c1'}
    {a:'a2',b:'b2',c:'c2'}
    {a:'a3',b:'b3',c:'c3'})

slices:
  people[start:stop]
  people[start:]
  people[:stop]
  people[start::length]
  people[::length]

iteration:
  people do: [ person | stdout print: person ]

returns:
  1 == 2 then: [ ^ ]
  1 == 1 then: [ ^2 ]
  ^the-answer

maybe:
  jane? = people find: [ person | person name = 'Jane' ]
  jane? then: [ jane | stdout print: jane ],
        else: [ stdout print: 'Jane missing' ]

references:
  jane := person new
  bob: &person
  bob? else: [ stdout print: 'No bob yet!' ]
  bob? assign: (db select: "select * from people where name = 'Bob'")
  bob? then: [ bob | stdout print: bob ],
       else: [ stdout print: 'Bob not found' ]

deferred statements:
  db := database-session new
  db open: 'people.db'
  ... database close: db
  
  jane? = db select-one: "select * from people where name = 'Jane'"
  jane = jane? else: [ ^ ]
  jane height: jane height + 1 inch

closure chaining:
  find-person =
    [ db: &database session, person-name: string |
      db select-one: 'select * from people where name = ?1'
               bind: (person-name,) ]

  print-person = [ &person | stdout print: person ]
  print-missing = [ stdout print: 'Not found' ]
  chain = find-person + print-person - print-missing

  db := database-session new
  db open: 'people.db'
  ... database close: db
  
  chain evaluate: ( db, 'Bob' )

scoping classes and methods:
  #scope public
  #scope package
  #scope private