Is this a good use for restart-bind?



;;;; Using restart-bind to offer explanations for errors

;; Basic example: Let us define a version of member
;; that signals an error if it cannot find the item in then list.

(defun known-member (item list)
(let ((search-result (member item list)))
(or search-result
(error "~A was not in ~A." item list))))

;; This code can be rather annoying
#|
(known-member "bob" '("alice" "bob" "carol"))

=> Error in function KNOWN-MEMBER: "bob" was not in ("alice" "bob" "carol").
|#
;; On the other hand, over elaborate error messages are a problem too. The
;; message as written is good for many cases

;; Instead of adding further information to the error message, let us add an explanation option

(defun known-member (item list)
(let ((search-result (member item list)))
(restart-bind ((explain (lambda()
(let ((search-result (member item
list
:test #'equalp)))
(if search-result
(format *query-io*
"~&~S is EQUALP to ~S ~
but KNOWN-MEMBER uses EQL."
item (car search-result))
(let ((search-result
(member item
list
:test (lambda(x y)
(and (typep x 'sequence)
(typep y 'sequence)
(not (mismatch x y
:test #'equalp)))))))
(if search-result
(format *query-io*
"~&~S is a ~S but ~S is a ~S."
item (type-of item)
(car search-result)
(type-of (car search-result)))
(format *query-io*
"~&Could not find anything close to ~S in the list.~
~%So no local explanation in terms of the item being ~
in the list,~@
but the equality test being too strict."
item))))))
:report-function (lambda(stream)
(format stream "Examine list for near misses."))))
(or search-result
(error "~S was not in ~S." item list)))))

;; If the error message baffles, the explanation sometimes helps
#|

CL-USER> (known-member #(5 7) '((3 9)(4 8)(5 7)(6 6)))
#(5 7) is a (SIMPLE-VECTOR 2) but (5 7) is a CONS.

CL-USER> (known-member "bob" '("alice" "bob" "carol"))
"bob" is EQUALP to "bob" but KNOWN-MEMBER uses EQL.

|#
;; This occurred to me in the course of designing a macro.
;; What should it say if it could not expand because of an error
;; parsing the argument? Usually one wants the error message to be brief,
;; but if the macro is complicated one sometimes wants to ask for more detail

Alan Crowe
Edinburgh
Scotland
.