You are looking at historical revision 29780 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

This documentation uses special ellipses, .. and ...., to repeat the pattern(s) to the left zero or one and one or many times respectively

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

(bindings bind bind-case bind-case-lambda bind-case-lambda* bind-define
 bind-lambda bind-lambda* bind-let bind-let* bind-letrec bind*
 bindable? bind-set! bindrec generic-null-car-cdr! generic-null? generic-car
 generic-cdr)

which are all macros, except the generic functions. When calling bindings with one of this symbols, e.g.

(bindings 'bind)

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

(bind pat seq (where . fenders) .. xpr . xprs)
binds pattern variables of pat to subexpressions of seq
and executes xpr . xprs in this context. Fenders can be used to reject
an otherwise matching pattern.

bind

[syntax] (bind pat seq xpr . xprs)

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

bindable?

[syntax] (bindable? pat . fenders)

returns a unary predicate which checks, if its sequence argument matches the pattern argument, pat, of bindable? and passes all of its fenders.

bind-case

[syntax] (bind-case seq clause ....)

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

(pat (where . fenders) xpr . xprs)
(pat xpr . xprs)

Matches seq against a series of patterns and executes the body of the first matching pattern satisfying fenders (if given).

bind-case-lambda

[syntax] (bind-case-lambda clause ....)

where each clause is of one of two forms

(pat (where . fenders) xpr . xprs)
(pat xpr . xprs)

Combination of bind-case and lambda with one pattern argument

bind-case-lambda*

[syntax] (bind-case-lambda* clause ....)

where each clause is of one of two forms

(pat (where . fenders) xpr . xprs)
(pat xpr . xprs)

Combination of bind-case and lambda with multiple pattern arguments

bind-lambda

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

combination of lambda and bind, one pattern argument

bind-lambda*

[syntax] (bind-lambda* pat (where . fenders) .. 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*

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

bind* 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

generic-null-car-cdr!

[procedure] (generic-null-car-cdr! type-pred null-proc car-proc cdr-proc)

updates the table of generic functions.

As implemented, the binding macros can handle arbitrary nested combinations of (pseudo-)lists, vectors,strings and records. But updating the table additional sequence types can be handled without touching the macros' code.

Requirements

None

Usage

(use bindings)

Examples


(use bindings tuples)

(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)

(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 (x (a . b) y) '(5 #(1) 0)
  (if (zero? x)
    (list x a b y)
    (loop (list (- x 1) (cons a (cons a b)) (+ y 1)))))
; -> '(0 1 (1 1 1 1 1 . #()) 5)

(bind* loop (x y) '(5 0)
  (if (zero? x)
    (vector x y)
    (loop (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

((bindable? ()) '()) ; -> #t

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

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

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

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

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

((bindable? (a b) (where (even? a)) (a b)) '#(1 2)) ; -> #t 

(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)

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

((bind-case-lambda
	 ((e . f) (where (zero? e)) f)
	 ((e . f) (list e f)))
 '#(0 2 3 4 5))
;-> '#(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))

(define-record point x y)
(bind (x y) (make-point 1 2) (list x y)) ;-> '(1 2)
(bind (x (y z)) (vector 1 (make-point 2 3)) (list x y z))
      ;-> '(1 2 3)
(bind (x . y) (make-point 1 2) (list x y)) ;-> '(1 #(2))

;;adding new types to generics 
(generic-null-car-cdr! tuple? tuple-empty? tuple-left tuple-butleft)
(bind (x y z) (tuple 1 2 3) (list x y z)) ;-> '(1 2 3)
(bind (x (y z)) (vector 0 (tuple 1 2)) (list x y z)) ;-> '(0 1 2)

Last update

Sep 24, 2013

Author

Juergen Lorenz

License

Copyright (c) 2011-2013, 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

2.3
code partially rewritten, syntax of bindable? changed, matching records outsourced to the tests
2.2
renamed bind? to bindable?, bind/cc moved to list-bindings
2.1
generics (and hence bind and friends) now accept records, bind/cc added
2.0
bind-matches? and bind-loop changed to bindable? and bind*, where clauses and generic functions added, syms->vars removed
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