Classical semantics

Before introducing the inconsistent properties inherent in yesno, we consider the "classical" semantics of the language. Yesno is an object-oriented language and can be treated as any other object-oriented language. For programs which execute quickly, the inconsistent properties will not even be noticeable. It is only when we consider programs which either do not terminate, or are not known to terminate, that we will consider the inconsistent semantics in the language. Outside of those scenarios, they are scarcely visible.

Yesno is an object-oriented language, similar to popular languages such as Java or C++. Unlike Java or C++, but like other languages such as Smalltalk or Objective-C, yesno is totally dynamic, and is a meta-class system. By dynamic, we mean that classes can change over time. A class can be subclassed at run-time, or can have fields or methods added at run-time. By meta-class, we mean that classes are first-order objects, treated no differently than any other object. Every class is an instance of class Class, and in that sense Class is a meta-class.

To simplify the semantics of the language, we take the approach that everything is an object. Every datum is, of course, an object. Further, even control constructs are objects: if is an object. The language provides only 4 real constructs:

  1. Sending a message to an object. This is the backbone of yesno and constitutes nearly all computation. All expression evaluation, even simple arithmetic or looping, is done via message passing.
  2. Assigning a value to a variable. Variables are not typed in any way, but they are necessary for computation. After an expression is evaluated, the result can be stored in a variable.
  3. Constructing primitives. Primitives in this context mean: integers, booleans, strings and closures. Objects of these 4 types cannot be constructed in yesno itself, but must be already existant as part of the language. The interpreter, when parsing, will construct objects of these types from literals provided in the yesno source code.
  4. Return statements. This is a construct used to return values from closures.

All other constructs, such as class definitions or flow control, are done via message passing.

Creating new control constructs

Yesno has the object if predefined. if is an object of the class Conditional, and responds to the cond:then:else: message. Its first argument (corresponding to the cond: message part) is an expression which must evaluate to a boolean object. Its last two arguments (which correspond to the then: and else:) are expressions which must evaluate to closures. If the condition is true, then the first closure is evaluated (with no arguments); otherwise the second closure is evaluated (with no arguments).

The following is a brief example of using the if object:

a := 19.
b := 43.
if cond: (a < b)
    then: [ | b := b * 2. ]
    else: [ | b := 0. ].

This short program carries the intuitive semantics of assigning if-then-else constructs in other imperative languages.

However, this is the only control construct provided by yesno. Consider that we might want another control construct: the while loop. This can be defined as follows:

loopClosure := [ condition code |
    if cond: (condition do)
        then: [ | code do. loopClosure doWith: condition with: code. ]
        else: [ | ]. ].
Conditional addInstanceMessage: "cond:do:" withMethod: loopClosure.
while := if.

We have added another method to the Conditional class, one that could be used for a while construct. The closure evaluates its condition: if the condition is true, it executes the body of the loop, then recurses; otherwise, the loop terminates. The while loop can be used as in the following example:

b := 2.
a := 0.
while cond: [ | return a < 5. ] do: [ |
    b := b * 2.
    a := a + 1.

When this loop terminates, b will have a value of 64. Note that instead of a boolean expression for the condition, we used a closure. A closure is necessary in this case, because we need the expression to be evaluated every iteration through the loop.

Note that [: expr ] is shorthand notation for [ | return expr. ] and will be used from now on. That is, we can rewrite the loop above as:

while cond: [: a < 5 ] do: [ |
    b := b * 2.
    a := a + 1.

A linked list example

List := Object subclass.
List addInstanceMessage: "add:" withMethod: [ x |
    if cond: (self hasVariableSet: "head")
        then: [ | .tail add: x. ]
        else: [ | .head := x. .tail := List new. ]. ].
List addInstanceMessage: "show" withMethod: [:
    if cond: (self hasVariableSet: "head")
        then: [: .head show ++ " " ++ .tail show ]
        else: [: "" ] ].
v := List new.
v add: 7.
v add: 25.
v add: -3.
v add: "hello".
return v.

Next: inconsistent semantics