/
state.clj
116 lines (94 loc) · 2.63 KB
/
state.clj
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
(ns state-flow.state
(:refer-clojure :exclude [eval get])
(:require [cats.context :as ctx :refer [*context*]]
[cats.core :as m]
[cats.monad.exception :as e]
[cats.monad.state :as state]
[cats.protocols :as p]
[cats.util :as util]))
(declare error-context)
(defn- result-or-err [f & args]
(let [result ((e/wrap (partial apply f)) args)]
(if (e/failure? result)
result
@result)))
(defn error-state [mfn]
(state/state
(fn [s]
(let [new-pair ((e/wrap mfn) s)]
(if (e/failure? new-pair)
[new-pair s]
@new-pair)))
error-context))
(def error-context
"Same as state monad context, but short circuits if error happens, place error in return value"
(reify
p/Context
p/Functor
(-fmap [self f fv]
(error-state
(fn [s]
(let [[v s'] ((p/-extract fv) s)]
(if (e/failure? v)
[v s']
[(result-or-err f v) s'])))))
p/Monad
(-mreturn [_ v]
(error-state #(vector v %)))
(-mbind [_ self f]
(error-state
(fn [s]
(let [[v s'] ((p/-extract self) s)]
(if (e/failure? v)
[v s']
((p/-extract (f v)) s'))))))
state/MonadState
(-get-state [_]
(error-state #(vector %1 %1)))
(-put-state [_ newstate]
(error-state #(vector % newstate)))
(-swap-state [_ f]
(error-state #(vector %1 (f %1))))
p/Printable
(-repr [_]
"#<State-E>")))
(util/make-printable (type error-context))
(defn get
"Returns the equivalent of (fn [state] [state, state])"
[]
(state/get error-context))
(defn gets
[f & args]
"Returns the equivalent of (fn [state] [state, (apply f state args)])"
(state/gets #(apply f % args) error-context))
(defn put
"Returns the equivalent of (fn [state] [state, new-state])"
[new-state]
(state/put new-state error-context))
(defn modify
"Returns the equivalent of (fn [state] [state, (apply swap! state f args)])"
[f & args]
(state/swap #(apply f % args) error-context))
(defn return
"Returns the equivalent of (fn [state] [v, state])"
[v]
(m/return error-context v))
(defn ^:deprecated swap
"DEPRECATED: use modify"
[f]
(modify f))
(defn wrap-fn
"Wraps a (possibly side-effecting) function to a state monad"
[my-fn]
(error-state (fn [s] [(my-fn) s])))
(def state? state/state?)
(def run state/run)
(def eval state/eval)
(def exec state/exec)
(defn ensure-step
"Internal use only.
Given a state-flow step, returns value as/is, else wraps value in a state-flow step."
[value]
(if (state? value)
value
(return value)))