From: Klaus Harbo
Subject: Macro for Java-style try/catch
Date: 
Message-ID: <87smqnbebi.fsf@harbo.net>
I written a macro which implements Java-style try/catch that I thought
I'd share.  Searching Google, I haven't found anything similar
but I can easily have missed something.

The utility of the macro is best illustrated by an example, e.g.
the following function

     (defun trytest (dothr &key thr)
       (try (progn
              (print 11)
              (when dothr (throw thr (values 111 222)))
              'finished)
            (catch 'yes (bind1 bind2)
                   (print (* 1000 bind1))
                   (print bind2)
                   'yes-thrown)
            (catch 'no (binda bindb)
                   (print binda)
                   (print (* 1000 bindb))
                   'no-thrown)))

behaves as follows

     * (trytest nil)
     11 
     FINISHED
     * (trytest t :thr 'yes)
     11 
     111000 
     222 
     YES-THROWN
     * (trytest t :thr 'no)
     11 
     111 
     222000 
     NO-THROWN
     * 

Perhaps I've suffered irreparable damage programming Java, but I find
this type of packaging of catch/throw very convenient in many
situations.  See macro definition below.

Comments, suggestions for prossible improvements are welcome.


-Klaus.

PS Ain't CL macros cool?!  Trying doing that with #define.


;;
;; Java-style try/catch
;;
(defmacro try (code &rest catches)
  (let ((acc `(throw fintag ,code)))
    (dolist (c catches)
      (destructuring-bind
	  (kw ctag binding &body catchcode) c
	(unless (equal kw 'catch)
	  (error "Bad catch keyword ~S in expression ~S." kw c))
	(setf acc
	      `(multiple-value-bind
		   ,binding
		   (catch ,ctag ,acc)
		 (throw fintag (progn ,@catchcode))))))
    `(let ((fintag (gensym)))
       (catch fintag ,acc))))

From: Kent M Pitman
Subject: Re: Macro for Java-style try/catch
Date: 
Message-ID: <sfwsmqn2t3u.fsf@shell01.TheWorld.com>
Klaus Harbo <·····@harbo.net> writes:

> I written a macro which implements Java-style try/catch that I thought
> I'd share.  Searching Google, I haven't found anything similar
> but I can easily have missed something.

Probably for good reason, but I'll try to explain why.

I've included the entire text of your code so that it's easy to
compare back and forth between your original version and my rewrite
without fishing around in your newsreader.  I apologize in advance for
the length of the resulting message.
 
> The utility of the macro is best illustrated by an example, e.g.
> the following function
> 
>      (defun trytest (dothr &key thr)
>        (try (progn
>               (print 11)
>               (when dothr (throw thr (values 111 222)))
>               'finished)
>             (catch 'yes (bind1 bind2)
>                    (print (* 1000 bind1))
>                    (print bind2)
>                    'yes-thrown)
>             (catch 'no (binda bindb)
>                    (print binda)
>                    (print (* 1000 bindb))
>                    'no-thrown)))
> 
> behaves as follows
> 
>      * (trytest nil)
>      11 
>      FINISHED
>      * (trytest t :thr 'yes)
>      11 
>      111000 
>      222 
>      YES-THROWN
>      * (trytest t :thr 'no)
>      11 
>      111 
>      222000 
>      NO-THROWN
>      * 
> 
> Perhaps I've suffered irreparable damage programming Java, but I find
> this type of packaging of catch/throw very convenient in many
> situations.  See macro definition below.

I think you should try very hard to outgrow this need. You can also program
Fortran in Lisp by doing

 (PROG (A B C D)
    8  (SETQ A 33.7)
    9  (SETQ B 4.2)
   10  (SETQ A (/ A B))
   17  (IF (> A 1) (GO 10))
   19  (FORMAT T "~F" A)
   33  (RETURN))

but it isn't what the language is really intending you to do, except for
transition situations of programmers starting slowly into Lisp,
macro expansions where you're doing things at a subprimitive level,
and specialized applications like finite state machines.

You really should try to lean a little more heavily into the language itself,
especially insofar as it provides you with appropriate abstractions.

> Comments, suggestions for prossible improvements are welcome.
> 
> 
> -Klaus.
> 
> PS Ain't CL macros cool?!  Trying doing that with #define.

Certainly a good point, independent of the rest of my more specific comments.

> ;;
> ;; Java-style try/catch
> ;;
> (defmacro try (code &rest catches)
>   (let ((acc `(throw fintag ,code)))
>     (dolist (c catches)
>       (destructuring-bind
> 	  (kw ctag binding &body catchcode) c
> 	(unless (equal kw 'catch)
> 	  (error "Bad catch keyword ~S in expression ~S." kw c))
> 	(setf acc
> 	      `(multiple-value-bind
> 		   ,binding
> 		   (catch ,ctag ,acc)
> 		 (throw fintag (progn ,@catchcode))))))
>     `(let ((fintag (gensym)))
>        (catch fintag ,acc))))

Disclaimer: I really don't like this programming style and would never
 recommend it to another.  Nevertheless, let me offer you a few helpful
 notes about how to reduce the number of things that I dislike about it
 to merely "it's a crutch I think you should try to outgrow":


 * I think that you will confuse CL readers of your code if you have something
   named CATCH in your code that is not really a CATCH.  I suggest instead
   of (TRY ... (CATCH ...) ...) that you use (TRY ... (CATCHING ...) ...).

   Actually, related to this, I recommend using :CATCHING so that you only
   have to import/export one symbol if you want to use this in another package.
   
 * Also, you seem to assume that only one value will be thrown.  This limits
   the usefulness of your macro.

 * Also, I haven't done any programming in Java in a long time, but I believe
   that the catch clauses of a try are outside of the protection of each
   other.  Your implementation has it so that if one catch happens and it
   contains code that does a throw, the remaining catch clauses (to the right
   of the one that is executing) will still be active.  I don't think you'll
   find that very satisfactory to either Java _or_ CL programmers.  You should
   invalidate all of the clauses at the same time.

 * Don't use CATCH/THROW for the main code.  Use block/return.  Use throw
   only for situations where some throwers are not lexically visible.
   If you do this, you don't have to gensym something at runtime.

 * Don't use confusing little abbreviation-names for variables when writing
   your code.

 * Don't let the catch tag evaluate.  You _could_, because CATCH does this,
   but that's not how Java works.  It uses a fixed tag, and I think it will
   confuse programmers.

 * I added :FINALLY per Java's try/finally.

 * I guess what's _really_ troubling to me is that try/catch in Java is
   used for handling errors, not for catch/throw. What you _really_
   want is not to to have :catching name a catch tag, but to name a
   condition type, and to use SIGNAL or ERROR to signal the error,
   _not_ THROW.  By using the Java primitive, you kind of imply that
   this will happen and this is at the core of why I don't like this 
   operator and think of it mostly as a crutch.  It reinforces the false
   notion that our catch/throw is related to Java's; it is not.  Our
   handler-case/signal is quasi-related to Java's catch/throw, although
   in part because the handler-case abstraction builds in calls to 
   catch/throw (as does Java's) -- the type discrimination stuff we offer
   is lost in your abstraction.  I did not fix this in the code below.
   I've left that as an "exercise to the reader".

e.g.,

 (defmacro try (code &rest catches)
   (let* ((block-tag   (gensym "TRY-BLOCK"))
          (code-tag    (gensym "DISPATCH"))
          (tag-temp    (gensym "TAG"))
          (values-temp (gensym "VALUES"))
          (code `(return-from ,block-tag ,code))
          (dispatches  '())
          (cleanups '()))
     (dolist (clause catches)
       (case (car clause)
         ((:catching)
          (destructuring-bind (catch-tag clause-bindings
                               &body clause-body)
              (cdr clause)
            (push `((,catch-tag)
                    (destructuring-bind ,clause-bindings ,values-temp
                      (return-from ,block-tag (progn ,@clause-body))))
                  dispatches)
            (setf code
                  `(progn (setq ,values-temp
                            (multiple-value-list (catch ',catch-tag ,code)))
                          (setq ,tag-temp ',catch-tag)
                          (go ,code-tag)))))
         ((:finally)
          (dolist (cleanup (cdr clause)) (push cleanup cleanups)))
         (otherwise
          (error "Bad clause keyword ~S in ~S clause ~S."
                 (car clause) 'try clause))))
     (setq code
       `(let ((,tag-temp) (,values-temp))
          (block ,block-tag
            (tagbody
              ,code
              ,code-tag
              (ecase ,tag-temp
               ,@(reverse dispatches))))))
     (if (not cleanups) code
       `(unwind-protect ,code ,@(reverse cleanups)))))


Using a modified form of your tester:

Lisp> (defun trytest (&key throw-tag)
        (handler-case
            (try (progn
                   (print 11)
                   (when throw-tag (throw throw-tag (values 111 222)))
                   'finished)
              (:catching yes (bind1 bind2)
                (print (* 1000 bind1))
                (print bind2)
                'yes-thrown)
              (:catching no (binda bindb)
                (print binda)
                (print (* 1000 bindb))
                'no-thrown)
              (:finally
                (print 'done)))
           (error (c)
             (format t "~&Lossage: ~A~%" c)
             'lossage)))
TRYTEST

Lisp> (trytest :throw-tag nil)

11 
DONE 

=> FINISHED

Lisp> (trytest :throw-tag 'yes)

11 
111000 
222 
DONE 

=> YES-THROWN

Lisp> (trytest :throw-tag 'no)

11 
111 
222000 
DONE 

=> NO-THROWN

Lisp> (trytest :throw-tag 'maybe)

11 
DONE 
Lossage: Uncaught throw to tag MAYBE.

=> LOSSAGE
From: Klaus Harbo
Subject: Re: Macro for Java-style try/catch
Date: 
Message-ID: <87he73b45j.fsf@harbo.net>
Kent,

Your feedback is very much appreciated, and quite a bit more than I
had hoped for - thanks a lot.  After reading your code, I see that my
relative lack of experience with CL shows. ;-)

I have never taken the time to read properly up on tagbody, but I
agree with you that it does fit the bill quite nicely in this
instance.

I agree with most of your comments and asuggestions.

Kent M Pitman <······@world.std.com> writes:

>  * Don't let the catch tag evaluate.  You _could_, because CATCH does this,
>    but that's not how Java works.  It uses a fixed tag, and I think it will
>    confuse programmers.

You're right that Java doesn't do that - however my aim was not really
to emulate the Java behavior in all aspects, but rather implement the
try/catch style, retaining the evaluation like for CATCH.

>  * I added :FINALLY per Java's try/finally.

Great idea.

>  * I guess what's _really_ troubling to me is that try/catch in Java is
>    used for handling errors, not for catch/throw. What you _really_
>    want is not to to have :catching name a catch tag, but to name a
>    condition type, and to use SIGNAL or ERROR to signal the error,
>    _not_ THROW.  By using the Java primitive, you kind of imply that
>    this will happen and this is at the core of why I don't like this 
>    operator and think of it mostly as a crutch.  It reinforces the false
>    notion that our catch/throw is related to Java's; it is not.  Our
>    handler-case/signal is quasi-related to Java's catch/throw, although
>    in part because the handler-case abstraction builds in calls to 
>    catch/throw (as does Java's) -- the type discrimination stuff we offer
>    is lost in your abstraction.  I did not fix this in the code below.
>    I've left that as an "exercise to the reader".

I think I understand what you mean.  As it happens I was reading up on
the Condition System just yesterday and am still digesting their
implications on the higher-level structure of robust CL programs.  At
this point I am not entirely clear on how to choose between catch/throw and
signals/handlers.  I will be looking into that, though.

I wonder if there are any good references to papers discussing this
issue?  Preferably on-line, of course.


Thanks again.


best regards,

-Klaus.
From: Pascal Costanza
Subject: Re: Macro for Java-style try/catch
Date: 
Message-ID: <bbqfgg$nhi$1@f1node01.rhrz.uni-bonn.de>
Klaus Harbo wrote:

> As it happens I was reading up on
> the Condition System just yesterday and am still digesting their
> implications on the higher-level structure of robust CL programs.  At
> this point I am not entirely clear on how to choose between catch/throw and
> signals/handlers.  I will be looking into that, though.

It's relatively simple: don't use catch/throw for error/exception 
handling! ;)

You know about the return statement in Java. Common Lisp provides 
essentially two variants of such return statements:
- return-from allows you to return something from a function, or from a 
block, similar to Java's return.
- catch/throw allow you to do more or less the same, except that you can 
  skip more than one method at a time.

(It's very similar to the disctinction between lexically and dynamically 
scoped variables.)

So, as Kent already said, you can use return-from if the code that you 
want to exit with a value is the immediately surrounding code. If the 
code that you want to exit is several frames above in the call stack 
then you can use catch/throw. That's it. This has nothing to do with 
exceptions/errors whatsoever.

For exceptions you should stick exclusively to the condition system.


Pascal

-- 
Pascal Costanza               University of Bonn
···············@web.de        Institute of Computer Science III
http://www.pascalcostanza.de  R�merstr. 164, D-53117 Bonn (Germany)
From: Kent M Pitman
Subject: Re: Macro for Java-style try/catch
Date: 
Message-ID: <sfwptlr6nfu.fsf@shell01.TheWorld.com>
Pascal Costanza <········@web.de> writes:

> Klaus Harbo wrote:
> 
> > As it happens I was reading up on
> > the Condition System just yesterday and am still digesting their
> > implications on the higher-level structure of robust CL programs.  At
> > this point I am not entirely clear on how to choose between catch/throw and
> > signals/handlers.  I will be looking into that, though.
> 
> It's relatively simple: don't use catch/throw for error/exception
> handling! ;)
> 
> You know about the return statement in Java. Common Lisp provides
> essentially two variants of such return statements:
> - return-from allows you to return something from a function, or from
> a block, similar to Java's return.
> - catch/throw allow you to do more or less the same, except that you
> can skip more than one method at a time.
> 
> (It's very similar to the disctinction between lexically and
> dynamically scoped variables.)
> 
> So, as Kent already said, you can use return-from if the code that you
> want to exit with a value is the immediately surrounding code. If the
> code that you want to exit is several frames above in the call stack
> then you can use catch/throw. That's it. This has nothing to do with
> exceptions/errors whatsoever.
> 
> For exceptions you should stick exclusively to the condition system.

I agree with this analysis, but would have worded the part about conditions
slightly differently.  The condition system is not "another kind of control
mechanism" it is "a packaging of the existing control mechanisms".  Its 
sole purpose is to establish a protocol so that it can be written by 
different, non-communicating people who are cooperating.  (Some days this
can be you in one of your projects and you in another, but usually it will
be two distinct people.)  Protocols are half-programs that must be plug
compatible.  Like the protocol for getting power out of the wall, or the
protocol for putting power into the wall for other people to use.  It joins
at a modular plug.  But if someone is going to both provide power and consume
it, they aren't constrained by that, so the maker of a solar cell calculator
doesn't have to fuss with what kind of plug system to use snice there may be
no plug at all.  I don't know if that's helpful, but it helps me.

Klaus asked about [online] papers.  See my paper collection
 http://www.nhplace.com/kent/Papers/
which has at least the following two papers that are of interest:

 Exceptional Situations in Lisp
 http://www.nhplace.com/kent/Papers/Exceptional-Situations-1990.html

 Condition Handling in the Lisp Language Family
 http://www.nhplace.com/kent/Papers/Condition-Handling-2001.html
From: Nils Goesche
Subject: Re: Macro for Java-style try/catch
Date: 
Message-ID: <lyd6hr6txh.fsf@cartan.de>
Pascal Costanza <········@web.de> writes:

> Klaus Harbo wrote:
> 
> > As it happens I was reading up on the Condition System just
> > yesterday and am still digesting their implications on the
> > higher-level structure of robust CL programs.  At this point I am
> > not entirely clear on how to choose between catch/throw and
> > signals/handlers.  I will be looking into that, though.
> 
> It's relatively simple: don't use catch/throw for error/exception
> handling! ;)
> 
> You know about the return statement in Java. Common Lisp provides
> essentially two variants of such return statements:

> - return-from allows you to return something from a function, or
> from a block, similar to Java's return.

> - catch/throw allow you to do more or less the same, except that you
> can skip more than one method at a time.

But RETURN-FROM can do that, too:

(defun test ()
  (flet ((foo (n)
           (if (< n 5)
               (1+ n)
             (return-from test 42))))
    (loop for x = 1 then (foo x) do
          (format t "~&Calling FOO with ~A." x))))

The difference is that RETURN-FROM can only escape from blocks that
are /lexically visible/.

Regards,
-- 
Nils G�sche
"Don't ask for whom the <CTRL-G> tolls."

PGP key ID 0x0655CFA0
From: Wade Humeniuk
Subject: Re: Macro for Java-style try/catch
Date: 
Message-ID: <im3Ea.14508$6f3.3161280@news1.telusplanet.net>
"Klaus Harbo" <·····@harbo.net> wrote in message ···················@harbo.net...
> 
> I written a macro which implements Java-style try/catch that I thought
> I'd share.  Searching Google, I haven't found anything similar
> but I can easily have missed something.
> 
> The utility of the macro is best illustrated by an example, e.g.
> the following function
> 
>      (defun trytest (dothr &key thr)
>        (try (progn
>               (print 11)
>               (when dothr (throw thr (values 111 222)))
>               'finished)
>             (catch 'yes (bind1 bind2)
>                    (print (* 1000 bind1))
>                    (print bind2)
>                    'yes-thrown)
>             (catch 'no (binda bindb)
>                    (print binda)
>                    (print (* 1000 bindb))
>                    'no-thrown)))

Using the CL condition system the code could look something like

(define-condition test-throw (simple-condition)
  ((a :initarg :a)
   (b :initarg :b)))

(define-condition yes (test-throw) ())
(define-condition no (test-throw) ())

(defun trytest (&optional condition)
  (handler-case
      (progn
        (print 11)
        (when condition (signal condition :a 111 :b 222))
        'finished)
    (yes (condition)
         (with-slots (a b) condition
           (print (* 1000 a))
           (print b)
           'yes))
    (no (condition)
        (with-slots (a b) condition
          (print a)
          (print (* 100 b))
          'no))))

CL-USER 12 > (trytest 'yes)

11 
111000 
222 
YES

CL-USER 13 > (trytest 'no)

11 
111 
22200 
NO

CL-USER 14 > (trytest)

11 
FINISHED

CL-USER 15 > 
From: Klaus Harbo
Subject: Re: Macro for Java-style try/catch
Date: 
Message-ID: <87adcvaw6c.fsf@harbo.net>
"Wade Humeniuk" <····@nospam.nowhere> writes:

> Using the CL condition system the code could look something like
> 
> (define-condition test-throw (simple-condition)
>   ((a :initarg :a)
>    (b :initarg :b)))
> 
> (define-condition yes (test-throw) ())
> (define-condition no (test-throw) ())
> 
> (defun trytest (&optional condition)
>   (handler-case
>       (progn
>         (print 11)
>         (when condition (signal condition :a 111 :b 222))
>         'finished)
>     (yes (condition)
>          (with-slots (a b) condition
>            (print (* 1000 a))
>            (print b)
>            'yes))
>     (no (condition)
>         (with-slots (a b) condition
>           (print a)
>           (print (* 100 b))
>           'no))))
> 

This code basically does what I was trying to accomplish with my
macro, using standard CL.  No need for a macro.

Thx, Wade.

-Klaus.