Skip to content
2011/05/11 / highmt

続 clojureで継続モナドを使う

前回の続きです。
stateモナド変換子を使ってもうちょっとすっきりさせてみました。

(use '[clojure.contrib.monads])

(defn pass
  ([]
     (pass nil))
  ([v]
     (fn [s]
       (with-monad cont-m
         (m-result [v s]))))) 

(defn fail []
  (fn [s]
    (if s (s) ((pass) s))))

(defn choose-next [cc s]
  (if s
    (s)
    (cc [nil nil])))

(defn choose
  ([choices]
     (fn [s]
       (with-monad cont-m
         (call-cc
          (fn [cc] (choose cc choices s))))))
  ([cc choices s]
     (if (empty? choices)
       (choose-next cc s)
       (cc [(first choices) #(choose cc (next choices) s)]))))

(defn run-choose [m]
  (run-cont
   (m (fn []
        (with-monad cont-m
          (m-result [nil nil]))))))

(defn test-state-t []
  (run-choose
   (domonad (state-t cont-m)
     [v1 (choose [1 2 3])
      v2 (choose [7 8 9])
      v  (if (or (some nil? [v1 v2]) (odd? (* v1 v2)))
           (fail)
           (pass [v1 v2]))]
     v)))

;; example:
;; user=> (test-state-t)
;; [[1 8] #<user$choose$fn__1554 user$choose$fn__1554@55104da7>]
;; user=> (run-cont ((fnext *1)))
;; [[2 7] #<user$choose$fn__1554 user$choose$fn__1554@1c6745b9>]
;; user=> (run-cont ((fnext *1)))
;; [[2 8] #<user$choose$fn__1554 user$choose$fn__1554@430a14ad>]
;; user=> (run-cont ((fnext *1)))
;; [[2 9] #<user$choose$fn__1554 user$choose$fn__1554@67afe460>]
;; user=> (run-cont ((fnext *1)))
;; [[3 8] #<user$choose$fn__1554 user$choose$fn__1554@28fe53cf>]
;; user=> (run-cont ((fnext *1)))
;; [nil nil]

このへんになると型チェック機構がないとかなり厳しい感じがします…。
なんとかならないかなー。

ただ、いざ問題が起きたときにコンパイル時エラーと実行時エラーとどっちが問題を調べやすいか、というと、
場合によっては実行時エラーのほうが気が楽なこともあるかもしれないです。
コンパイル時の動作にトレース入れたりとかは実行時に比べると敷居が高い感じがするので…。

広告
%d人のブロガーが「いいね」をつけました。