diff options
author | Rich Hickey <richhickey@gmail.com> | 2009-01-01 20:52:00 +0000 |
---|---|---|
committer | Rich Hickey <richhickey@gmail.com> | 2009-01-01 20:52:00 +0000 |
commit | 8a6c52556d92ab6ab369c7bf2a8c956add582de9 (patch) | |
tree | 3058f6da1fb15c973ba165593b3b7edb2aa4f61f /src/clj | |
parent | 96757ae6fe6651f2753f7495a6ff476ed72c1798 (diff) |
Added uniform metadata handling for atoms/refs/agents/vars/namespaces
Note - breaking change for agent/ref when supplying validator - validator must be passed using :validator option
Added :validator and :meta options to agent/ref/atom
Added alter-meta! and reset-meta! for reference types
renamed set-validator to set-validator!
Validators now can simply return false, or throw
Refactoring, added IMeta, IReference
Switched to longs for Ref ids
Diffstat (limited to 'src/clj')
-rw-r--r-- | src/clj/clojure/core.clj | 102 | ||||
-rw-r--r-- | src/clj/clojure/core_proxy.clj | 52 |
2 files changed, 86 insertions, 68 deletions
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj index c6e81bad..55407a39 100644 --- a/src/clj/clojure/core.clj +++ b/src/clj/clojure/core.clj @@ -150,8 +150,8 @@ #^{:arglists '([obj]) :doc "Returns the metadata of obj, returns nil if there is no metadata."} meta (fn meta [x] - (if (instance? clojure.lang.IObj x) - (. #^clojure.lang.IObj x (meta))))) + (if (instance? clojure.lang.IMeta x) + (. #^clojure.lang.IMeta x (meta))))) (def #^{:arglists '([#^clojure.lang.IObj obj m]) @@ -1047,14 +1047,31 @@ [sym] (. clojure.lang.Var (find sym))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Refs ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(defn #^{:private true} + setup-reference [r options] + (let [opts (apply hash-map options)] + (when (:meta opts) + (.resetMeta r (:meta opts))) + (when (:validator opts) + (.setValidator r (:validator opts))) + r)) + (defn agent - "Creates and returns an agent with an initial value of state and an - optional validate fn. validate-fn must be nil or a side-effect-free fn of - one argument, which will be passed the intended new state on any state + "Creates and returns an agent with an initial value of state and + zero or more options (in any order): + + :meta metadata-map + + :validator validate-fn + + If metadata-map is supplied, it will be come the metadata on the + agent. validate-fn must be nil or a side-effect-free fn of one + argument, which will be passed the intended new state on any state change. If the new state is unacceptable, the validate-fn should - throw an exception." + return false or throw an exception." ([state] (new clojure.lang.Agent state)) - ([state validate-fn] (new clojure.lang.Agent state validate-fn))) + ([state & options] + (setup-reference (agent state) options))) (defn send "Dispatch an action to an agent. Returns the agent immediately. @@ -1119,14 +1136,21 @@ [] (. clojure.lang.Agent shutdown)) (defn ref - "Creates and returns a Ref with an initial value of x and an optional validate fn. - validate-fn must be nil or a side-effect-free fn of one argument, which will - be passed the intended new state on any state change. If the new - state is unacceptable, the validate-fn should throw an - exception. validate-fn will be called on transaction commit, when - all refs have their final values." + "Creates and returns a Ref with an initial value of x and zero or + more options (in any order): + + :meta metadata-map + + :validator validate-fn + + If metadata-map is supplied, it will be come the metadata on the + ref. validate-fn must be nil or a side-effect-free fn of one + argument, which will be passed the intended new state on any state + change. If the new state is unacceptable, the validate-fn should + return false or throw an exception. validate-fn will be called on + transaction commit, when all refs have their final values." ([x] (new clojure.lang.Ref x)) - ([x validate-fn] (new clojure.lang.Ref x validate-fn))) + ([x & options] (setup-reference (ref x) options))) (defn deref "Also reader macro: @ref/@agent/@var/@atom Within a transaction, @@ -1135,11 +1159,45 @@ or atom, returns its current state." [#^clojure.lang.IRef ref] (. ref (get))) -(defn set-validator +(defn atom + "Creates and returns an Atom with an initial value of x and zero or + more options (in any order): + + :meta metadata-map + + :validator validate-fn + + If metadata-map is supplied, it will be come the metadata on the + atom. validate-fn must be nil or a side-effect-free fn of one + argument, which will be passed the intended new state on any state + change. If the new state is unacceptable, the validate-fn should + return false or throw an exception." + ([x] (new clojure.lang.Atom x)) + ([x & options] (setup-reference (atom x) options))) + +(defn swap! + "Atomically swaps the value of atom to be: + (apply f current-value-of-atom args). Note that f may be called + multiple times, and thus should be free of side effects. Returns + the value that was swapped in." + [#^clojure.lang.Atom atom f & args] (.swap atom f args)) + +(defn compare-and-set! + "Atomically sets the value of atom to newval if and only if the + current value of the atom is identical to oldval. Returns true if + set happened, else false" + [#^clojure.lang.Atom atom oldval newval] (.compareAndSet atom oldval newval)) + +(defn reset! + "Sets the value of atom to newval without regard for the + current value. Returns newval." + [#^clojure.lang.Atom atom newval] (.reset atom newval)) + +(defn set-validator! "Sets the validator-fn for a var/ref/agent/atom. validator-fn must be nil or a side-effect-free fn of one argument, which will be passed the intended new state on any state change. If the new state is unacceptable, the - validator-fn should throw an exception. If the current state (root + validator-fn should return false or throw an exception. If the current state (root value if var) is not acceptable to the new validator, an exception will be thrown and the validator will not be changed." [#^clojure.lang.IRef iref validator-fn] (. iref (setValidator validator-fn))) @@ -1148,6 +1206,18 @@ "Gets the validator-fn for a var/ref/agent/atom." [#^clojure.lang.IRef iref] (. iref (getValidator))) +(defn alter-meta! + "Atomically sets the metadata for a namespace/var/ref/agent/atom to be: + + (apply f its-current-meta args) + + f must be free of side-effects" + [#^clojure.lang.IReference iref f & args] (.alterMeta iref f args)) + +(defn reset-meta! + "Atomically resets the metadata for a namespace/var/ref/agent/atom" + [#^clojure.lang.IReference iref metadata-map] (.resetMeta iref metadata-map)) + (defn commute "Must be called in a transaction. Sets the in-transaction-value of ref to: diff --git a/src/clj/clojure/core_proxy.clj b/src/clj/clojure/core_proxy.clj index b1e4c4cb..bda9824a 100644 --- a/src/clj/clojure/core_proxy.clj +++ b/src/clj/clojure/core_proxy.clj @@ -339,57 +339,5 @@ (lazy-cons (new clojure.lang.MapEntry (first pseq) (v (first pseq))) (thisfn (rest pseq))))) (keys pmap)))))) -(import '(java.util.concurrent.atomic AtomicReference)) -(defn atom - "Creates and returns a new Atom with an initial value of x and an - optional validate fn. validate-fn must be nil or a side-effect-free - fn of one argument, which will be passed the intended new state on - any state change. If the new state is unacceptable, the validate-fn - should throw an exception." - ([x] (atom x nil)) - ([x validator-fn] - (let [validator (AtomicReference. nil) - atom (proxy [AtomicReference clojure.lang.IRef] [x] - (getValidator [] (.get validator)) - (setValidator [f] - (when f - (try - (f @this) - (catch Exception e - (throw (IllegalStateException. "Invalid atom state" e))))) - (.set validator f)))] - (set-validator atom validator-fn) - atom))) - -(defn swap! - "Atomically swaps the value of atom to be: - (apply f current-value-of-atom args). Note that f may be called - multiple times, and thus should be free of side effects. Returns - the value that was swapped in." - [#^AtomicReference atom f & args] - (let [validate (get-validator atom)] - (loop [oldv (.get atom)] - (let [newv (apply f oldv args)] - (when validate - (try - (validate newv) - (catch Exception e - (throw (IllegalStateException. "Invalid atom state" e))))) - (if (.compareAndSet atom oldv newv) - newv - (recur (.get atom))))))) - -(defn compare-and-set! - "Atomically sets the value of atom to newval if and only if the - current value of the atom is identical to oldval. Returns true if - set happened, else false" - [#^AtomicReference atom oldval newval] - (let [validate (get-validator atom)] - (when validate - (try - (validate newval) - (catch Exception e - (throw (IllegalStateException. "Invalid atom state" e))))) - (.compareAndSet atom oldval newval))) |