プログラミングGauche 19.7 簡易な例外機構のまとめ
マクロとcall/ccを使ったcatchとthrowの実装の解説を備忘録として残します.
;; 簡易例外機構
(define *signals* '())
(define-syntax catch
(syntax-rules (finally)
[(_ (sig body ...) (finally follow ...))
(let* ((signals-backup *signals*)
(val (call/cc (lambda (k)
(set! *signals* (cons (cons 'sig k) *signals*))
body ...))))
(set! *signals* signals-backup)
follow ...
val)]
[(_ (sig body ...))
(catch (sig body ...) (finally))]))
(define-syntax throw
(syntax-rules ()
[(_ sig val) ((cdr (assq 'sig *signals*)) val)]))
;; サンプルコード
(define (div n d)
(if (= d 0)
(throw DivedeZeroError
(print #`"ERROR: Divide Zero Error Occured...\n divide ,n by ZERO!\n--------------------"))
(/ n d)))
(define (percentage a b)
(catch (DivedeZeroError
(print (* (div a b) 100.0) "%"))
(finally
(print "follow ..."))))
処理の流れを説明します.
*signal*の初期値をsignals-backupに束縛します.
その後*signal*には(set! *signal* ...)の行でcarにシグナル,cdrに継続を渡すk,というペアのリストが束縛されます.
((sig . k))という形になります.
サンプルコードでは((DivisedZeroError . k))が入ります.
その後bodyが実行されます.
サンプルコードではpercentageの最初のprint行.
ここでdivが呼ばれます.
(= d 0)のが真の時にthrowが呼ばれます.
(cdr (assq 'sig *signals*))が評価され継続を表すkが返され,(k val)でthrowのprint 行を実行し,戻り値がcatchのvalに束縛されます.
(※ twitterでkeenさん(@blackenedgold)さんから,継続を渡すkではなく,継続を表すkと教えていただいたので訂正しました.)
ここではまってたのですが,(= d 0)なのにシグナルが違っていた場合は(cdr #f)となりエラーが返ります.
シグナルを間違えるなって話ですね.
(= d 0)が#fの時は(/ n d)が実行され値が返ります.
catchに返ってくると*signals*にシグナルの初期値を戻します.
そしてfinally ...を実行します.
最後にvalに束縛した値を返して終了です.
ここではpercentageはprintしてるので#<undef>が返ります.
こうして一つ一つ追えばそんなに難しくないですね.
でも理解するのに時間かかりました...
最後に実行例紹介して終わります.
gosh> (percentage 1 40)
2.5%
follow ...
#<undef>
gosh> (percentage 10 0)
ERROR: Divide Zero Error Occured...
divide 10 by ZERO!
--------------------
follow ...
#<undef>
マクロもcall/ccも難しい