The Kawa Scheme Language

The Kawa Scheme Language

Eval and Environments

eval expression [environment]

eval evaluates expression in the environment indicated by environment.

The default for environment is the result of (interaction-environment).

null-environment version

This procedure returns an environment that contains no variable bindings, but contains (syntactic) bindings for all the syntactic keywords.

The effect of assigning to a variable in this environment (such as let) is undefined.

scheme-report-environment version

The version must be an exact non-negative inetger corresponding to a version of one of the Revisedversion Reports on Scheme. The procedure returns an environment that contains exactly the set of bindings specified in the corresponding report.

This implementation supports version that is 4 or 5.

The effect of assigning to a variable in this environment (such as car) is undefined.

interaction-environment

This procedure return an environment that contains implementation-defined bindings, as well as top-level user bindings.

environment-bound? environment symbol

Return true #t if there is a binding for symbol in environment; otherwise returns #f.

fluid-let ((variable init) ...) body ...

Evaluate the init expressions. Then modify the dynamic bindings for the variables to the values of the init expressions, and evaluate the body expressions. Return the result of the last expression in body. Before returning, restore the original bindings. The temporary bindings are only visible in the current thread, and its descendent threads.

base-uri [node]

If node is specified, returns the base-URI property of the node. If the node does not have the base-URI property, returns #f. (The XQuery version returns the empty sequence in that case.)

In the zero-argument case, returns the "base URI" of the current context. By default the base URI is the current working directory (as a URL). While a source file is loaded, the base URI is temporarily set to the URL of the document.

load path

The path can be an (absolute) URL or a filename of a source file, which is read and evaluated line-by-line. The path can also be a fully-qualified class name. (Mostly acts like the -f command-line option, but with different error handling.) Since load is a run-time function it doesn't know about the enclosing lexical environment, and the latter can't know about definitions introduced by load. For those reasons it is highly recommended that you use instead use require or include.

load-relative path

Same as load, except that path is a URI that is relative to the context's current base URI. Since load-relative is a run-time function it doesn't work well with Kawa's orientation towards compilation. It is highly recommended that you use instead use include-relative.

Locations

A location is a place where a value can be stored. An lvalue is an expression that refers to a location. (The name "lvalue" refers to the fact that the left operand of set! is an lvalue.) The only kind of lvalue in standard Scheme is a variable. Kawa also allows computed lvalues. These are procedure calls used in "lvalue context", such as the left operand of set!.

You can only use procedures that have an associated setter. In that case, (set! (f arg ...) value) is equivalent to ((setter f) arg ... value) Currently, only a few procedures have associated setters, and only builtin procedures written in Java can have setters.

For example:

(set! (car x) 10)

is equivalent to:

((setter car) x 10)

which is equivalent to:

(set-car! x 10)

setter procedure

Gets the "setter procedure" associated with a "getter procedure". Equivalent to (procedure-property procedure 'setter). By convention, a setter procedure takes the same parameters as the "getter" procedure, plus an extra parameter that is the new value to be stored in the location specified by the parameters. The expectation is that following ((setter proc) args ... value) then the value of (proc args ...) will be value.

The setter of setter can be used to set the setter property. For example the Scheme prologue effectively does the following:

(set! (setter vector-set) vector-set!)

Kawa also gives you access to locations as first-class values:

location lvalue

Returns a location object for the given lvalue. You can get its value (by applying it, as if it were a procedure), and you can set its value (by using set! on the application). The lvalue can be a local or global variable, or a procedure call using a procedure that has a setter.

(define x 100)
(define lx (location x))
(set! (lx) (cons 1 2)) ;; set x to (1 . 2)
(lx)  ;; returns (1 . 2)
(define lc (location (car x)))
(set! (lc) (+ 10 (lc)))
;; x is now (11 . 2)

define-alias variable lvalue

Define variable as an alias for lvalue. In other words, makes it so that (location variable) is equivalent to (location lvalue). This works both top-level and inside a function.

define-private-alias variable lvalue

Same as define-alias, but the variable is local to the current module.

Some people might find it helpful to think of a location as a settable thunk. Others may find it useful to think of the location syntax as similar to the C ‘&’ operator; for the ‘*’ indirection operator, Kawa uses procedure application.

You can use define-alias to define a shorter type synonym, similar to Java's import TypeName (single-type-import) declaration:

(define-alias StrBuf java.lang.StringBuffer)

Parameter objects

A parameter object is a procedure that is bound to a location, and may optionally have a conversion procedure. The procedure accepts zero or one argument. When the procedure is called with zero arguments, the content of the location is returned. On a call with one argument the content of the location is updated with the result of applying the parameter object's conversion procedure to the argument.

Parameter objects are created with the make-parameter procedure which takes one or two arguments. The second argument is a one argument conversion procedure. If only one argument is passed to make-parameter the identity function is used as a conversion procedure. A new location is created and asociated with the parameter object. The initial content of the location is the result of applying the conversion procedure to the first argument of make-parameter.

Note that the conversion procedure can be used for guaranteeing the type of the parameter object's binding and/or to perform some conversion of the value.

The parameterize special form, when given a parameter object and a value, binds the parameter object to a new location for the dynamic extent of its body. The initial content of the location is the result of applying the parameter object's conversion procedure to the value. The parameterize special form behaves analogously to let when binding more than one parameter object (that is the order of evaluation is unspecified and the new bindings are only visible in the body of the parameterize special form).

When a new thread is created using future or runnable then the child thread inherits initial values from its parent. Once the child is running, changing the value in the child does not affect the value in the parent or vice versa. (In the past this was not the case: The child would share a location with the parent except within a parameterize. This was changed to avoid unsafe and inefficient coupling between threads.)

Note that parameterize and fluid-let have similar binding and sharing behavior. The difference is that fluid-let modifies locations accessed by name, while make-parameter and parameterize create anonymous locations accessed by calling a parameter procedure.

The R5RS procedures current-input-port and current-output-port are parameter objects.

make-parameter init [converter]

Returns a new parameter object which is bound in the global dynamic environment to a location containing the value returned by the call (converter init). If the conversion procedure converter is not specified the identity function is used instead.

The parameter object is a procedure which accepts zero or one argument. When it is called with no argument, the content of the location bound to this parameter object in the current dynamic environment is returned. When it is called with one argument, the content of the location is set to the result of the call (converter arg), where arg is the argument passed to the parameter object, and an unspecified value is returned.

(define radix
  (make-parameter 10))

(define write-shared
  (make-parameter
    #f
    (lambda (x)
      (if (boolean? x)
          x
          (error "only booleans are accepted by write-shared")))))

(radix)           ⇒  10
(radix 2)
(radix)           ⇒  2
(write-shared 0)  gives an error

(define prompt
  (make-parameter
    123
    (lambda (x)
      (if (string? x)
          x
          (with-output-to-string (lambda () (write x)))))))

(prompt)       ⇒  "123"
(prompt ">")
(prompt)       ⇒  ">"

parameterize ((expr1 expr2) ...) body

The expressions expr1 and expr2 are evaluated in an unspecified order. The value of the expr1 expressions must be parameter objects. For each expr1 expression and in an unspecified order, the local dynamic environment is extended with a binding of the parameter object expr1 to a new location whose content is the result of the call (converter val), where val is the value of expr2 and converter is the conversion procedure of the parameter object. The resulting dynamic environment is then used for the evaluation of body (which refers to the R5RS grammar nonterminal of that name). The result(s) of the parameterize form are the result(s) of the body.

(radix)                                              ⇒  2
(parameterize ((radix 16)) (radix))                  ⇒  16
(radix)                                              ⇒  2

(define (f n) (number->string n (radix)))

(f 10)                                               ⇒  "1010"
(parameterize ((radix 8)) (f 10))                    ⇒  "12"
(parameterize ((radix 8) (prompt (f 10))) (prompt))  ⇒  "1010"