You are looking at historical revision 25632 of this page. It may differ significantly from its current revision.

bindings

The bindings module is a light-weight alternative to the matchable egg together with some enhancements, in particular the bind macro, which is Common Lisp's destructuring-bind.

Contrary to matchable, there is no attempt to implement ellipses, Scheme's dotted lists must do. Instead of the match macro you should use bind-case. And contrary to matchable, all binding macros can destructure arbitrary nested sequences, i.e. mixtures of lists, pseudo-lists, vectors and strings.

Documentation

Programming interface

bindings

Like modules written in the Design by Contract style, this module contains its documentation builtin in the form of a dispatcher routine with the modules name, bindings, which when called with no argument

(bindings)

returns the list of exported symbols, namely

(bind bind-case bind-case-lambda bind-case-lambda* bind-define
 bind-lambda bind-lambda* bind-let bind-let* bind-letrec bind-loop
 bind-matches? bind-set! bindrec syms->vars)

which are all macros, while calling bindings with one of this symbols, e.g.

(bindings 'bind)

shows the documentation of this symbol in all it's glory, i.e. together with admissible forms and a documentation string

(bind pat seq xpr . xprs)
binds pattern variables of pat to subexpressions of seq
and executes xpr . xprs in this context

Another example:

(bindings 'bind-case)

will show you something like

(bind-case seq (pat => test? xpr . xprs))
(bind-case seq (pat xpr . xprs))
(bind-case seq (pat => test? xpr . xprs) clause ...)
(bind-case seq (pat xpr . xprs) clause ...)
matches seq against a series of patterns [with fender test?, a thunk]
and executes the body of the first matching pattern [satisfying (test?)]

bind

[syntax] (bind pat seq xpr . xprs)

binds pattern variables of pat to subexpressions of seq and executes xpr . xprs in this context

bind-matches?

[syntax] (bind-matches? seq pat [ok?])

checks, if the sequence expression seq matches pattern pat and fender ok? - a thunk - is true

bind-case

[syntax] (bind-case seq clause0 clause1 ...)

where seq is a sequence expression and each clause is of one of two forms

(pat => test? xpr . xprs)
(pat xpr . xprs)

Matches seq against a series of patterns [with fender test?, a thunk] and executes the body of the first matching pattern satisfying (test?)

bind-case-lambda

[syntax] (bind-case-lambda clause0 clause1 ...)

where each clause is of one of two forms

(pat => test? xpr . xprs)
(pat xpr . xprs)

Combination of bind-case and lambda with one pattern argument

bind-case-lambda*

[syntax] (bind-case-lambda* clause0 clause1 ...)

where each clause is of one of two forms

(pat => test? xpr . xprs)
(pat xpr . xprs)

Combination of bind-case and lambda with multiple pattern arguments

bind-lambda

[syntax] (bind-lambda pat xpr . xprs)

combination of lambda and bind, one pattern argument

bind-lambda*

[syntax] (bind-lambda* pat xpr . xprs)

combination of lambda and bind, multiple pattern arguments

bind-let

[syntax] (bind-let [loop] ((pat seq) ...) xpr . xprs)

like let, named and unnamed, but binds patterns to sequence templates. In the named case loop is bound to a one-parameter-procedure accessible in the body xpr . xprs

bind-let*

[syntax] (bind-let* ((pat seq) ...) xpr . xprs)

like let*, but binds patterns to sequence templates

bind-letrec

[syntax] (bind-letrec ((pat seq) ...) xpr . xprs)

like letrec, but binds patterns to sequence templates

bind-loop

[syntax] (bind-loop proc pat seq xpr . xprs)

bind-loop is for bind what named let is for let. proc is bound to a one-parameter procedure, which can be used in the body xpr . xprs

bindrec

[syntax] (bindrec pat seq xpr . xprs)

bind pattern variables of pat to subsequences of seq recursively

bind-define

[syntax] (bind-define pat seq)

defines pattern variables of pat with values matching subexpressions of seq in one go

bind-set!

[syntax] (bind-set! pat seq)

sets symbols of pat to corresponding subexpressions of seq

syms->vars

[syntax] (syms->vars sym0 sym1 ...)

transforms its symbol arguments to variables initialized with their own names

Requirements

None

Usage

(use bindings)

Examples


(use bindings)

(let ((stack #f) (push! #f) (pop! #f))
  (bind-set! (stack (push! pop!))
    (list
      '()
      (vector
        (lambda (xpr) (set! stack (cons xpr stack)))
        (lambda () (set! stack (cdr stack))))))
  (push! 1)
  (push! 0)
  stack)
; -> '(0 1)

(let ((x #f) (y #f) (z #f))
  nd executes the body of the first matching pattern [satisfying
(test?)]
(bind-set! (x (y . z)) '(1 #(2 3 3)))
  (list x y z))
; -> '(1 2 #(3 3))

(begin
  (bind-define (top push! pop!)
    (let ((lst '()))
      (vector
        (lambda () (car lst))
        (lambda (xpr) (set! lst (cons xpr lst)))
        (lambda () (set! lst (cdr lst))))))
  (push! 0)
  (push! 1)
  (pop!)
  (top))
; -> 0

(bind a 1 a) ; -> 1

(bind (x y z w) '(1 2 3 4) (list x y z w) ; -> '(1 2 3 4)

(bind (x . y) '#(1 2 3 4) (list x y)) ; -> '(1 #(2 3 4))

(bind (x (y (z u . v)) w) '(1 #(2 "foo") 4)
  (list x y z u v w))
; -> '(1 2 #\f #\o "o" 4)

(bind (x (y (z . u)) v . w) (vector 1 (list 2 (cons 3 4)) 5 6)
  (list x y z u v w))
; -> '(1 2 3 4 5 #(6))

((bind-lambda (a (b . C) . d)
   (list a b C d))
 '(1 #(20 30 40) 2 3))
; -> '(1 20 #(30 40) (2 3))

((bind-lambda* ((a (b . C) . d) (e . f))
   (list a b C d e f))
 '(1 #(20 30 40) 2 3) '#(4 5 6))
; -> '(1 20 #(30 40) (2 3) 4 #(5 6))

(bind-loop proc (x (a . b) y) '(5 #(1) 0)
  (if (zero? x)
    (list x a b y)
    (proc (list (- x 1) (cons a (cons a b)) (+ y 1)))))
; -> '(0 1 (1 1 1 1 1 . #()) 5)

(bind-loop proc (x y) '(5 0)
  (if (zero? x)
    (vector x y)
    (proc (vector (- x 1) (+ y 1)))))
; -> '#(0 5)

(bind-let (((x y (z . w)) '(1 2 #(3 4 5))))
  (list x y z w))
; -> '(1 2 3 #(4 5))

(bind-let (
  (((x y) z) '(#(1 2) 3))
  (u (+ 2 2))
  ((v w) '#(5 6))
  )
  (list x y z u v w))
; -> '(1 2 3 4 5 6)

(bind-let loop (((a b) '(5 0)))
  (if (zero? a)
    (list a b)
    (loop (list (list (- a 1) (+ b 1))))))
; -> '(0 5)

(bind-let loop (
  ((x . y) '(1 2 3))
  ((z) '#(10))
  )
  (if (zero? z)
    (list x y z)
    (loop (list (cons (+ x 1) (map add1 y)) (list (- z 1))))))
; -> '(11 (12 13) 0)

(bind-let* (
  (((x y) z) '(#(1 2) 3))
  (u (+ 1 2 x))
  ((v w) (list (+ z 2) 6))
  )
  (list x y z u v w))
; -> '(1 2 3 4 5 6)

(bindrec ((o?) e?)
  (vector (list (lambda (m) (if (zero? m) #f (e? (- m 1)))))
          (lambda (n) (if (zero? n) #t (o? (- n 1)))))
  (list (o? 95) (e? 95)))
; -> '(#t #f)

(bind-letrec (
  ((o? (e?))
   (list (lambda (m) (if (zero? m) #f (e? (- m 1))))
         (vector (lambda (n) (if (zero? n) #t (o? (- n 1)))))))
  )
  (list (o? 95) (e? 95)))
; -> '(#t #f)

(let ((two '(1 2)))
  (bind-matches? two ()))
; -> #f

(bind-matches? '() ()) ; -> #t

(bind-matches? '(1 (2 3) . 4) (a (b C) . d)) ; -> #t

(bind-matches? '(1 #(2 3) 4 5) (a (b C) . d)) ; -> #t

(bind-matches? '(1 (2 3) 4) (a (b . C) . d)) ; -> #t

(bind-matches? '#(1 2 3 4 5) (a (b . C) . d)) ; -> #f

(bind-matches? '(1 (2 3) 4 5) (a (b C) d)) ; -> #f

(bind-case '#(1 2)
  (() '())
  ((a) (list a))
  ((a b) (list a b))
  ((a b C) (list a b C)))
; -> '(1 2))

(letrec (
  (my-map
    (lambda (fn lst)
      (bind-case lst
        (() '())
        ((x . xs) (cons (fn x) (map fn xs))))))
  )
  (my-map add1 '(1 2 3)))
; -> '(2 3 4)

(let ((xpr '(1 2 3 4 5)))
  ((bind-case-lambda
     ((a (b . C) . d) (list a b C d))
     ((e . f) => (lambda () (zero? (car xpr))) e)
     ((e . f) (list e f)))
   xpr))
; -> '(1 (2 3 4 5))

(let ((xpr '#(0 2 3 4 5)))
  ((bind-case-lambda
     ((e . f) => (lambda () (zero? (vector-ref xpr 0))) f)
     ((e . f) (list e f)))
   xpr))
; -> '#(2 3 4 5)

((bind-case-lambda
   ((a (b . C) . d) (list a b C d))
   ((e . f) (list e f)))
 '(1 #(2 3 4) 5 6))
; -> '(1 2 #(3 4) (5 6))

((bind-case-lambda*
   (((a b C . d) (e . f))
    (list a b C d e f)))
 '(1 2 3) '#(4 5 6))
; -> '(1 2 3 () 4 #(5 6))

((bind-case-lambda*
   (((a (b . C) . d) (e . f))
    (list a b C d e f)))
 '(1 #(20 30 40) 2 3) '(4 5 6))
; -> '(1 20 #(30 40) (2 3) 4 (5 6))

Last update

Sep 02, 2011

Author

Juergen Lorenz

License

Copyright (c) 2011, Juergen Lorenz
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
Neither the name of the author nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission. 
  
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Version History

1.0
all binding macros can now destructure arbitrary nested sequences, i.e mixtures of lists, pseudo-lists, vectors and strings; dependency on contracts removed.
0.1
initial import