From: Tom Breton
Subject: Just-in-time declarations, alternative to (let ... )
Date: 
Message-ID: <m3hfx6ha5m.fsf@world.std.com>
I always prefer a style that lets me declare variables just in time.
Lisp doesn't seem to like this style.  let and its variants all want
everything variable to be declared up top.  Besides putting distance
between declaration, construction and use, this has the effect of
making it tricky to do computations between declarations, EG if one
uses let*.

It should be easy to implement just-in-time declaration with a Lisp
macro... Scan for a keyword (say, "letq") and transform the call into
something of the form (let (name ...) ... (setq name ...) ... ).

I'm just wondering if there exists a canonical form of this: names,
semantics?

-- 
Tom Breton

From: Tim Bradshaw
Subject: Re: Just-in-time declarations, alternative to (let ... )
Date: 
Message-ID: <ey3r9w9lv98.fsf@todday.aiai.ed.ac.uk>
* Tom Breton wrote:

> It should be easy to implement just-in-time declaration with a Lisp
> macro... Scan for a keyword (say, "letq") and transform the call into
> something of the form (let (name ...) ... (setq name ...) ... ).

I'm a bit unclear why you'd want this.  In Lisp I almost always bind
variables as close to where I want them as I can get away with.  So I
often write things like:

	(let ((x ...))
	  ...
	  (let ((y <something-using-x>))
	    ... use y ...)
	  ... x still in scope ...)

I'm trying to see why this doesn't do what you want, but I can't,
could you explain?  There is an issue of the extra indentation
introduced by the multiple LETs, which is a pain, but I usually find
that it's worth it because it shows you when the bindings go out of
scope as well: in your mechanism it would seem that variables would
have to stay in scope until the end of the surrounding `block'.

(And of course, now when I write C I tend to do:

	{
	    int i = 3;
	    ...
	    {
		int j = 4;
		...
	    }
	    ...
	}

And C people think I'm mad...)

--tim

Appendix:	  

I misread your article and originally wrote this (picking up
`declaration' with the CL rather than C sense) and I wrote this, which
I'll leave in because it's perhaps useful: This is hard in general,
not just because you really need a code walker to do it `right' (you
can obviously hack it for most cases without one), but because of
issues about what DECLARE says.  In particular you need to make sure
that the variable is bound in the LET to an object of the right type.
This is not legal:

	(let ((x nil))
	  (declare (type x some-type-not-including-nil))
	  ...
	  (setf x ...)
	  ...)

The problem is that it may be hard to get a decent object of the right
type when you want to do the binding.  The SERIES package has this
problem, and it can cause nightmares in Lisps which actually want this
to be true, which include CMUCL, and, bizarrely, Genera (for arrays).
From: Tom Breton
Subject: Re: Just-in-time declarations, alternative to (let ... )
Date: 
Message-ID: <m3n26xv5xz.fsf@world.std.com>
Tim Bradshaw <···@aiai.ed.ac.uk> writes:

> * Tom Breton wrote:
> 
> > It should be easy to implement just-in-time declaration with a Lisp
> > macro... Scan for a keyword (say, "letq") and transform the call into
> > something of the form (let (name ...) ... (setq name ...) ... ).
> 

I obviously didn't make myself clear.  AISI, the macro would have one
name, and "letq" would have another and be only meaningful at the head
of a list that's directly in the macro.  I guess it sounded like I
would do this thru changing eval or something?  Let me copy my example
from the other message:

(heres-a-block-with-letqs-in-it
	(do-this)	
	(letq a b) 
	(do-that b)
	(letq c d)
	(do-other-thing c)) 
==>
(let
	(a b)  ;;Here they're bound, as nil.
	(do-this)	
	(setq a b) ;;Here it's assigned to
	(do-that b)
	(setq c d) ;;Here it's assigned to
	(do-other-thing c))

The macro heres-a-block-with-letqs-in-it would scan for lists
beginning with letq; that should be easy, general, and safe.

> I'm trying to see why this doesn't do what you want, but I can't,
> could you explain?  There is an issue of the extra indentation
> introduced by the multiple LETs, which is a pain, but I usually find
> that it's worth it because it shows you when the bindings go out of
> scope as well: 

Extra indentation is not the problem.  I indent like blazes anyways.

In this case, I don't think more indentation has the virtue of showing
logical structure better, because what's being shown by nested lets is
not logical structure but lexical structure.

> in your mechanism it would seem that variables would
> have to stay in scope until the end of the surrounding `block'.

Nothing else would make sense, ISTM.  In any case, since the
surrounding block is what would otherwise be the let statement's
block, I don't see a downside there.
 
> (And of course, now when I write C I tend to do:
> 
> 	{
> 	    int i = 3;
> 	    ...
> 	    {
> 		int j = 4;
> 		...
> 	    }
> 	    ...
> 	}
> 
> And C people think I'm mad...)

Yeah, I do that in C too.  C++ spoils you that way.

-- 
Tom Breton
From: Marco Antoniotti
Subject: Re: Just-in-time declarations, alternative to (let ... )
Date: 
Message-ID: <lwzpawojyl.fsf@galvani.parades.rm.cnr.it>
I strongly believe (i.e. it is a matter of faith/religion :) ) that if
you have long "let" (or a long function) then it is a good time to
break up the whole thing and to rewrite it from scratch.  The fact
that C++ and Java (and Dylan) allow for interspersed declarations is
not an advantage for me.


-- 
Marco Antoniotti ===========================================
PARADES, Via San Pantaleo 66, I-00186 Rome, ITALY
tel. +39 - (0)6 - 68 10 03 16, fax. +39 - (0)6 - 68 80 79 26
http://www.parades.rm.cnr.it
From: Tim Bradshaw
Subject: Re: Just-in-time declarations, alternative to (let ... )
Date: 
Message-ID: <ey3n26wll2y.fsf@todday.aiai.ed.ac.uk>
* Tom Breton wrote:
> Tim Bradshaw <···@aiai.ed.ac.uk> writes:
>> * Tom Breton wrote:
>> 
>> > It should be easy to implement just-in-time declaration with a Lisp
>> > macro... Scan for a keyword (say, "letq") and transform the call into
>> > something of the form (let (name ...) ... (setq name ...) ... ).
>> 

> I obviously didn't make myself clear.  AISI, the macro would have one
> name, and "letq" would have another and be only meaningful at the head
> of a list that's directly in the macro.  I guess it sounded like I
> would do this thru changing eval or something?  Let me copy my example
> from the other message:

> (heres-a-block-with-letqs-in-it
> 	(do-this)	
> 	(letq a b) 
> 	(do-that b)
> 	(letq c d)
> 	(do-other-thing c)) 
> ==>
> (let
> 	(a b)  ;;Here they're bound, as nil.
> 	(do-this)	
> 	(setq a b) ;;Here it's assigned to
> 	(do-that b)
> 	(setq c d) ;;Here it's assigned to
> 	(do-other-thing c))

Ah but this is very problematic.  Consider this:

	(let ((a 2))
	  (letq-block
	    (function a) ; *
	    (letq a 1)
	    (function a)))

Any kind of naive thing will lose at (*).  So you either need to do
something clever like variable renaming (code walker to do this right,
or perhaps SYMBOL-MACROLET?), or just get obscure and, I think, buggy
behaviour.

I'm afraid I still don't see the disadvantage of just using local LETs
-- what does this give you that they don't? (I could easily be missing
the point here!)

--tim
	
From: Marco Antoniotti
Subject: Re: Just-in-time declarations, alternative to (let ... )
Date: 
Message-ID: <lwww60o1k0.fsf@galvani.parades.rm.cnr.it>
Tim Bradshaw <···@aiai.ed.ac.uk> writes:

> * Tom Breton wrote:
> > Tim Bradshaw <···@aiai.ed.ac.uk> writes:
> >> * Tom Breton wrote:
> >> 
> >> > It should be easy to implement just-in-time declaration with a Lisp
> >> > macro... Scan for a keyword (say, "letq") and transform the call into
> >> > something of the form (let (name ...) ... (setq name ...) ... ).
> >> 
> 
> > I obviously didn't make myself clear.  AISI, the macro would have one
> > name, and "letq" would have another and be only meaningful at the head
> > of a list that's directly in the macro.  I guess it sounded like I
> > would do this thru changing eval or something?  Let me copy my example
> > from the other message:
> 
> > (heres-a-block-with-letqs-in-it
> > 	(do-this)	
> > 	(letq a b) 
> > 	(do-that b)
> > 	(letq c d)
> > 	(do-other-thing c)) 
> > ==>
> > (let
> > 	(a b)  ;;Here they're bound, as nil.
> > 	(do-this)	
> > 	(setq a b) ;;Here it's assigned to
> > 	(do-that b)
> > 	(setq c d) ;;Here it's assigned to
> > 	(do-other-thing c))
> 
> Ah but this is very problematic.  Consider this:
> 
> 	(let ((a 2))
> 	  (letq-block
> 	    (function a) ; *
> 	    (letq a 1)
> 	    (function a)))
> 
> Any kind of naive thing will lose at (*).  So you either need to do
> something clever like variable renaming (code walker to do this right,
> or perhaps SYMBOL-MACROLET?), or just get obscure and, I think, buggy
> behaviour.

You need a code walker or a restriction on the use of 'letq' or better
'vardef' (the opposite of 'defvar' :) ).

	(let ((a 2))
 	  (with-vardef
 	    (fun a)
 	    (vardef a 1)
 	    (fun a)))

IMHO should expand into

	(let ((a 2))
           (block VARDEF001 ; guess how I generate these names.
             (fun a)
             (let ((a 1))
                (fun a))))

The problems come up when you have something like

	(let ((a 2))
	   (with-vardef
	     (fun a)
	     (let ((b 3))
		(vardef a 3)
		(fun a))))

If you restrict 'vardef' to be only at 'with-vardef' toplevels then you do
not need a code walker, otherwise you need one to recursively go down
the body and do all sorts of things.

Anyway, since we are at it here is my code snippet :)

llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll
;;; -*- Package: COMMON-LISP -*-

;;; vardef-forms.lisp -- WITH-VARDEF and VARDEF forms for Common Lisp.
;;; Provide a way to introduce variable in a block sequence with the
;;; semantics (roughly) provided by C++, Java, Dylan etc.
;;;
;;; This code is put in the public domain and it is provided AS IS,
;;; with no warranty implied about its use or functionalities. Use at
;;; your own risk.
;;;
;;; Author: Marco Antoniotti <·······@cs.nyu.edu>

(defpackage "VARDEF-FORMS" (:use "COMMON-LISP" #+:CMU "CONDITIONS")
  (:nicknames "VARDEF")
  (:export "VARDEF" "WITH-VARDEF" "VARDEF-ERROR"))

(in-package "VARDEF")

(define-condition vardef-error (program-error)
  ((name :reader vardef-error-name :initarg :name)
   (form :reader vardef-error-form :initarg :form)
   )
  (:report (lambda (c s)
	     (format s "VARDEF form [~S] not within a WITH-VARDEF block."
		     (vardef-error-name c))))
  (:documentation
   "Error signalled by VARDEF macro when used in wrong context.")
  )

(defmacro with-vardef (&body forms)
  "WITH-VARDEF provides a context within which the VARDEF forms can be used."
  `(block ,(gensym "VARDEF-BLOCK-") ,@(expand-vardefs forms)))

(defmacro vardef (name form &key type)
  "VARDEF forms can be used only within a WITH-VARDEF block.
Otherwise a VARDEF-ERROR (i.e. a PROGRAM-ERROR) is signalled.
Their syntax is (VARDEF <name> <init-form> :type <declaration>)."
  (declare (ignore type))
  `(error 'vardef-error :name ',name :form ',form))

(defun expand-vardefs (forms)
  "EXPAND-VARDEF is the real workhorse in charge of the expansion."
  (cond ((null forms) ())
	((eq (first (first forms)) 'vardef)
	 (destructuring-bind (vardef-kwd name init-form &key type)
	     (first forms)
	   (declare (ignore vardef-kwd))
	   
	   ;; Remember that we splice the result.
	   ;; An optimization would be to recognize several VARDEFs in
	   ;; sequence and to block them into a single LET*.
	   
	   `((let ((,name ,init-form))
	       ,@(when type `((declare (type ,type ,name))))
	       ,@(expand-vardefs (rest forms))))))
	(t
	 (cons (first forms)
	       (expand-vardefs (rest forms))))
	))

;;; Note on :TYPE expansion.
;;; Since we are introducing variable names, it is appropriate to
;;; expand the :TYPE keyword of FUNCTION type specifiers with TYPE
;;; instead of FTYPE.  At least this is what I understood from the
;;; Hyperspec.

;;; Examples:
#|
* (use-package "VARDEF")
T
* (macroexpand '(with-vardef
	       (print (fun a))
	       (vardef a 1 :type fixnum)
	       (print (fun a))))
(BLOCK #:VARDEF-BLOCK-824
  (PRINT (FUN A))
  (LET ((A 1))
    (DECLARE (TYPE FIXNUM A))
    (PRINT (FUN A))))
T
* (macroexpand '(with-vardef
	       (print (fun a))
	       (vardef a 1 :type (or null fixnum))
	       (vardef b '(qwe rty uio))
	       (print (fun a))))
(BLOCK #:VARDEF-BLOCK-827
  (PRINT (FUN A))
  (LET ((A 1))
    (DECLARE (TYPE (OR NULL FIXNUM) A))
    (LET ((B '(QWE RTY UIO)))
      (PRINT (FUN A)))))
T
* (let ((a 22))
     (with-vardef
       (print (1+ a))
       (vardef a 1 :type fixnum)
       (print (1+ a))))
23 
2 
2
* (vardef www 'qwe)
;;; ERROR
#|

;;; end of file -- vardef-forms.lisp --


llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll

-- 
Marco Antoniotti ===========================================
PARADES, Via San Pantaleo 66, I-00186 Rome, ITALY
tel. +39 - (0)6 - 68 10 03 16, fax. +39 - (0)6 - 68 80 79 26
http://www.parades.rm.cnr.it
From: Barry Margolin
Subject: BLOCK (was Re: Just-in-time declarations, alternative to (let ... ))
Date: 
Message-ID: <DrMV1.84$eJ2.1408135@burlma1-snr1.gtei.net>
In article <··············@galvani.parades.rm.cnr.it>,
Marco Antoniotti  <·······@galvani.parades.rm.cnr.it> wrote:
>(defmacro with-vardef (&body forms)
>  "WITH-VARDEF provides a context within which the VARDEF forms can be used."
>  `(block ,(gensym "VARDEF-BLOCK-") ,@(expand-vardefs forms)))

What's the point of a BLOCK with a GENSYMed name?  The only purpose of
BLOCK is to introduce a name that can be supplied to RETURN-FROM, but if
the name is essentially random how is someone supposed to use it?  It looks
like you're using this as a substitute for PROGN.

-- 
Barry Margolin, ······@bbnplanet.com
GTE Internetworking, Powered by BBN, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
From: Marco Antoniotti
Subject: Re: BLOCK (was Re: Just-in-time declarations, alternative to (let ... ))
Date: 
Message-ID: <lwyaqfjoym.fsf@galvani.parades.rm.cnr.it>
Barry Margolin <······@bbnplanet.com> writes:

> In article <··············@galvani.parades.rm.cnr.it>,
> Marco Antoniotti  <·······@galvani.parades.rm.cnr.it> wrote:
> >(defmacro with-vardef (&body forms)
> >  "WITH-VARDEF provides a context within which the VARDEF forms can be used."
> >  `(block ,(gensym "VARDEF-BLOCK-") ,@(expand-vardefs forms)))
> 
> What's the point of a BLOCK with a GENSYMed name?  The only purpose of
> BLOCK is to introduce a name that can be supplied to RETURN-FROM, but if
> the name is essentially random how is someone supposed to use it?  It looks
> like you're using this as a substitute for PROGN.

You are right of course.  Here is the corrected code.

llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll
;;; -*- Package: COMMON-LISP -*-

;;; vardef-forms.lisp -- WITH-VARDEF and VARDEF forms for Common Lisp.
;;; Provide a way to introduce variable in a block sequence with the
;;; semantics (roughly) provided by C++, Java, Dylan etc.
;;;
;;; This code is put in the public domain and it is provided AS IS,
;;; with no warranty implied about its use or functionalities. Use at
;;; your own risk.
;;;
;;; Author: Marco Antoniotti <·······@cs.nyu.edu>

(defpackage "VARDEF-FORMS" (:use "COMMON-LISP" #+:CMU "CONDITIONS")
  (:nicknames "VARDEF")
  (:export "VARDEF" "WITH-VARDEF" "VARDEF-ERROR"))

(in-package "VARDEF-FORMS")

(define-condition vardef-error (program-error)
  ((name :reader vardef-error-name :initarg :name)
   (form :reader vardef-error-form :initarg :form)
   )
  (:report (lambda (c s)
	     (format s "VARDEF form [~S] not within a WITH-VARDEF block."
		     (vardef-error-name c))))
  (:documentation
   "Error signalled by VARDEF macro when used in wrong context.")
  )

(defmacro with-vardef (&body forms)
  "WITH-VARDEF provides a context within which the VARDEF forms can be used."
  `(progn ,@(expand-vardefs forms)))

(defmacro vardef (name form &key type)
  "VARDEF forms can be used only within a WITH-VARDEF block.
Otherwise a VARDEF-ERROR (i.e. a PROGRAM-ERROR) is signalled.
Their syntax is (VARDEF <name> <init-form> :type <declaration>)."
  (declare (ignore type))
  `(error 'vardef-error :name ',name :form ',form))

(defun expand-vardefs (forms)
  "EXPAND-VARDEF is the real workhorse in charge of the expansion."
  (cond ((null forms) ())
	((eq (first (first forms)) 'vardef)
	 (destructuring-bind (vardef-kwd name init-form &key type)
	     (first forms)
	   (declare (ignore vardef-kwd))
	   
	   ;; Remember that we splice the result.
	   ;; An optimization would be to recognize several VARDEFs in
	   ;; sequence and to block them into a single LET*.
	   
	   `((let ((,name ,init-form))
	       ,@(when type `((declare (type ,type ,name))))
	       ,@(expand-vardefs (rest forms))))))
	(t
	 (cons (first forms)
	       (expand-vardefs (rest forms))))
	))

;;; Note on :TYPE expansion.
;;; Since we are introducing variable names, it is appropriate to
;;; expand the :TYPE keyword of FUNCTION type specifiers with TYPE
;;; instead of FTYPE.  At least this is what I understood from the
;;; Hyperspec.

;;; Examples:
#|
* (use-package "VARDEF")
T
* (macroexpand '(with-vardef
	       (print (fun a))
	       (vardef a 1 :type fixnum)
	       (print (fun a))))
(PROGN
  (PRINT (FUN A))
  (LET ((A 1))
    (DECLARE (TYPE FIXNUM A))
    (PRINT (FUN A))))
T
* (macroexpand '(with-vardef
	       (print (fun a))
	       (vardef a 1 :type (or null fixnum))
	       (vardef b '(qwe rty uio))
	       (print (fun a))))
(PROGN
  (PRINT (FUN A))
  (LET ((A 1))
    (DECLARE (TYPE (OR NULL FIXNUM) A))
    (LET ((B '(QWE RTY UIO)))
      (PRINT (FUN A)))))
T
* (let ((a 22))
     (with-vardef
       (print (1+ a))
       (vardef a 1 :type fixnum)
       (print (1+ a))))
23 
2 
2
* (vardef www 'qwe)
;;; ERROR
|#

;;; end of file -- vardef-forms.lisp --

llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll

-- 
Marco Antoniotti ===========================================
PARADES, Via San Pantaleo 66, I-00186 Rome, ITALY
tel. +39 - (0)6 - 68 10 03 16, fax. +39 - (0)6 - 68 80 79 26
http://www.parades.rm.cnr.it
From: Tom Breton
Subject: Re: Just-in-time declarations, alternative to (let ... )
Date: 
Message-ID: <m3btncs2sk.fsf@world.std.com>
Marco Antoniotti <·······@galvani.parades.rm.cnr.it> writes:

> 
> You need a code walker or a restriction on the use of 'letq' or better
> 'vardef' (the opposite of 'defvar' :) ).
> 
> 	(let ((a 2))
>  	  (with-vardef
>  	    (fun a)
>  	    (vardef a 1)
>  	    (fun a)))
> 

That answers my question about existing names and semantics, then.  Thanks.


> IMHO should expand into
> 
> 	(let ((a 2))
>            (block VARDEF001 ; guess how I generate these names.
>              (fun a)
>              (let ((a 1))
>                 (fun a))))
> 
> The problems come up when you have something like
> 
> 	(let ((a 2))
> 	   (with-vardef
> 	     (fun a)
> 	     (let ((b 3))
> 		(vardef a 3)
> 		(fun a))))
> 
> If you restrict 'vardef' to be only at 'with-vardef' toplevels then you do
> not need a code walker, otherwise you need one to recursively go down
> the body and do all sorts of things.

Ah, it's my turn to be conservative now.  I'd prefer restricting
"vardef" aka "letq" to be at toplevel+1, because it's easier to read
and understand, and easier to write, and just needs the one macro.


-- 
Tom Breton
From: Tom Breton
Subject: Re: Just-in-time declarations, alternative to (let ... )
Date: 
Message-ID: <m3ems8s35m.fsf@world.std.com>
Tim Bradshaw <···@aiai.ed.ac.uk> writes:

> * Tom Breton wrote:
> > Tim Bradshaw <···@aiai.ed.ac.uk> writes:
> >> * Tom Breton wrote:
> >> 
> >> > It should be easy to implement just-in-time declaration with a Lisp
> >> > macro... Scan for a keyword (say, "letq") and transform the call into
> >> > something of the form (let (name ...) ... (setq name ...) ... ).
> >> 
> 
> > I obviously didn't make myself clear.  AISI, the macro would have one
> > name, and "letq" would have another and be only meaningful at the head
> > of a list that's directly in the macro.  I guess it sounded like I
> > would do this thru changing eval or something?  Let me copy my example
> > from the other message:
> 
> > (heres-a-block-with-letqs-in-it
> > 	(do-this)	
> > 	(letq a b) 
> > 	(do-that b)
> > 	(letq c d)
> > 	(do-other-thing c)) 
> > ==>
> > (let
> > 	(a b)  ;;Here they're bound, as nil.
> > 	(do-this)	
> > 	(setq a b) ;;Here it's assigned to
> > 	(do-that b)
> > 	(setq c d) ;;Here it's assigned to
> > 	(do-other-thing c))
> 
> Ah but this is very problematic.  Consider this:
> 
> 	(let ((a 2))
> 	  (letq-block
> 	    (function a) ; *
> 	    (letq a 1)
> 	    (function a)))
> 
> Any kind of naive thing will lose at (*).  

No, I considered that.  Consider your example again: it's already
solved.  a doesn't get bound to 2, it gets bound to nil.  That could
occasionally still be a problem, but much less so.

 	(let ((a 2))
 	  (letq-block
 	    (function a) ; *
 	    (letq a 1)
 	    (function a)))

==>
 	(let ((a 2))
 	  (let (a) ;;Just wiped out any enclosing bindings.
 	    (function a) ;;Passes nil, not 2.
 	    (setq a 1)
 	    (function a)))


> I'm afraid I still don't see the disadvantage of just using local LETs
> -- what does this give you that they don't? (I could easily be missing
> the point here!)

That you avoid having to *manage* the let blocks.

-- 
Tom Breton
From: David B. Lamkins
Subject: Re: Just-in-time declarations, alternative to (let ... )
Date: 
Message-ID: <H7eV1.1071$es1.567829@news.teleport.com>
In article <··············@world.std.com> , Tom Breton <···@world.std.com> 
wrote:

>
>I always prefer a style that lets me declare variables just in time.
>Lisp doesn't seem to like this style.  let and its variants all want
>everything variable to be declared up top.  Besides putting distance
>between declaration, construction and use, this has the effect of
>making it tricky to do computations between declarations, EG if one
>uses let*.
>

Maybe this is a stylistic preference.  You know that you can nest LET forms,
right?

(let ((foo 17)
      (bar 'xyzzy))
  ... more code ...
  (let ((baz (f foo bar)))
    (g baz) 
    ... etc ...
    ))

I usually don't write code that has vast distances between declarations and
references of variables.  When I do spot it, I generally try to rewrite to
bring the variable's declaration closer to point of use, or factor the code
differently.

>It should be easy to implement just-in-time declaration with a Lisp
>macro... Scan for a keyword (say, "letq") and transform the call into
>something of the form (let (name ...) ... (setq name ...) ... ).
>

In the general case, rewriting based upon reserved words in not very easy;
you need to take into account not only the name of the symbol, but the
context in which it appears.  OTOH, you seem to be suggesting something
simpler, a rewrite of the form

(letq ((x 4) (y 5)) ...) ==> (let (x y) (setq x 4 y 5) ...)

If so, this is easy to do with a macro, but pointless since you're replacing
the initial binding with assignment.

>I'm just wondering if there exists a canonical form of this: names,
>semantics?

Perhaps you could give a concrete example?

---
David B. Lamkins <http://www.teleport.com/~dlamkins/>
From: Tom Breton
Subject: Re: Just-in-time declarations, alternative to (let ... )
Date: 
Message-ID: <m3pvbtv6jc.fsf@world.std.com>
"David B. Lamkins" <········@teleport.com> writes:

> In article <··············@world.std.com> , Tom Breton <···@world.std.com> 
> wrote:
> 
> >
> >I always prefer a style that lets me declare variables just in time.
> >Lisp doesn't seem to like this style.  let and its variants all want
> >everything variable to be declared up top.  Besides putting distance
> >between declaration, construction and use, this has the effect of
> >making it tricky to do computations between declarations, EG if one
> >uses let*.
> >
> 
> Maybe this is a stylistic preference.  You know that you can nest LET forms,
> right?

Yes, I know.  But while adding and removing variables to a single let
is easy, adding and removing lets is harder and more error-prone.

Example: The other day I was maintaining code that had a big let* of
maybe 40 lines of declarations, and I realized that a problematic
declaration had to be tested for failure -- sometimes it should go as
before, sometimes it should do something new.  So -- carefully, I
thott -- I split the let into 2 lets, and watched it crash and die,
because I had not correctly split the let block.  

Sure, I could write an emacs function to do that, but that's
considerably more work for a less general, less safe solution.  Then
I'd have to write another one to rejoin lets.


> >It should be easy to implement just-in-time declaration with a Lisp
> >macro... Scan for a keyword (say, "letq") and transform the call into
> >something of the form (let (name ...) ... (setq name ...) ... ).
> >
> 
> In the general case, rewriting based upon reserved words in not very easy;
> you need to take into account not only the name of the symbol, but the
> context in which it appears.  OTOH, you seem to be suggesting something
> simpler, a rewrite of the form
> 
> (letq ((x 4) (y 5)) ...) ==> (let (x y) (setq x 4 y 5) ...)

Close; it would be something like this:

(heres-a-block-with-letqs-in-it
	(do-this)	
	(letq a b) 
	(do-that b)
	(letq c d)
	(do-other-thing c)) 
==>
(let
	(a b)  ;;Here they're bound, as nil.
	(do-this)	
	(setq a b) ;;Here it's assigned to
	(do-that b)
	(setq c d) ;;Here it's assigned to
	(do-other-thing c))

The macro would scan for lists beginning with letq; that should be
easy, general, and safe.

> If so, this is easy to do with a macro, but pointless since you're replacing
> the initial binding with assignment.
> 
> >I'm just wondering if there exists a canonical form of this: names,
> >semantics?
> 
> Perhaps you could give a concrete example?

If the above isn't concrete enuff, let me know.

-- 
Tom Breton
From: David B. Lamkins
Subject: Re: Just-in-time declarations, alternative to (let ... )
Date: 
Message-ID: <d1xV1.2061$es1.1087710@news.teleport.com>
In article <··············@world.std.com> , Tom Breton <···@world.std.com> 
wrote:

>"David B. Lamkins" <········@teleport.com> writes:

[snip]

>> Maybe this is a stylistic preference.  You know that you can nest LET forms,
>> right?
>
>Yes, I know.  But while adding and removing variables to a single let
>is easy, adding and removing lets is harder and more error-prone.
>
>Example: The other day I was maintaining code that had a big let* of
>maybe 40 lines of declarations, and I realized that a problematic
>declaration had to be tested for failure -- sometimes it should go as
>before, sometimes it should do something new.  So -- carefully, I
>thott -- I split the let into 2 lets, and watched it crash and die,
>because I had not correctly split the let block.  
>
>Sure, I could write an emacs function to do that, but that's
>considerably more work for a less general, less safe solution.  Then
>I'd have to write another one to rejoin lets.
>

Since you're using Emacs, you should learn to take advantage of it.  Edit
your Lisp code using lisp-mode, and you can do things like c-m-f and c-m-b
to move forward and backward over an entire s-expression.  Even better,
c-m-q will reindent an entire form; I use this as a matter of habit when I
make a large-scale change -- errors are immediately obvious after
reindenting.

>
>> >It should be easy to implement just-in-time declaration with a Lisp
>> >macro... Scan for a keyword (say, "letq") and transform the call into
>> >something of the form (let (name ...) ... (setq name ...) ... ).
>> >
>> 
>> In the general case, rewriting based upon reserved words in not very easy;
>> you need to take into account not only the name of the symbol, but the
>> context in which it appears.  OTOH, you seem to be suggesting something
>> simpler, a rewrite of the form
>> 
>> (letq ((x 4) (y 5)) ...) ==> (let (x y) (setq x 4 y 5) ...)
>
>Close; it would be something like this:
>
>(heres-a-block-with-letqs-in-it
> (do-this) 
> (letq a b) 
> (do-that b)
> (letq c d)
> (do-other-thing c)) 
>==>
>(let
> (a b)  ;;Here they're bound, as nil.
> (do-this) 
> (setq a b) ;;Here it's assigned to
> (do-that b)
> (setq c d) ;;Here it's assigned to
> (do-other-thing c))
>

You're right, that is straightforward.  Although I'd choose a more
perspicuous name rather than "heres-a-block-with-letqs-in-it".  I'd rule out
"with-letqs", since the with-... forms conventionally have the declarations
up front, and burying them in the body.

>The macro would scan for lists beginning with letq; that should be
>easy, general, and safe.
>

Yes, then you'd extract the variable names, build the binding list for the
let, and rewrite the body with letq => setq.  Pretty simple.  Here's a quick
first cut.  You may want to extend this to handle (letq var1 val1 ...) as an
analogue to multiple SETQ assignments.  Also, it bothers me that you can't
introduce declarations...

(defmacro using-letq (&body body)
  (let ((vars ()))
    (flet ((letq-form-p (form)
             (and (consp form)
                  (eq 'letq (first form)))))
      (dolist (form body)
        (when (letq-form-p form)
          (push (second form) vars)))
      `(let (,@vars)
         ,@(mapcar #'(lambda (form)
                       (if (letq-form-p form)
                         `(setq ,@(rest form))
                         form))
                   body)))))

(macroexpand '(using-letq (foo bar) (letq a baz) (letq c (fun gunk))))

==> 

(LET (C A) (FOO BAR) (SETQ A BAZ) (SETQ C (FUN GUNK)))

>> If so, this is easy to do with a macro, but pointless since you're replacing
>> the initial binding with assignment.
>> 
>> >I'm just wondering if there exists a canonical form of this: names,
>> >semantics?
>> 
>> Perhaps you could give a concrete example?
>
>If the above isn't concrete enuff, let me know.
>
>-- 
>Tom Breton


---
David B. Lamkins <http://www.teleport.com/~dlamkins/>
From: David B. Lamkins
Subject: Re: Just-in-time declarations, alternative to (let ... )
Date: 
Message-ID: <h8yV1.2119$es1.1123112@news.teleport.com>
In article <······················@news.teleport.com> , "David B. Lamkins"
<········@teleport.com> wrote:

[snip]

>>
>>> >It should be easy to implement just-in-time declaration with a Lisp
>>> >macro... Scan for a keyword (say, "letq") and transform the call into
>>> >something of the form (let (name ...) ... (setq name ...) ... ).

[snip]

The code in my first post didn't allow you to assign a letq var more than
once.  This does:


(defmacro using-letq (&body body)
  (let ((vars ()))
    (flet ((letq-form-p (form)
             (and (consp form)
                  (eq 'letq (first form)))))
      (dolist (form body)
        (when (letq-form-p form)
          (push (second form) vars)))
      `(let (,@(remove-duplicates vars))  ;; changed
         ,@(mapcar #'(lambda (form)
                       (if (letq-form-p form)
                         `(setq ,@(rest form))
                         form))
                   body)))))

---
David B. Lamkins <http://www.teleport.com/~dlamkins/>
From: Bill Newman
Subject: Re: Just-in-time declarations, alternative to (let ... )
Date: 
Message-ID: <wnewmanF0xG0z.DAt@netcom.com>
David B. Lamkins (········@teleport.com) wrote:
: In article <······················@news.teleport.com> , "David B. Lamkins"
: <········@teleport.com> wrote:

: [snip]

: >>
: >>> >It should be easy to implement just-in-time declaration with a Lisp
: >>> >macro... Scan for a keyword (say, "letq") and transform the call into
: >>> >something of the form (let (name ...) ... (setq name ...) ... ).

: [snip]

: The code in my first post didn't allow you to assign a letq var more than
: once.  This does:


: (defmacro using-letq (&body body)
:   (let ((vars ()))
:     (flet ((letq-form-p (form)
:              (and (consp form)
:                   (eq 'letq (first form)))))
:       (dolist (form body)
:         (when (letq-form-p form)
:           (push (second form) vars)))
:       `(let (,@(remove-duplicates vars))  ;; changed
:          ,@(mapcar #'(lambda (form)
:                        (if (letq-form-p form)
:                          `(setq ,@(rest form))
:                          form))
:                    body)))))

Why put all the declarations in a single LET at the front? That makes it
hard to use DECLARE, without which I'd be lost. And it means you
need to worry about (and define?) what happens with code like

   (let ((x 11))
     (using-letq
       (setf x 12)
       (print x)
       (letq x 13))
     (print x))

How about using the definition below instead? (It lets you use
DECLARE, and although you still need to worry about code analogous to
the fragment above, it interprets it in what seems to me to be a
reasonable way.)

I named it USING-LET++, on the theory that this behavior imitates
C++. I know that missing this feature from C++ is what motivated me to
wish for it in Scheme, long ago. (I thought that it'd be nice if
Scheme's DEFINE-in-a-sequence-of-expressions construct behaved this
way.)

  Bill Newman

* (defmacro using-let++ (&body body)
  (labels ((transformed-body (body)
             (declare (type list body))
             (let ((expr (first body)))
               (cond ((null expr)
                      expr)
                     ((and (consp expr) (eq (first expr) 'let++))
                      (destructuring-bind
                          (let++ symbol value)
                          expr
                        (declare (ignore let++))
                        `((let ((,symbol ,value))
                            ,@(transformed-body (rest body))))))
                     (t
                      (cons expr (transformed-body (rest body))))))))
    `(progn ,@(transformed-body body))))


USING-LET++
* (macroexpand-1 '(using-let++ (foo x)
                               (let++ y 12)
                               (declare (type fixnum y))
                               (foo y)
                               (let++ z 11)
                               (foo z)))

(PROGN
 (FOO X)
 (LET ((Y 12))
   (DECLARE (TYPE FIXNUM Y))
   (FOO Y)
   (LET ((Z 11))
     (FOO Z))))
T
* 
From: Tom Breton
Subject: Re: Just-in-time declarations, alternative to (let ... )
Date: 
Message-ID: <m390igs1cx.fsf@world.std.com>
"David B. Lamkins" <········@teleport.com> writes:

> In article <··············@world.std.com> , Tom Breton <···@world.std.com> 
> wrote:
> 
> >
> >Example: The other day I was maintaining code that had a big let* of
> >maybe 40 lines of declarations, and I realized that a problematic
> >declaration had to be tested for failure -- sometimes it should go as
> >before, sometimes it should do something new.  So -- carefully, I
> >thott -- I split the let into 2 lets, and watched it crash and die,
> >because I had not correctly split the let block.  
> >
> >Sure, I could write an emacs function to do that, but that's
> >considerably more work for a less general, less safe solution.  Then
> >I'd have to write another one to rejoin lets.
> >
> 
> Since you're using Emacs, you should learn to take advantage of it.  Edit
> your Lisp code using lisp-mode, and you can do things like c-m-f and c-m-b
> to move forward and backward over an entire s-expression.  Even better,
> c-m-q will reindent an entire form; I use this as a matter of habit when I
> make a large-scale change -- errors are immediately obvious after
> reindenting.

Thanks I guess, but I used those.  It's easy to say that I should have
split the block correctly, but we all know that writing code always
means making little mistakes that are obvious in hindsite.  Adding and
removing nested lets is always going to be a juggling act, unless one
writes code to make the editor do it (Argued as an inferior solution,
above).

> >Close; it would be something like this:
> >
> >(heres-a-block-with-letqs-in-it
> > (do-this) 
> > (letq a b) 
> > (do-that b)
> > (letq c d)
> > (do-other-thing c)) 
> >==>
> >(let
> > (a b)  ;;Here they're bound, as nil.
       ^ c, of course.  My bad.  See what I mean about mistakes?
> > (do-this) 
> > (setq a b) ;;Here it's assigned to
> > (do-that b)
> > (setq c d) ;;Here it's assigned to
> > (do-other-thing c))
> >
> 
> You're right, that is straightforward.  Although I'd choose a more
> perspicuous name rather than "heres-a-block-with-letqs-in-it".  

My choice of the name heres-a-block-with-letqs-in-it was
jocose.

> I'd rule out
> "with-letqs", since the with-... forms conventionally have the declarations
> up front, and burying them in the body.

"using-letq" seems as good as any.  I was tempted to "standardize" on
Marco Antoniotti's names vardef and with-vardef, but that would be
confusing since the behavior would be different.

> >The macro would scan for lists beginning with letq; that should be
> >easy, general, and safe.
> >
> 
> Yes, then you'd extract the variable names, build the binding list for the
> let, and rewrite the body with letq => setq.  Pretty simple.  

Yup, that was the idea.


-- 
Tom Breton
From: Steve Gonedes
Subject: Re: Just-in-time declarations, alternative to (let ... )
Date: 
Message-ID: <m290igrb34.fsf@KludgeUnix.com>
Tom Breton <···@world.std.com> writes:

< "David B. Lamkins" <········@teleport.com> writes:
 
< Yes, I know.  But while adding and removing variables to a single let
< is easy, adding and removing lets is harder and more error-prone.
< 
< Example: The other day I was maintaining code that had a big let* of
< maybe 40 lines of declarations, and I realized that a problematic
< declaration had to be tested for failure -- sometimes it should go as
< before, sometimes it should do something new.  So -- carefully, I
< thott -- I split the let into 2 lets, and watched it crash and die,
< because I had not correctly split the let block.  


< Close; it would be something like this:
< 
< (heres-a-block-with-letqs-in-it
< 	(do-this)	
< 	(letq a b) 
< 	(do-that b)
< 	(letq c d)
< 	(do-other-thing c)) 
< ==>
< (let
< 	(a b)  ;;Here they're bound, as nil.
< 	(do-this)	
< 	(setq a b) ;;Here it's assigned to
< 	(do-that b)
< 	(setq c d) ;;Here it's assigned to
< 	(do-other-thing c))


If you can't break the function up maybe &aux will work?

(defun funky (file &aux one two three)
  (setq one 1)
  (declare (fixnum one))
 ...)

?
From: Tom Breton
Subject: Re: Just-in-time declarations, alternative to (let ... )
Date: 
Message-ID: <m34st4s130.fsf@world.std.com>
Steve Gonedes <········@worldnet.att.net> writes:

> 
> If you can't break the function up maybe &aux will work?
> 
> (defun funky (file &aux one two three)
>   (setq one 1)
>   (declare (fixnum one))
>  ...)
> 
> ?

Thanks I guess, but that wasn't really what I meant.  &aux is
basically the same as let. I guess it lets you write some tiny
functions a little bit prettier, but I don't think it adds much more
than that.

-- 
Tom Breton
From: Howard R. Stearns
Subject: Re: Just-in-time declarations, alternative to (let ... )
Date: 
Message-ID: <3627D1D9.D939E3C0@elwood.com>
I confess I haven't followed all the hard work by the many respondents. 
Nonetheless, I'll risk admitting that...

I'm confused by the fact that the question asks about separating the
DECLARATION of variables and their use, but the many replies have seem
(at first glance) more to do with separating BINDING or ASSIGNMENT from
their use.

Tom, which do you really have a problem with, BINDINGS or DECLARATIONS?


Tom Breton wrote:
> 
> I always prefer a style that lets me declare variables just in time.
> Lisp doesn't seem to like this style.  let and its variants all want
> everything variable to be declared up top.  Besides putting distance
> between declaration, construction and use, this has the effect of
> making it tricky to do computations between declarations, EG if one
> uses let*.
> 
> It should be easy to implement just-in-time declaration with a Lisp
> macro... Scan for a keyword (say, "letq") and transform the call into
> something of the form (let (name ...) ... (setq name ...) ... ).
> 
> I'm just wondering if there exists a canonical form of this: names,
> semantics?
> 
> --
> Tom Breton
From: Tom Breton
Subject: Re: Just-in-time declarations, alternative to (let ... )
Date: 
Message-ID: <m3zpavonv9.fsf@world.std.com>
"Howard R. Stearns" <······@elwood.com> writes:

> I confess I haven't followed all the hard work by the many respondents. 
> Nonetheless, I'll risk admitting that...
> 
> I'm confused by the fact that the question asks about separating the
> DECLARATION of variables and their use, but the many replies have seem
> (at first glance) more to do with separating BINDING or ASSIGNMENT from
> their use.
> 
> Tom, which do you really have a problem with, BINDINGS or DECLARATIONS?

Bindings, which are called declarations in other languages.  Sorry
about that, I should have been more accurate.  My bad.


-- 
Tom Breton