by Panicz Maciej Godek
This SRFI is currently in draft status. Here is an explanation of each status that a SRFI can hold. To provide input on this SRFI, please send email to
firstname.lastname@example.org. To subscribe to the list, follow these instructions. You can access previous messages via the mailing list archive.
The SRFI-2 library introduced the
for short-circuited evaluation in the style of the
form with the ability to capture the (non-
in the style of the
let* form. This document extends
and-let* form with the abilities to pattern-match
(or "destructurally bind") the values of evaluated expressions
(where the match failure causes short-circuiting rather than
raising an error), and to handle multiple values (where only the
falsehood of the first value causes short-circuiting).
It is well known that, in the context of the
#f as a false value, and every other value
is considered true (or "true-ish").
This design decision has led to the development of the idiom in which the
#f is considered to represent "the absence of value" or a "failure
of computation", whereas and other value is considered to represent a meaningful
In strongly typed languages, this pattern is usually expressed in terms of algebraic
data types. For example, the Haskell language defines the polymorphic type
Maybe a as either
Just a or
Just a is used to represent the value of type
Nothing is used to represent the absence of value.
Moreover, in many functional languages, the types isomorphic to
are considered to be "monads", which means that they can use some facilities
that those languages provide for sequencing functions that return things that
are considered to be "monads". In particular, Haskell provides a special syntactic
form called the
do notation, which allows the function like
h x = case (f x) of Just y -> case (g (k y)) of Just z -> Just (l z) Nothing -> Nothing Nothing -> Nothingto be rewritten as
h x = do y <- (f x) z <- (g (k y)) return (l z)
optional values, and
l return obligatory
Since these facilities rely on some advanced features of type systems
that are lacking from most Scheme implementations, Schemers have invented
a special form specifically for dealing with optional values. The form
and-let* and has been defined in the SRFI-2 document.
Accordingly, the equivalent code using Scheme can be written as
(define (h x) (and-let* ((y (f x)) (z (g (k y)))) (l z)))
However, the original definition of
and-let* does not allow
one to handle multiple values. It also doesn't allow one to destructure the value
of the evaluated expression. The purpose of this SRFI is to extend the
and-let* form with these capabilities.
Therefore, with the capabilities from this SRFI, the two first examples from the Rationale section of SRFI 2, namely:
(and-let* ((my-list (compute-list)) ((not (null? my-list)))) (do-something my-list)) (define (lookup key alist) (and-let* ((x (assq key alist))) (cdr x)))
could roughly be rewritten as:
(and-let* ((`(,my . ,list) (compute-list))) (do-something `(,my . ,list))) (define (lookup key alist) (and-let* ((`(,key . ,value) (assq key alist))) key))
(The first example is not exactly equivalent, because it assumes
that the test
(not (null? my-list)) has the same meaning
(pair? my-list), which doesn't need to be true, even
if usually is. Moreover, the body of the form contains an unnecessary
cons operation which may incur performance penalty.)
The extensions provided here do not invalidate or modify any of the prior
uses of the SRFI 2's
In particular, the
and-let* form allowed two types of expressions
inside of its "claws":
This SRFI document allows
variable to be an arbitrary
pattern that is a valid pattern of the underlying pattern matcher
(see SRFI 200
Pattern Matching for more details):
In particular, since patterns can be literal values, it is possible to negate the condition:
(and-let* ((my-list (compute-list)) (#f (null? my-list))) (do-something my-list))
If the value of an expression does not match the pattern, then the
value of the whole
and-let* form is
This variant of the
and-let* form also supports multiple
values, each of which can be a pattern:
(pattern patterns ... expression)
If the first
pattern is a variable, then it must have
#f value in order to proceed with evaluation
— otherwise, the value of the whole
expression returns fewer values than expected,
then the value of the whole
and-let* expression becomes
#f. If it returns more values than expected, then the
additional values are ignored.
(define-syntax and-let* (lambda (stx) (syntax-case stx () ((_) #'#t) ((_ ()) #'#t) ((_ () body ...) #'(let () body ...)) ((_ ((value expression) rest ...) body ...) (identifier? #'value) #'(let ((value expression)) (and value (and-let* (rest ...) body ...)))) ((_ ((value expression) rest ...) body ...) #'(match expression (value (and-let* (rest ...) body ...)) (_ #f))) ((_ ((condition) rest ...) body ...) #'(and condition (and-let* (rest ...) body ...))) ((_ ((value values ... expression) rest ...) body ...) (identifier? #'value) #'(call-with-values (lambda () expression) (lambda args (match args ((value values ... . _) (and value (and-let* (rest ...) body ...))) (_ #f))))) ((_ ((values ... expression) rest ...) body ...) #'(call-with-values (lambda () expression) (lambda args (match args ((values ... . _) (and-let* (rest ...) body ...)) (_ #f))))) )))
A working implementation for Guile Scheme can be found in the (grand syntax) module of the (grand scheme) glossary.
A working implementation for the Racket language can be found in the "grand-syntax.rkt" module of the Sracket framework.
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 (including the next paragraph) 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.