You are looking at historical revision 30382 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. Other types of sequences can be added by clients.

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

[procedure] (bindings sym ..)

Documentation procedure. Called without argument it returns the list of exported symbols, with one of those symbols as argument it returns its documentation.

bind

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

binds pattern variables of pat to subexpressions of seq and executes xpr . xprs in this context, provided all fenders return #t, if supplied.

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 (pat (where . fenders) .. 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 (pat (where . fenders) .. xpr . xprs) ....)

Combination of bind-case and lambda with one pattern argument

bind-case-lambda*

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

named version of bind. loop 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? gnull? gcar gcdr)

updates the tables of three generic functions.

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

Requirements

None

Usage

(require-library low-level-macros bindings)
(import-for-syntax (only low-level-macros macro-rules))
(import bindings low-level-macros macro-helpers)

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) (even? a)) '#(1 2)) ; -> #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) (my-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))

;;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

Jan 28, 2014

Author

Juergen Lorenz

License

Copyright (c) 2011-2014, 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.5
all macros are low-level. Module depends on low-level-macros
2.4.1
documentation procedure beautified
2.4
generic functions rewritten, records removed from tests
2.3.4
internal generic-pair? added again bugfixed
2.3.3
exception-handler added, internal generic-pair? removed
2.3.2
bind-case improved, format replaced by print
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