The evaluation strategy given in Lambda: Basic Procedure Creation describes how procedures are interpreted. Interpretation operates directly on expanded Scheme source code, recursively calling the evaluator to obtain the value of nested expressions.
Most procedures are compiled, however. This means that Guile has done some pre-computation on the procedure, to determine what it will need to do each time the procedure runs. Compiled procedures run faster than interpreted procedures.
Loading files is the normal way that compiled procedures come to being. If Guile sees that a file is uncompiled, or that its compiled file is out of date, it will attempt to compile the file when it is loaded, and save the result to disk. Procedures can be compiled at runtime as well. See Reading and Evaluating Scheme Code, for more information on runtime compilation.
Compiled procedures, also known as programs, respond to all
procedures that operate on procedures: you can pass a program to
procedure?
, procedure-name
, and so on (see Procedure Properties and Meta-information). In addition, there are a few more accessors for low-level
details on programs.
Most people won’t need to use the routines described in this section, but it’s good to have them documented. You’ll have to include the appropriate module first, though:
(use-modules (system vm program))
Returns #t
if obj is a compiled procedure, or #f
otherwise.
Returns the address of the program’s entry, as an integer. This address
is mostly useful to procedures in (system vm debug)
.
Return the number of free variables captured by this program.
Accessors for a program’s free variables. Some of the values captured are actually in variable “boxes”. See Variables and the VM, for more information.
Users must not modify the returned value unless they think they’re really clever.
Source location annotations for programs, along with their accessors.
Source location information propagates through the compiler and ends
up being serialized to the program’s metadata. This information is
keyed by the offset of the instruction pointer within the object code
of the program. Specifically, it is keyed on the ip
just
following an instruction, so that backtraces can find the source
location of a call that is in progress.
Accessors for a representation of the “arity” of a program.
The normal case is that a procedure has one arity. For example,
(lambda (x) x)
, takes one required argument, and that’s it. One
could access that number of required arguments via (arity:nreq
(program-arities (lambda (x) x)))
. Similarly, arity:nopt
gets
the number of optional arguments, and arity:rest?
returns a true
value if the procedure has a rest arg.
arity:kw
returns a list of (kw . idx)
pairs,
if the procedure has keyword arguments. The idx refers to the
idxth local variable; See Variables and the VM, for more
information. Finally arity:allow-other-keys?
returns a true
value if other keys are allowed. See Optional Arguments, for more
information.
So what about arity:start
and arity:end
, then? They
return the range of bytes in the program’s bytecode for which a given
arity is valid. You see, a procedure can actually have more than one
arity. The question, “what is a procedure’s arity” only really makes
sense at certain points in the program, delimited by these
arity:start
and arity:end
values.
Return an association list describing the arguments that program accepts, or
#f
if the information cannot be obtained.
The alist keys that are currently defined are ‘required’, ‘optional’, ‘keyword’, ‘allow-other-keys?’, and ‘rest’. For example:
(program-arguments-alist (lambda* (a b #:optional c #:key (d 1) #:rest e) #t)) ⇒ ((required . (a b)) (optional . (c)) (keyword . ((#:d . 4))) (allow-other-keys? . #f) (rest . d))
Return a representation of the arguments of program as a lambda
list, or #f
if this information is not available.
For example:
(program-lambda-list (lambda* (a b #:optional c #:key (d 1) #:rest e) #t)) ⇒