-
Notifications
You must be signed in to change notification settings - Fork 4
/
core.cljc
71 lines (64 loc) · 2.24 KB
/
core.cljc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
(ns darkleaf.effect.core
(:refer-clojure :exclude [test mapv reduce])
(:require
[cloroutine.core :refer [cr]]
[darkleaf.effect.internal :as i])
#?(:cljs (:require-macros [darkleaf.effect.core :refer [with-effects]])))
(defn effect [x]
(i/with-kind x :effect))
(defn ! [x]
(case (i/kind x)
:effect x
:coroutine x
(i/with-kind [x] :wrapped)))
(defmacro ^{:style/indent :defn} with-effects [& body]
`(i/with-kind
(cr {! i/coeffect} ~@body)
:coroutine))
(defn- update-head [coll f & args]
(if (seq coll)
(-> coll
(pop)
(conj (apply f (peek coll) args)))
coll))
(defn- stack->continuation [stack]
(fn [coeffect]
(loop [stack stack
coeffect coeffect]
(if (empty? stack)
[coeffect nil]
(let [stack (update-head stack (fn clone [mutable-coroutine]
(mutable-coroutine identity)))
coroutine (peek stack)
val (i/with-coeffect coeffect coroutine)]
(case (i/kind val)
:effect [val (stack->continuation stack)]
:coroutine (recur (conj stack val) nil)
:wrapped (recur stack (first val))
;; coroutine is finished
(recur (pop stack) val)))))))
(defn continuation [effn]
(fn [args]
(let [coroutine (apply effn args)
stack (list coroutine)
cont (stack->continuation stack)
coeffect ::not-used]
(cont coeffect))))
(defn perform
([effect-!>coeffect continuation coeffect-or-args]
(loop [[effect continuation] (continuation coeffect-or-args)]
(if (nil? continuation)
effect
(recur (continuation (effect-!>coeffect effect))))))
([effect-!>coeffect continuation coeffect-or-args respond raise]
(try
(let [[effect continuation] (continuation coeffect-or-args)]
(if (nil? continuation)
(respond effect)
(effect-!>coeffect effect
(fn [coeffect]
(perform effect-!>coeffect continuation coeffect
respond raise))
raise)))
(catch #?(:clj java.lang.Throwable, :cljs js/Error) error
(raise error)))))