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.

parameter list

parameter_list => parameter,

parameter => name parameter_constraint*

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

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 event 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.

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 strings. 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 string

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 not name has string then
    raise 'parameter'
fi

has string = expression

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

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

owns string

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 not name owns string then
    raise 'parameter'
fi

owns string = expression

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

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

can string

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

if name is not object or name[string] is not method then
    raise 'parameter'
fi

body

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

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.

The short form uses an expression instead of statements in curly braces. It returns the value of a single expression. The short form does not allow for an exception section.

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) { 
    return int(n / 2) 
}

define halve(n) { 
    return 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) { 
        return int(n / 2) 
    }

}

obj := {   
    halve(n) { 
        return 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(parameters...)

The method invocation pattern is used all functions.

object.method_name(parameters...)

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 strings, the names of the function's parameters. If the operand is not a function, it raises a 'type' exception.

Tail Recursion

If the last thing a function does is return the value of recursively calling itself, then there is an optimization that turns the recursive call into a loop. This can have huge savings in time and memory, making many recursive techniques practical. The optimization is not made if the function contains one or more inner functions that use any of its variables or if the function has an exception handler.

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

return factorial(5)   # the result is 120

If the last thing a function does is return the result of calling a function, or return null after calling a function (ignoring the function's result) then there is an optimization that short circuits the return path.

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 string operand. So

raise 'mishap'

raises a 'mishap' exception.

The operand can be any non-empty string. 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 string, 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

Proxy

A function can be used as a proxy for an object. If an object 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.