Guillaume
Guillaume

Reputation: 279

Common List: Use handler-case in different packages

I've got the following lisp code :

;;; hop.lisp

(defpackage #:hop
  (:use #:cl ))

(in-package :hop)
(export 'hop)

(defun hop ()
  (restart-case
      (error "Hop")
    (hop ()
      (format t "hop"))))

Where I define a dummy function that always fails but provides a restart : hop.

In another package, in this file :

;;; hip.lisp

(defpackage #:hip
  (:use #:cl #:hop))

(in-package :hip)

(defun dhip ()
  (hop:hop))

(defun hip ()
  (handler-case
      (hop:hop)
    (error (e)
      (declare (ignore e))
      (format t "restarts: ~a~%" (compute-restarts))
      (invoke-restart 'hop))))

I define the function (hip) and (dhip) that call the function (hop) from the first package.

When I call (dhip), sbcl offers me a prompt where I can choose to restart using my restart hop :

Hop
   [Condition of type SIMPLE-ERROR]

Restarts:
 0: [HOP] HOP
 1: [RETRY] Retry SLIME REPL evaluation request.
 2: [*ABORT] Return to SLIME's top level.
 3: [ABORT] abort thread (#<THREAD "repl-thread" RUNNING     {1009868103}>)

Backtrace:
  0: (HOP)
  1: (DHIP)
  2: (SB-INT:SIMPLE-EVAL-IN-LEXENV (DHIP) #<NULL-LEXENV>)
  3: (EVAL (DHIP))
--more--

Which is what I expected.

However, when I call (hip), my restart hop is not listed by (compute-restarts), and it fails to use it :(

No restart HOP is active.
   [Condition of type SB-INT:SIMPLE-CONTROL-ERROR]

Restarts:
 0: [RETRY] Retry SLIME REPL evaluation request.
 1: [*ABORT] Return to SLIME's top level.
 2: [ABORT] abort thread (#<THREAD "repl-thread" RUNNING    {1009868103}>)

Backtrace:
  0: (SB-INT:FIND-RESTART-OR-CONTROL-ERROR HOP NIL T)
  1: (INVOKE-RESTART HOP)
  2: ((FLET #:FUN1 :IN HIP) #<unused argument>)
  3: (HIP)
  4: (SB-INT:SIMPLE-EVAL-IN-LEXENV (HIP) #<NULL-LEXENV>)
  5: (EVAL (HIP))

Do you know what can be done to make this works ?

Thanks,

Guillaule

Upvotes: 3

Views: 216

Answers (1)

Rainer Joswig
Rainer Joswig

Reputation: 139251

This has nothing to do with the packages.

With HANDLER-CASE the stack is already unwound, when the handler runs. Thus the restart established in the function is gone.

Use HANDLER-BIND instead. It runs the handler in the context of the error and thus the restarts of the function are available.

Example:

(defun hip ()
  (handler-bind ((error (lambda (e)
                          (declare (ignore e))
                          (format t "restarts: ~a~%" (compute-restarts))
                          (invoke-restart 'hop))))
      (hop)))

Upvotes: 4

Related Questions