Misty Programming Language:Functions

A function is a parameterized expression or sequence of statements. Functions are first-class values. Functions can be stored in variables, records, and arrays; functions can be passed as inputs to other functions; functions can be returned as the results of functions. Functions can not be sent in messages to other processes. Function objects are immutable values. A function introduces a new scope: variables defined in a functions are not visible outside of the function, but will be visible to inner functions.

Making Functions

Function objects are made with the function operator that takes an input list and a body and produces a function object.

function_literal "function" space optional_function_name input_list space body ''' function_stuff

function_stuff function_operators optional_name input_list space body

optional_function_name "" name

A function may be given a name for recursion and documentation.

A function literal may not be placed in a do loop.

Functions can also be made with the def statement and record literals.

Input List

The input list can be open (each input name on its own line) or closed (all input names on the same line, separated with ,comma). The input names are bound to the input values when the function is called. An input is a def that is initialized by the caller. An input list can contain at most 4 names. If you need more than 4, consider using a single record or array instead.

input_list '(' input_list_filler ')'

input_list_filler "" input more_closed_inputs indent input more_open_inputs outdent

input name optional_default

optional_default "" space '|' space expression

more_closed_inputs "" ',' space input more_closed_inputs

more_open_inputs "" linebreak input more_open_inputs

An input list is a list of zero thru four names, separated by commas or stacked vertically. When the function is invoked, the input values will be assigned to these names and made available to the body. The body can not modify the inputs, but can modify the values of the inputs if they are not stone. Inputs behave as def, not var. The number of names in the input list determines the function's arity. If the function is invoked with too many input values, the excess input values are ignored.

If the function is invoked with too few inputs, the missing values will get null. The |default operator is allowed in input lists, replacing null input values with values.

Body

And then, the body. There are two forms: the expression form wrapped in parens, and the statement form wrapped in braces.

body paren_expression '{' statements disruption '}'

disruption "" "disruption" statements

The expression form is a single expression. The result of the expression is the return value.

The statement form includes a string of statements. The return statement gives the return value. Implicit return (falling through the bottom) is not allowed.

The statement form can also include preconditions, postconditions, and a disruption part.

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 inputs.The context (or lexical closure) remains available to the inner function even after the outer function has finished its invocation and returned. If an inner function redefines names that are already defined in the context (inputs, def, or ver), it is not able to access the outer names.

Invocation

Functions are invoked with the ()paren suffix operator.

function_value(input values)

The parens can contain zero thru four expressions whose values will be passed to the function as inputs. Each value will be assigned to a named input. Inputs that do not have values will be initialized to null. If there are too many input values, then there is a disruption.

call my_function(do, re, mi)    # function invocation

Scope

intrinsic

module

outer function

inner function

A function creates as scope. A scope is a set of names belonging to the particular invocation of a function. The names are declared by

A name in a function can only be declared once, and it must be declared before it is used.

In addition to the declared names, a function also has access to the names in the scopes of all of the functions it is nested in, including the misty module and the intrinsics. A function may use those outer names as if they were its own without declaration. Names that were declared with var may have there values replaced by the assign statement. A function may continue to enjoy access to the scope of an outer function even after the outer function has returned.

A function is not required to use any of the outer names. A function is free to reuse any of those outer names by declaration, but in doing so, it loses the right to use the outer version.

An inner function can see the scope of an outer function, but an outer function may not see the scope of an inner function.

Disrupts

Disrupts are interruptions to the normal flow of a program. They can be viewed in two parts: the disrupt, which interrupts the current operation, and the handling of the disruption. Disrupts should not be used for ordinary outcomes. Disrupts should only be used for emergencies or unexpected situations.

disrupt Statement

The disrupt 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 a disruption part. If there isn't ultimately a disruption part, then the actor halts.

It is sometimes necessary to log an explanation before a disrupt.

disruption

A function can have an optional disruption part.

function (input_list) {
    statements
disruption
    statements
}

If a function does not have a disruption part, it is given one by default that acts like

disruption
    disrupt

When a disrupt statement is executed in the main part of the function body, control goes to the statements of the disruption part. The disruption part must either return or disrupt. If it returns, then the disrupt situation is over and control returns to the calling function. If the disruption disrupts, then execution of the current function is abandoned and control passes to the disruption part of the caller. If there is no caller, then the actor disrupts.

A disruption part may not contain an inner function, but it may call another function that has an inner function.

Example:

disruption
    return plan_R()

Ongoing Return

If a function returns the calling of a function,

return a_function()

then an optimization may be made that converts the call into a parameterized jump. This enables an interesting set of recursive algorithms, avoiding memory exhaustion. This makes continuation passing style possible.

def factorial(integer, step | 1) (
    integer > 1
    then factorial(integer - 1, step * integer)    # This updates integer and step and loops
    else step
)

factorial(5)   # the result is 120

The optimization will not be performed on functions that contain explicit disruption parts or inner functions.

Proxy

A function can be used as a proxy for a record. If the function is called as

function.name(input_value0, input_value1)

then the function will be called as though it had been written as

function("name", [input_value0, input_value1])

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

Example:

def my_record: {
    a(value) (
        "a-" & value
    )
}

def pseudorecord(
    name
    input_values
) {
    if function?(my_record[name])
        return apply(my_record[name], input_values)
    fi
    log error: "something is wrong with" ≈ name
    disrupt
}

function?(pseudorecord) # true
stone?(pseudorecord)    # true
record?(pseudorecord)   # false
length(pseudorecord)    # 0
arity(pseudorecord)     # 2

pseudorecord.a("ok")    # "a-ok"
pseudorecord.b("ok")    # disrupt

Operator Functions

function_operator "/\" "\/" '|' '=' "<>" '<' "<=" '>' ">=" '&' "&&" '+' '-' '*' '/' '//' "[]"

The operators can be used as functions by prefixing the operators with 'apstrophe. These operator functions, or functinos, can be used like any other functions. They can be passed as input values or stored in records and arrays.

So '+ is the binary add function that can be passed to the reduce function to make sums.

Examples:

'/\(3, 4)   # null  and
'\/(3, 4)   # null  or
'|(3, 4)    # 3     default
'=(3, 4)    # false equal
'<>(3, 4)   # true  not equal
'<(3, 4)    # true  less
'<=(3, 4)   # true  less or equal
'>(3, 4)    # false greater
'>=(3, 4)   # false greater or equal
'&(3, 4)    # "34"  concat
'&&(3, 4)   # "3 4" concat with space
'+(3, 4)    # 7     add
'-(3, 4)    # -1    subtract
'*(3, 4)    # 12    multiply
'/(3, 4)    # 0.75  divide
'//(3, 4)    # 0     integer divide
'[](3, 4)   # null  get

Three are short circuiting in their operator form, but not in their operator function form: '/\and   '\/or   '|default.

'=equal

The equal function can take two or three input values. The first two input values are values to be compared. If all of the inputs are numbers, an optional tolerance input value may be included which must be a non-negative number. The result is true if the absolute value of the difference between the two numbers is less than or equal to the tolerance.

If the input values are texts, and if the tolerance is true, then the comparison is case-insensitive.

Examples:

def first: 12.3775
def second: 12.38
assign exactly: '=(first, second)         # exactly is false
assign sloppy: '=(first, second, 0.01)    # sloppy is true

def first: "vorpal"
def second: "VORPAL"
assign exactly: '=(first, second)         # exactly is false
assign sloppy: '=(first, second, true)    # sloppy is true

'<>not equal

The not equal function can take two or three input values. The first two input values are values to be compared. The optional third input value is a tolerance. When comparing numbers, the tolerance is a number that indicates the allowable difference. When comparing texts, the tolerance is a logical that if true ignores case.

'[]get

The get function takes two input values. The first input value is an array, record, or text. The second input value is an element number or key. It returns an element, a property, or a character, or null.

Callback

These builtin functions take a callback input value:

A callback function has this signature:

function  (value, reason)

The value is the value of the request if it was successful. The value is null if it was unsuccessful.

If the value is null, then the reason may include an error message or indication of the cause of the request.