For Gauche 0.9.15Search (procedure/syntax/module):

Next: , Previous: , Up: Core library   [Contents][Index]

6.15 Procedures and continuations

In Scheme, procedures are fundamental blocks to build a program (see 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 Continuations).

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


6.15.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

[R7RS base] 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 Generic function and method, for the details.

Since you can make any type of objects applicable at any time (see 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

[R7RS base] 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 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 fourth 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 Types and classes, for the details of <top>, <bottom> and Gauche’s type handling.

Function: procedure-type proc

The argument must be a procedure. Returns a descriptive type of the proc, as much as the system knows. See Types and classes, for the details of procedure types.

The “type” returned by this procedure is just a hint. Currently, most Scheme-defined procedures doesn’t have much type info.

(procedure-type filter) ⇒ #<^ <top> <top> -> *>

Some of subrs (C-defined procedures) has a bit more info, derived from C type information.

(procedure-type cons) ⇒ #<^ <top> <top> -> <pair>>

Eventually we’ll improve this system to return more precise type info, and use it for optimizations and other static analysis.


6.15.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.)


6.15.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 Making procedures.

SRFI-235 defines left-section, which is effectively the same as pa$ (see srfi.235 - Combinators (SRFI)).

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 (R7RS (scheme list)) procedures (see scheme.list - R7RS lists).

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 R7RS identifier, portable programs may want to use the alias compose, with which you can easily add a portable definition using SRFI-0, for example.

SRFI-210 provides compose-left and compose-right; see srfi.210 - Procedures and syntax for multiple values.

Function: identity obj

Returns obj. You can use values to get the same effect for the porable code, but using this emphasizes that you’re dealing with exactly one value.

Note: SRFI-210 introduces multi-valued identity (unfortunately), which is a synonym to values (see srfi.210 - Procedures and syntax for multiple values).

Function: constantly obj …

[SRFI-235] Returns a procedure that takes arbitrary number of arguments, discards them, and returns obj obj …. Same as (^ _ (values obj …)).

Function: complement pred

[SRFI-235] 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: flip proc

[SRFI-235] Returns a procedure that passes its arguments to proc in reverse order, that is, ((flip proc) a b … y z) is (proc z y … b a).

Function: swap proc

[SRFI-235] Proc must be a procedure that takes two arguments or more. Returns a procedure that passes its first and second arguments swapped to proc, that is, ((swap proc) a b arg …) is (proc b a arg …).

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

6.15.4 Optional argument parsing

Gauche supports optional and keyword arguments in extended lambda syntax (see 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 restarg 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.


6.15.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.


6.15.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 Regular expressions.

Note: Defining object-apply method affect globally. Although it is cool to define object-apply on strings as in the above example, it is not recommended as a general trick, for it’ll change the behavior of string across the entire program.

Keep the above example personal experiments. In general, you should only define object-apply on your own classes.

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)

6.15.7 Continuations

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

[R7RS base] 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 signaled 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 callback 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 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 body after

[R7RS base] This is a primitive to manage dynamic environment. Dynamic environment is a set of states which are kept during execution of a certain expression. For example, the current output ports are switched during execution of with-output-to-port. They can be nested dynamically, as opposed to the lexical environment, in which nesting is determined statically from the program source.

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

The intention is that the before thunk sets up the dynamic environment for execution of body, and the after thunk restores it to the previous state.

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

If a control flow goes into body by invoking a continuation captured inside body 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)

Note: Since after is guaranteed to be called when an error causes body to abort, it may appear tempting to use dynamic-wind to use resource clean-up, just like try-catch construct in Java. It’s not for that. Since the control may return to body, the situation dynamic-wind handles should be considered more like a context switch.

For resource clean-up, you can use exception handling mechanism such as guard and unwind-protect (see High-level exception handling mechanism), which is built on top of dynamic-wind.

As a rule of thumb, after should do things that can be reverted by before, such as manipulating error handler stack (instead of actually handling errors).


6.15.8 Continuation prompts

Continuations are often explained as “the rest of computation”. It is enough for theorists to write papers, but in practice, we cannot reify the computations “from now on for ever”. There has to be an end–it may be until a single program terminates, or until a server returns a response to a request, or a REPL prints the result of evaluation. Implementations have chosen the “end” of continuations suitable for there needs. Sometimes, though, the ambiguity caused confusion.

Continuation prompts are to make the “end” of continuation explicit. It is called a prompt because, in the interactive environment, REPL prompt is usually delimits the continuation. If you think of activation records as a stack, a continuation prompt is the “bottom” of the stack.

Furthermore, you can have multiple kinds of continuation prompts, so that multiple features that use continuation prompts independently won’t step on each other.

(Partial continuations can also be defined on top of continuation prompts, and SRFI-226 indeed defines so. We’ll integrate existing partial continuations with continuation prompts in future versions.)

Function: make-continuation-prompt-tag :optional name

[SRFI-226] Creates and returns a new continuation prompt tag, which is an opaque object to specify the boundary of continuation frames. A new boundary can be introduced with call-with-continuation-prompt. You can nest bounded continuation frames with the smae continuation prompt tag. Operations that takes a continuation prompt tag seraches the innermost boundary with the tag.

The optional name argument is the name of the continuation prompt tag. It is for display purpose only to help diagnostics.

Function: default-continuation-prompt-tag

[SRFI-226] Returns a default continuation prompt tag. When the program starts, the initial continuation boundary has the default continaution prompt tag. Most procedures that takes an optional continuation prompt tag assumes the default tag if the argument is omitted.

Function: continuation-prompt-tag? obj

[SRFI-226] Returns #t if obj is a continuation prompt tag, #f otherwise.

Function: call-with-continuation-prompt thunk :optional prompt-tag abort-handler

[SRFI-226] Calls thunk with a continuation delimited by a continuation prompt tag prompt-tag. If prompt-tag is omitted, the default prompt tag is assumed.

The abort-handler is a procedure to be called when, during executing thunk, its continuation is aborted by abort-current-continuation. The objects passed to abort-current-continuation are passed to abort-handler, which is called with the continuation of call-with-continuation-prompt itself. If thunk doesn’t abort the continuation, abort-handler won’t be called.

When abort-handler is omitted, it installs a default abort handler, which work in a very specific way. First, it takes one argument, which must be a thunk (let’s call it continuation-thunk for now). The default abort handler reinstates the continuation with the continuation prompt tag prompt-tag and calls the continuation thunk. Say, it works like a trampoline to push back the control to the same context as thunk is called.

See the entry of abort-current-continuation below for exmaples.

Function: abort-current-continuation prompt-tag obj …

[SRFI-226] Aborts all active procedure calls in the current continuation up to the continuation prompt tagged with prompt-tag. The control goes to the abort handler installed by the most recent call-with-continuation-prompt with the prompt-tag. The rest of the arguments obj … are passed to the abort handler.

When the current continuation does not have a continuation prompt tagged with prompt-tag, a <continuation-violation> error is thrown.

(let ([tag (make-continuation-prompt-tag)])
 (call-with-continuation-prompt
   (^[] (+ 1 (abort-current-continuation tag 'foo 'bar) 2))
   tag
   list))
 ⇒ (foo bar)

If a dynamic handlers are inserted in the continuation frames to be aborted, their ‘after’ handlers are executed before the abort handler is called.

(let ((tag (make-continuation-prompt-tag)))
 (call-with-continuation-prompt
   (^[]
     (dynamic-wind
       (^[] #f)
       (^[] (abort-current-continuation tag 'foo))
       (^[] (print 'yo))))
   tag
   print))
 ⇒ prints yo, then prints foo.

You may wonder the difference between the traditional call/cc and the combination of call-with-continuation-prompt and abort-current-continuation. Actually the latter is more lower-level construct, and call/cc is a high-level construct implemented on top of it.


6.15.9 Continuation marks

Continuation marks are a set of key-value associations attached to continuation frames. Keys and values are arbitrary Scheme object. It sounds obscure, but it allows to implement various useful language features; notably, the new parameterize is implemented on top of continuation marks, allowing its body to be evaluated in tail context (see Parameters).

In general, though, continuations marks are low-level tools to construct high-level features, rather than to be used as day-to-day tools.

Marking a continuation frame

Special Form: with-continuation-mark key value expr

[SRFI-226] Both key and value are evaluated, and adds the key-value association as a mark to the current continuation, then evaluate expr with the same continuation as this form. That is, if this form is in the tail context, expr is evaluated in the same tail context.

If the current continuation is already marked with key, then its value is replaced with value.

Macro: with-continuation-mark ((key value) …) expr

[SRFI-226] All keys and values are evaluted, then add those key-value associations as marks to the current continuation, then evaluate expr with the same continuation as this form.

Extracting continuation marks

Function: call-with-immediate-continuation-mark key proc :optional fallback

[SRFI-226] Tail-calls proc with the value for key associated in the current continuation. If the current continuation doesn’t have a value for key, fallback is passed to proc instead. If fallback is omitted, #f is assumed.

(with-continuation-mark 'a 'b
  (call-with-immediate-continuation-mark 'a list))
 ⇒ (b)

(with-continuation-mark 'a 'b
  (cons (call-with-immediate-continuation-mark 'a list) 1))
 ⇒ ((#f) . 1)
Function: continuation-marks cont :optional prompt-tag

[SRFI-226] Returns a continuation mark set object, which contains continuation marks of the confinutaion frames between the continuation cont and the continuation delimited by prompt-tag. If prompt-tag is omitted, default prompt tag is assumed.

Function: current-continuation-marks :optional prompt-tag

[SRFI-226] Returns a continuation mark set object, which contains continuation marks of the confinutaion frames between the current continuation and the continuation delimited by prompt-tag. If prompt-tag is omitted, default prompt tag is assumed.

Continuation mark set

Function: continuation-mark-set? obj

[SRFI-226] Returns #t if obj is a continuation mark set, #f otherwise.

Function: continuation-mark-set->list mark-set key :optional prompt-tag

[SRFI-226] Pick up values associated to key from a continuation mark set mark-set, and returns a fresh list of them. A value in more recent continuation frames come earlier in the list.

You can pass #f to mark-set to make the procedure use (current-continuation-marks prompt-tag).

It searches the mark set up to the continuation frame delimited by a continuation prompt prompt-tag. If prompt-tag is omitted, the default prompt tag is used.

Function: continuation-mark-set-first mark-set key :optional fallback prompt-tag

[SRFI-226] Returns the most recent value associated with key in the continuation mark set mark-set. If there’s a mark with key exists, it matches (car (continuation-mark-set->list key prompt-tag).

If there’s no mark with key in the mark set, fallback is returned. If fallback is omitted, #f is assumed.

If prompt-tag is omitted, the default prompt tag is used.

Continuation mark key

You can use arbitrary Scheme object as keys for continuation marks, but you if you’re writing libraries, you want to make sure that your keys won’t conflict with the keys used by other parts of the code.

A continuation mark key is an opaque object that is guaranteed to be different (in terms of equal?) to other continuation mark keys.

Function: make-continuation-mark-key :optional name

[SRFI-226] Returns a newly created continuation key, which is guaranteed never to be equal to any prior or future continuation mark keys.

The optional name argument is for debugging purpose.

Note that SRFI-226 does not require a continuation mark key has disjoint types from other Scheme types. Currently, Gauche uses an uninterned symbol as a continuation mark key. It may be changed in future, so do not count on it.

Function: continuation-mark-key? obj

[SRFI-226] Returns #t if obj can be an object returned by make-continuation-mark-key, #f otherwise.

Note that SRFI-226 does not require a continuation mark key has a disjoint type, so this may return #t for an object that’s not created by make-continuation-mark-key. In fact, Gauche currently uses an uninterned symbol for a continuation mark key, and this procedure returns #t for any symbols.


6.15.10 Multiple values

Function: values obj …

[R7RS base] Returns obj … as multiple values. Caller can capture multiple values by a built-in syntax receive or let-values (Binding constructs), or the R7RS procedure call-with-values described below.

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

[R7RS base] 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.

SRFI-210’s value/mv is an enhancement of values-ref (see srfi.210 - Procedures and syntax for multiple values).

(values-ref mv-expr k) ≡ (value/mv k mv-expr)
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)

SRFI-210’s list/mv is an enhancement of values->list (see srfi.210 - Procedures and syntax for multiple values).

(values->list mv-expr) ≡ (list/mv mv-expr)

6.15.11 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 utilities (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 …

[R7RS generator] Works like fold on the generated values by generator procedures gen gen2 … (See 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 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 …

[R7RS generator] 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.

This is a handy procedure to consume generated values with side effects.

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")

The same effects can be achieved by combining generator->list and gmap (see Generator operations). This procedure is provided for the backward compatibility.

(generator->list (gmap proc gen gen2 …))
Function: generator-find pred gen

[R7RS generator] Returns the first item from the generator gen that satisfies the predicate pred.

The following example returns the first line matching the regexp #/XYZ/ from the file foo.txt.

(with-input-from-file "foo.txt"
  (cut generator-find #/XYZ/ read-line))

Note: If you want to pick all the lines matching the regexp, like the grep command, you can use gfilter and generator->list.


Next: , Previous: , Up: Core library   [Contents][Index]


For Gauche 0.9.15Search (procedure/syntax/module):