torsdag 11 juli 2013

Core.async for simple user-interaction

Alex Miller gives an excellent example on how to make rock-paper-scissors with core.async which made me want to try it on my mekanikmaskin - a computer-aided learning and examinating tool.

The cool thing about core.async is that it makes sort-of a reactive programming possible. Put something in a channel, and watch the program take care of it. Almost like REPLs waiting inside functions wherever you like.

(ns async-math)
(require '[clojure.core.async :as async :refer :all])
(defn query-maker
"generates a simple math-query and the answer to it"
[]
(let [a (rand-int 10) b (rand-int 10)]
{:query (str "what is " a " + " b "?")
:answer (+ a b)}))
;; {:query "what is 1 + 2 ?" :answer 3}
;; an un-buffered channel:
(def answer-chan (chan))
(future ;;to let go of the repl
(go ;;start async transaction
(while true ;; run forever
(let [task (query-maker)] ;; create a task
(println (:query task)) ;; output query (apparently this could be put in a channel)
(if (== (<! answer-chan) (:answer task)) ;;wait for a new entry in answer-chan
(println "correct!")
(println "wrong"))))))
;;short helper for answering
(defn ans [x]
(>!! answer-chan x))
;;the REPL interaction becames:
;;what is 2 + 7?
;;async-math> (ans 9)
;;correct!
;;what is 0 + 5?
;;async-math> (ans 10)
;;wrong!
;;what is 3 + 3?
;;async-math> _
view raw async-math.clj hosted with ❤ by GitHub

And yes, it's a terrible mixture between printlns and the async constructs, but the way to create a more advanced interaction is for another day.