An Algebraic Effect System for Golang.

Move core into package

+263 -256
+1 -1
abort/abort.go
··· 1 1 package abort 2 2 3 - import fx "github.com/vic/fx.go" 3 + import "github.com/vic/fx.go/fx" 4 4 5 5 type AbortFn[E any] func(E) fx.FxNil 6 6 type AbortAb[E any] = fx.And[AbortFn[E], fx.Nil]
+1 -1
abort/abort_test.go
··· 3 3 import ( 4 4 "testing" 5 5 6 - fx "github.com/vic/fx.go" 6 + "github.com/vic/fx.go/fx" 7 7 ) 8 8 9 9 func TestSuccess(t *testing.T) {
+1 -1
abort/result.go
··· 1 1 package abort 2 2 3 - import fx "github.com/vic/fx.go" 3 + import "github.com/vic/fx.go/fx" 4 4 5 5 type Result[V, E any] func() (*V, *E) 6 6
-250
fx.go
··· 1 - package fx 2 - 3 - type Fx[S, V any] struct { 4 - // an immediate value 5 - imm func() V 6 - // the continuation of a suspended effect 7 - sus func(S) Fx[S, V] 8 - // the resume function of an stopped effect 9 - res func() Fx[S, V] 10 - } 11 - 12 - type FxPure[V any] = Fx[Nil, V] 13 - type FxNil = FxPure[Nil] 14 - 15 - type Nil pnil 16 - type pnil struct{} 17 - 18 - var PNil Nil = Nil(pnil{}) 19 - var PureNil FxNil = Pure(PNil) 20 - 21 - func Pure[V any](v V) FxPure[V] { return Const[Nil](v) } 22 - 23 - func identity[V any](v V) V { return v } 24 - 25 - func Const[S, V any](v V) Fx[S, V] { return Fx[S, V]{imm: func() V { return v }} } 26 - 27 - func Pending[S, V any](f func(S) Fx[S, V]) Fx[S, V] { return Fx[S, V]{sus: f} } 28 - 29 - func Func[S, V any](f func(S) V) Fx[S, V] { 30 - return Pending(func(s S) Fx[S, V] { return Const[S](f(s)) }) 31 - } 32 - 33 - func Ctx[V any]() Fx[V, V] { return Func(identity[V]) } 34 - 35 - // An stopped effect that panics if resumed. 36 - // Only useful with Replace. 37 - // 38 - // For example, an Abort effect halts since it has no possible value for V 39 - // but then its Handler can Replace the halted effect with an Error value. 40 - // See: abort/result.go 41 - func Halt[S, V any]() Fx[S, V] { 42 - return Stop(func() Fx[S, V] { 43 - return Fx[S, V]{ 44 - imm: func() V { 45 - panic("tried to Resume a halted effect. try using Replace instead") 46 - }, 47 - } 48 - }) 49 - } 50 - 51 - // Creates an stopped effect from a resume function 52 - func Stop[S, V any](e func() Fx[S, V]) Fx[S, V] { 53 - return Fx[S, V]{res: e} 54 - } 55 - 56 - // Resume an effect if it was previously stopped. 57 - func Resume[S, V any](e Fx[S, V]) Fx[S, V] { 58 - if e.res != nil { 59 - return e.res() 60 - } 61 - if e.imm != nil { 62 - return Const[S](e.imm()) 63 - } 64 - return Pending(func(s S) Fx[S, V] { return Resume(e.sus(s)) }) 65 - } 66 - 67 - // Replace with y if x is already Halted. Otherwise x continues. 68 - func Replace[S, V any](y func() Fx[S, V]) func(Fx[S, V]) Fx[S, V] { 69 - return func(x Fx[S, V]) Fx[S, V] { 70 - if x.res != nil { 71 - return y() 72 - } 73 - if x.imm != nil { 74 - return Const[S](x.imm()) 75 - } 76 - return Pending(func(s S) Fx[S, V] { return Replace(y)(x.sus(s)) }) 77 - } 78 - } 79 - 80 - // Continue an effect by transforming its immediate value into another effect. 81 - func Then[T, U, S, V any](cmap func(T) S, fmap func(V) Fx[T, U]) func(Fx[S, V]) Fx[T, U] { 82 - return func(e Fx[S, V]) Fx[T, U] { 83 - if e.res != nil { 84 - return Stop(func() Fx[T, U] { return Then(cmap, fmap)(e.res()) }) 85 - } 86 - if e.imm != nil { 87 - return fmap(e.imm()) 88 - } 89 - return Pending(func(t T) Fx[T, U] { return Then(cmap, fmap)(e.sus(cmap(t))) }) 90 - } 91 - } 92 - 93 - type And[A, B any] func() (A, B) 94 - 95 - func swap[A, B any](ab And[A, B]) And[B, A] { 96 - var ba And[B, A] = func() (B, A) { 97 - a, b := ab() 98 - return b, a 99 - } 100 - return ba 101 - } 102 - 103 - func left[A, B any](ab And[A, B]) A { 104 - a, _ := ab() 105 - return a 106 - } 107 - 108 - func right[A, B any](ab And[A, B]) B { 109 - _, b := ab() 110 - return b 111 - } 112 - 113 - func ContraMap[V, S, R any](f func(R) S) func(Fx[S, V]) Fx[R, V] { 114 - return func(e Fx[S, V]) Fx[R, V] { 115 - if e.res != nil { 116 - return Stop(func() Fx[R, V] { return ContraMap[V](f)(e.res()) }) 117 - } 118 - if e.imm != nil { 119 - return Const[R](e.imm()) 120 - } 121 - return Pending(func(r R) Fx[R, V] { 122 - return ContraMap[V](f)(e.sus(f(r))) 123 - }) 124 - } 125 - } 126 - 127 - func MapM[S, U, V any](f func(U) Fx[S, V]) func(Fx[S, U]) Fx[S, V] { 128 - return FlatCont(identity, identity, f) 129 - } 130 - 131 - func MapH[S, V, U any](f func(V) U) func(Fx[S, V]) Fx[S, U] { 132 - return MapM(func(v V) Fx[S, U] { return Const[S](f(v)) }) 133 - } 134 - 135 - func Map[S, V, U any](e Fx[S, V], f func(V) U) Fx[S, U] { 136 - return MapH[S](f)(e) 137 - } 138 - 139 - func FlatMap[A, U, B, V any](e Fx[A, U], f func(U) Fx[B, V]) Fx[And[A, B], V] { 140 - return FlatMapH[A](f)(e) 141 - } 142 - 143 - func FlatMapH[A, U, B, V any](f func(U) Fx[B, V]) func(Fx[A, U]) Fx[And[A, B], V] { 144 - return FlatCont[And[A, B]](left, right, f) 145 - } 146 - 147 - func FlatCont[N, A, U, B, V any](amap func(N) A, bmap func(N) B, fmap func(U) Fx[B, V]) func(Fx[A, U]) Fx[N, V] { 148 - return Then(amap, func(u U) Fx[N, V] { return Then(bmap, Const[N, V])(fmap(u)) }) 149 - } 150 - 151 - func AndNil[S, V any](e Fx[S, V]) Fx[And[S, Nil], V] { 152 - return Then[And[S, Nil], V](left, Const)(e) 153 - } 154 - 155 - func AndContra[A, B, V any](e Fx[A, V], f func(B) A) Fx[B, V] { 156 - return ContraMap[V](f)(e) 157 - } 158 - 159 - func AndSwap[A, B, V any](e Fx[And[A, B], V]) Fx[And[B, A], V] { 160 - return Then[And[B, A], V](swap[B, A], Const)(e) 161 - } 162 - 163 - func AndFlat[A, B, V any](e Fx[A, Fx[B, V]]) Fx[And[A, B], V] { 164 - return FlatMap(e, identity) 165 - } 166 - 167 - func AndNest[A, B, V any](e Fx[And[A, B], V]) Fx[A, Fx[B, V]] { 168 - return Pending(func(a A) Fx[A, Fx[B, V]] { return Const[A](ProvideLeft(e, a)) }) 169 - } 170 - 171 - func AndCollapse[A, V any](e Fx[And[A, A], V]) Fx[A, V] { 172 - return Pending(func(a A) Fx[A, V] { return ProvideLeft(e, a) }) 173 - } 174 - 175 - func ProvideFirstLeft[A, B, C, V any](e Fx[And[And[A, C], B], V], a A) Fx[And[C, B], V] { 176 - return AndFlat(ProvideLeft(AndNest(e), a)) 177 - } 178 - 179 - func ProvideFirstRight[A, B, D, V any](e Fx[And[A, And[B, D]], V], b B) Fx[And[A, D], V] { 180 - return AndSwap(ProvideFirstLeft(AndSwap(e), b)) 181 - } 182 - 183 - func ProvideFirsts[A, B, C, D, V any](e Fx[And[And[A, C], And[B, D]], V], a A, b B) Fx[And[C, D], V] { 184 - return ProvideFirstRight(ProvideFirstLeft(e, a), b) 185 - } 186 - 187 - func Provide[S, V any](e Fx[S, V], s S) Fx[Nil, V] { 188 - return ProvideLeft(AndNil(e), s) 189 - } 190 - 191 - func ProvideBoth[A, B, V any](e Fx[And[A, B], V], a A, b B) Fx[Nil, V] { 192 - return Provide(ProvideLeft(e, a), b) 193 - } 194 - 195 - func ProvideRight[A, B, V any](e Fx[And[A, B], V], b B) Fx[A, V] { 196 - return ProvideLeft(AndSwap(e), b) 197 - } 198 - 199 - func ProvideLeft[A, B, V any](e Fx[And[A, B], V], a A) Fx[B, V] { 200 - if e.res != nil { 201 - return Stop(func() Fx[B, V] { return ProvideLeft(e.res(), a) }) 202 - } 203 - if e.imm != nil { 204 - return Const[B](e.imm()) 205 - } 206 - return Pending(func(b B) Fx[B, V] { 207 - var ab And[A, B] = func() (A, B) { return a, b } 208 - var loop func(e Fx[And[A, B], V]) Fx[B, V] 209 - loop = func(e Fx[And[A, B], V]) Fx[B, V] { 210 - for { 211 - e = e.sus(ab) 212 - if e.res != nil { 213 - return Stop(func() Fx[B, V] { return loop(e.res()) }) 214 - } 215 - if e.imm != nil { 216 - return Const[B](e.imm()) 217 - } 218 - } 219 - } 220 - return loop(e) 221 - }) 222 - } 223 - 224 - func Apply[F ~func(I) O, I, O any](i I) Fx[F, O] { 225 - return Map(Ctx[F](), func(f F) O { return f(i) }) 226 - } 227 - 228 - func Suspend[A ~func(I) Fx[B, O], B, I, O any](i I) Fx[And[A, B], O] { 229 - return AndFlat(Apply[A](i)) 230 - } 231 - 232 - func Handler[A ~func(I) Fx[B, O], B, I, O any](a A) func(Fx[And[A, B], O]) Fx[B, O] { 233 - return func(e Fx[And[A, B], O]) Fx[B, O] { return ProvideLeft(e, a) } 234 - } 235 - 236 - func Handle[F ~func(Fx[And[A, B], O]) Fx[B, O], A ~func(I) Fx[B, O], B, I, O any](i I) Fx[And[F, B], O] { 237 - return Suspend[F](Suspend[A](i)) 238 - } 239 - 240 - func Eval[V any](e Fx[Nil, V]) V { 241 - for { 242 - if e.res != nil { 243 - panic("tried to evaluate an stopped effect. try using fx.Resume or fx.Replace on it.") 244 - } 245 - if e.imm != nil { 246 - return e.imm() 247 - } 248 - e = e.sus(PNil) 249 - } 250 - }
+45
fx/and.go
··· 1 + package fx 2 + 3 + type And[A, B any] func() (A, B) 4 + 5 + func swap[A, B any](ab And[A, B]) And[B, A] { 6 + var ba And[B, A] = func() (B, A) { 7 + a, b := ab() 8 + return b, a 9 + } 10 + return ba 11 + } 12 + 13 + func left[A, B any](ab And[A, B]) A { 14 + a, _ := ab() 15 + return a 16 + } 17 + 18 + func right[A, B any](ab And[A, B]) B { 19 + _, b := ab() 20 + return b 21 + } 22 + 23 + func AndNil[S, V any](e Fx[S, V]) Fx[And[S, Nil], V] { 24 + return Then[And[S, Nil], V](left, Const)(e) 25 + } 26 + 27 + func AndContra[A, B, V any](e Fx[A, V], f func(B) A) Fx[B, V] { 28 + return ContraMap[V](f)(e) 29 + } 30 + 31 + func AndSwap[A, B, V any](e Fx[And[A, B], V]) Fx[And[B, A], V] { 32 + return Then[And[B, A], V](swap[B, A], Const)(e) 33 + } 34 + 35 + func AndFlat[A, B, V any](e Fx[A, Fx[B, V]]) Fx[And[A, B], V] { 36 + return FlatMap(e, identity) 37 + } 38 + 39 + func AndNest[A, B, V any](e Fx[And[A, B], V]) Fx[A, Fx[B, V]] { 40 + return Pending(func(a A) Fx[A, Fx[B, V]] { return Const[A](ProvideLeft(e, a)) }) 41 + } 42 + 43 + func AndCollapse[A, V any](e Fx[And[A, A], V]) Fx[A, V] { 44 + return Pending(func(a A) Fx[A, V] { return ProvideLeft(e, a) }) 45 + }
+49
fx/fx.go
··· 1 + package fx 2 + 3 + type Fx[S, V any] struct { 4 + // an immediate value 5 + imm func() V 6 + // the continuation of a suspended effect 7 + sus func(S) Fx[S, V] 8 + // the resume function of an stopped effect 9 + res func() Fx[S, V] 10 + } 11 + 12 + func Pure[V any](v V) FxPure[V] { return Const[Nil](v) } 13 + 14 + func identity[V any](v V) V { return v } 15 + 16 + func Const[S, V any](v V) Fx[S, V] { return Fx[S, V]{imm: func() V { return v }} } 17 + 18 + func Pending[S, V any](f func(S) Fx[S, V]) Fx[S, V] { return Fx[S, V]{sus: f} } 19 + 20 + func Func[S, V any](f func(S) V) Fx[S, V] { 21 + return Pending(func(s S) Fx[S, V] { return Const[S](f(s)) }) 22 + } 23 + 24 + func Ctx[V any]() Fx[V, V] { return Func(identity[V]) } 25 + 26 + // Continue an effect by transforming its immediate value into another effect. 27 + func Then[T, U, S, V any](cmap func(T) S, fmap func(V) Fx[T, U]) func(Fx[S, V]) Fx[T, U] { 28 + return func(e Fx[S, V]) Fx[T, U] { 29 + if e.res != nil { 30 + return Stop(func() Fx[T, U] { return Then(cmap, fmap)(e.res()) }) 31 + } 32 + if e.imm != nil { 33 + return fmap(e.imm()) 34 + } 35 + return Pending(func(t T) Fx[T, U] { return Then(cmap, fmap)(e.sus(cmap(t))) }) 36 + } 37 + } 38 + 39 + func Eval[V any](e Fx[Nil, V]) V { 40 + for { 41 + if e.res != nil { 42 + panic("tried to evaluate an stopped effect. try using fx.Resume or fx.Replace on it.") 43 + } 44 + if e.imm != nil { 45 + return e.imm() 46 + } 47 + e = e.sus(PNil) 48 + } 49 + }
+17
fx/handle.go
··· 1 + package fx 2 + 3 + func Apply[F ~func(I) O, I, O any](i I) Fx[F, O] { 4 + return Map(Ctx[F](), func(f F) O { return f(i) }) 5 + } 6 + 7 + func Suspend[A ~func(I) Fx[B, O], B, I, O any](i I) Fx[And[A, B], O] { 8 + return AndFlat(Apply[A](i)) 9 + } 10 + 11 + func Handler[A ~func(I) Fx[B, O], B, I, O any](a A) func(Fx[And[A, B], O]) Fx[B, O] { 12 + return func(e Fx[And[A, B], O]) Fx[B, O] { return ProvideLeft(e, a) } 13 + } 14 + 15 + func Handle[F ~func(Fx[And[A, B], O]) Fx[B, O], A ~func(I) Fx[B, O], B, I, O any](i I) Fx[And[F, B], O] { 16 + return Suspend[F](Suspend[A](i)) 17 + }
+39
fx/map.go
··· 1 + package fx 2 + 3 + func ContraMap[V, S, R any](f func(R) S) func(Fx[S, V]) Fx[R, V] { 4 + return func(e Fx[S, V]) Fx[R, V] { 5 + if e.res != nil { 6 + return Stop(func() Fx[R, V] { return ContraMap[V](f)(e.res()) }) 7 + } 8 + if e.imm != nil { 9 + return Const[R](e.imm()) 10 + } 11 + return Pending(func(r R) Fx[R, V] { 12 + return ContraMap[V](f)(e.sus(f(r))) 13 + }) 14 + } 15 + } 16 + 17 + func MapM[S, U, V any](f func(U) Fx[S, V]) func(Fx[S, U]) Fx[S, V] { 18 + return FlatCont(identity, identity, f) 19 + } 20 + 21 + func MapH[S, V, U any](f func(V) U) func(Fx[S, V]) Fx[S, U] { 22 + return MapM(func(v V) Fx[S, U] { return Const[S](f(v)) }) 23 + } 24 + 25 + func Map[S, V, U any](e Fx[S, V], f func(V) U) Fx[S, U] { 26 + return MapH[S](f)(e) 27 + } 28 + 29 + func FlatMap[A, U, B, V any](e Fx[A, U], f func(U) Fx[B, V]) Fx[And[A, B], V] { 30 + return FlatMapH[A](f)(e) 31 + } 32 + 33 + func FlatMapH[A, U, B, V any](f func(U) Fx[B, V]) func(Fx[A, U]) Fx[And[A, B], V] { 34 + return FlatCont[And[A, B]](left, right, f) 35 + } 36 + 37 + func FlatCont[N, A, U, B, V any](amap func(N) A, bmap func(N) B, fmap func(U) Fx[B, V]) func(Fx[A, U]) Fx[N, V] { 38 + return Then(amap, func(u U) Fx[N, V] { return Then(bmap, Const[N, V])(fmap(u)) }) 39 + }
+11
fx/nil.go
··· 1 + package fx 2 + 3 + type Nil pnil 4 + type pnil struct{} 5 + 6 + var PNil Nil = Nil(pnil{}) 7 + var PureNil FxNil = Pure(PNil) 8 + 9 + type FxNil = FxPure[Nil] 10 + 11 + type FxPure[V any] = Fx[Nil, V]
+50
fx/provide.go
··· 1 + package fx 2 + 3 + func ProvideFirstLeft[A, B, C, V any](e Fx[And[And[A, C], B], V], a A) Fx[And[C, B], V] { 4 + return AndFlat(ProvideLeft(AndNest(e), a)) 5 + } 6 + 7 + func ProvideFirstRight[A, B, D, V any](e Fx[And[A, And[B, D]], V], b B) Fx[And[A, D], V] { 8 + return AndSwap(ProvideFirstLeft(AndSwap(e), b)) 9 + } 10 + 11 + func ProvideFirsts[A, B, C, D, V any](e Fx[And[And[A, C], And[B, D]], V], a A, b B) Fx[And[C, D], V] { 12 + return ProvideFirstRight(ProvideFirstLeft(e, a), b) 13 + } 14 + 15 + func Provide[S, V any](e Fx[S, V], s S) Fx[Nil, V] { 16 + return ProvideLeft(AndNil(e), s) 17 + } 18 + 19 + func ProvideBoth[A, B, V any](e Fx[And[A, B], V], a A, b B) Fx[Nil, V] { 20 + return Provide(ProvideLeft(e, a), b) 21 + } 22 + 23 + func ProvideRight[A, B, V any](e Fx[And[A, B], V], b B) Fx[A, V] { 24 + return ProvideLeft(AndSwap(e), b) 25 + } 26 + 27 + func ProvideLeft[A, B, V any](e Fx[And[A, B], V], a A) Fx[B, V] { 28 + if e.res != nil { 29 + return Stop(func() Fx[B, V] { return ProvideLeft(e.res(), a) }) 30 + } 31 + if e.imm != nil { 32 + return Const[B](e.imm()) 33 + } 34 + return Pending(func(b B) Fx[B, V] { 35 + var ab And[A, B] = func() (A, B) { return a, b } 36 + var loop func(e Fx[And[A, B], V]) Fx[B, V] 37 + loop = func(e Fx[And[A, B], V]) Fx[B, V] { 38 + for { 39 + e = e.sus(ab) 40 + if e.res != nil { 41 + return Stop(func() Fx[B, V] { return loop(e.res()) }) 42 + } 43 + if e.imm != nil { 44 + return Const[B](e.imm()) 45 + } 46 + } 47 + } 48 + return loop(e) 49 + }) 50 + }
+46
fx/stop.go
··· 1 + package fx 2 + 3 + // Creates an stopped effect from a resume function 4 + func Stop[S, V any](e func() Fx[S, V]) Fx[S, V] { 5 + return Fx[S, V]{res: e} 6 + } 7 + 8 + // Resume an effect if it was previously stopped. 9 + func Resume[S, V any](e Fx[S, V]) Fx[S, V] { 10 + if e.res != nil { 11 + return e.res() 12 + } 13 + if e.imm != nil { 14 + return Const[S](e.imm()) 15 + } 16 + return Pending(func(s S) Fx[S, V] { return Resume(e.sus(s)) }) 17 + } 18 + 19 + // Replace with y if x is already Halted. Otherwise x continues. 20 + func Replace[S, V any](y func() Fx[S, V]) func(Fx[S, V]) Fx[S, V] { 21 + return func(x Fx[S, V]) Fx[S, V] { 22 + if x.res != nil { 23 + return y() 24 + } 25 + if x.imm != nil { 26 + return Const[S](x.imm()) 27 + } 28 + return Pending(func(s S) Fx[S, V] { return Replace(y)(x.sus(s)) }) 29 + } 30 + } 31 + 32 + // An stopped effect that panics if resumed. 33 + // Only useful with Replace. 34 + // 35 + // For example, an Abort effect halts since it has no possible value for V 36 + // but then its Handler can Replace the halted effect with an Error value. 37 + // See: abort/result.go 38 + func Halt[S, V any]() Fx[S, V] { 39 + return Stop(func() Fx[S, V] { 40 + return Fx[S, V]{ 41 + imm: func() V { 42 + panic("tried to Resume a halted effect. try using Replace instead") 43 + }, 44 + } 45 + }) 46 + }
fx_test.go fx/fx_test.go
+1 -1
rw/read.go
··· 1 1 package rw 2 2 3 - import fx "github.com/vic/fx.go" 3 + import "github.com/vic/fx.go/fx" 4 4 5 5 type Reader[T any] func() *T 6 6
+1 -1
rw/rw_test.go
··· 4 4 "strings" 5 5 "testing" 6 6 7 - fx "github.com/vic/fx.go" 7 + "github.com/vic/fx.go/fx" 8 8 ) 9 9 10 10 func TestReadWrite(t *testing.T) {
+1 -1
rw/write.go
··· 1 1 package rw 2 2 3 3 import ( 4 - fx "github.com/vic/fx.go" 4 + "github.com/vic/fx.go/fx" 5 5 ) 6 6 7 7 type Writer[T any] func(*T)