exav - EXecution As a Value
The fundamental building block of Clojure is the immutable value. Values can be shared, values can be saved, values can be inspected, values can be transformed.
Programs often must ask for input from the external world. exav models this much like core.async; we write code which looks very much like a simple function which is producing a value, except that sprinkled throughout are operations which cause execution to be suspended until some external event takes place to supply us with a new value.
core.async is wonderful, but its fundamental building block is the channel, which is not a value. We have no way of looking inside the computation which is suspended. We have no way of rewinding it. We have no way of persisting it. core.async code builds up a black box, to be fed with data.
I want the ability to rewind time, hotload new code, then run time forward again. I want the ability to script complex interactions in a game that take place over arbitrary amounts of time, and for my entire game state to exist in a map that can be saved as EDN or Transit. exav gives me the tools to do this.
exav currently has one basic building block: the
proc macro. The
proc macro works very much like the
go macro in core.async, except that instead of returning a channel, it returns a function, and instead of blocking on
<!, it blocks on calls to
wait. The function returned by proc takes zero, one, or two arguments.
(let [p (proc (str "Hello, " (wait :name))) ; calling a proc with no arguments starts the process state (p) ; a proc will always return a map with a standard structure ; if the :waitfor key exists, the value is whatever was passed to (wait) waitfor (:waitfor state) ; => :name ; calling a proc with two arguments continues the process ; you must pass the state returned by the proc, and the value to be returned by (wait) state2 (p state "Bob") ; if the :result key exists, it will be the only thing in the map, and is equal to the ; final return value of the proc result (:result state2) ; => "Hello, Bob" ; note that it is completely legal to hold onto and re-use old states! result2 (:result (p state "Phil")) ; => "Hello, Phil" ; a proc can also be called with just one argument, which is the same as passing nil ; for the second argument result3 (:result (p state)) ; => "Hello, " ])
wait always takes a single argument; a value that describes what the proc is waiting for.
Copyright © 2014 Jeremy Penner
Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.