-
Notifications
You must be signed in to change notification settings - Fork 3
/
ExprState.scala
107 lines (83 loc) · 3.49 KB
/
ExprState.scala
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
package com.rallyhealth.vapors.v1
package data
final case class ExprState[+I, +O](
factTable: FactTable,
private val maybeInput: Option[I],
private val maybeOutput: Option[O],
) {
def input(implicit hasInput: HasInput[I, O]): I = maybeInput.getOrElse(ExprState.Nothing.asInstanceOf[I])
def output(implicit hasOutput: HasOutput[I, O]): O = maybeOutput.getOrElse(ExprState.Nothing.asInstanceOf[O])
def withFactTable(factTable: FactTable): ExprState[I, O] = copy(factTable = factTable)
def bimap[A, B](
mapInput: I => A,
mapOutput: O => B,
): ExprState[A, B] = copy(maybeInput = maybeInput.map(mapInput), maybeOutput = maybeOutput.map(mapOutput))
def withBoth[A, B](
input: A,
output: B,
): ExprState[A, B] = copy(maybeInput = Some(input), maybeOutput = Some(output))
/**
* @note the given function will NOT be invoked if the state has no output.
*/
def flatMapOutput[A](fn: O => Option[A]): ExprState[I, A] = copy(maybeOutput = maybeOutput.flatMap(fn))
/**
* @note the given function will NOT be invoked if the state has no output.
*/
def mapOutput[A](fn: O => A): ExprState[I, A] = copy(maybeOutput = maybeOutput.map(fn))
def withOutput[A](output: A): ExprState[I, A] = copy(maybeOutput = Some(output))
def withNoOutput: ExprState[I, Nothing] = copy(maybeOutput = None)
/**
* @note the given function will NOT be invoked if the state has no input.
*/
def flatMapInput[A](fn: I => Option[A]): ExprState[A, O] = copy(maybeInput = maybeInput.flatMap(fn))
/**
* @note the given function will NOT be invoked if the state has no input.
*/
def mapInput[A](fn: I => A): ExprState[A, O] = copy(maybeInput = maybeInput.map(fn))
def withInput[A](input: A): ExprState[A, O] = copy(maybeInput = Some(input))
def withNoInput: ExprState[Nothing, O] = copy(maybeInput = None)
/**
* A helpful method for simultaneously shifting the output of this expression to become the input
* of the next expression state while also setting the output of that expression.
*/
def swapAndReplaceOutput[A](output: A): ExprState[O, A] =
copy(maybeInput = maybeOutput, maybeOutput = Some(output))
override def toString: String = {
s"ExprState(input = ${maybeInput.getOrElse(ExprState.Nothing)}, output = ${maybeOutput.getOrElse(ExprState.Nothing)}, factTable = $factTable)"
}
}
object ExprState {
/**
* Applies the same implicit conversion logic provided by [[<:<.refl]], but for the entire [[ExprState]] object.
*
* Requires evidence that either or both of the known input and output types are subtypes (or the same type)
* as the desired input and output types, respectively.
*/
implicit def conforms[AI, AO, BI, BO](
state: ExprState[AI, AO],
)(implicit
evBI: AI <:< BI,
evBO: AO <:< BO,
): ExprState[BI, BO] = state.bimap(evBI, evBO)
type Empty = ExprState[Nothing, Nothing]
def Empty(factTable: FactTable): Empty = ExprState[Nothing, Nothing](factTable, None, None)
final val Empty: Empty = Empty(FactTable.empty)
/** A sentinel value for empty ExprState input or output */
final case object Nothing
type Input[+I] = ExprState[I, Nothing]
object Input {
def apply[I](
input: I,
factTable: FactTable,
): ExprState[I, Nothing] =
ExprState(factTable, Some(input), None)
}
type Output[+O] = ExprState[Nothing, O]
object Output {
def apply[O](
output: O,
factTable: FactTable,
): ExprState[Nothing, O] =
ExprState(factTable, None, Some(output))
}
}