Args
must be a sequence (list, vector, or string) or a primitive Java array. (This is an extension over standard Scheme, which requires thatargs
be a list.) Calls theproc
(which must be a procedure), using as arguments thearg1
... values plus all the elements ofargs
.
Same as
(
, unlessproc
arg1
...)proc
and all the following arguments are compile-time constants. (That is: They are either constant, or symbols that have a global binding and no lexical binding.) In that case,proc
is applied to the arguments at compile-time, and the result replaces theconstant-fold
form. If the application raises an exception, a compile-time error is reported. For example:(constant-fold vector 'a 'b 'c)is equivalent to
(quote #(a b c))
, assumingvector
has not been re-bound.
You can associate arbitrary properties with any procedure.
Each property is a (key
, value
)-pair. Usually the
key
is a symbol, but it can be any object.
The system uses certain internal properties:
'name
refers to the name used when a procedure is printed;
'emacs-interactive
is used to implement Emacs interactive
specification;
'setter
is used to associate a setter
prcedure.
procedure-property
proc
key
[default
]
Get the property value corresponding to the given
key
. Ifproc
has no property with the givenkey
, returndefault
(which defaults to#f
) instead.
To change the print name of the standard +
procedure (probably
not a good idea!), you could do:
(set-procedure-property! + 'name 'PLUS)
Note this only changes the name property used for printing:
+ ⇒ #<procedure PLUS> (+ 2 3) ⇒ 5 (PLUS 3 4) ⇒ ERROR
As a matter of style, it is cleaner to use the define-procedure
form, as it is a more declarative interface.
define-procedure
name
[propname:
propvalue
] ...
method
...
Defines
name
as a compound procedure consisting of the specifiedmethod
s, with the associated properties. Applyingname
select the "best"method
, and applies that. See the following section on generic procedures.For example, the standard
vector-ref
procedure specifies one method, as well as thesetter
property:(define-procedure vector-ref setter: vector-set! (lambda ((vector :: <vector>) (k :: <int>)) (invoke vector 'get k)))
A generic procedure is a collection of method procedures. (A "method procedure" is not the same as a Java method, but the terms are related.) You can call a generic procedure, which selects the "closest match" among the component method procedures: I.e. the most specific method procedure that is applicable given the actual arguments.
Warning: The current implementation of selecting the "best" method is not reliable if there is more than one method. It can select depending on argument count, and it can select between primitive Java methods. However, selecting between different Scheme procedures based on parameter types should be considered experimental. The main problem is we can't determine the most specific method, so Kawa just tries the methods in order.
make-procedure
[keyword:
value
]...
method...
Create a generic procedure given the specific methods. You can also specify property values for the result.
The
keyword
s specify how the arguments are used. Amethod:
keyword is optional and specifies that the following argument is a method. Aname:
keyword specifies the name of the resulting procedure, when used for printing. Unrecognized keywords are used to set the procedure properties of the result.(define plus10 (make-procedure foo: 33 name: 'Plus10 method: (lambda (x y) (+ x y 10)) method: (lambda () 10)))
The formal arguments list of a lambda expression has two extendsions over standard Scheme: Kawa borrows the extended formal argument list of DSSSL, and Kawa allows you to declare the type of the parameter.
lambda-expression
::=
(lambda
formals
opt-return-type
body
)
return-type
::=
type
opt-return-type
::=
[[::
] type
]
where
formals
::=
(
formal-arguments
)
| rest-arg
You can of course also use the extended format in a define
:
(define (name
formal-arguments
) [rtype
]body
)
formal-arguments
::=
req-opt-args
(rest-key-args
| .
rest-arg
)
req-opt-args
::=
req-arg
... [#!optional
opt-arg
...]
rest-key-args
::=
[#!rest
rest-arg
] [#!key
key-arg
...]
req-arg
::=
variable
[::
type
] | (
variable
[[::
] type
] )
opt-arg
::=
arg-with-default
key-arg
::=
arg-with-default
arg-with-default
::=
variable
[::
type
]
| (
variable
[::
type
[initializer
] | initializer
[[::
] type
]] )
rest-arg
::=
variable
When the procedure is applied to a list of actual arguments, the formal and actual arguments are processed from left to right as follows:
The
req-arg
s are bound to actual arguments starting with the first actual argument. It shall be an error if there are fewer actual arguments then there arereq-arg
s.Next the
opt-arg
s are bound to remaining actual arguemnts. If there are fewer remaining actual arguments than there areopt-arg
s, then the remainingvariable
s are bound to the correspondinginitializer
, if one was specified, and otherwise to#f
. Theinitializer
is evaluated in an environment in which all the previous formal parameters have been bound.If there is a
rest-arg
, it is bound to a list of all the remaining actual arguments. These remaining actual arguments are also eligible to be bound to keyword arguments. If there is norest-arg
and there are nokey-arg
s, then it shall be an error if there are any remaining actual arguments.If
#!key
was specified, then there shall be an even number of remaining actual arguments. These are interpreted as a series of pairs, where the first member of each pair is a keyword specifying the argument name, and the second is the corresponding value. It shall be an error if the first member of a pair is not a keyword. It shall be an error if the argument name is not the same as a variable in akey-arg
s, unless there is arest-arg
. If the same argument name occurs more than once in the list of actual arguments, then the first value is used. If there is no actual argument for a particularkey-arg
, then the variable is bound to the correspondinginitializer
, if one was specified, and otherwise to#f
. Theinitializer
is evaluated in an environment in which all the previous formal parameters have been bound.
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.
If rtype
(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 specifies the
function's return type. It is syntactic sugar for
(as <TYPE> (begin BODY))
.
cut
slot-or-expr
slot-or-expr*
[
]<...>
where each
slot-or-expr
is either anexpression
or the literal symbol<>
.It is frequently necessary to specialize some of the parameters of a multi-parameter procedure. For example, from the binary operation
cons
one might want to obtain the unary operation(lambda (x) (cons 1 x))
. This specialization of parameters is also known as partial application, operator section, or projection. The macrocut
specializes some of the parameters of its first argument. The parameters that are to show up as formal variables of the result are indicated by the symbol<>
, pronouced as "slot". In addition, the symbol<...>
, pronounced as "rest-slot", matches all residual arguments of a variable argument procedure.A
cut
-expression is transformed into alambda expression
with as many formal variables as there are slots in the listslot-or-expr
*. The body of the resultinglambda expression
calls the firstslot-or-expr
with arguments from theslot-or-expr
* list in the order they appear. In case there is a rest-slot symbol, the resulting procedure is also of variable arity, and the body calls the firstslot-or-expr
with remaining arguments provided to the actual call of the specialized procedure.Here are some examples:
(cut cons (+ a 1) <>)
is the same as(lambda (x2) (cons (+ a 1) x2))
(cut list 1 <> 3 <> 5)
is the same as(lambda (x2 x4) (list 1 x2 3 x4 5))
(cut list)
is the same as(lambda () (list))
(cut list 1 <> 3 <...>)
is the same as(lambda (x2 . xs) (apply list 1 x2 3 xs))
The first argument can also be a slot, as one should expect in Scheme:
(cut <> a b)
is the same as(lambda (f) (f a b))
cute
slot-or-expr
slot-or-expr*
[
]<...>
The macro
cute
(a mnemonic for "cut with evaluated non-slots") is similar tocut
, but it evaluates the non-slot expressions at the time the procedure is specialized, not at the time the specialized procedure is called.For example
(cute cons (+ a 1) <>)
is the same as(let ((a1 (+ a 1))) (lambda (x2) (cons a1 x2)))
As you see from comparing this example with the first example above, the
cute
-variant will evaluate(+ a 1)
once, while thecut
-variant will evaluate it during every invocation of the resulting procedure.