tl;dr: In the past I'd always thought of memoization as a way to save the computer work. It hadn't occurred to me that it could also save me work.
I was re-reading "The Joy of Clojure" and came across a gem I'd missed the first time. Listing 14.12 is described as "A function to create or retrieve a unique Agent for a given player name".
(def agent-for-player
(memoize
(fn [player-name]
(-> (agent [])
(set-error-handler! #(println "ERROR: " %1 %2))
(set-error-mode! :fail)))))
;; The above doesn't quite work for me, set-error-handler!
;; doesn't seem to return the agent. Doesn't make the pattern
;; less compelling, though.
The authors comment that this allows you to maintain player-name as an index into a table of agents without having to explicitly manager and lookup agents.
There are two caveats to this approach: 1) player-name must be immutable, and 2) You really need to understand the memoization mechanism. clojure.core/memoize, for instance, will keep a internal map of args/response until the end of time. You could use http://clojure.github.io/core.memoize/ to modify the strategy if you so choose.
The place where I'd try this first is in what I call "micro-logs". Frequently as I'm working, I want to log some data to a side channel, and this pattern saves having to manage this manually and cluttering up my code.
(def get-or-create-micro-log
(memoize
(fn [file]
(io/make-parents file)
(.createNewFile file)
(-> (io/writer file :append true)
(agent
:error-mode :fail
:error-handler #(println "ERROR: " %1 %2))))))
(defn microlog
"Useful micro-pattern to send off a write to various
files via agents without having to maintain a lookup
table. Symlinks can get you into trouble; at a
minimum they will duplicate the agent."
[lg line]
(let [a (-> (io/file lg)
(.getAbsoluteFile)
(get-or-create-micro-log))
output (str (str/trim-newline line) "\n")]
(send-off a
(fn [writer]
(doto writer
(.write output)
(.flush))))))
(defn microlog-all
[lg all-data]
(doseq [d all-data]
(microlog lg d)))
Edits: Used io/make-parents instead of File calls. Changed send to send-off, since this is I/O. Cleaned up creation of agent.
No comments:
Post a Comment