Next: , Previous: SRFI-31, Up: SRFI Support


6.4.18 SRFI-39 - Parameters

This SRFI provides parameter objects, which implement dynamically bound locations for values. The functions below are available from

     (use-modules (srfi srfi-39))

A parameter object is a procedure. Called with no arguments it returns its value, called with one argument it sets the value.

     (define my-param (make-parameter 123))
     (my-param) => 123
     (my-param 456)
     (my-param) => 456

The parameterize special form establishes new locations for parameters, those new locations having effect within the dynamic scope of the parameterize body. Leaving restores the previous locations, or re-entering through a saved continuation will again use the new locations.

     (parameterize ((my-param 789))
       (my-param) => 789
       )
     (my-param) => 456

Parameters are like dynamically bound variables in other Lisp dialets. They allow an application to establish parameter settings (as the name suggests) just for the execution of a particular bit of code, restoring when done. Examples of such parameters might be case-sensitivity for a search, or a prompt for user input.

Global variables are not as good as parameter objects for this sort of thing. Changes to them are visible to all threads, but in Guile parameter object locations are per-thread, thereby truely limiting the effect of parameterize to just its dynamic execution.

Passing arguments to functions is thread-safe, but that soon becomes tedious when there's more than a few or when they need to pass down through several layers of calls before reaching the point they should affect. And introducing a new setting to existing code is often easier with a parameter object than adding arguments.


— Function: make-parameter init [converter]

Return a new parameter object, with initial value init.

A parameter object is a procedure. When called (param) it returns its value, or a call (param val) sets its value. For example,

          (define my-param (make-parameter 123))
          (my-param) => 123
          
          (my-param 456)
          (my-param) => 456
     

If a converter is given, then a call (converter val) is made for each value set, its return is the value stored. Such a call is made for the init initial value too.

A converter allows values to be validated, or put into a canonical form. For example,

          (define my-param (make-parameter 123
                             (lambda (val)
                               (if (not (number? val))
                                   (error "must be a number"))
                               (inexact->exact val))))
          (my-param 0.75)
          (my-param) => 3/4
     
— library syntax: parameterize ((param value) ...) body ...

Establish a new dynamic scope with the given params bound to new locations and set to the given values. body is evaluated in that environment, the result is the return from the last form in body.

Each param is an expression which is evaluated to get the parameter object. Often this will just be the name of a variable holding the object, but it can be anything that evaluates to a parameter.

The param expressions and value expressions are all evaluated before establishing the new dynamic bindings, and they're evaluated in an unspecified order.

For example,

          (define prompt (make-parameter "Type something: "))
          (define (get-input)
            (display (prompt))
            ...)
          
          (parameterize ((prompt "Type a number: "))
            (get-input)
            ...)
     
— Parameter object: current-input-port [new-port]
— Parameter object: current-output-port [new-port]
— Parameter object: current-error-port [new-port]

This SRFI extends the core current-input-port and current-output-port, making them parameter objects. The Guile-specific current-error-port is extended too, for consistency. (see Default Ports.)

This is an upwardly compatible extension, a plain call like (current-input-port) still returns the current input port, and set-current-input-port can still be used. But the port can now also be set with (current-input-port my-port) and bound dynamically with parameterize.

— Function: with-parameters* param-list value-list thunk

Establish a new dynamic scope, as per parameterize above, taking parameters from param-list and corresponding values from values-list. A call (thunk) is made in the new scope and the result from that thunk is the return from with-parameters*.

This function is a Guile-specific addition to the SRFI, it's similar to the core with-fluids* (see Fluids and Dynamic States).


Parameter objects are implemented using fluids (see Fluids and Dynamic States), so each dynamic state has it's own parameter locations. That includes the separate locations when outside any parameterize form. When a parameter is created it gets a separate initial location in each dynamic state, all initialized to the given init value.

As alluded to above, because each thread usually has a separate dynamic state, each thread has it's own locations behind parameter objects, and changes in one thread are not visible to any other. When a new dynamic state or thread is created, the values of parameters in the originating context are copied, into new locations.

SRFI-39 doesn't specify the interaction between parameter objects and threads, so the threading behaviour described here should be regarded as Guile-specific.