From: ········@gmail.com
Subject: bind*
Date: 
Message-ID: <567e4b9f-69c7-4ca8-a162-a4d8e8c59eec@a1g2000hsb.googlegroups.com>
I was thinking (like, perhaps, no less than half of the people in this
group) about unifying variable binding declarations (let, multiple-
value-bind, with-input-from-string etc.) in one form.

...Just as Gary King did, which resulted in him producing the METABANG-
BIND package. Recently I've had a look at it and felt, that it was a
little over-complicated for just a syntactic-sugar.  I don't mean to
criticize Gary's solution, the more so since my idea of bind-form
dispatch was different, i.e.: according to the structure of the bind
clause and types of it's elements.

So, I've programmed the following simple implementation of a universal
bind form. What do you think of it? Is there any major flaw in it?

(defmacro bind* ((&rest clauses) &body body)
    (let ((rez body))
	(mapc #'(lambda (clause)
                          (setf rez `((,@(expand-bind-clause
clause) ,@rez)))
                    (reverse clauses))
	(car rez)))

(defparameter *bind-dispatch-table* nil)

(defmacro def-bind-rule (rule expansion)
  `(push (cons #'(lambda (clause)
		              (and (listp clause) (cdr clause) ,rule))
	               #'(lambda (clause)
		             ,expansion))
	     *bind-dispatch-table*))

(defun expand-bind-clause (clause)
  (funcall (or (cdr (find-if #'identity *bind-dispatch-table*
			              :key #'(lambda (x)
				                    (funcall (car x) clause))))
	            (error "No bind* rule for clause: ~a" clause))
	     clause))


; rules

(def-bind-rule (not (cddr clause))
                      `(let (,clause)))

(def-bind-rule (and (listp (car clause)) (listp (cadr clause)) (not
(cddr clause)))
                      `(destructuring-bind ,(car clause) ,(cadr
clause)))

(def-bind-rule (and (cddr clause) (every #'identity (mapcar #'atom
(butlast clause))))
                      `(multiple-value-bind ,(butlast clause) ,@(last
clause)))

(def-bind-rule (and (atom (car clause)) (eql (cadr clause) :in)
		               (cddr clause) (stringp (caddr clause)))
                       `(with-input-from-string (,(car clause) ,@(cddr
clause))))

(def-bind-rule (and (atom (car clause)) (eql (cadr clause) :in)
		               (cddr clause) (pathnamep (caddr clause)))
                      `(with-open-file (,(car clause) ,@(cddr
clause))))

(def-bind-rule (and (atom (car clause)) (eql (cadr clause) :out)
		              (let ((third (caddr clause)))
		                 (or (null third) (stringp third) (keywordp
third))))
                      `(with-output-to-string (,(car clause) ,@(cddr
clause))))

(def-bind-rule (and (atom (car clause)) (eql (cadr clause) :out)
		              (pathnamep (caddr clause)))
                      `(with-open-file (,(car clause) ,(caddr
clause) :direction :output ,@(cdddr clause))))

Best regards,
Vsevolod

From: ········@gmail.com
Subject: Re: bind*
Date: 
Message-ID: <7a3b69f2-b28b-4cfb-a9d0-4841a7fc09ed@f63g2000hsf.googlegroups.com>
...To achieve something like this:

CL-USER> (macroexpand-1 '(bind* ((a 1)
				 (b c (values 2 3))
				 ((d &rest b) (4 a 6 7))
				 (e :in "abc")
				 (g :out #p"tmp.txt"))
			  (format g "~a ~a" b (read e))))
(LET ((A 1))
  (MULTIPLE-VALUE-BIND
      (B C)
      (VALUES 2 3)
    (DESTRUCTURING-BIND
        (D &REST B)
        (4 A 6 7)
      (WITH-INPUT-FROM-STRING (E "abc")
        (WITH-OPEN-FILE (G #P"tmp.txt" :DIRECTION :OUTPUT)
          (FORMAT G "~a ~a" B (READ E)))))))
From: Paul Donnelly
Subject: Re: bind*
Date: 
Message-ID: <87d4nkbqsu.fsf@plap.localdomain>
········@gmail.com writes:

> I was thinking (like, perhaps, no less than half of the people in this
> group) about unifying variable binding declarations (let, multiple-
> value-bind, with-input-from-string etc.) in one form.
>
> ...Just as Gary King did, which resulted in him producing the METABANG-
> BIND package. Recently I've had a look at it and felt, that it was a
> little over-complicated for just a syntactic-sugar.  I don't mean to
> criticize Gary's solution, the more so since my idea of bind-form
> dispatch was different, i.e.: according to the structure of the bind
> clause and types of it's elements.

Yeah, I don't much like the all the keywords in METABAND-BIND either.

> So, I've programmed the following simple implementation of a universal
> bind form. What do you think of it? Is there any major flaw in it?

(A B (VALUES 'A 'B)) is a little weird to me, but since ((A B) X) is
reserved for DESTRUCTURING-BIND, I guess it makes sense. I like it
better than ((:VALUES A B) (VALUES 'A 'B)). The real challenge with this
approach will be to support plists, structures, objects, and arrays,
should you choose to, without having as many keywords as MB-B.

But don't call it BIND*! That way lies EQ, EQL, EQUAL, and EQUALP.
From: danb
Subject: Re: bind*
Date: 
Message-ID: <6664f14a-bf4a-4750-bd46-6810cb9d9940@r66g2000hsg.googlegroups.com>
········@gmail.com writes:
> (A B (VALUES 'A 'B))

Someone (maybe the OP?) mentioned this a few months ago.

On May 17, 3:48 pm, Paul Donnelly wrote:

> is a little weird to me, but since ((A B) X) is
> reserved for DESTRUCTURING-BIND, I guess it makes sense.

It's only weird because you've never seen it before.
I like it.  It's concise and easy to parse.
The only thing I'm worried about is the file and string
bindings, which have to be intuited from their types,
so they only work with literals.

> But don't call it BIND*!

FWIW, Arc uses the name WITH for (some kind of) binding.

--Dan

------------------------------------------------
Dan Bensen  http://www.prairienet.org/~dsb/

cl-match:  expressive pattern matching in Lisp
http://common-lisp.net/project/cl-match/
From: Paul Donnelly
Subject: Re: bind*
Date: 
Message-ID: <87ve1cqhty.fsf@plap.localdomain>
danb <·········@gmail.com> writes:

> ········@gmail.com writes:
>> (A B (VALUES 'A 'B))
>
> Someone (maybe the OP?) mentioned this a few months ago.
>
> On May 17, 3:48 pm, Paul Donnelly wrote:
>
>> is a little weird to me, but since ((A B) X) is
>> reserved for DESTRUCTURING-BIND, I guess it makes sense.
>
> It's only weird because you've never seen it before.
> I like it.  It's concise and easy to parse.
> The only thing I'm worried about is the file and string
> bindings, which have to be intuited from their types,
> so they only work with literals.

Nah, it's weird because things like COND, CASE, LET and DO have led me
to expect full parenthesization rather than omission and some context
sensitivity. So yes, it's unusual, but I'm not just saying "looks
funny". In the face of the syntax for destructuring it makes sense
though.
From: Matthew D Swank
Subject: Re: bind*
Date: 
Message-ID: <oLPXj.6960$aq.3113@newsfe05.lga>
On Sat, 17 May 2008 17:01:05 -0700, danb wrote:

> ········@gmail.com writes:
>> (A B (VALUES 'A 'B))
> 
> Someone (maybe the OP?) mentioned this a few months ago.
> 
> On May 17, 3:48 pm, Paul Donnelly wrote:
> 
>> is a little weird to me, but since ((A B) X) is reserved for
>> DESTRUCTURING-BIND, I guess it makes sense.
> 
> It's only weird because you've never seen it before. I like it.  It's
> concise and easy to parse. The only thing I'm worried about is the file
> and string bindings, which have to be intuited from their types, so they
> only work with literals.


Well, if you wanted more sophisticated binders short of a full blown 
pattern matcher, you could define an extension protocol like the one for 
setf methods.


Matt
From: Matthew D Swank
Subject: Re: bind*
Date: 
Message-ID: <ijQXj.6961$aq.5557@newsfe05.lga>
On Sat, 17 May 2008 17:01:05 -0700, danb wrote:

> ········@gmail.com writes:
>> (A B (VALUES 'A 'B))
> 
> Someone (maybe the OP?) mentioned this a few months ago.
> 
> On May 17, 3:48 pm, Paul Donnelly wrote:
> 
>> is a little weird to me, but since ((A B) X) is reserved for
>> DESTRUCTURING-BIND, I guess it makes sense.
> 
> It's only weird because you've never seen it before. I like it.  It's
> concise and easy to parse. 

In fact it's so _not_ weird, that one wonders why it wasn't adopted as 
the common lisp syntax for multiple-value-bind.

Is there any ambiguity as to what is going on if I write this:

(let ((a b (values 'a 'b)))
  <fun with a and b>)

Matt
From: ········@gmail.com
Subject: Re: bind*
Date: 
Message-ID: <4a73ccff-b7e9-4b0a-bad3-58852014fe2a@f63g2000hsf.googlegroups.com>
Thanks for the comments, guys!

> The only thing I'm worried about is the file and string
> bindings, which have to be intuited from their types,
> so they only work with literals.

I see, that I've fallen into the trap of confusing macroexpansion and
evaluation time, so the idea to dispatch by clause element type has a
limited use. Generally, only structure information can be used.

> The real challenge with this approach will be to support
> plists, structures, objects, and arrays

For objects there is quite a straightforward solution, which I present
below, but for other mentioned types, it seems, that it would be hard
to implement their support without keywords. Moreover, I doubt such
support is necessary, because there are no corresponding Lisp forms,
like multiple-value-bind of with-output-to-string (which, I think,
means there's no general need for them). And the idea of a general
bind form is first of all to compress all the available forms into one
for the sake of less indentation and typing, but without the loss of
intuitive intelligibility. And I think it's important not to lay if
thick here in terms of conventions. But if someone wants to tailor the
form to his needs he can always def-(another)-with-rule...

So, the updated version is here: http://paste.lisp.org/display/60892

P.S. Well, I think, there remains a controversy in the heuristic
choice between -string and -file. I think it's better this way (and
not with the unconditional use of keywords, like :instr and :outfile),
because, in most cases with-open-file is given a literal pathname, not
a symbol, and it's much easier to remember :in, than :instr/:infile.

Best regards,
vsevolod
From: ········@gmail.com
Subject: Re: bind*
Date: 
Message-ID: <e91e8794-9780-4a74-abd1-721a35bb04dd@m36g2000hse.googlegroups.com>
On May 17, 1:53 pm, ········@gmail.com wrote:

> (def-bind-rule (not (cddr clause))
>                       `(let (,clause)))

It seems like LET* might be a better default here.

--
wm