A lambda
expression evaluates to a procedure.
The environment in effect when the lambda
expression was
evaluated is remembered as part of the procedure. When
the procedure is later called with some actual arguments,
the environment in which the lambda
expression was evaluated
will be extended by binding the variables in the formal
argument list to fresh locations, and the corresponding actual
argument values will be stored in those locations.
(A fresh location is one that is distinct from every previously
existing location.) Next, the expressions in the body of the
lambda expression will be evaluated
sequentially in the extended environment. The results of
the last expression in the body will be returned as the results
of the procedure call.
(lambda (x) (+ x x)) ⇒ a procedure
((lambda (x) (+ x x)) 4) ⇒ 8
(define reverse-subtract
(lambda (x y) (- y x)))
(reverse-subtract 7 10) ⇒ 3
(define add4
(let ((x 4))
(lambda (y) (+ x y))))
(add4 6) ⇒ 10
The formal arguments list of a lambda expression has some extensions over standard Scheme: Kawa borrows the extended formal argument list of DSSSL, and allows you to declare the type of the parameter. More generally, you can use patterns.
lambda-expression
::=
(lambda
formals
option-pair
* opt-return-type
body
)
opt-return-type
::=
[::
type
]
formals
::=
(
formal-arguments
)
| rest-arg
An opt-return-type
specifies the return type of the procedure:
The result of evaluating the body
is coerced to the specified type
.
Deprecated: If the first form of the function body is an unbound
identifier of the form <TYPE>
(that is the first character
is ‘<
’ and the last is ‘>
’), then that is another way to specify the
function’s return type.
See properties for
how to set and use an option-pair
.
The define
form has a short-hand that combines
a lambda definition with binding the lambda to a variable:
(define (name
formal-arguments
)opt-return-type
body
)
formal-arguments
::=
required-or-guard
* [#!optional
optional-arg
...] rest-key-args
rest-key-args
::=
[#!rest
rest-arg
] [#!key
key-arg
...] [guard
]
| [#!key
key-arg
...] [rest-parameter
] [guard
]
| .
rest-arg
When the procedure is applied to an argument list, the latter is matched against formal parameters. This may involve some complex rules and pattern matching.
required-or-guard
::=
required-arg
| guard
required-arg
::=
pattern
| (
pattern
::
type
)
The required-arg
s are matched against the
actual (pre-keyword) arguments in order,
starting with the first actual argument.
It is an error if there are fewer pre-keyword
arguments then there are required-arg
s.
While a pattern
is most commonly an identifier,
more complicated patterns are possible, thus more (or fewer)
variables may be bound than there are arguments.
Note a pattern
may include an opt-type-specifier
.
For example:
(define (isquare x::integer) (* x x))
In this case the actual argument is coerced to an integer
and then the result matched against the pattern x
.
This is how parameter types are specified.
The pattern
may be enclosed in parentheses for clarify
(just like for optional parameters), but in that case the
type specifier is required to avoid ambiguity.
optional-arg
::=
variable
opt-type-specifier
| (
pattern
opt-type-specifier
[initializer
[supplied-var
]])
supplied-var
::=
variable
Next the optional-arg
s are bound to remaining pre-keyword arguments.
If there are fewer remaining pre-keyword arguments than there are
optional-arg
s, then the remaining variable
s are bound
to the corresponding initializer
.
If no initializer
was specified, it defaults to #f
.
(TODO: If a type
is specified the default for initializer
is the default value of the type
.)
The initializer
is evaluated in an
environment in which all the previous formal parameters have been bound.
If a supplied-var
is specified, it has type boolean,
and is set to true if there was an actual corresponding argument,
and false if the initializer was evaluated.
key-arg
::=
variable
opt-type-specifier
| (
variable
opt-type-specifier
[initializer
[supplied-var
]] )
Keyword parameters follow #!key
.
For each variable
if there is an actual keyword parameter whose keyword matches variable
,
then variable
is bound to the corresponding value.
If there is no matching artual argument, then the initializer
is
evaluated and bound to the argument.
If initializer
is not specified, it defaults to #f
.
The initializer
is evaluated in an
environment in which all the previous formal parameters have been bound.
(define (fun x #!key (foo 1) (bar 2) (baz 3)) (list x foo bar baz)) (fun 9 baz: 10 foo: 11) ⇒ (9 11 2 10)
The following cause a match failure, unless there is a rest parameter:
There may not be extra non-keyword arguments (prefix or postfix) beyond those matched by required and optional parameters.
There may not be any duplicated keyword arguments.
All keyowrds in the actual argument list must match one of the keyword formal parameters.
It is not recommended to use both keyword parameters and a rest parameter that can match keyword arguments. Currently, the rest parameter will include any arguments that match the explicit keyword parameters, as well any that don’t, though this may change.
On the other hand, it is fine to have both keyword parameters and a rest parameter does not accept keywords. In that case the rest parameter will match any “postfix” arguments:
#|kawa:8|# (define (fun x #!key k1 k2 #!rest r) (format "x:~w k1:~w k2:~w r:~w" x k1 k2 r)) (fun 3 k2: 12 100 101) ⇒ x:3 k1:#f k2:12 r:(100 101)
The supplied-var
argument is as for optional arguments.
Performance note: Keyword parameters are implemented very efficiently and compactly when explicit in the code. The parameters are sorted by the compiler, and the actual keyword arguemnts at the call state are also sorted at compile-time. So keyword matching just requires a fast linear scan comparing the two sorted lists. This implementation is also very compact, compared to say a hash table.
If a type
is specified, the corresponding actual argument (or
the initializer
default value) is coerced to the specified type
.
In the function body, the parameter has the specified type.
A “rest parameter” matches any arguments not matched by other parameters. You can write it using any of the following ways:
In addition, if formals
is just a rest-arg
identifier,
or a formal-arguments
ends with .
(i.e. is a dotted list) that is equivalent to using rest-arg
#!rest
.
These forms are similar but differ in the type of the rest-arg
and whether keywords are allowed (as part of the rest-arg
):
If #!rest
rest-arg
is used with no type
specifier
(or a type
specifier of list
)
then rest-arg
is a list.
Keywords are not allowed if #!key
has been seen.
(For backward compatibility, it is allowed to have extra keywords
if #!rest
is followed by !key
.)
If there are any keywords, then rest-arg
is more specifically
an arglist
.
If #!rest
rest-arg
is used with type
specifier
that is a Java array (for example #!rest r::string[]
then rest-arg
has that type. Each argument must be compatible
with the element type of the array.
Keywords are not allowed (even if type
is object[]
).
The generated method will be compiled like a Java varargs methods if possible (i.e. no non-trivial patterns or keyword paremeters).
Using @
rest-arg
is equivalent to
#!rest
:
Keywords are not allowed; the type of rest-arg
::object[]rest-arg
is a Java array;
the method is compiled like a Java varargs method.
For @:
rest-arg
then rest-arg
is a vector,
specifically an argvector
. Keywords are allowed.
A guard
is evaluated when it appears in the formal
parameter list.
If it evaluates to false, then matching fails.
Guards can appears before or after required arguments,
or at the very end, after all other formal parameters.