Features of Hush

Hush aims to be a pure object-oriented language with a clean syntax. It supports the following features:

Natural method names

Objects respond to method calls. A method call expression has the receiver on the left and the method name with arguments following.

Method names contain space separated words beginning with a lower case letter. To include arguments in the method call, you place a colon ':' after one of the words:

    x = 2           -- set x to the object 2
    x sqrt          -- method call "sqrt" to x
    x pow:0.5       -- method call "pow:" to x with argument 0.5
    x mulBy:2       -- method call "mulBy:" to x with argument 2

Method calls can be as many words as you want, with arguments given in any place:

    canvas put pixel x:1 y:2 col:3      -- canvas is sent "put,pixel,x:y:col:"
                                        -- with arguments 1 and 2 and 3

    foo bar baz:1 and car:2             -- foo is sent "bar,baz:and,car:"
                                        -- with arguments 1 and 2

Operators are just method calls with 1 argument and you can define you own methods for operators just like normal. Operators also have an associated binding power and associativity so that standard mathematical expressions give the expected results:

    a = b * c + d / e       -- equivalent to (b * c) + (d / e)

Enumerations and constants

Enumerations and constants are supported in Hush. They both take the same form as a class name then a forward tick then a lower case name:

    Math'pi                 -- constant pi
    State'idle              -- enumeration

Defining enumerations and constants is very easy:

    class Math
        constant pi = 3.1415926

    class State
        enumerate idle, forward, reverse

Hidden methods and classes

Methods can be hidden from other classes with the 'hidden' keyword. These methods can only be accessed by a method call from an object of the same class.

    class MyClass
        hidden myOwnMethod is
            ...

Inner classes can also be hidden:

    class MyOuterClass
        hidden class MyInnerClass
            ...

Mixin inheritance

Inheritance takes on a more general form with the use of a special member object. You can think of an object as a linked list of instances, with a method call beginning at the leaf and propagating to the root until a match is found. This inheritance chain is fully dynamic and you can change the base of an object at run-time.

    class MyClass is BaseClass
        ...
        changeBaseTo:newBase is
            base = newBase

Dynamic bases also permit the use of mixin classes: wrapper classes which add functionality to an object. Say you wrote a general sort method which could operate on any object which responded to "at:" and "at:put:" (an Array for example). Then you could add this new sort method to any object just by wrapping or mixing the instance.

    class Sort is Object
        single < > obj is              -- mixin operator
            ^ new base:obj                 -- wrap object in new Sort instance

        sort is                            -- general sort method
            ...
            
    class SomeProgram
        someMethod is
            Sort < > Array lo:1 up:10  -- mixin Sort to Array instance
            ...

Short circuit scoping

The two short circuit operators 'andThen' and 'orElse' behave correctly when it comes to local variable creation. Only the variables guaranteed to be initialised are accessible within the respective blocks of a conditional expression:

    if (x = 1 < 2) andThen (y = 2 > 3) then
        -- both x and y are valid here

    else
        -- only x is valid here because of short circuit behaviour

    -- neither x or y are valid outside the block

Of course, if the variables are created outside the block then they are accessible everywhere.

Exception handling

Any expression can be followed by an 'except' block:

    file = File open:"foo.txt"
    except FileIOError do
        "can't open file foo.txt" println

You can use the 'do' expression to provide an exception handler for a block of expressions:

    do
        file = File open:"foo.txt"
        data = file read bytes:42
        file close
    except FileIOError do
        "there was an error" println

Any object can be raised as an exception with the 'raise' keyword. You can also store the raised object to use later:

    do
        foo bar
        raise 123
    except e is Int do
        e println

Unbound methods

An unbound method is a method call without an object to receive that call. This method can be stored for later application to one or more objects. To create an unbound method, just place a hash symbol '#' in place of the receiver:

    meth = # println
    meth apply to:"hello world"

This code will first create a method call 'println' and store it in the object 'meth'. Then the method call is applied to the string "hello world". The end result is that "hello world" is sent the method call 'println' and it prints itself out.

Unbound methods provide a neat way of applying the same method call to a list or array in a similar fashion to functional mapping of lists:

    -- pictures is a list of objects which can be draw to a Canvas
    canvas = Canvas width:320 height:240
    meth = # draw to:canvas
    pictures apply:meth

Closures

A closure is created with square brackets, and evaluated with the 'evaluate' method call:

    closure = ["hello world"] println
    closure evaluate

Closures can also take arguments:

    sq = [:x ^ x*x]
    result = sq evaluate with:3
    result println

In this example, 'x' is the argument and is bound to the value '3' when the closure is evaluated. The result will then print the value '9'.


Home page