Module /fusion/procedure
Operators for creating and manipulating procedures.
While Fusion (and Lisps in general) are commonly perceived as functional languages, they are built around procedures that may or may not operate in a functional manner. That is, Fusion procedures may or may not cause side effects, and the language carefully avoids the term "function" except when describing code known to be free of side effects.
Multiple Results
As expected, procedures accept arguments and return values. But you may not expect that a procedure can return multiple values. This feature is distinct from "returning a sequence of values": they are honest-to-goodness individual return values, not wrapped up in a collection object.
While use of multiple results should be rare, there are use cases where they
are worthwhile. For example, struct_zip and
struct_unzip are mirror procedures for
transforming between a struct and two lists (names and elements). Since the
whole point of struct_unzip is to build two lists, the most natural design
is to use multiple results, rather than a pair or list of values which
the caller must then destructure. Similarly, an integer division operator
could return the quotient and the remainder.
This feature is expressed by two forms: values returns zero or
more results, and let_values binds them to local names:
(values 1 true "thing") => 1
true
"thing"
(let_values
[((a b c) // The names to bind
(values 1 true "thing"))] // The multiple results
(list c b a))
=> ["thing", true, 1]
To define a procedure with multiple results, use values in tail position:
(define (struct_unzip s)
// The real implementation isn't arranged this way,
// but you get the point.
(let [(names ...),
(elements ...)]
(values names elements)))
Since struct_unzip returns two values, it must be called in a context that
expects two values. The values don't get spliced into place as multiple
arguments:
(struct_zip (struct_unzip {g:1,f:2,h:3}))
=>
ERROR: procedure argument expects 1 result but received 2
Results were:
[f, g, h]
[2, 1, 3]
And since the results are not a list or sexp, you can't use apply either:
(apply struct_zip (struct_unzip {g:1,f:2,h:3}))
=>
ERROR: procedure argument expects 1 result but received 2
Results were:
[f, g, h]
[2, 1, 3]
The only context that works is a let_values clause with the correct number
of bound identifiers:
(let_values
[((names elements)
(struct_unzip {g:1,f:2,h:3}))]
(struct_zip names elements)) => {f:2,g:1,h:3}
Exported Bindings
(always v)
Returns a procedure that accepts (and ignores) any number of arguments and
always returns v.
(apply proc arg ... sequence)
Calls the given proc with arguments that are the (optional) args prepended
to the elements of sequence. The proc is called in tail position.
(apply + [1, 2]) => 3
(apply + 10 11 (sexp 1 2)) => 24
(compose p1 p2)
Returns a procedure that first applies p2 to its (single) argument, and then
applies p1 to the (single) result.
(conjoin p1 p2)
Returns a single-argument predicate that is the conjunction of the
single-argument predicates p1 and p2 as with and, applying them left to
right until one returns an untruthy value.
In other words, the result is similar to using and to compose calls to the
predicates.
(curry_left proc arg ...+)
Returns a procedure based on proc and the given leftmost args.
(define prepend56
(curry_left append_m [5, 6]))
(prepend56) --> [5,6]
(prepend56 [1]) --> [5,6,1]
(define prepend567
(curry_left append_m [5, 6] [7]))
(prepend567 [1] [2,3]) --> [5,6,7,1,2,3]
(curry_right proc arg ...+)
Returns a procedure based on proc and the given rightmost args.
(define append56
(curry_right append_m [5, 6]))
(append56) --> [5,6]
(append56 [1]) --> [1,5,6]
(define append567
(curry_right append_m [5, 6] [7]))
(append567 [1] [2,3]) --> [1,2,3,5,6,7]
(disjoin p1 p2)
Returns a single-argument predicate that is the disjunction of the
single-argument predicates p1 and p2 as with or, applying them left to
right until one returns a truthy value.
In other words, the result is similar to using or to compose calls to the
predicates.
(identity v)
Returns v.
(is_procedure value)
Returns true when value is a procedure, false otherwise.
(lambda (arg ...) body ...+)
Returns a procedure. When invoked, the caller's arguments are bound to the
arg identifiers (the formal arguments) and the body is evaluated and
returned.
body may be one or more forms; the last form is in tail position and its
result is the result of the procedure invocation.
The Fusion runtime system may optimize procedure instantiation, so it is
unspecified whether one or more lambda expressions will return distinct
or identical objects for any evaluation.
(lambda rest_id body ...+)
This variant, which declares a single formal argument rather than a sequence
of them, returns a procedure that accepts any number of values, which are
collected into an immutable sexp and bound to the rest_id:
((lambda args args) 8 9 10) --> (8 9 10)
(let_values (((ident ...) expr) ...) body ...+)
Captures multiple results, creating local
bindings for the idents, with the body in scope. The exprs are evaluated
left-to-right, and must return as many values as there are corresponding
idents, which are then bound to those results. After the bindings are
installed the body is evaluated. body may be one or more forms; the last
form is in tail position and its result is the result of the entire expression.
(negate p)
Returns a single-argument procedure that applies not to the result of the
single-argument procedure p.
(thunk body ...+)
Returns a zero-argument procedure that evaluates the body forms.
Equivalent to (lambda () body ...).
(values value ...)
Produces multiple results, returning the zero
or more values. Usually used in conjuction with let_values to bind the
results to names.
(| arg ... | body ...+)
A more concise alternative to lambda.
The | (pipe) identifier is followed by zero or more argument identifiers,
then another pipe, then the body of the procedure.
The translation from | to lambda is straightforward:
(| x ... | body ...) -> (lambda (x ...) body ...)
(| | body ...) -> (lambda () body ...)
Note that (| | body) and (|| body) generate identical code, but they are
technically not the same syntactic form. The Ion grammar specifies that ||
is a single, two-character symbol, so the latter expression uses the form named
||, which creates a no-argument procedure.
(|| body ...+)
Returns a zero-argument procedure that evaluates the body forms.
Equivalent to (lambda () body ...).
See also |.