(define-condition socket-connection-error (error) ((text :initarg :text :reader text))) (defun sock-connect (host port) (let ((r (random 2))) (if (= r 1) (format t "connected to ~a:~a.~%" host port) (error 'socket-connection-error :text "failed to connect to server"))) t) (defmacro sock-send (s &rest req) `(format ,s ,@req) "hello, world") (defun get-page (host page &optional (port 80)) (let ((sock (sock-connect host port))) (sock-send sock "GET ~a HTTP/1.0~2%" page))) (defun http-get (host page &optional (port 80)) (get-page host page port)) ;; like try-catch (defun http-get-1 (host page &optional (port 80)) (handler-case (get-page host page port) (socket-connection-error (ex) (format t "error: ~a~%" (text ex))))) ;; introducing restarts (defun http-get-2 (host page &optional (port 80)) (restart-case (get-page host page port) (get-default-contents () "hello, world"))) ;; more restarts (defun http-get-3 (host page &optional (port 80)) (restart-case (get-page host page port) (get-default-contents () "hello, world") (use-value (v) v) (print-error (ex) (format t "Error: ~a~%" (text ex))))) (defun apply-policy-1 () (handler-bind ((socket-connection-error #'(lambda (ex) (invoke-restart 'get-default-contents)))) (http-get-3 "www.lisp.org" "/"))) (defun apply-policy-2 () (handler-bind ((socket-connection-error #'(lambda (ex) (invoke-restart 'use-value "blah!")))) (http-get-3 "www.lisp.org" "/"))) (defun apply-policy-3 () (handler-bind ((socket-connection-error #'(lambda (ex) (invoke-restart 'print-error ex)))) (http-get-3 "www.lisp.org" "/"))) ;; restarts can be interactive ;; more restarts (defun http-get-4 (host page &optional (port 80)) (restart-case (get-page host page port) (get-default-contents () "hello, world") (use-value (v) :report "Specify return value." :interactive (lambda () (list (read))) v) (print-error (ex) (format t "Error: ~a~%" (text ex))))) ;; restarts do not unwind the stack (defun http-get-5 (host page &optional (port 80)) (loop (restart-case (return (get-page host page port)) (get-default-contents () "hello, world") (use-value (v) :report "Specify a new URL." :interactive (lambda () (list (read))) (setf host v)) (print-error (ex) (format t "Error: ~a~%" (text ex)))))) (defun http-get-6 (host page &optional (port 80)) (loop (restart-case (return (get-page host page port)) (use-value (v) (setf host v))))) (defun test-6 () (handler-bind ((socket-connection-error #'(lambda (ex) (invoke-restart 'use-value (progn (format t "Enter a new URL:") (read)))))) (http-get-6 "www.lisp.org" "/")))