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 parameters 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.
Function objects are made with the function operator that takes an parameter list and a body and produces a function object.
function_literal
"function" space optional_function_name parameter_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.
Function objects can also be made with the def statement and record literals.
The parameter list can be open (each parameter name on its own line) or closed
(all parameter names on the same line, separated with a space).
The parameter names are bound to the arguments when the function is called. A parameter is a def that is initialized by the caller.
parameter_list
'(' parameter_list_filler ')'
'[' name optional_arity ']'
parameter_list_filler
""
parameter more_closed_parameters
indent parameter more_open_parameters outdent
parameter name optional_default
optional_default
""
space '|' space expression
more_closed_parameters
""
space parameter more_closed_parameters
more_open_parameters
""
linebreak parameter more_open_parameters
optional_arity
""
space number_literal
When the function is invoked, the arguments will be
assigned to these names and made available to the body. The
body can not modify the parameters, but can modify the values of the parameters if they are not stone. Parameters behave as def, not var. The number of
names in the parameter list determines the function's arity.
If the function is invoked with too many arguments, it disrupts.
If the function is invoked with too few parameters, the missing values will get null. The |default operator is allowed in parameter lists, replacing a null argument with a value from an expression.
There is an alternate form for functions that can receive all of their arguments as a single array.
A single parameter is bound to an array of arguments. See apply.
An optional arity can be provided that sets the maximum number of arguments and provide a value to the length function.
The default arity is null, meaning unlimited.
And then, the body, a string of statements wrapped in {}braces.
body
'{' statements disruption '}'
disruption
""
"disruption" statements
The return statement or the jump statement gives the return value. Implicit return (falling through the bottom) returns null.
The body can be followed by a disruption clause.
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.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 (parameters, def, or var), it is not able to access the outer names.
Functions are invoked with the ()paren suffix operator.
function_value
(parameter values)
The parens can contain zero thru four 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 arguments,
then there is a disruption.
call my_function(do re mi) # function invocation
intrinsic
subprogram
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
def statementuse statementvar statementA 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 subprogram 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 its right to use the outer version.
An inner function can see the scope of an outer function, but an outer function may not see into the scope of an inner function. A function can be both an inner function (to the function it is nested inside) and an outer function (of the functions nested inside of itself).
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 StatementThe 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 clause.
function (parameter_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 clause. The
disruption clause must end with 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 clause of the caller.
If there is no caller, then the actor halts.
A disruption clause may not contain an inner function, but it may call another function that has an inner function.
disruption
return plan_R()
A function can be used as a proxy for a record. If a function is called as
function
.name(parameter_value_a parameter_value_b)
then the function will be called as though it had been written as
function
("name"[parameter_value_a parameter_value_b])
The function will be called with the name of the method and an array of arguments. The result that is returned by the function will be given to the caller.
def my_record: {
a(value) (
"a-" & value
)
}
def pseudorecord(
name
parameter_values
) {
if function?(my_record[name])
return apply(my_record[name] parameter_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
function_operator
"'[]'"
''' infix_operator '''
The operators can be used as functions by wrapping the operators with 'single quote. These operator functions, or functinos, can be used like any other functions. They can be passed as arguments or stored in records and arrays.
So '+' is the binary add function that can be passed to the reduce function to make sums.
assign total: reduce(data '+')
'/\'(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 functino form:
'/\'and '\/'or '|'default.
The equal function can take two or three arguments. The first two arguments are values to be compared.
If all of the parameters are numbers, an optional tolerance argument 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 arguments are texts, and if the tolerance is true, then the comparison is case-insensitive.
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
The not equal function can take two or three arguments. The first two arguments are values to be compared. The optional third argument 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.
The get function takes two arguments. The first argument is an array, record, or text. The second argument is an element number or key. It returns an element, a property, or a character, or null.
There are several function types that are essential to Misty. They are
A callback function is a function that is used to deliver functional results from the future. A callback function has this signature:
function (value reason)
The value is the value of the operation if it was successful. The value is null if it is unsuccessful.
If the value is null, then the optional reason may include an error message or indication of the cause of the unsuccess.
Requestor functions take a callback.
The send statement can take a callback to receive replies.
A factory function is any function that returns a function. A requestor factory is a function that returns a requestor function. These standard functions are requestor factories:
functions returned by these intrinsic functions:
A requestor function encapsulates a unit of work, communicating the result thru a callback, allowing work to progress over many turns. A requestor function has this signature:
function requestor(callback value)
When the requestor function is finished, it calls the callback function with the result.
The optional value is some value that is used to determine the result.
A common usage is to send a message to some actor. When the reply eventually arrives, extract a result from the reply and pass it to the callback.
A requestor function can optionally return a cancel function.
A requestor function may optionally return a cancel function. When the cancel function is called, it attempts to stop the work of the requestor. A cancel function try to send a message to some actor informing it that the result is no longer needed. The purpose of cancel is to stop work that is no longer required. It is advisory. It is not an undo. It is not guaranteed, particularly in the case where cancel is called after some actor has completed its work.
Cancel is most effective with the requestor factories that are organizing the work.
A cancel function has this signature:
function cancel(reason abandon | true)
The reason is optional, but if included might be logged or propagated. If abandon is true, then simply stop the requested work. If abandon is false, then if possible, complete the assignment, perhaps by calling the callback with the reason.