From: ·········@math.ufl.edu
Subject: Multiple-values and altered DEFUN
Date: 
Message-ID: <1194894192.882550.215030@o80g2000hse.googlegroups.com>
Eight years ago, in

http://groups.google.com/group/comp.lang.lisp/browse_thread/thread/e952239cfba4a55/

I asked [edited]:

    What is a correct way to define macros `defun-RF' and `RF'
    so that a creation such as

	(defun extremely-long-function-name (x)
	  ...
	  (return-from extremely-long-function-name 'a)
	  ...
	  (return-from extremely-long-function-name 'b)      )

    can be replaced by

	(defun-RF extremely-long-function-name (x)
	  ...
	  (RF 'a)
	  ...
	  (RF 'b)      )


Barry Margolin kindly posted a soln [edited]:

    (defmacro defun-RF (func-name arglist &rest body)
      `(defun ,func-name ,arglist
	 (flet ((RF (RetVal) (return-from ,func-name RetVal)))
	   ,@body)))

that I am using now. (Other folks also kindly posted solns.)
  What is a version that returns multiple-values?  With the
current definition, function

    (defun-RF foo (bool)
      (if bool (RF (floor 2007 500))
	(return-from foo (floor 2007 500)) ) )

when evaluated, gives

    (foo nil)   =>  4 ; 7

but does not give multiple-values with

    (foo t)     =>  4

From: Rainer Joswig
Subject: Re: Multiple-values and altered DEFUN
Date: 
Message-ID: <joswig-DE93D9.20310512112007@news-europe.giganews.com>
In article <························@o80g2000hse.googlegroups.com>,
 ·········@math.ufl.edu wrote:

> Eight years ago, in
> 
> http://groups.google.com/group/comp.lang.lisp/browse_thread/thread/e952239cfba4a55/
> 
> I asked [edited]:
> 
>     What is a correct way to define macros `defun-RF' and `RF'
>     so that a creation such as
> 
> 	(defun extremely-long-function-name (x)
> 	  ...
> 	  (return-from extremely-long-function-name 'a)
> 	  ...
> 	  (return-from extremely-long-function-name 'b)      )
> 
>     can be replaced by
> 
> 	(defun-RF extremely-long-function-name (x)
> 	  ...
> 	  (RF 'a)
> 	  ...
> 	  (RF 'b)      )
> 
> 
> Barry Margolin kindly posted a soln [edited]:
> 
>     (defmacro defun-RF (func-name arglist &rest body)
>       `(defun ,func-name ,arglist
> 	 (flet ((RF (RetVal) (return-from ,func-name RetVal)))
> 	   ,@body)))
> 
> that I am using now. (Other folks also kindly posted solns.)
>   What is a version that returns multiple-values?  With the
> current definition, function
> 
>     (defun-RF foo (bool)
>       (if bool (RF (floor 2007 500))
> 	(return-from foo (floor 2007 500)) ) )
> 
> when evaluated, gives
> 
>     (foo nil)   =>  4 ; 7
> 
> but does not give multiple-values with
> 
>     (foo t)     =>  4

You can also use this:

? (defun this-should-be-a-really-long-name (bar)
    (block me
      (return-from me (floor bar 3))))
THIS-SHOULD-BE-A-REALLY-LONG-NAME

? (THIS-SHOULD-BE-A-REALLY-LONG-NAME 20)
6
2
From: Peder O. Klingenberg
Subject: Re: Multiple-values and altered DEFUN
Date: 
Message-ID: <ks8x53s018.fsf@beto.netfonds.no>
·········@math.ufl.edu writes:

>   What is a version that returns multiple-values?

(defmacro defun-RF (func-name arglist &rest body)
  `(defun ,func-name ,arglist
    (macrolet ((RF (form) `(return-from ,',func-name ,form)))
      ,@body)))

>     (defun-RF foo (bool)
>       (if bool (RF (floor 2007 500))
> 	(return-from foo (floor 2007 500)) ) )

Now gives

CL-USER> (foo nil)
4
7
CL-USER> (foo t)
4
7

...Peder...
-- 
I wish a new life awaited _me_ in some off-world colony.
From: ·········@math.ufl.edu
Subject: Re: Multiple-values and altered DEFUN
Date: 
Message-ID: <1194912196.696866.56270@o80g2000hse.googlegroups.com>
On Nov 12, 3:04 pm, ·····@news.klingenberg.no (Peder O. Klingenberg)
wrote:

> ·········@math.ufl.edu writes:
> >   What is a version that returns multiple-values?
>
> (defmacro defun-RF (func-name arglist &rest body)
>   `(defun ,func-name ,arglist
>     (macrolet ((RF (form) `(return-from ,',func-name ,form)))
>       ,@body)))

Thank you Peder Klingenberg.  The original thread had a similar
macro, and Barry Margolin pointed out a kind of inner/outer-block
difference in behavior between the `flet' and the `macrolet'.  I
believe he was referring to something like the following.

  ;; The MACROLET version, whence the "m" in "m-RF".
  (defmacro m-RF (func-name arglist &rest body)
    `(defun ,func-name ,arglist
       (macrolet ((RF (value) (list 'return-from ',func-name value)))
	 ,@body ) ) )

  ;; The FLET version, whence the "f" in "f-RF".
  (defmacro f-RF (func-name arglist &rest body)
    `(defun ,func-name ,arglist
       (flet ((RF (value) (return-from ,func-name value)))
	 ,@body ) ) )

  ;; Define fncs `mm' and `ff' which are identical, except that the
  ;; first uses MACROLET and the latter uses FLET.
  (m-RF mm (x)
	(cons 'Normal-Block-exit
	      (block mm
		(if (zerop x) (return-from mm 'Used_ReturnFrom)
			      (RF 'Used_RF) ) ) ) )
  ;;
  (f-RF ff (x)
	(cons 'Normal-Block-exit
	      (block ff
		(if (zerop x) (return-from ff 'Used_ReturnFrom)
			      (RF 'Used_RF) ) ) ) )

  ;; Test macrolet version
  (mm 0)   =>  (NORMAL-BLOCK-EXIT . USED_RETURNFROM)
  (mm 5)   =>  (NORMAL-BLOCK-EXIT . USED_RF)

  ;;  With the lexical scoping of `flet'.
  (ff 0)   =>  (NORMAL-BLOCK-EXIT . USED_RETURNFROM)
  (ff 5)   =>  USED_RF

While it is unlikely in the extreme that I would make a
block with the same name as the enclosing fnc, nonetheless
the lexical `flet' behavior is how I envision the
defun-RF/RF pair operating.

Also, I am curious whether `flet' can be used to define a
local fnc that handles multiple values.
From: Peder O. Klingenberg
Subject: Re: Multiple-values and altered DEFUN
Date: 
Message-ID: <ksejetg97q.fsf@beto.netfonds.no>
·········@math.ufl.edu writes:

> The original thread had a similar macro, and Barry Margolin pointed
> out a kind of inner/outer-block difference in behavior between the
> `flet' and the `macrolet'.

Well, yes.  The flet-version defines calls return-from outside any
lexical scope of blocks in BODY, and is thus guaranteed to return from
the block defined by the define-rf macro.

The macro version, on the other hand, expands into the middle of BODY,
and will thus see your explicit block as the nearest lexical block
with the correct name.

This can be viewed as a form of variable capture, only with tags
instead of variables, and the solution is the same as for any other
variable capture problems in macros: GENSYM.

I guess it's a matter of viewpoint wether one likes one behaviour or
the other.  I think I would view a macro such as define-rf to be
merely sugar so save some typing, and use it in full understanding of
it expanding into a return-from.  From that point of view, the
principle of least surprise would indicate that it's actually a
feature to be able to define a new block with the same name and have
rf return there.

If, on the other hand, you want to view defun-rf as a full-blown
abstraction in its own right, it's reasonable to expect RF to return
from the whole function, regardless of the blocks defined in its
body.

Here's a version that works more like the flet version.  Actually it
works exactly like the macrolet version, except that it guarantees
that you won't be able to define another block with the same name:

(defmacro defun-RF (func-name arglist &rest body)
   (let ((rf (gensym)))
     `(defun ,func-name ,arglist
       (block ,rf 
	 (macrolet ((RF (form) `(return-from ,',rf ,form)))
           ,@body)))))

(defun-RF mm ()
  (cons 'Normal-Block-exit
	(block mm
	      (RF 'Used_RF))))

Macroexpands to:

(DEFUN MM ()
  (BLOCK #:G2546
    (MACROLET ((RF (FORM) `(RETURN-FROM #:G2546 ,FORM)))
      (CONS 'NORMAL-BLOCK-EXIT (BLOCK MM (RF 'USED_RF))))))

Which in turn yields:

CL-USER> (mm)
USED_RF

> Also, I am curious whether `flet' can be used to define a
> local fnc that handles multiple values.

No.  The arguments are already evaluated by the time your function is
called, and it will only see the primary values.  You need a macro or
special operator to receive the multiple values.  See section 3.1.7 in
the Hyperspec.

If you really, really want to have the return-from call in a function,
without making your user code handle multiple values specially, you can
do something like

(defmacro defun-rf-silly (func-name arglist &rest body)
  (flet ((%%rf (&rest values)
           (return-from ,func-name (values-list values))))
    (macrolet ((rf (form) (multiple-value-call '%%rf ,form)))
      ,@body)))

(completely untested, hand typed into the news post, caveat
emptor)

I don't think that buys you much.  And you'd probably want to use a
gensym instead of %%rf to avoid exposing that implementation detail to
your user code.

...Peder...
-- 
Sl�v uten dop.
From: ·········@math.ufl.edu
Subject: Re: Multiple-values and altered DEFUN
Date: 
Message-ID: <1194996936.961273.326110@19g2000hsx.googlegroups.com>
Thank you, Peder, for a careful explanation and analysis.
I'll experiment with the various alternatives; thanks again.
--
Prof. Jonathan LF King	 Mathematics dept, Univ. of Florida
From: Peder O. Klingenberg
Subject: Re: Multiple-values and altered DEFUN
Date: 
Message-ID: <ks4pfrrz6e.fsf@beto.netfonds.no>
·····@news.klingenberg.no (Peder O. Klingenberg) writes:

> (defmacro defun-RF (func-name arglist &rest body)
>   `(defun ,func-name ,arglist
>     (macrolet ((RF (form) `(return-from ,',func-name ,form)))
>       ,@body)))

A fine reading of the hyperspec leads me to believe that this
definition is in fact undefined territory.  The definition of macrolet
says "[...] but the consequences are undefined if the local macro
definitions reference any local variable or function bindings that are
visible in that lexical environment".

So YMMV.  I worked for me as I typed it into the REPL of Lispworks
5.0.2 without compiling anything.

...Peder...
-- 
I wish a new life awaited _me_ in some off-world colony.
From: Pascal Costanza
Subject: Re: Multiple-values and altered DEFUN
Date: 
Message-ID: <5prrr3FsqravU1@mid.individual.net>
Peder O. Klingenberg wrote:
> ·····@news.klingenberg.no (Peder O. Klingenberg) writes:
> 
>> (defmacro defun-RF (func-name arglist &rest body)
>>   `(defun ,func-name ,arglist
>>     (macrolet ((RF (form) `(return-from ,',func-name ,form)))
>>       ,@body)))
> 
> A fine reading of the hyperspec leads me to believe that this
> definition is in fact undefined territory.  The definition of macrolet
> says "[...] but the consequences are undefined if the local macro
> definitions reference any local variable or function bindings that are
> visible in that lexical environment".

That's not the case here. The HyperSpec refers to the macro definition 
itself, not the code that is produced by the macro.

Here is what you shouldn't do:

(let ((x 42))
   (macrolet ((foo ()
                (if (> x 10)
                  '(print 'yes)
                  '(print 'no))))
     (foo)))

The reason is that macros are expanded at compile time (for compiled 
implementations), when the bindings of local variables don't exist yet. 
So at the time when the macro is expanded, there simply is no variable x 
yet.

Here is what you can do:

(let ((x 42))
   (macrolet ((foo ()
                '(if (> x 10)
                   (print 'yes)
                   (print 'no))))
     (foo)))

Here, the macro expands into code, which at runtime will be able to 
access a variable x that is alive and well.

It's important to keep these two cases separated (actually anyway when 
you're implementing macros, not only for local macros).


Pascal

-- 
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: Peder O. Klingenberg
Subject: Re: Multiple-values and altered DEFUN
Date: 
Message-ID: <kszlxjqip3.fsf@beto.netfonds.no>
Pascal Costanza <··@p-cos.net> writes:

> That's not the case here.

Thanks for clearing that up.  I typed this in the way I thought it
should work, and it worked, but I just thought I'd double check (I
don't use macrolet that often).  Never a good idea.  It's late, and
I don't have nearly enough caffeine in my system. :)

...Peder...
-- 
I wish a new life awaited _me_ in some off-world colony.