Skip to content

Instantly share code, notes, and snippets.

@hadronzoo
Created November 5, 2014 23:03
Show Gist options
  • Save hadronzoo/f5e21a6290cfb714070e to your computer and use it in GitHub Desktop.
Save hadronzoo/f5e21a6290cfb714070e to your computer and use it in GitHub Desktop.
Clojure MapCursor
(ns cursors.core
(:refer-clojure :exclude [swap! reset!]))
(defprotocol ICursor
(-path [c])
(-state [c]))
(defprotocol IToCursor
(-to-cursor [c state path]))
(defn swap! [cursor f & args]
(let [state (-state cursor)
path (-path cursor)]
(-> (clojure.core/swap! state update-in path
#(apply f (list* % args)))
(get-in path)
(-to-cursor state path))))
(defn reset! [cursor newval]
(swap! cursor (fn [_] newval)))
(defn cursor? [c]
(satisfies? ICursor c))
(declare ->MapCursor)
(extend-protocol IToCursor
java.lang.Object
(-to-cursor [this state path] this)
clojure.lang.APersistentMap
(-to-cursor [this state path]
(->MapCursor this state path)))
(deftype MapCursor [value state path]
ICursor
(-path [_] path)
(-state [_] state)
IToCursor
(-to-cursor [this state path] this)
clojure.lang.IDeref
(deref [this]
(get-in @state path))
clojure.lang.MapEquivalence
clojure.lang.IPersistentCollection
(equiv [this x] (.equiv value x))
(cons [this o]
(-> (if (map? o)
(reduce #(apply assoc %1 %2) this o)
(if-let [[k v] (seq o)]
(assoc this k v)
this))
(-to-cursor state path)))
clojure.lang.IObj
(withMeta [this mta]
(-to-cursor (.withMeta value mta) state path))
(meta [this] (meta value))
clojure.lang.Counted
(count [this] (count value))
clojure.lang.Seqable
(seq [this]
(when (pos? (count value))
(map (fn [[k v]]
(clojure.lang.MapEntry. k (-to-cursor v state (conj path k))))
value)))
^{:min-version "1.4.0"}
clojure.core.protocols.CollReduce
^{:min-version "1.4.0"}
(coll-reduce [this f] (.coll-reduce value f))
^{:min-version "1.4.0"}
(coll-reduce [this f val] (.coll-reduce value f val))
clojure.lang.IHashEq
(hasheq [this] (.hasheq value))
Object
(hashCode [this] (.hashCode value))
(equals [this x] (.equals value x))
(toString [this] (.toString value))
clojure.lang.ILookup
(valAt [this k] (.valAt this k nil))
(valAt [this k default]
(let [v (.valAt value k default)]
(if-not (= v default)
(-to-cursor v state (conj path k))
default)))
clojure.lang.Associative
(containsKey [this k] (.containsKey value k))
(entryAt [this k] (.valAt this k))
(assoc [this k v]
(-to-cursor (assoc value k v) state path))
(empty [this]
(-to-cursor (empty value) state path))
java.util.Map
(get [this k] (.valAt this k))
(isEmpty [this] (.isEmpty value))
(size [this] (.size value))
(keySet [this] (.keySet value))
(put [_ _ _]
(throw (UnsupportedOperationException.)))
(putAll [_ _]
(throw (UnsupportedOperationException.)))
(clear [_]
(throw (UnsupportedOperationException.)))
(remove [_ _]
(throw (UnsupportedOperationException.)))
(values [this] (.values value))
(entrySet [this] (.entrySet value))
clojure.lang.IPersistentMap
(assocEx [this k v]
(assoc this k v))
(without [this k]
(-to-cursor (dissoc value k) state path))
clojure.lang.IFn
(invoke [this k]
(.valAt this k))
(invoke [this k default]
(.valAt this k default)))
(prefer-method print-method clojure.lang.IPersistentMap clojure.lang.IDeref)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment