[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

6.18 Procedures and continuations

In Scheme, procedures are fundamental blocks to build a program (See section Making Procedures). A procedure represents a certain computation, possibly parameterized, and can be applied to the actual arguments to execute the computation. Scheme also provides the means to extract the continuation of the current computation and wraps it in a procedure (See section Continuations).

Gauche extends the concept of procedure application, allowing you to apply any object; for example, you can set up Gauche to accept ("abc" 2) can be a valid application syntax. See section Applicable objects, for the details.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

6.18.1 Procedure class and applicability

Builtin Class: <procedure>

Represents a procedure. Ordinary Scheme procedures created by lambda is an instance of this class, as well as built-in primitive procedures written in C. Note that, in Gauche, other type of objects can behave as a procedure; so checking whether an object is a procedure or not doesn’t mean much unless you want to mess around with Gauche internals.

Function: procedure? obj

[R5RS] Returns #t if obj is inherently applicable objects, #f otherwise. By inherently applicable we mean Gauche unconditionally understands that obj can be called as a procedure; an instance of <procedure> is so, as well as generic functions (<generic>) and methods (<method>). See section Generic function and method, for the details.

Since you can make any type of objects applicable at any time (See section Applicable objects), the fact that procedure? returned #f doesn’t mean that the object cannot be applied. To check if an object can be applied or not, use applicable? below.

Function: apply proc arg1 … args

[R5RS] Calls a procedure proc with a list of arguments, (arg1 … . args). The last argument args must be a proper list. Returns (a) value(s) proc returns.

 
(apply list 'a 'b '(c d e)) ⇒ (a b c d e)

(apply + 1 2 '(3 4 5))      ⇒ 15
Function: applicable? obj class …

Checks if obj can be called with the types of arguments listed in class …. That is, when (applicable? foo <string> <integer>) returns #t, then you can call foo as (foo "x" -2), for example. (It doesn’t mean you won’t get an error; foo may be accept only nonnegative integers, which you cannot tell from the result of applicable?. But if applicable? returns #t, Gauche won’t complain “foo is not applicable” when you call foo.

This procedure takes applicable objects into account. So, for example, (applicable? #/a/ <string>) returns #t, for the regular expressions are applicable to a string (See section Regular expressions).

For generic functions, applicable? returns #t if it has at least one method such that each of its specifiers is a superclass of the corresponding class argument given to applicable?.

 
(define-method foo ((x <sequence>) (y <integer>)) #f)

(applicable? foo <sequence> <integer>) ⇒ #t
(applicable? foo <string> <integer>) ⇒ #t
(applicable? foo <hash-table> <integer>) ⇒ #f
(applicable? foo <string> <real>) ⇒ #f

The second example returns #t since <string> is a subclass of <sequence>, while the third example returns #f since <hash-table> isn’t a subclass of <sequence>. The fource example returns #f since <real> isn’t a subclass of <integer>.

Traditional Scheme procedures (such as ones created by lambda) only cares the number of arguments but not their types; it accepts any type as far as the number of arguments matches. To check such a condition, pass <top> as the argument class. (<top> is a superclass of all classes.)

 
(applicable? cons <top> <top>) ⇒ #t

If you want to check an object is applicable to a certain number of some class of arguments, you can pass <bottom> as the argument class instead. (<bottom> is a subclass of all classes.)

 
(define-method foo ((x <sequence>) (y <integer>)) #f)

(applicable? foo <top> <top>) ⇒ #f
(applicable? foo <bottom> <bottom>) ⇒ #t

See section Types and classes, for the details of <top>, <bottom> and Gauche’s type handling.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

6.18.2 Universal accessor

Function: ~ obj key keys …
Function: (setter ~) obj key keys …

The procedure ~ can be used to access a part of various aggregate types.

 
;; Access to an element of a sequence by index
(~ '(a b c) 0)       ⇒ a
(~ '#(a b c) 2)      ⇒ c
(~ "abc" 1)          ⇒ #\b
(~ '#u8(10 20 30) 1) ⇒ 20

;; Access to an element of a collection by key
(~ (hash-table 'eq? '(a . 1) '(b . 2)) 'a)
  ⇒ 1

;; Access to a slot of an object by slot name
(~ (sys-localtime (sys-time)) 'hour)
  ⇒ 20

The access can be chained:

 
(~ '#((a b c) (d e f) (g h i)) 1 2) ⇒ f

(~ (hash-table 'eq? '(a . "abc") '(d . "def")) 'a 2)
  ⇒ #\c

You can think ~ as left-associative, that is,

 
(~ x k j) ≡ (~ (~ x k) j)

and so on.

The generalized setter set! can be used with ~ to replace the specified element.

 
(define z (vector 'a 'b 'c))
(set! (~ z 1) 'Z)

z ⇒ #(a Z c)

(define z (vector (list (vector 'a 'b 'c)
                        (vector 'd 'e 'f)
                        (vector 'g 'h 'i))
                  (list (vector 'a 'b 'c)
                        (vector 'd 'e 'f)
                        (vector 'g 'h 'i))))

z ⇒ #((#(a b c) #(d e f) #(g h i))
     (#(a b c) #(d e f) #(g h i)))

(set! (~ z 1 2 0) 'Z)
z ⇒  #((#(a b c) #(d e f) #(g h i))
     (#(a b c) #(d e f) #(Z h i)))

Internally, a call to ~ is implemented by a generic function ref. See Object system for more about generic functions.

Generic function: ref object key :optional args …
Generic function: (setter ref) object key value

Many aggregate types defines a specialized method of these to provide uniform access and mutation. Meaning of optional arguments args of ref depends on each specialized method, but it is common that the first optional argument of ref is a fallback value, which is to be returned when object doesn’t have a meaningful association with key.

The manual entry of each aggregate type shows the specialized method and its semantics in detail.

Conceptually, ~ can be understood as follows:

 
(define ~
  (getter-with-setter
   (case-lambda
     [(obj selector) (ref obj selector)]
     [(obj selector . more) (apply ~ (ref obj selector) more)])
   (case-lambda
     [(obj selector val) ((setter ref) obj selector val)]
     [(obj selector selector2 . rest)
      (apply (setter ~) (ref obj selector) selector2 rest)])))

(Gauche may use some short-cut for optimization, though, so this code may not reflect the actual implementation.)


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

6.18.3 Combinators

Gauche has some primitive procedures that allows combinatory programming.

Function: pa$ proc arg …

Partial application. Returns a procedure, and when it is called with arguments m …, it is equivalent to call (proc arg … m …).

 
(define add3 (pa$ + 3))
(add3 4) ⇒ 7

(map (pa$ * 2) '(1 2 3)) ⇒ (2 4 6)

Macros cut and cute defined in SRFI-26 provide a similar abstraction, with a bit more flexible but less compact notation. See section Making Procedures.

Function: apply$ proc
Function: map$ proc
Function: for-each$ proc

Partial application versions of apply, map and for-each.

 
(define map2* (map$ (pa$ * 2)))
(map2* '(1 2 3)) ⇒ (2 4 6)
Function: count$ pred
Function: fold$ kons :optional knil
Function: fold-right$ kons :optional knil
Function: reduce$ f :optional ridentity
Function: reduce-right$ f :optional ridentity
Function: filter$ pred
Function: remove$ pred
Function: partition$ pred
Function: member$ item
Function: find$ pred
Function: find-tail$ pred
Function: any$ pred
Function: every$ pred
Function: delete$ pred
Function: assoc$ item

Partial application versions of some srfi-1 procedures (See section srfi-1 - List library).

Function: .$ f …
Function: compose f …

Combine procedures. All arguments must be procedures. When two procedures are given, (.$ f g) is equivalent to the following code:

 
(lambda args (call-with-values (lambda () (apply g args)) f))

When more than two arguments are passed, they are composed as follows:

 
(.$ f g h ...) ≡ (.$ (.$ f g) h ...)

Some examples:

 
(define not-zero? (.$ not zero?))
(not-zero? 3) ⇒ #t
(not-zero? 0) ⇒ #f

(define dot-product (.$ (apply$ +) (map$ *)))
(dot-product '(1 2 3) '(4 5 6)) ⇒ 32

A couple of edge cases: if only one argument is given, the argument itself is returned. If no arguments are given, the procedure values is returned.

Note: The name .$ comes from the fact that . is commonly used for function composition in literatures and some programming languages, and that Gauche uses suffix $ to indicate combinators. However, since it is not a valid R5RS/R6RS identifier, portable programs may want to use the alias compose, with which you can easily add a portable definition using srfi-0, for example.

Function: complement pred

Returns a procedure that reverses the meaning of the predicate pred. That is, for the arguments for which pred returns true return false, and vice versa.

 
(map (complement even?) '(1 2 3)) ⇒ '(#t #f #t)
(map (complement =) '(1 2 3) '(1 1 3)) ⇒ '(#f #t #f)
((complement (lambda () #f))) ⇒ #t
Function: any-pred pred …

Returns a procedure which applies given argument(s) to each predicate pred. If any pred returns a non-#f value, the value is returned. If all the preds return #f, #f is returned.

 
(define string-or-symbol? (any-pred string? symbol?))
(string-or-symbol? "abc") ⇒ #t
(string-or-symbol? 'abc)  ⇒ #t
(string-or-symbol? 3)     ⇒ #f

(define <> (any-pred < >))
(<> 3 4) ⇒ #t
(<> 3 3) ⇒ #f

((any-pred (cut memq <> '(a b c))
           (cut memq <> '(1 2 3)))
 'b)  ⇒ '(b c)
Function: every-pred pred …

Returns a procedure which applies given argument(s) to each predicate pred. If every pred returns a non-#f value, the value returned by the last pred is returned. If any pred returns #f, every-pred returns #f without calling further preds.

 
((every-pred odd? positive?) 3)  ⇒ #t
((every-pred odd? positive?) 4)  ⇒ #f
((every-pred odd? positive?) -3) ⇒ #f

(define safe-length (every-pred list? length))
(safe-length '(a b c))  ⇒ 3
(safe-length "aaa")     ⇒ #f

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

6.18.4 Optional argument parsing

Gauche supports optional and keyword arguments in extended lambda syntax (See section Making Procedures). However, you can also use the following macros to parse optional and keyword arguments, without relying Gauche’s extension.

 
(define (foo a b :optional (c #f) (d 'none))
  body ...)

;; is roughly equivalent to ...

(define (foo a b . args)
  (let-optionals* args ((c #f) (d 'none))
    body ...))

Explicitly parsing the extended arguments may be useful for portable programs, since it is rather straightforward to implement those macros rather than extend lambda syntax.

Those macros can also be useful to factor out common argument parsing routines.

Macro: let-optionals* restargs (var-spec …) body …
Macro: let-optionals* restargs (var-spec … . restvar) body …

Given a list of values restargs, binds variables according to var-spec, then evaluates body.

Var-spec can be either a symbol, or a list of two elements and its car is a symbol. The symbol is the bound variable name. The values in restargs are bound to the symbol in order. If there are not as many values in restargs as var-spec, the rest of symbols are bound to the default values, determined as follows: If var-spec is just a symbol, the default value is undefined. If var-spec is a list, the default value is the result of evaluation of the second element of the list. In the latter case the second element is only evaluated when there are not enough arguments. The binding proceeds in the order of var-spec, so the second element may refer to the bindings of previous var-spec.

In the second form, restvar must be a symbol and bound to the list of values whatever left from restargs after binding to var-spec.

It is not an error if restarg has more values than var-specs. The extra values are simply ignored in the first form.

 
(define (proc x . args)
  (let-optionals* args ((a 'a)
                        (b 'b)
                        (c 'c))
    (list x a b c)))

(proc 0)         ⇒ (0 a b c)
(proc 0 1)       ⇒ (0 1 b c)
(proc 0 1 2)     ⇒ (0 1 2 c)
(proc 0 1 2 3)   ⇒ (0 1 2 3)

(define (proc2 . args)
  (let-optionals* args ((a 'a) . b)
    (list a b)))

(proc2)          ⇒ (a ())
(proc2 0)        ⇒ (0 ())
(proc2 0 1)      ⇒ (0 (1))
(proc2 0 1 2)    ⇒ (0 (1 2))

(define (proc3 . args)
  (let-optionals* args ((a 0)
                        (b (+ a 1))
                        (c (+ b 1)))
    (list a b c)))

(proc3)          ⇒ (0 1 2)
(proc3 8)        ⇒ (8 9 10)
(proc3 8 2)      ⇒ (8 2 3)
(proc3 8 2 -1)   ⇒ (8 2 -1)
Macro: get-optional restargs default

This is a short version of let-optionals* where you have only one optional argument. Given the optional argument list restargs, this macro returns the value of optional argument if one is given, or the result of default otherwise. Default is not evaluated unless restargs is an empty list.

 
(define (proc x . maybe-opt)
  (let ((option (get-optional maybe-opt #f)))
    (list x option)))

(proc 0)         ⇒ (0 #f)
(proc 0 1)       ⇒ (0 1)
Macro: let-keywords restarg (var-spec …) body …
Macro: let-keywords restarg (var-spec … . restvar) body …

This macro is for keyword arguments. Var-spec can be one of the following forms:

(symbol expr)

If the restrag contains keyword which has the same name as symbol, binds symbol to the corresponding value. If such a keyword doesn’t appear in restarg, binds symbol to the result of expr.

(symbol keyword expr)

If the restarg contains keyword keyword, binds symbol to the corresponding value. If such a keyword doesn’t appear in restarg, binds symbol to the result of expr.

The default value expr is only evaluated when the keyword is not given to the restarg.

If you use the first form, let-keyword throws an error when restarg contains a keyword argument that is not listed in var-specs. When you want to allow keyword arguments other than listed in var-specs, use the second form.

In the second form, restvar must be either a symbol or #f. If it is a symbol, it is bound to a list of keyword arguments that are not processed by var-specs. If it is #f, such keyword arguments are just ignored.

 
(define (proc x . options)
  (let-keywords options ((a 'a)
                         (b :beta 'b)
                         (c 'c)
                         . rest)
    (list x a b c rest)))

(proc 0)         ⇒ (0 a b c ())
(proc 0 :a 1)    ⇒ (0 1 b c ())
(proc 0 :beta 1) ⇒ (0 a 1 c ())
(proc 0 :beta 1 :c 3 :unknown 4) ⇒ (0 a 1 3 (:unknown 4))
Macro: let-keywords* restarg (var-spec …) body …
Macro: let-keywords* restarg (var-spec … . restvar) body …

Like let-keywords, but the binding is done in the order of var-specs. So each expr can refer to the variables bound by preceding var-specs.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

6.18.5 Procedure arity

Interface to query procedure’s arity. The API is taken from MzScheme (PLT Scheme).

Function: arity proc

Given procedure proc, returns an integer, an arity-at-least object, or a list of integer(s) and arity-at-least objects.

An integer result indicates proc takes exactly that number of arguments. An arity-at-least indicates proc takes at least (arity-at-least-value arity-at-least) arguments. The list indicates there are multiple procedures with different arities.

Since one can add methods to an existing procedure or generic function at any moment in Gauche, the value returned by arity only indicates the current state of the procedure. It will change if new method is added to the procedure/generic-function.

 
(arity cons) ⇒ 2
(arity list) ⇒ #<arity-at-least 0>
(arity make) ⇒ (#<arity-at-least 1>)
Function: arity-at-least? obj

Returns true if obj is an arity-at-least object.

Function: arity-at-least-value arity-at-least

Returns the number of required arguments the arity-at-least object indicates.

Function: procedure-arity-includes? proc k

If a procedure proc can take k arguments, returns #t. Otherwise returns #f.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

6.18.6 Applicable objects

Gauche has a special hook to make an arbitrary object applicable.

Generic Function: object-apply object arg

If an object that is neither a procedure nor a generic function is applied to some arguments, the object and the arguments are passed to a generic function object-apply.

This can be explained better by examples.

For example, suppose you try to evaluate the following expression:

 
("abcde" 2)

The operator evaluates to a string, which is neither a procedure nor a generic function. So Gauche interprets the expression as if it were like this:

 
(object-apply "abcde" 2)

Gauche doesn’t define a method of object-apply that takes <string> and <integer> by default, so this signals an error. However, if you define such a method:

 
(define-method object-apply ((s <string>) (i <integer>))
  (string-ref s i))

Then the first expression works as if a string is applied on the integer:

 
("abcde" 2) ⇒ #\c

This mechanism works on almost all occasions where a procedure is allowed.

 
(apply "abcde" '(1))   ⇒ (#\b)
(map "abcde" '(3 2 1)) ⇒ (#\d #\c #\b)

Among Gauche built-in objects, <regexp> object and <regmatch> object have object-apply defined. See section Regular expressions.

Generic Function: (setter object-apply) object argvalue

If a form of applying an applicable object appears in the first position of set! form, this method is called, that is:

 
(set! (object arg …) value)
 ⇒ ((setter object-apply) object argvalue)

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

6.18.7 Continuations

Function: call-with-current-continuation proc
Function: call/cc proc

[R5RS] Encapsulates the current continuation to a procedure (“continuation procedure”), and calls proc with it. When proc returns, its value becomes call/cc’s value. When the continuation procedure is invoked with zero or more arguments somewhere, the further calculation is abandoned and call/cc returns with the arguments given to the continuation procedure.

First class continuation is one of the most distinct feature of Scheme, but this margin is too small to contain explanation. Please consult to the appropriate documents.

There’s a nontrivial interaction between C language runtime and Scheme continuation. Suppose the following scenario:

  1. An application’s C runtime calls back a Scheme routine. For example, GUI framework calls back a draw routine written in Scheme.
  2. A continuation is captured in the Scheme routine.
  3. The Scheme routine returns to the C runtime.
  4. The continuation captured in 2 is invoked.

It is no problem to invoke the continuation, but if the control is about to return to the Scheme routine to the C runtime (that is, to execute step 3 again), an error is signalled as follows.

 
*** ERROR: attempt to return from a ghost continuation.

This is because C routines don’t expect the calling function to return more than once. The C stack frame on which the Scheme callbak was originally called is likely to be deallocated or modified at the time the continuation is invoked.

If you think of a continuation as a chain of control frames, growing from root towards upward, you can imagine that, once a control returns to the C world, the chain is cut at the boundary. You can still execute such rootless continuations, but you have to move the control away from it before it tries to return to its root that no longer exists. You can call another continuation, or raise an exception, for example.

Using partial continuations (or delimited continuations) is another way to avoid such complications. See section gauche.partcont - Partial continuations.

Macro: let/cc var body …

This macro expands to : (call/cc (lambda (var) body …)). The API is taken from PLT Scheme.

Function: dynamic-wind before thunk after

[R5RS] Before, thunk and after are all procedures with no arguments. In normal situation, dynamic-wind calls before, then thunk, then after, then returns whatever value(s) thunk returned.

If a control flow goes out from thunk by invoking a continuation captured outside of the dynamic scope of dynamic-wind (for example, an error is signalled in thunk), after is called.

If a control flow goes into thunk by invoking a continuation captured inside thunk from outside of the dynamic scope of dynamic-wind, before is called.

 
(letrec ((paths '())
         (c #f)
         (add (lambda (s) (push! paths s))))
  (dynamic-wind
   (lambda () (add 'connect))
   (lambda ()
     (add (call/cc (lambda (c0) (set! c c0) 'talk1))))
   (lambda () (add 'disconnect)))
  (if (< (length paths) 4)
      (c 'talk2)
      (reverse paths)))
 ⇒ (connect talk1 disconnect connect talk2 disconnect)

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

6.18.8 Multiple values

Function: values obj …

[R5RS] Returns obj … as multiple values. Caller can capture multiple values by a built-in syntax receive (Binding constructs), or the R5Rs procedure call-with-values described below. See also srfi-11 - Let-values.

 
(values 1 2) ⇒ 1 and 2
Function: call-with-values producer consumer

[R5RS] Call a procedure producer with no argument. Then applies a procedure consumer on the value(s) producer returned. Returns the value(s) consumer returns.

 
(call-with-values (lambda () (values 1 2)) cons)
  ⇒ (1 . 2)
Macro: values-ref mv-expr k

Returns k-th value of what mv-expr returns. Conceptually, it is the same as the following code.

 
(call-with-values (lambda () mv-expr) (lambda r (list-ref r k)))

This macro uses shortcuts for the typical cases like k is zero.

Similar to Common Lisp’s nth-value, but the argument order is flipped to match other Scheme’s *-ref procedures.

Macro: values->list mv-expr

Evaluates mv-expr, puts all the results into a list and returns it. It is called multiple-value-list in Common Lisp.

 
(values->list (div-and-mod 10 3)) ⇒ (3 1)

(values->list 1) ⇒ (1)

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

6.18.9 Folding generated values

Sometimes a procedure is used as a generator of a series of values, by yielding one value at a time. Customary an EOF object is used to mark the end of the series. For example, read-char is such a procedure that yields a series of characters, terminated by EOF.

Since it is such a handy abstraction, Gauche provides a set of utilties (see gauche.generator - Generators) to construct and generators out of various sources, including other generators.

The generated values needs to be consumed eventually. Here we provide several procedures to do that. These are useful when combined with input procedures like read, so we have them built-in instead of putting them in a separate module.

Function: generator-fold proc seed gen gen2 …

Works like fold on the generated values by generator procedures gen gen2 … (See section Walking over lists, for the details of fold).

When one generator is given, for each value v generated by gen, proc is called as (proc v r), where r is the current accumulated result; the initial value of the accumulated result is seed, and the return value from proc becomes the next accumulated result. When gen returns EOF, the accumulated result at that time is returned from generator-fold.

When more than one generator is given, proc is called as (proc v1 v2r), where v1, v2 … are the values yielded from gen, gen2, …, respectively, and r is the current accumulated result. The iteration terminates when any one of the generators returns EOF.

 
(with-input-from-string "a b c d e"
  (cut generator-fold cons 'z read))
  ⇒ (e d c b a . z)
Function: generator-fold-right proc seed gen gen2 …

Works like fold-right on the generated values by generator procedures gen gen2 … (See section Walking over lists, for the details of fold-right).

This is provided for completeness, but it isn’t a good way to handle generators; in order to combine values right-associatively, we should read all the values from the generators (until any one of the generator returns EOF), then start calling proc as

 
(proc v0_0 v1_0 ... (proc v0_1 v1_1 ... (proc v0_n v1_n ... seed) ...))

where vn_m is the m-th value yielded by n-th generator.

 
(with-input-from-string "a b c d e"
  (cut generator-fold-right cons 'z read))
  ⇒ (a b c d e . z)

As you see, keeping all intermediate values kind of defeats the benefit of generators.

Function: generator-for-each proc gen gen2 …

A generator version of for-each. Repeatedly applies proc on the values yielded by gen, gen2 … until any one of the generators yields EOF. The values returned from proc are discarded.

Function: generator-map proc gen gen2 …

A generator version of map. Repeatedly applies proc on the values yielded by gen, gen2 … until any one of the generators yields EOF. The values returned from proc are collected into a list and returned.

 
(with-input-from-string "a b c d e"
  (cut generator-map symbol->string read))
  ⇒ ("a" "b" "c" "d" "e")

[ < ] [ > ]   [ << ] [ Up ] [ >> ]

This document was generated by Shiro Kawai on May 28, 2012 using texi2html 1.82.