For Development HEAD DRAFTSearch (procedure/syntax/module):

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

4.3 Making procedures

Special Form: lambda formals body …
Special Form: ^ formals body …

[R7RS+ base] Evaluates to a procedure. The environment in effect when this expression is evaluated is stored in the procedure. When the procedure is called, body is evaluated sequentially in the stored environment extended by the bindings of the formal arguments, and returns the value(s) of the last expression in the body.

^ is a concise alias of lambda. It is Gauche’s extension.

(lambda (a b) (+ a b))
  ⇒ procedure that adds two arguments

((lambda (a b) (+ a b)) 1 2) ⇒ 3

((^(a b) (+ a b)) 1 2)       ⇒ 3

Gauche also extends R7RS lambda to take extended syntax in formals to specify optional and keyword arguments easily. The same functionality can be written in pure R7RS, with parsing variable-length arguments explicitly, but the code tends to be longer and verbose. It is recommended to use extended syntax unless you’re writing portable code.

Formals should have one of the following forms:

  • (variable …) : The procedure takes a fixed number of arguments. The actual arguments are bound to the corresponding variables.
    ((lambda (a) a) 1)    ⇒ 1
    ((lambda (a) a) 1 2)  ⇒ error - wrong number of arguments
  • variable : The procedure takes any number of arguments. The actual arguments are collected to form a new list and bound to the variable.
    ((lambda a a) 1 2 3)  ⇒ (1 2 3)
  • (variable_0variable_N-1 . variable_N) : The procedure takes at least N arguments. The actual arguments up to N is bound to the corresponding variables. If more than N arguments are given, the rest arguments are collected to form a new list and bound to variable_N.
    ((lambda (a b . c) (print "a=" a " b=" b " c=" c)) 1 2 3 4 5)
     ⇒ prints a=1 b=2 c=(3 4 5)
  • (variableextended-spec …) : Extended argument specification. Zero or more variables that specifies required formal arguments, followed by an extended spec, a list beginning with a keyword :optional, :key or :rest.

    The extended-spec part consists of the optional argument spec, the keyword argument spec and the rest argument spec. They can appear in any combinations.

    :optional optspec

    Specifies optional arguments. Each optspec can be either one of the following forms:

    (variable init-expr)

    The variable names the formal argument, which is bound to the value of the actual argument if given, or the value of the expression init-expr otherwise. If optspec is just a variable, and the actual argument is not given to it, then variable will be bound to #<undef> (see Undefined values).

    The expression init-expr is only evaluated if the actual argument for variable is not given. The scope in which init-expr is evaluated includes the preceding formal arguments.

    ((lambda (a b :optional (c (+ a b))) (list a b c))
     1 2)    ⇒ (1 2 3)
    ((lambda (a b :optional (c (+ a b))) (list a b c))
     1 2 -1) ⇒ (1 2 -1)
    ((lambda (a b :optional c) (list a b c))
     1 2)    ⇒ (1 2 #<undef>)
    ((lambda (:optional (a 0) (b (+ a 1))) (list a b))
     )       ⇒ (0 1)

    The procedure signals an error if more actual arguments than the number of required and optional arguments are given, unless it also has :key or :rest argument spec.

    ((lambda (:optional a b) (list a b)) 1 2 3)
     ⇒ error - too many arguments
    ((lambda (:optional a b :rest r) (list a b r)) 1 2 3)
     ⇒ (1 2 (3))
    :key keyspec … [:allow-other-keys [variable]]

    Specifies keyword arguments. Each keyspec can be either one of the following forms.

    (variable init-expr)
    ((keyword variable) init-expr)

    The variable names the formal argument, which is bound to the actual argument given with the keyword of the same name as variable. When the actual argument is not given, init-expr is evaluated and the result is bound to variable in the second and third form, or #<undef> is bound in the first form.

    (define f (lambda (a :key (b (+ a 1)) (c (+ b 1)))
                (list a b c)))
    (f 10)            ⇒ (10 11 12)
    (f 10 :b 4)       ⇒ (10 4 5)
    (f 10 :c 8)       ⇒ (10 11 8)
    (f 10 :c 1 :b 3)  ⇒ (10 3 1)

    With the third form you can name the formal argument differently from the keyword to specify the argument.

    ((lambda (:key ((:aa a) -1)) a) :aa 2)
      ⇒ 2

    By default, the procedure with keyword argument spec raises an error if a keyword argument with an unrecognized keyword is given. Giving :allow-other-keys in the formals suppresses this behavior. If you give variable after :allow-other-keys, the list of unrecognized keywords and their arguments are bound to it. Again, see the example below will help to understand the behavior.

    ((lambda (:key a) a)
     :a 1 :b 2)  ⇒ error - unknown keyword :b
    ((lambda (:key a :allow-other-keys) a)
     :a 1 :b 2)  ⇒ 1
    ((lambda (:key a :allow-other-keys z) (list a z))
     :a 1 :b 2)  ⇒ (1 (:b 2))

    When used with :optional argument spec, the keyword arguments are searched after all the optional arguments are bound.

    ((lambda (:optional a b :key c) (list a b c))
     1 2 :c 3)  ⇒ (1 2 3)
    ((lambda (:optional a b :key c) (list a b c))
     :c 3)      ⇒ (:c 3 #<undef>)
    ((lambda (:optional a b :key c) (list a b c))
     1 :c 3)     ⇒ error - keyword list not even
    :rest variable

    Specifies the rest argument. If specified without :optional argument spec, a list of remaining arguments after required arguments are taken is bound to variable. If specified with :optional argument spec, the actual arguments are first bound to required and all optional arguments, and the remaining arguments are bound to variable.

    ((lambda (a b :rest z) (list a b z))
     1 2 3 4 5)  ⇒ (1 2 (3 4 5))
    ((lambda (a b :optional c d :rest z) (list a b c d z))
     1 2 3 4 5)  ⇒ (1 2 3 4 (5))
    ((lambda (a b :optional c d :rest z) (list a b c d z))
     1 2 3)      ⇒ (1 2 3 #<undef> ())

    When the rest argument spec is used with the keyword argument spec, both accesses the same list of actual argument—the remaining arguments after required and optional arguments are taken.

    ((lambda (:optional a :rest r :key k) (list a r k))
     1 :k 3)  ⇒ (1 (:k 3) 3)

    See also let-optionals*, let-keywords and let-keywords* macros in Optional argument parsing for an alternative way to receive optional/keyword arguments within the spec of R7RS.

Macro: ^c body …

A shorthand notation of (lambda (c) body …). where c can be any character in #[_a-z].

(map (^x (* x x)) '(1 2 3 4 5)) ⇒ (1 4 9 16 25)
Macro: cut expr-or-slot expr-or-slot2 …
Macro: cute expr-or-slot expr-or-slot2 …

[SRFI-26] Convenience macros to notate a procedure compactly. This form can be used to realize partial application, a.k.a sectioning or projection.

Each expr-or-slot must be either an expression or a symbol <>, indicating a ’slot’. The last expr-or-slot can be a symbol <...>, indicating a ’rest-slot’. Cut expands into a lambda form that takes as many arguments as the number of slots in the given form, and whose body is an expression

  (expr-or-slot expr-or-slot2 …)

where each occurrence of <> is replaced to the corresponding argument. In case there is a rest-slot symbol, the resulting procedure is also of variable arity, and all the extra arguments are passed to the call of expr-or-slot. See the fourth example below.

(cut cons (+ a 1) <>)  ≡ (lambda (x2) (cons (+ a 1) x2))
(cut list 1 <> 3 <> 5) ≡ (lambda (x2 x4) (list 1 x2 3 x4 5))
(cut list)             ≡ (lambda () (list))
(cut list 1 <> 3 <...>)
   ≡ (lambda (x2 . xs) (apply list 1 x2 3 xs))
(cut <> a b)           ≡ (lambda (f) (f a b))

;; Usage
(map (cut * 2 <>) '(1 2 3 4))
(for-each (cut write <> port) exprs)

Cute is a variation of cut that evaluates expr-or-slots before creating the procedure.

(cute cons (+ a 1) <>)
   ≡ (let ((xa (+ a 1))) (lambda (x2) (cons xa x2)))

Gauche provides a couple of different ways to write partial applications concisely; see the $ macro below, and also the pa$ procedure (see Combinators).

Macro: $ arg …

A macro to chain applications, hinted from Haskell’s $ operator (although the meaning is different). Within the macro arguments arg …, $ delimits the last argument. For example, the following code makes the last argument for the procedure f to be (g c d …)

  ($ f a b $ g c d ...)
  ≡ (f a b (g c d ...))

The $ notation can be chained.

  ($ f a b $ g c d $ h e f ...)
  ≡ (f a b (g c d (h e f ...)))

If $* appears in the argument list instead of $, it fills the rest of the arguments, instead of just the last argument.

  ($ f a b $* g c d ...)
  ≡ (apply f a b (g c d ...))

  ($ f a b $* g $ h $* hh ...)
  ≡ (apply f a b (g (apply h (hh ...))))

Furthermore, if the argument list ends with $ or $*, the whole expression becomes a procedure expecting the last argument(s).

  ($ f a b $ g c d $ h e f $)
  ≡ (lambda (arg) (f a b (g c d (h e f arg))))
  ≡ (.$ (cut f a b <>) (cut g c d <>) (cut h e f <>))

  ($ f a b $ g c d $ h e f $*)
  ≡ (lambda args (f a b (g c d (apply h e f args))))
  ≡ (.$ (cut f a b <>) (cut g c d <>) (cut h e f <...>))

The more functional the code becomes, the more you are tempted to write it as a chain of nested function calls. Scheme’s syntax can get awkward in such code. Close parentheses tend to clutter at the end of expressions. Inner applications tends to pushed toward right columns with the standard indentation rules. Compare the following two code functionally equivalent to each other:

(intersperse ":"
             (map transform-it
                  (delete-duplicates (map cdr
                                          (group-sequence input)))))

($ intersperse ":"
   $ map transform-it
   $ delete-duplicates
   $ map cdr $ group-sequence input)

It is purely a matter of taste, and also this kind of syntax sugars can be easily abused. Use with care, but it may work well if used sparingly, like spices.

As a corner case, if neither $ nor $* appear in the argument list, it just calls the function. It is useful when the function has long name and you don’t want to indent arguments too further right.

  ($ f a b c) ≡ (f a b c)
Macro: case-lambda clause …

[R7RS case-lambda] Each clause should have the form (formals expr …), where formals is a formal arguments list as for lambda.

This expression evaluates to a procedure that accepts a variable number of arguments and is lexically scoped in the same manner as procedures resulting from lambda expressions. When the procedure is called with some arguments, then the first clause for which the arguments agree with formals is selected, where agreement is specified as for the formals of a lambda expression. The variables of formals are bound to the given arguments, and the expr … are evaluated within the environment.

It is an error for the arguments not to agree with the formals of any clause.

(define f
    [() 'zero]
    [(a) `(one ,a)]
    [(a b) `(two ,a ,b)]))

(f)       ⇒ zero
(f 1)     ⇒ (one 1)
(f 1 2)   ⇒ (two 1 2)
(f 1 2 3) ⇒ Error: wrong number of arguments to case lambda

(define g
    [() 'zero]
    [(a) `(one ,a)]
    [(a . b) `(more ,a ,@b)]))

(g)       ⇒ zero
(g 1)     ⇒ (one 1)
(g 1 2 3) ⇒ (more 1 2 3)

Note that the clauses are examined sequentially to match the number of arguments, so in the following example g2 never returns (one ...).

(define g2
    [() 'zero]
    [(a . b) `(more ,a ,@b)]
    [(a) `(one ,a)]))

(g2 1)    ⇒ (more 1)

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

For Development HEAD DRAFTSearch (procedure/syntax/module):