-
Notifications
You must be signed in to change notification settings - Fork 0
/
decepticon.gleam
113 lines (99 loc) · 3.27 KB
/
decepticon.gleam
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
import decepticon/stateful.{type State} as state
import gleam/result
/// The main type of this library- which represents a `State` wrapping a `Result`.
/// See the `decepticon/stateful` module for standalone `State`
pub type StateResult(a, s, err) {
StateResult(run: State(Result(a, err), s))
}
/// Given a `StateResult`, return the `Result` of the last executed action.
pub fn eval(state_result: StateResult(a, s, err), s: s) -> Result(a, err) {
state.eval(state_result.run, s)
}
/// Given a `StateResult`, return the final version of the internal state.
pub fn exec(state_result: StateResult(a, s, err), s: s) -> s {
state.exec(state_result.run, s)
}
/// Lift a `Result` into the `StateResult` type. Useful for when you have a
/// non-stateful action that is simply a `Result` and wish to weave it in
/// as a `use` statement
pub fn try(result_value: Result(a, err)) {
StateResult(run: state.action(result_value))
}
/// Lift a `Result`'s value argument into a `StateResult`
pub fn action(action_value: a) {
StateResult(run: state.action(Ok(action_value)))
}
/// Lift a `Result`'s error argument into a `StateResult`
pub fn error(error: err) {
StateResult(run: state.action(Error(error)))
}
/// Lift a `State` type into a `StateResult` type. Wrapping it's
/// action type in `result.Ok`
pub fn ok_state(state: State(a, s)) -> StateResult(a, s, err) {
StateResult(run: state.map(state, fn(a) { Ok(a) }))
}
/// Lift a `State` type into a `StateResult` type. Wrapping it's
/// action type in `result.Error`
pub fn error_state(state: State(err, s)) -> StateResult(a, s, err) {
StateResult(run: state.map(state, fn(err) { Error(err) }))
}
/// Return the current value of state
pub fn get() -> StateResult(s, s, err) {
ok_state(state.get())
}
/// Set the current value of state
pub fn put(state_value: s) -> StateResult(Nil, s, err) {
ok_state(state.put(state_value))
}
/// Map over the action type of a `StateResult`
pub fn map(
over state_result: StateResult(a, s, err),
with map_fn: fn(a) -> b,
) -> StateResult(b, s, err) {
StateResult(
run: state.map(state_result.run, fn(a_result) {
result.map(a_result, map_fn)
}),
)
}
/// Applicative Functor method for `StateResult`
pub fn apply(
prev: StateResult(fn(a) -> b, s, err),
next: StateResult(a, s, err),
) -> StateResult(b, s, err) {
let combined =
state.do(prev.run, fn(result_fn) {
case result_fn {
Ok(map_fn) -> {
state.map(next.run, fn(arg) { result.map(arg, map_fn) })
}
Error(err) -> state.action(Error(err))
}
})
StateResult(run: combined)
}
/// Map over the error type of a `StateResult`
pub fn map_error(
over state_result: StateResult(a, s, err),
with map_error_fn: fn(err) -> err2,
) -> StateResult(a, s, err2) {
StateResult(
run: state.map(state_result.run, fn(a_result) {
result.map_error(a_result, map_error_fn)
}),
)
}
/// The basic mechanism of chaining `StateResult` actions. This is most similar to `result.try`
pub fn do(
over state_result: StateResult(a, s, err),
with and_then_fn: fn(a) -> StateResult(b, s, err),
) -> StateResult(b, s, err) {
StateResult(
run: state.do(state_result.run, fn(a_result) {
case a_result {
Ok(a) -> and_then_fn(a).run
Error(err) -> state.action(Error(err))
}
}),
)
}