What is it?

A class is an object and method container. Classes support inheritance and operator overloading.

Table of Contents:

Constructing a Class

Classes begin with the class keyword followed by the class name, and its superclass in parentheses (if missing, Object is assumed). The following creates an empty class called Car:

class Car(Object)
{
}

Alternatively this class can be written without specifying Object as its superclass:

class Car
{
}

Constructor

A class may have a single constructor: init. The constructor is invoked when a user instantiates the class via the new keyword. This constructor is optional. If it does not exist, the superclass' constructor will be invoked during instantiation.

class Car
{
    (@) init
    {
        // call the superclass' init first
        super init
    }
}

The meta-class has its own constructor. This constructor is called only once upon class registration. The meta-class constructor is also named init, but uses (@@) to signify that it is a meta-method.

class Car
{
    (@@) init
    {
        @@carCount = 0
    }
}

Adding Objects

An empty class is pretty boring, so we want to add methods and objects to it. Instance-variables begin with @, and meta-variables begin with @@. meta-variables are shared between all instances of a class, while instance-variables are not. An instance-object or meta-object may have a reader or writer auto-created with it via the keywords: @r, @w, or both with "@rw":

class Car
{
    // create a reader and writer for the instance-variable speed
    @speed, @rw

    // create a meta-variable with no reader or writer
    @@carCount
}

Readers are simply the name of the variable, and writers are of the format: setVariableName:

car = new Car

// set speed using the auto-created writer
car setSpeed: 50

// read and output speed using the auto-created reader
io putLine: [car speed]
==> 50

Adding Methods

Instance-methods begin with the (@) symbol, and meta-methods begin with the (@@) symbol. Methods follow a named-argument syntax, where each argument is named:

Instance Method Syntax

(@) methodName
(@) methodNameWithArgument1: argument1 argument2: argument2 … argumentN: argumentN

Meta Method Syntax

(@@) methodName
(@@) methodNameWithArgument1: argument1 argument2: argument2 … argumentN: argumentN

Let’s add four new methods to the Car class, turnOn, turnOff, setGear:, and setWindow:value:

class Car
{
    // either true or false
    @isOn, @r

    // 0 through 5, 0 is reverse
    @gear, @r

    // an array indicating how far each window is up
    @windowStates, @r

    (@) init
    {
        @isOn = false
        @gear = 1
        // four windows, all windows are up
        @windowStates = [100, 100, 100, 100]
    }

    (@) turnOn
    {
        @isOn = true
    }

    (@) turnOff
    {
        @isOn = false
    }

    (@) setGear: value
    {
        @gear = value
    }

    (@) setWindow: number
            value: value
    {
        @windowStates[number] = value
    }
}

Specifiers

A class can be specified as abstract, atomic, final or singleton:

  • Abstract means the class cannot be instantiated via the new keyword. A class that inherits from it, and that is not abstract, must be instantiated instead.

  • Atomic means that the class is always copied amd passed to methods by value.

  • Final means that objects and methods cannot be added to the class. It also means that no class can inherit from it.

  • Singleton means that the class can only be instantiated once via the new keyword. All base Taffy classes that are singleton provide a global instantiation of themselves, such as true, false, kernel, etc.

Specifiers are prepended, and separated by spaces, before the class name, such as:

abstract class AbstractClass {}
abstract final class AbstractAndFinalClass {}

Operator Overloading

An operator is overloaded by defining a function for it. Each operator has one of three ways of overloading it. Certain operators, such as and -, are applied to two objects: the left-hand-side, and the right-hand-side. So the method that overloads these operators must take an object. Other operators, such as + and !, have no right-hand-side.

For operators that have no right-hand-side:

  • #operator(value)

For operators that have a right-hand-side

  • #operator(value): rightHandSide

For prefix operators, or operators that are specified before an identifier:

  • #prefixOperator(value)

class BankAccount
{
    @balance

    (@) #operator(+=): value
    {
        @balance += value
        return @balance
    }

    (@) #operator(++)
    {
        @balance++
        return @balance
    }

    (@) #prefixOperator(!)
    {
        return "not implemented"
    }
}

b = new BankAccount
b += 100
==> 100

b++
==> 101

The following operators can be overloaded:

Prefix operators:

  • !

Operators that take no argument:

  • ++

  • --

Operators that take an argument:

  • +

  • +=

  • -

  • -=

  • /

  • /=

  • *

  • *=

  • ^

  • ^=

  • %

  • !

  • <

  • <=

  • >

  • >=

  • ~=

  • <<

  • <<=

  • >>

  • >>=

  • |

  • |=

  • &

  • &=

  • ^^

  • ^^=