在重构此 Common Lisp 代码片段时,如何成功删除包装处理程序案例(错误处理)情况的 cond 子句?
While refactoring this Common Lisp code snippet, how to succesfully remove the cond clause wrapping a handler-case (error handling) situation?
我正在使用 SBCL、Emacs、Slime 和 Dexador(用于 HTTP 请求的库)。我有这个功能有效:
(defun old-handle-response-and-status (final-url method &optional user-content)
(let ((status-code)
(response))
(cond ((equal method "get")
(multiple-value-bind (bresponse bstatus-code)
(handler-case (dex:get final-url)
(dex:http-request-bad-request ()
(values nil
"The server returned a failed request of 400 (bad request) status."))
(dex:http-request-failed (e)
(values nil
(format nil "The server returned a failed request of ~a status." (dex:response-status e)))))
(list (setf response bresponse)
(setf status-code bstatus-code))))
((equal method "post")
(multiple-value-bind (bresponse bstatus-code)
(handler-case (dex:post final-url
:content user-content)
(dex:http-request-bad-request ()
(values nil
"The server returned a failed request of 400 (bad request) status."))
(dex:http-request-failed (e)
(values nil
(format nil "The server returned a failed request of ~a status." (dex:response-status e)))))
(list (setf response bresponse)
(setf status-code bstatus-code)))))))
它适用于 GET
、POST
,并且 错误处理 如预期请求面临错误。表明它有效的标志性示例是:
CL-USER> (old-handle-response-and-status "http://www.paulgraham.com" "get")
("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">
<html>
(big HTML omitted)
</html>"
200)
CL-USER> (old-handle-response-and-status "https://httpbin.org/post" "post" '(("name" . "pedro")))
("{
medium JSON omitted
}
"
200)
CL-USER> (old-handle-response-and-status "https://httpstat.us/409" "get")
(NIL "The server returned a failed request of 409 status.")
好的。在重构这段代码时,我试图删除 cond
子句。因此我做了一个新的更短的版本:
(defun new-handle-response-and-status (method-call)
(let ((status-code)
(response))
(multiple-value-bind (bresponse bstatus-code)
(handler-case method-call
(dex:http-request-bad-request ()
(values nil
"The server returned a failed request of 400 (bad request) status."))
(dex:http-request-failed (e)
(values nil
(format nil "The server returned a failed request of ~a status." (dex:response-status e)))))
(list (setf response bresponse)
(setf status-code bstatus-code)))))
它主要工作,但仅在请求成功时:
CL-USER> (new-handle-response-and-status (dex:get "http://www.paulgraham.com"))
("
HTML omitted
</html>"
NIL)
CL-USER> (new-handle-response-and-status (dex:post "https://httpbin.org/post" :content '(("name" . "pedro"))))
("{
medium JSON omitted
}
"
NIL)
当请求是失败的 HTTP 请求时,重构不会按预期工作!
调用时:
CL-USER> (new-handle-response-and-status (dex:get "https://httpstat.us/409"))
Slime 调试器抛出:
An HTTP request to "https://httpstat.us/409" returned 409 conflict.
我期待:
(NIL "The server returned a failed request of 409 status.")
我尝试将输入调整为带引号的 s 表达式并插入 eval
:
(defun new-handle-response-and-status (method-call)
(let ((status-code)
(response))
(multiple-value-bind (bresponse bstatus-code)
(handler-case (eval method-call)
(dex:http-request-bad-request ()
(values nil
"The server returned a failed request of 400 (bad request) status."))
(dex:http-request-failed (e)
(values nil
(format nil "The server returned a failed request of ~a status." (dex:response-status e)))))
(list (setf response bresponse)
(setf status-code bstatus-code)))))
有效:
CL-USER> (new-handle-response-and-status '(dex:get "https://httpstat.us/409"))
(NIL "The server returned a failed request of 409 status.")
但是,这感觉是一种不好的做法 - 与重构工作并不真正兼容。有没有不使用 eval
就能解决这个问题的方法?
也许使用 funcall
?
问题是您在调用函数之前调用 dex:get
或 dex:post
,因此处理程序绑定无效。
您需要传递一个调用它的函数,然后调用该函数。
(defun new-handle-response-and-status (method-call)
(let ((status-code)
(response))
(multiple-value-bind (bresponse bstatus-code)
(handler-case (funcall method-call)
(dex:http-request-bad-request ()
(values nil
"The server returned a failed request of 400 (bad request) status."))
(dex:http-request-failed (e)
(values nil
(format nil "The server returned a failed request of ~a status." (dex:response-status e)))))
(list (setf response bresponse)
(setf status-code bstatus-code)))))
(new-handle-response-and-status (lambda () (dex:get "https://httpstat.us/409")))
或者您可以将其转换为宏:
(defmacro new-handle-response-and-status (method-call)
`(let ((status-code)
(response))
(multiple-value-bind (bresponse bstatus-code)
(handler-case ,method-call
(dex:http-request-bad-request ()
(values nil
"The server returned a failed request of 400 (bad request) status."))
(dex:http-request-failed (e)
(values nil
(format nil "The server returned a failed request of ~a status." (dex:response-status e)))))
(list (setf response bresponse)
(setf status-code bstatus-code)))))
记住评价规则。
CL-USER 36 > (defun foo (a)
(print 'foo1)
a
(print 'foo2)
'return-value)
FOO
CL-USER 37 > (foo (print 'bar1))
BAR1
FOO1
FOO2
RETURN-VALUE
(print 'bar1)
在 foo
.
的 之外 进行评估
我正在使用 SBCL、Emacs、Slime 和 Dexador(用于 HTTP 请求的库)。我有这个功能有效:
(defun old-handle-response-and-status (final-url method &optional user-content)
(let ((status-code)
(response))
(cond ((equal method "get")
(multiple-value-bind (bresponse bstatus-code)
(handler-case (dex:get final-url)
(dex:http-request-bad-request ()
(values nil
"The server returned a failed request of 400 (bad request) status."))
(dex:http-request-failed (e)
(values nil
(format nil "The server returned a failed request of ~a status." (dex:response-status e)))))
(list (setf response bresponse)
(setf status-code bstatus-code))))
((equal method "post")
(multiple-value-bind (bresponse bstatus-code)
(handler-case (dex:post final-url
:content user-content)
(dex:http-request-bad-request ()
(values nil
"The server returned a failed request of 400 (bad request) status."))
(dex:http-request-failed (e)
(values nil
(format nil "The server returned a failed request of ~a status." (dex:response-status e)))))
(list (setf response bresponse)
(setf status-code bstatus-code)))))))
它适用于 GET
、POST
,并且 错误处理 如预期请求面临错误。表明它有效的标志性示例是:
CL-USER> (old-handle-response-and-status "http://www.paulgraham.com" "get")
("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">
<html>
(big HTML omitted)
</html>"
200)
CL-USER> (old-handle-response-and-status "https://httpbin.org/post" "post" '(("name" . "pedro")))
("{
medium JSON omitted
}
"
200)
CL-USER> (old-handle-response-and-status "https://httpstat.us/409" "get")
(NIL "The server returned a failed request of 409 status.")
好的。在重构这段代码时,我试图删除 cond
子句。因此我做了一个新的更短的版本:
(defun new-handle-response-and-status (method-call)
(let ((status-code)
(response))
(multiple-value-bind (bresponse bstatus-code)
(handler-case method-call
(dex:http-request-bad-request ()
(values nil
"The server returned a failed request of 400 (bad request) status."))
(dex:http-request-failed (e)
(values nil
(format nil "The server returned a failed request of ~a status." (dex:response-status e)))))
(list (setf response bresponse)
(setf status-code bstatus-code)))))
它主要工作,但仅在请求成功时:
CL-USER> (new-handle-response-and-status (dex:get "http://www.paulgraham.com"))
("
HTML omitted
</html>"
NIL)
CL-USER> (new-handle-response-and-status (dex:post "https://httpbin.org/post" :content '(("name" . "pedro"))))
("{
medium JSON omitted
}
"
NIL)
当请求是失败的 HTTP 请求时,重构不会按预期工作! 调用时:
CL-USER> (new-handle-response-and-status (dex:get "https://httpstat.us/409"))
Slime 调试器抛出:
An HTTP request to "https://httpstat.us/409" returned 409 conflict.
我期待:
(NIL "The server returned a failed request of 409 status.")
我尝试将输入调整为带引号的 s 表达式并插入 eval
:
(defun new-handle-response-and-status (method-call)
(let ((status-code)
(response))
(multiple-value-bind (bresponse bstatus-code)
(handler-case (eval method-call)
(dex:http-request-bad-request ()
(values nil
"The server returned a failed request of 400 (bad request) status."))
(dex:http-request-failed (e)
(values nil
(format nil "The server returned a failed request of ~a status." (dex:response-status e)))))
(list (setf response bresponse)
(setf status-code bstatus-code)))))
有效:
CL-USER> (new-handle-response-and-status '(dex:get "https://httpstat.us/409"))
(NIL "The server returned a failed request of 409 status.")
但是,这感觉是一种不好的做法 - 与重构工作并不真正兼容。有没有不使用 eval
就能解决这个问题的方法?
也许使用 funcall
?
问题是您在调用函数之前调用 dex:get
或 dex:post
,因此处理程序绑定无效。
您需要传递一个调用它的函数,然后调用该函数。
(defun new-handle-response-and-status (method-call)
(let ((status-code)
(response))
(multiple-value-bind (bresponse bstatus-code)
(handler-case (funcall method-call)
(dex:http-request-bad-request ()
(values nil
"The server returned a failed request of 400 (bad request) status."))
(dex:http-request-failed (e)
(values nil
(format nil "The server returned a failed request of ~a status." (dex:response-status e)))))
(list (setf response bresponse)
(setf status-code bstatus-code)))))
(new-handle-response-and-status (lambda () (dex:get "https://httpstat.us/409")))
或者您可以将其转换为宏:
(defmacro new-handle-response-and-status (method-call)
`(let ((status-code)
(response))
(multiple-value-bind (bresponse bstatus-code)
(handler-case ,method-call
(dex:http-request-bad-request ()
(values nil
"The server returned a failed request of 400 (bad request) status."))
(dex:http-request-failed (e)
(values nil
(format nil "The server returned a failed request of ~a status." (dex:response-status e)))))
(list (setf response bresponse)
(setf status-code bstatus-code)))))
记住评价规则。
CL-USER 36 > (defun foo (a)
(print 'foo1)
a
(print 'foo2)
'return-value)
FOO
CL-USER 37 > (foo (print 'bar1))
BAR1
FOO1
FOO2
RETURN-VALUE
(print 'bar1)
在 foo
.