Haskellの>>=は実質継続渡しみたいなものなので、継続をうまいこと使えばSchemeにもモナドが実装できるはず(?)
とりあえずMaybeモナドを実装してみました。
(define (empty-stack) '()) (define (empty-stack? stack) (null? stack)) (define-syntax push! (syntax-rules () ((_ x stack) (set! stack (cons x stack))))) (define-syntax pop! (syntax-rules () ((_ stack) (begin (let ((head (car stack))) (set! stack (cdr stack)) head))))) (define (just x) (cons 'just x)) (define nothing (cons 'nothing #f)) (define (just? x) (symbol=? 'just (car x))) (define (nothing? x) (symbol=? 'nothing (car x))) (define (maybe-value x) (cdr x)) (define maybe-do-start-stack (empty-stack)) (define-syntax maybe-do (syntax-rules () ((_ action ...) (call/cc (lambda (cont) (push! cont maybe-do-start-stack) (let ((result ((lambda () action ...)))) (pop! maybe-do-start-stack) result)))))) (define (maybe-bind x) (call/cc (lambda (cont) (if (just? x) (cont (maybe-value x)) ((pop! maybe-do-start-stack) nothing))))) (define (maybe-return x) (just x))
>>=と違ってcall/ccでは継続を呼び出さなくても実質contの位置に戻ってきてしまうので、nothingが発生した場合用に最終的な計算結果を受け取るべき位置の部分の継続をmaybe-do-start-stackに保存しています。
使い方は以下の通りです。モナドから値を取り出したいときにmaybe-bindを使います。
(define (maybe-example) (maybe-do (let ((hoge (maybe-bind (just 3))) (fuga (maybe-bind (just 4)))) (maybe-return (+ hoge fuga))))) (maybe-example) ;; => (just 7)
継続自体は大学の講義でやった気がするのですが、遊び呆けていたためなにも覚えておらず。
ちゃんと勉強しておけばよかった……。