Misty Programming Language:

Functions

A function is a parameterized sequence of statements. Functions are first-class values. Functions can be stored in variables, objects, and arrays, functions can be passed as parameters to other functions, and functions can be returned as the results of functions. Functions are immutable. A functions introduces a new scope: variables defined in a functions are not visible outside of the function.

function Operators

function => 'function' [name] '(' parameter_list ')' body

The function operator combines four sets of information in order to produce a function value: The operator, the optional name, the parameter list, and the body.

function name(parameter_list) {
    statements
}

name

The name is optional. If the name is omitted, then the function is anonymous. If the name is specified, statements in the body can use the name to call the function recursively. The name also provides the function value with a name property.

parameter list

parameter_list => parameter,

parameter => name parameter_constraint*

parameter_constraint
=> 'default' expression
=> 'length' expression
=> 'arity' expression
=> 'is' ['not'] characteristic
=> 'has' ['not'] text ['=' expression]
=> 'owns' ['not'] text ['=' expression]
=> 'can' ['not'] text

The parameter_list is a list of zero or more names, separated by commas. When the function is invoked, the parameter values will be assigned to these names and made available to the body. The body can modify the parameters as variables. The number of names in the parameter_list determines the maximum number of parameters to the function. If the function is invoked with too few parameters, the missing values will be replaced by null. If the function is invoked with too many parameters, an 'arity' exception is raised. The function's exception handler will not get an opportunity to handle the exception because the exception is raised before the function gets called

Constraints may be added to parameters than can check types or supply default values. The contraints get run in order immediately after the function is called, but before the body executes. If the constraints raise an exception, the function's exception handler may handle it.

The function value's parameters property is a fixed array of parameter names.

default expression

The default constraint assigns the value of the expression to the parameter if the parameter's value would otherwise be null. It expands to

if name = null
    name : expression
fi

length expression

The length constraint tests that the parameter has a particular length. This is usually used with arrays and texts. It expands to

if length name <> expression then
    raise 'parameter'
fi

arity expression

The arity constraint tests that the parameter is a function that takes a certain number of parameters. It expands to

if name is not method then
    raise 'parameter'
fi
if arity name <> expression then
    raise 'arity'
fi

is characteristic

The is constraint tests that the parameter has a particular characteristic. (See the is operator.) It expands to

if name is not characteristic then
    raise 'parameter'
fi

is not characteristic

The is not constraint tests that the parameter has not a particular characteristic. It expands to

if name is characteristic then
    raise 'parameter'
fi

has text

The has constraint tests that the parameter is an object containing (possibly inheriting) a member with a particular name. (See the has operator.) It expands to

if name has not text then
    raise 'parameter'
fi

has not text

The has not constraint tests that the parameter is an object containing (possibly inheriting) a member with a particular name. (See the has operator.) It expands to

if name has text then
    raise 'parameter'
fi

has text = expression

The has constraint can also test that the member has a particular value. It expands to

if name has not text or name[text] <> expression then
    raise 'parameter'
fi

owns text

The owns constraint tests that the parameter is an object containing (but not inheriting) a member with a particular name. (See the owns operator.) It expands to

if name owns not text then
    raise 'parameter'
fi

owns not text

The owns constraint tests that the parameter is an object containing (but not inheriting) a member with a particular name. (See the owns operator.) It expands to

if name owns text then
    raise 'parameter'
fi

owns text = expression

The owns constraint can also test that the member has a particular value. It expands to

if name owns not text or name[text] <> expression then
    raise 'parameter'
fi

can text

The can constraint tests that the parameter is an object containing a member with a particular name that can be called. It expands to

if name can not text then
    raise 'parameter'
fi

can not text

The can constraint tests that the parameter is an object containing a member with a particular name that can be called. It expands to

if name can text then
    raise 'parameter'
fi

body

body
=> '{' (statement | define_statement | variable_statement)* [exception_handler] '}'
=> '=>' expression

The body is a list of statements wrapped in curly braces. See Statements. The statements are executed when the function is invoked. If a function does not explicitly return or turn, it will return null by falling through the bottom.

The body can end with an exception section.

exception name
    statements

An exception section can handle exceptions raised in the body or by functions called from the body. See Exceptions below.

Short form

The short form uses an expression as a return value instead of a block statements in curly braces. The short form does not allow for an exception section. See Expressions.

function name(parameters) => expression

With the very short form, the function keyword is omitted and no function name is allowed.

(parameters) => expression

Context

The context of a function is the function in which it is made. An outer function provides the context for an inner function. The inner function has access to all of the outer function's variables, definitions, and parameters (except when the inner function reuses those names as its own variables, definitions, or parameters). The context (or lexical closure) remains available to the inner function even after the outer function has finished its invocation and returned.

Implied Functions

There is a shorter form of function creation that can be used in define statements (see Statements).

The next two statements both create a halve function. The only difference is that the second is more compact.

define halve : function halve(n) => int(n / 2)

define halve(n) => int(n / 2)

There is a shorter form of function creation that can be used in object literals (see Objects).

The next two statements have the same result, creating a halve function within an object. The only difference is that the second is more compact.

obj : {
    halve : function halve(n) => int(n / 2)
}

obj : {
    halve(n) => int(n / 2)
}

Invocation

There are two ways to execute or invoke functions. The more common way is to use the () (paren) suffix operator. There are two forms, function invocation form and method invocation form.

The function invocation pattern is used only with functions that do not use the $ operator.

function_value(arguments)

The method invocation pattern is used all functions.

object.method_name(arguments)

In both forms, the parens can contain zero or more expressions whose values will be passed to the function as parameters. Each value will be assigned to a named parameter. Parameters that do not have values will be initialized to null. If there are too many parameters, then a 'function' exception is raised.

my_function()    # function invocation

The other way to invoke a function is with the apply operator. The first operand is a function value, the second is an array of parameters, or null if there are no parameters, or any other value if there is just a single non-array, non-null parameter.

Methods and $

If a function uses the $ operator, then it is called a method. When a function is called with the method invocation pattern, $ is bound to the object. Otherwise, $ is bound to null.

The method invocation pattern includes a reference to the object that contains the method.

my_object.my_method()    # method invocation

The process for finding a method in an object is more complicated than the process of getting a value from an object. First, the call process is done on the object. Values which are not functions are skipped and the delegation chain may be explored more deeply. If the result does not yield a function, then the method name is matched to one of the built-in methods (see Objects). If that does not yield a function, then the search is repeated, this time for a default method. If that also does not yield a function, then a 'function' exception is raised.

The $ operator returns a reference to the object.

When a object reference is produced by the fix operator, assignment is not permitted to the object or any of its members through that reference. An exception is made when changing an immediate property of $ in a method that was invoked with the method invocation pattern. This allows public methods to continue to work with fixed references.

If a method is called with the function invocation pattern

my_function : my_object.my_method
my_function()    # function invocation

then a 'function' exception will be raised.

Function Operators

Functions and methods can be acted upon with these operators.

function apply parameters

The apply operator is an alternative way of invoking a function.

If parameters is null, then the function will be invoked with no parameters. If parameters is an array, then its values will be passed to the function as parameters. Otherwise, the function will be called with parameters as the single parameter.

If the function is a method, then a 'function' exception is raised. If function is an object, its apply method will be called with the parameters as its parameter. Otherwise, if the function is not a function, then a 'type' exception is raised..

It returns the result of the invocation of the function.

arity function

The arity operator returns the function's number of parameters. It works on functions and methods. If the operand is not a function, it raises a 'type' exception.

name function

The name operator returns the function's name. It works on functions and methods. If the operand is not a function, it raises a 'type' exception.

parameters function

The parameters operator returns a fixed array of texts, the names of the function's parameters. If the operand is not a function, it raises a 'type' exception.

Exceptions

Exceptions are interruptions to the normal flow of a program. They can be viewed in two parts: the raising of an exception, which interrupts the current operation, and the handling of an exception, which is the response to the raising. Exceptions can be raised by the raise statement. Exceptions can also be raised by the system itself.

raise Statement

The raise statement is like the return statement in that it stops the processing of the current function. However, instead of transferring control back to the calling function, control goes to an exception handler.

The raise statement takes a text operand. So

raise 'mishap'

raises a 'mishap' exception.

The operand can be any non-empty text. The Misty System itself raises these exceptions: 'arity', 'array', 'done', 'exception', 'fix', 'function', 'parameter', 'syntax', 'type'. If raise is given an operand that is not a non-empty text, then an 'exception' exception is raised. If it can be detected at compile time, it will cause a syntax error.

exception Handler

exception_handler => 'exception' exception_constant
(statement | define_statement | variable_statement)*

exception_constant => name

A function can have an optional exception handler.

function name(parameter_list) {
    statements
exception exception_constant
    statements
}

If a function does not have an exception handler, it is given one by default that acts like

exception exception_constant
    raise exception_constant

When a raise statement is executed in the main part of the function body, the exception type is stored in the exception_constant, and control goes to the statements of the exception handler. The exception handler must either return or raise. If it returns, then the exception situation is over and control returns to the calling function. If the exception handler raises, either by raising its exception variable or by raising a new exception or by taking an action that results in the raising of an exception, then execution of the current function is abandoned and control passes to the exception handler of the caller. If there is no caller, then what happens next depends on the run-time environment. It might

A typical exception handler will examine the exception object and determine if it can do some useful action or raise the exception so that it can be handled by a caller.

Example:

exception e
    if e
    case 'invalid' then
        return null
    case 'default' then
        return $.default_value();
    case 'type' then
        raise 'error'
    else
        raise e
    fi

Tail Recursion

If a function returns the result of calling another function, then a tail recursion optimization may be made. There are two possible optimizations, the t optimization and the ir optimization. Neither optimization will be performed on functions that contain explicit exception handlers.

If a function returns the result of calling itself, and if the function does not produce inner functions, then the t optimization will be performed. This converts the call into a jump, which can have a big improvement in the performance of recursive functions.

define factorial(n, s default 1) {
    if n > 1 then
        return factorial(n - 1, s then s * n else n)
    fi
    return s
}

return factorial(5)   # the result is 120

If the t optimization is not performed, then the ir optimization is considered. This optimization short-circuits the return path. This makes continuation passing style possible.

Proxy

A function can be used as a proxy for an object. If a function has exactly two parameters, name and arguments, then it may be called as an object's method. If the function is called as

function.name(argument1, argument2)

Then the function will be called as though it had been

function('name', [argument1, argument2])

The function will be called with the name of the method and an array of arguments as arguments. The result that is returned by the function will be returned to the caller.

If a function is called as a method and the function does not have the name and arguments parameters, then a 'type' exception will be raised. Functions do not have methods.