49 lines
2.7 KiB
Markdown
49 lines
2.7 KiB
Markdown
# exav - EXecution As a Value
|
|
|
|
## Why?
|
|
|
|
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.
|
|
|
|
## Usage
|
|
|
|
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.
|
|
|
|
## License
|
|
|
|
Copyright © 2014 Jeremy Penner
|
|
|
|
Distributed under the Eclipse Public License either version 1.0 or (at
|
|
your option) any later version.
|