ALAMBDA and ALAMBDA*
Joo ChurlSoo
This SRFI is currently in withdrawn status. Here is an explanation of each status that a SRFI can hold. To provide input on this SRFI, please send email to srfi-92@nospamsrfi.schemers.org
. To subscribe to the list, follow these instructions. You can access previous messages via the mailing list archive.
This SRFI introduces ALAMBDA and ALAMBDA*, each of which has two modes of operation:
Note: the name and contents of this SRFI have been changed, to reflect that CHECK-LAMBDA, and COND-LAMBDA were combined into the new ALAMBDA.
The first mode of operation reduces not only the clutter of various error conditionals by checking actual arguments but also somewhat lengthy code by combining optional argument handling methods such as LET-OPTIONALS and LET-KEYWORDS into a single syntax.
Optional variables include not only optional fixed variables
but also optional non-fixed variables. The formers are the same
as those of opt
form of ALET and the latters are the
same as those of cat
and key
forms of
ALET (see SRFI-86). The following are examples to show the
similarities.
1. optional fixed variables (opt
form):
((lambda (str . rest) (alet* ((len (string-length str)) (opt rest (start 0 (and (integer? start) (<= 0 start len))) (end len (and (integer? end) (<= start end len))))) (substring str start end))) "abcdefg" 1 6) => "bcdef" ((alambda* (str "opt" (start 0 (and (integer? start) (<= 0 start (string-length str)))) (end (string-length str) (and (integer? end) (<= start end (string-length str))))) (substring str start end)) "abcdefg" 1 6) => "bcdef"
2. optional non-fixed non-named variables (cat
form):
((lambda (str . rest) (alet ((cat rest (start 0 (and (list? start) (= 2 (length start)) (eq? 'start (car start))) (cadr start)) ; true (end (string-length str) (and (list? end) (= 2 (length end)) (eq? 'end (car end))) (cadr end)))) ; true (substring str start end))) "abcdefg" '(end 6) '(start 1)) => "bcdef" ((alambda* (str "cat" (start 0 (and (list? start) (= 2 (length start)) (eq? 'start (car start))) (cadr start)) ; true (end (string-length str) (and (list? end) (= 2 (length end)) (eq? 'end (car end))) (cadr end))) ; true (substring str start end)) "abcdefg" '(end 6) '(start 1)) => "bcdef" 3. optional non-fixed named variables (`key' form): ((lambda (str . rest) (alet* ((len (string-length str)) (key rest (start 0 (and (integer? start) (<= 0 start len))) (end len (and (integer? end) (<= start end len))))) (substring str start end))) "abcdefg" 'end 6 'start 1) => "bcdef" ((alambda* (str "key" (start 0 (and (integer? start) (<= 0 start (string-length str)))) (end (string-length str) (and (integer? end) (<= start end (string-length str))))) (substring str start end)) "abcdefg" 'end 6 'start 1) => "bcdef"
Like optional variables, required variables can be divded into three groups, namely, conventional required fixed variables, required non-fixed non-named variables, and required non-fixed named variables. These are best explained by simple examples.
1. required fixed variables:
(define vec-ref (alambda* ((vec (vector? vec)) (num (and (integer? num) (<= 0 num) (< num (vector-length vec))))) (vector-ref vec num))) (vec-ref '#(1 2 3) 1) => 2 (vec-ref 1 '#(1 2 3)) => bad argument 1 vec (vector? vec) (vec-ref '#(1 2 3)) => wrong number of arguments
2. required non-fixed non-named variables:
(define str-ref (alambda* ("required cat" (str (string? str)) (n (and (integer? n) (<= 0 n) (< n (string-length str))))) (string-ref str n))) (str-ref "abc" 1) => #\b (str-ref 1 "abc") => #\b (str-ref 1 2) => bad arguments (1 2) str (string? str) (str-ref "abc") => wrong number of arguments
3. required non-fixed named variables:
(define lis-ref (alambda* ("required key" (lis (list? lis)) (num (and (integer? num) (<= 0 num) (< num (length lis))))) (list-ref lis num))) (lis-ref 'num 1 'lis '(1 2 3)) => 2 (lis-ref 'lis '(1 2 3) 'num 1) => 2 (lis-ref 'lis '(1 2 3) 'nu 3) => no corresponding value to key num (nu 3) (lis-ref 'lis '(1 2 3) 'nu) => wrong number of arguments
The second mode of operation reduces the clutter of procedures
more precisely than CASE-LAMBDA of SRFI-16 by adding
<test>
to its formal argument list to check
each of actual arguments. The following are examples to show the
differences.
(define list-ref/set! (case-lambda ((a b) (list-ref a b)) ((a b c) (set-car! (list-tail a b) c)) (a (error "bad arguments" a)))) (define ref/set! (alambda (cond (((a (list? a)) b) (list-ref a b)) (((a (string? a)) b) (string-ref a b)) (((a (vector? a)) b) (vector-ref a b)) (((a (list? a)) b c) (set-car! (list-tail a b) c)) (((a (string? a)) b c) (string-set! a b c)) (((a (vector? a)) b c) (vector-set! a b c)) (a (error "bad arguments" a)))))
The syntax is defined in the extended BNF of R5RS.
(alambda <sentence>) (alambda* <sentence>) syntax-rules identifier: cond <sentence> --> <extended formals> <body> | (cond <clause>+) <extended formals> --> | (<required spec> <optional spec> <key option>) | (<required spec> <key option>) | (<opiontal spec> <key option>) | (<required spec> <optional spec> <key option> . <variable>) | (<required spec> <key option> . <variable>) | (<optional spec> <key option> . <variable>) | <variable> | () <required spec> --> <required fixed variable spec>+ | <required fixed variable spec>* <required non-fixed spec> <required fixed variable spec> --> <variable> | (<variable> <test spec>) <test spec> --> <test> | <test> <true substitute> | <test> <true substitute> <false substitute> <required non-fixed spec> --> <required cat spec> | <required key spec> | <required cat spec> <required key spec> | <required key spec> <required cat spec> <required cat spec> --> "required cat" <required fixed variable spec>+ <required key spec> --> "required key" <required key variable spec>+ <required key variable spec> --> <required fixed variable spec> | ((<variable> <keyword>)) | ((<variable> <keyword>) <test spec>) <optional spec> --> <opt spec> | <cat spec> | <key spec> | <opt spec> <cat spec> | <opt spec> <key spec> | <cat spec> <key spec> | <key spec> <cat spec> | <opt spec> <cat spec> <key spec> | <opt spec> <key spec> <cat spec> <opt spec> --> "opt" <opt variable spec>+ <opt variable spec> --> <variable> | (<variable> <default>) | (<variable> <default> <test spec>) <cat spec> --> "cat" <opt variable spec>+ <key spec> --> "key" <key variable spec>+ <key variable spec> --> <opt variable spec> | ((<variable> <keyword>)) | ((<variable> <keyword>) <default>) | ((<variable> <keyword>) <default> <test spec>) <key option> --> #f | #t | <empty> <test> --> <expression> <true substitute> --> <expression> <false substitute> --> <expression> <default> --> <expression> <keyword> --> <any scheme object> <clause> --> (<cond formals> <body>) <cond formals> --> (<cond variable spec>*) | <variable> | (<cond variable spec>+ . <variable>) <cond variable spec> --> <variable> | (<variable> <test>)
The ALAMBDA* is to the ALAMBDA what the LET* is to the LET.
The <default>
s, <test>
s,
<true substitute>
s, and <false
substitute>
s of ALAMBDA* are evaluated in an
environment that all the bindings of previous
<variable>
s are visible.
1. the first mode of operation:
There are three kinds of required variables, namely, required fixed variable, required non-fixed non-named variable, and required non-fixed named variable. They determine the number of required actual arguments, that is, the minimum arity of the resulting procedure.
The required fixed variables are placed before any string
marker in <extended formals>
. They are bound
to successive actual arguments starting with the first actual
argument. If there is a <test>
, it is
evaluated. If it returns a false value and there is no
<false substitute>
, an error is signaled. If
it returns a false value and there is a <false
substitute>
, the variable is rebound to the result of
evaluating <false substitute>
instead of
signaling an error. If it returns a true value and there is a
<true substitute>
, the variable is rebound to
the result of evaluating <true
substitute>
.
The required non-fixed non-named variables follow a "required
cat" marker in <extended formals>
. The
variable is temporarily bound to each of remaining required
actual arguments sequentially, until <test>
returns a true value, then the variable is finally bound to the
passed argument. If there is no <test>
, the
first one of the remaining required actual arguments is regarded
as passing. If any actual argument does not pass
<test>
, an error is signaled. If there is a
<false substitute>
and
<test>
returns a false value, the variable is
finally bound to the result of evaluating <false
substitute>
instead of the above process. If there is a
<true substitute>
and
<test>
returns a true value, the variable is
rebound to the result of evaluating <true
substitute>
.
The required non-fixed named variables follow a "required key"
marker in <extended formals>
. Unlike SRFI-89,
the keywords are not self-evaluating symbols, but any scheme
objects. The keyword used at a call site for the corresponding
variable is a symbol of the same name as the variable. But the
keyword can be any scheme object when the required parameter is
specified as a double parenthesized variable and a keyword. The
remaining required actual arguments must be an even number. They
are sequentially interpreted as a series of pairs, where the
first member of each pair is a keyword corresponding to the
variable, and the second is the corresponding value. If there is
no element for a particular keyword, an error is signaled. When
there is a <test>
, it is evaluated. If it
returns a false value and there is no <false
substitute>
, an error is signaled. If it returns a
false value and there is a <false substitute>
,
the variable is rebound to the result of evaluating
<false substitute>
instead of signaling an
error. If it returns a true value and there is a <true
substitute>
, the variable is rebound to the result of
evaluating <true substitute>
.
The optional fixed variables follow an "opt" marker in
<extended formals>
. The binding method is the
same as that of the required fixed variables except that the
variable is bound to the result of evaluating
<default>
instead of signaling an error if
there are no remaining actual arguments. If
<default>
is not specified, #f
is
the default.
The optional non-fixed non-named variables follow a "cat"
marker in <extended formals>
. The binding
method is the same as that of the required non-fixed non-named
variables except that the variable is bound to the result of
evaluating <default>
instead of signaling an
error if any actual argument does not pass
<test>
. If <default>
is not
specified, #f
is the default.
The optional non-fixed named variables follow a "key" marker
in <extended formals>
. The binding method is
the same as that of the required non-fixed named variables except
that the variable is bound to the result of evaluating
<default>
instead of signaling an error if
there is no corresponding value to a particular keyword. If
<default>
is not specified, #f
is
the default.
The following key options can be used to control binding
behavior in case that the keyword of keyword-value pair at the
call site is different from any keywords specified in
<extended formals>
.
#f
-- An error is signaled in case of required
non-fixed named variables. In case of optional non-fixed named
variables, the variable is bound to the corresponding
<default>
.#t
-- The remaining actual arguments are
continually interpreted as a single element until the element
is a particular keyword.When there are remaining actual arguments, an error is signaled if dotted rest variable is not given. If dotted rest variable is given, it is bound to the remaining actual arguments.
2. the second mode of operation:
This is an extended form of CASE-LAMBDA of SRFI-16. Like
CASE-LAMBDA, it returns a procedure of the first
<clause>
, the <cond
formals>
of which is matched with the number of actual
arguments. But if there is a <test>
and the
result of evaluation returns a false value, the subsequent
<clause>
is processed in spite of the match.
If no <clause>
matches, an error is
signaled.
((alambda (a (b (number? b)) "opt" (c 10) "key" ((d 'dd) 30 (number? d)) e ((f "ff") 40) (g 50) . h) (list a b c d e f g h)) 0 1 2 'dd 3 44 55 'g 6 'dd 4 'f 66 77 "ff" 5) => (0 1 2 3 #f 40 6 (44 55 dd 4 f 66 77 "ff" 5)) ((alambda (a (b (number? b)) "opt" (c 10) "key" ((d 'dd) 30 (number? d)) e ((f "ff") 40) (g 50) #f . h) (list a b c d e f g h)) 0 1 2 'dd 3 44 55 'g 6 'dd 4 'f 66 77 "ff" 5) => (0 1 2 3 #f 40 50 (44 55 g 6 dd 4 f 66 77 "ff" 5)) ((alambda (a (b (number? b)) "opt" (c 10) "key" ((d 'dd) 30 (number? d)) e ((f "ff") 40) (g 50) #t . h) (list a b c d e f g h)) 0 1 2 'dd 3 44 55 'g 6 'dd 4 'f 66 77 "ff" 5) => (0 1 2 3 #f 5 6 (44 55 dd 4 f 66 77)) (define ref/set! (alambda* (cond (((a (list? a)) (b (and (integer? b) (<= 0 b) (< b (length a))))) (list-ref a b)) (((a (string? a)) (b (and (integer? b) (<= 0 b) (< b (string-length a))))) (string-ref a b)) (((a (vector? a)) (b (and (integer? b) (<= 0 b) (< b (vector-length a))))) (vector-ref a b)) (((a (list? a)) (b (and (integer? b) (<= 0 b) (< b (length a)))) c) (set-car! (list-tail a b) c)) (((a (string? a)) (b (and (integer? b) (<= 0 b) (< b (string-length a)))) (c (char? c))) (string-set! a b c)) (((a (vector? a)) (b (and (integer? b) (<= 0 b) (< b (vector-length a)))) c) (vector-set! a b c)) (a (error "bad arguments" a))))) (let ((str (string #\a #\b #\c)) (lis (list 1 2 3)) (vec (vector 4 5 6))) (display (ref/set! str 1)) (display " ") (display (ref/set! lis 1)) (display " ") (display (ref/set! vec 1)) (newline) (display str) (display " ") (display lis) (display " ") (display vec) (newline) (ref/set! str 1 #\z) (ref/set! lis 1 8) (ref/set! vec 1 9) (display str) (display " ") (display lis) (display " ") (display vec) (newline)) => b 2 5 abc (1 2 3) #(4 5 6) azc (1 8 3) #(4 9 6)
The implementation is written in R5RS hygienic macros and requires SRFI-23 (Error reporting mechanism).
[R5RS] Richard Kelsey, William Clinger, and Jonathan Rees: Revised(5) Report on the Algorithmic Language Scheme http://www.schemers.org/Documents/Standards/R5Rs/ [SRFI 16] Lars T Hansen: Syntax for procedures of variable arity. https://srfi.schemers.org/srfi-16/ [SRFI 86] Joo ChurlSoo: MU and NU simulating VALUES & CALL-WITH-VALUES, and their related LET-syntax. https://srfi.schemers.org/srfi-86/ [SRFI 89] Marc Feeley: Optional and named parameters. https://srfi.schemers.org/srfi-89/
Copyright (c) 2006 Joo ChurlSoo.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ``Software''), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Editor: Francisco Solsona