CLで書かれていたのをScheme (Sagittarius)に書き直しただけなので非常に簡単ではあったが・・・
こんな感じ。
(import (rnrs) (clos user))
;; From following URLs
;; http://d.hatena.ne.jp/wasabiz/20121014/1350174261
;; http://basking-cat.blogspot.jp/2012/10/clojurestate.html
(define-syntax perform
(syntax-rules ()
((_ ((name value)) expr ...)
(fmap value (lambda (name) expr ...)))
((_ ((name value) . rest) expr ...)
(bind value (lambda (name) (perform rest expr ...))))))
(define-generic bind)
(define-generic fmap)
;; List Monad
(define-method bind ((m <list>) f) (apply append (map f m)))
(define-method fmap ((m <list>) f) (map f m))
;; State Monad
(import (sagittarius object) (match))
(define-class <state> () ((run :init-keyword :run :reader state-run)))
(define (run-state m v) ((state-run m) v))
(define (eval-state m v) (car (run-state m v)))
(define (exec-state m v) (cadr (run-state m v)))
(define (make-state f) (make <state> :run f))
(define (get-state) (make-state (lambda (s) (list s s))))
(define (put-state) (make-state (lambda (_) (list '() x))))
(define-method bind ((m <state>) f)
(make-state (lambda (s)
(match (run-state m s)
((a ss)
(run-state (f a) ss))))))
(define-method fmap ((m <state>) f)
(make-state (lambda (s)
(match (run-state m s)
((a ss)
(list (f a) ss))))))
;; cursor
(define-class <cursor> ()
((x :init-keyword :x)
(y :init-keyword :y)))
(define-method write-object ((c <cursor>) p)
(format p "#<cursor (x ~a) (y ~a)>" (~ c 'x) (~ c 'y)))
(define (make-cursor x y) (make <cursor> :x x :y y))
(define (right n)
(make-state (lambda (cursor)
(let ((x (+ (~ cursor 'x) n)))
(list x (make-cursor x (~ cursor 'y)))))))
(define (down n)
(make-state (lambda (cursor)
(let ((y (+ (~ cursor 'y) n)))
(list y (make-cursor (~ cursor 'x) y))))))
(define (square n)
(perform ((x (right n))
(s (down x)))
s))
(let* ((c (make-cursor 0 0))
(es (exec-state (square 10) c)))
(print c)
(print es))
;; seqM and mapM
;; from https://gist.github.com/3889104
(define (seqM ms)
(define (rec ms)
(match ms
((m . ms)
(if (null? ms)
(fmap m (lambda (x) (cons x '())))
(bind m (lambda (x) (fmap (rec ms) (lambda (y) (cons x y)))))))
(_ '())))
(if (null? ms)
'()
(rec ms)))
(define (mapM f ms) (seqM (map f ms)))
(define-class <maybe> () ((x :init-keyword :x :reader maybe-x)))
(define-method write-object ((m <maybe>) p)
(format p "#<maybe ~s>" (maybe-x m)))
(define (make-maybe x) (make <maybe> :x x))
(define-method bind ((m <maybe>) f)
(match (maybe-x m)
((:just . x) (f x))
(:nothing (make-maybe :nothing))))
(define-method fmap ((m <maybe>) f)
(make-maybe (match (maybe-x m)
((:just . x) (cons :just (f x)))
(:nothing :nothing))))
;; Test
(define (buz xs)
(define (bar x)
(if (negative? x)
(make-maybe :nothing)
(make-maybe (cons :just (sqrt x)))))
(mapM bar xs))
(print (buz '(1 4 9)))
(print (buz '(1 -4 9)))
Gaucheなら多分あまり手を入れなくても動くはず。TinyCLOSを持ってる処理系はキーワードの処理だけ何とかすれば、多少手を入れればいけるはず。問題は、僕はMonadをよく分かっていないし、そのありがたみを享受したこともないので、こで何がうれしいのかいまいち分からない。Haskelやれってことか?
No comments:
Post a Comment