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

6.16 Control features


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

6.16.1 Procedures

Builtin Class: <procedure>
Function: procedure? obj

[R5RS] Returns #t if obj is a procedure, #f otherwise.

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

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

6.16.1.1 Mapping

Function: map proc list1 list2 …

[R5RS+] Applies proc for each element(s) of given list(s), and returns a list of the results. R5RS doesn't specify the application order of map, but Gauche guarantees proc is always applied in order of the list(s). Gauche's map also terminates as soon as one of the list is exhausted.

 
(map car '((a b) (c d) (e f))) ⇒ (a c e)

(map cons '(a b c) '(d e f))
  ⇒ ((a . d) (b . e) (c . f))

Note that the gauche.collection module (See section gauche.collection - Collection framework) extends map to work on any type of collection.

Function: for-each proc list1 list2 …

[R5RS] Applies proc for each element(s) of given list(s) in order. The results of proc are discarded. The return value of for-each is undefined. When more than one list is given, for-each terminates as soon as one of the list is exhausted.

Note that the gauche.collection module (See section gauche.collection - Collection framework) extends for-each to work on any type of collection.


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

6.16.1.2 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: compose f …

Combine procedures. All arguments must be procedures. When two procedures are given, (compose 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:

 
(compose f g h ...) ≡ (compose (compose f g) h ...)

Some examples:

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

(define dot-product (compose (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.

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.16.1.3 Optional argument parsing

To have optional arguments or keyword arguments in Scheme, you have to take variable arguments as a list and decompose them by yourself. The following macros help it.

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 regards it an error when restarg contains a keyword argument that is not listed in var-specs. For the backward compatibility it only issues warning now, but in future releases it will raise an error. 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.16.1.4 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.16.2 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 expression.

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.16.3 Continuation

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.

Gauche supports full continuation, with a few limitations in rare cases. Normally a continuation has an unlimited extent. However, if a continuation is created during “callback” from C code— that is, you call some C-implemented function that calls Scheme code again—the continuation's extent is limited until the Scheme evaluation returns to the C code. If you try to invoke the continuation from out of its extent, Gauche detects it and signals an error. This is a fundamental limitation and not likely to be addressed.

Note that it is still allowed to invoke a continuation from such callbacks. Also, the typical higher-order functions such as map, for-each or apply are not using callbacks, and not affected by this limitation

Fortunately, there are not much cases that you need to create an unlimited extent continuation in such callbacks. So far, the following code is executed in such callbacks. Besides them, typical callback functions from external C libraries, like GUI toolkit, obeys the same limitation.

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


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

6.16.5 Delayed evaluation

Gauche provides an extended lazy evaluation mechanism according to srfi-45. Instead of two primitives as R5RS, we have three: lazy, delay, and force.

It is found that the traditional mechanism that uses only delay and force didn't mix well with tail-recursive algorithms: It required unbound memory, despite that the body of the algorithm could be expressed in iterative manner. For the detailed explanation please look at the srfi-45 document. Here we explain how to use those primitives.

Special Form: lazy expression
Special Form: delay expression

[SRFI-45][R5RS] These forms creates a promise that delays the evaluation of expression. Expression will be evaluated when the promise is passed to force.

If expression itself is expected to yield a promise, you should use lazy. Othewise, you should use delay. If you can think in types, the difference may be clearer.

 
lazy  : Promise a -> Promise a
delay : a -> Promise a

Since we don't have static typing, we can't enforce this usage. The programmer has to choose appropriate one from the context. Generally, lazy appearers only to surround the entire body of function that express a lazy algorithm.

For the real-world example of use of lazy, you may want to check the implementation of util.stream (See section util.stream - Stream library).

Function: force promise

[R5RS] If promise is not a promise, it is just returned.

Otherwise, if promise's value hasn't been computed, force makes promise's encapsulated expression be evaluated, and returns the result.

Once promise's value is computed, it is memorized in it so that subsequent force on it won't cause the computation.

Function: promise? obj

Returns #t iff obj is a promise object.

The following example represents Fibonacci numbers by a lazy list. The list fib is calculated by adding fib itself shifted one element. Since it uses cached result of previous elements, calculation of n-th Fibonacci number can be done in O(n).

 
(define (lcar lis)   ;; lazy car
  (car (force lis)))

(define (lcdr lis)   ;; lazy cdr
  (cdr (force lis)))

(define (ltake lis n)  ;; lazy take
  (if (<= n 0) '() (cons (lcar lis) (ltake (lcdr lis) (- n 1)))))

(define (lmap proc l1 l2)  ;; lazy map
  (if (null? l1)
    '()
    (cons (proc (lcar l1) (lcar l2))
          (delay (lmap proc (lcdr l1) (lcdr l2))))))

;; lazy list of fibonacci numbers
(define fibs (list* 1 1 (delay (lmap + fibs (cdr fibs)))))

;; take a look
(ltake fibs 20)
  ⇒ (1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 
      987 1597 2584 4181 6765)

Note that, although it is elegant, it also requires O(n) storage even when you only need n-th Fibonacci number. That's because the delay expression in the tail of fibs is grabbing the head of fibs list and never releases it.


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

This document was generated by Shiro Kawai on October, 7 2008 using texi2html 1.78.