Skip to content

Instantly share code, notes, and snippets.

@mnzk
Last active December 17, 2015 11:39
Show Gist options
  • Save mnzk/5604170 to your computer and use it in GitHub Desktop.
Save mnzk/5604170 to your computer and use it in GitHub Desktop.
エメラルドなコレクションモナド
;;変更箇所のみ
(extend-type clojure.lang.IPersistentCollection
Functor
(fmap [m f] (map f m))
Monad
(bind [m f] (mapcat f m))
MonadPlus
(mfilter [m p] (filter p m))
;; 追加
Comonad
(extract [m] m))
;; 追加
(defmacro ^:private m-ctor* [m]
(if (isa? m clojure.lang.IPersistentCollection)
list
`#(new ~m %)))
(defmacro for-m
([m exprs expr]
`(binding [*monad* (m-ctor* m)] ;; <-- 変更
(extract (for-m ~exprs ~expr))))
([[var val & exprs] expr]
(let [[key expr' & exprs'] exprs]
(cond (identical? key :if)
`(for-m [~var (mfilter (point ~val) (fn [~var] ~expr'))
~@exprs']
~expr)
(identical? key :let)
(let [bindings (partition 2 expr')]
`(for-m [[~var ~@(map first bindings)]
(for-m [~var ~val]
[~var ~@(map second bindings)])
~@exprs']
~expr))
(empty? exprs)
`(fmap (point ~val) (fn [~var] ~expr))
:else
`(bind (point ~val) (fn [~var] (for-m ~exprs ~expr)))))))
;; 実行例
(require '[clojure.test :refer [is]])
(is (= (for-m ^clojure.lang.IPersistentCollection
[a [1 2], b '[x y z]]
(list a b)))
'((1 x) (1 y) (1 z) (2 x) (2 y) (2 z)))

emerald.monadのリストモナド的な部分の改造。

元のコードは、リストでは動くけどベクタだと動かない。なぜなら clojure.lang.PersistentVectorは、publicなコンストラクタを公開していないから。 ベクタだけ特別扱いしてもよいが、clojure.lang.IPersistentCollectionで組み込みコレクションを統一的に扱っているのでseqlistにしてみた。

*monad*にコンストラクタをバインドする部分は、モナドのコンテキストを決定する部分であり、これがreturnの代わりになってるようだ。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment