exav - EXecution As a Value
Go to file
Jeremy Penner b7255523c2 fix readme 2015-05-06 09:38:23 -04:00
doc Initial commit 2015-05-06 09:36:01 -04:00
src/clj/exav Initial commit 2015-05-06 09:36:01 -04:00
test/exav Initial commit 2015-05-06 09:36:01 -04:00
.gitignore Initial commit 2015-05-06 09:36:01 -04:00
LICENSE Initial commit 2015-05-06 09:36:01 -04:00
README.md fix readme 2015-05-06 09:38:23 -04:00
project.clj Initial commit 2015-05-06 09:36:01 -04:00


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 >! or <!, 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.