///|
using @vector {type Vector}
///|
struct Cmd(@val.Cmd)
///|
pub(all) struct Emit[Msg]((Msg) -> Cmd)
///|
pub fn[A, B] Emit::map(self : Emit[A], map : (B) -> A) -> Emit[B] {
Emit(value => (self.0)(map(value)))
}
///|
pub struct DecodeError {
message : String
}
///|
pub enum Status[T] {
Pending
Loaded(T)
Failed(Error)
}
///|
pub impl[T : Eq] Eq for Status[T] with fn equal(left, right) {
match (left, right) {
(Pending, Pending) | (Failed(_), Failed(_)) => true
(Loaded(a), Loaded(b)) => a == b
_ => false
}
}
///|
pub let none : Cmd = Cmd(@val.none)
///|
pub fn batch(commands : Array[Cmd]) -> Cmd {
Cmd(@val.batch(commands.map(command => command.0)))
}
///|
pub fn delay(command : Cmd, milliseconds : Int) -> Cmd {
Cmd(@val.delay(command.0, milliseconds))
}
///|
pub fn effect(f : async () -> Unit noraise) -> Cmd {
Cmd(@val.effect(f))
}
///|
pub fn[A] perform(to_cmd : Emit[A], f : async () -> A noraise) -> Cmd {
Cmd(@val.perform(value => to_cmd(value).0, f))
}
///|
pub fn[A, E : Error] attempt(
to_cmd : Emit[Result[A, E]],
f : async () -> A raise E,
) -> Cmd {
Cmd(@val.attempt(result => to_cmd(result).0, f))
}
///|
pub fn decode_error(message : String) -> DecodeError {
guard message != "" else { abort("decode error must not be empty") }
{ message, }
}
///|
pub fn DecodeError::message(self : DecodeError) -> String {
self.message
}
///|
pub fn[Model] no_cmd(model : Model) -> (Model, Cmd) {
(model, none)
}
///|
pub fn[Model] with_cmd(model : Model, command : Cmd) -> (Model, Cmd) {
(model, command)
}
///|
pub fn[Model] with_cmds(model : Model, commands : Array[Cmd]) -> (Model, Cmd) {
(model, batch(commands))
}
///|
struct Val[A](@val.Val[A])
///|
pub fn[A] Val::constant(value : A) -> Val[A] {
Val(@val.Val::constant(value))
}
///|
pub fn[A : Eq, B] Val::map(self : Val[A], map : (A) -> B) -> Val[B] {
Val(self.0.map(map))
}
///|
pub fn[A : Eq, B : Eq, C] Val::map2(
a : Val[A],
b : Val[B],
map : (A, B) -> C,
) -> Val[C] {
Val(@val.Val::map2(a.0, b.0, map))
}
///|
pub fn[A : Eq, B : Eq, C : Eq, D] Val::map3(
a : Val[A],
b : Val[B],
c : Val[C],
map : (A, B, C) -> D,
) -> Val[D] {
Val(@val.Val::map3(a.0, b.0, c.0, map))
}
///|
pub fn[A : Eq, B : Eq, C : Eq, D : Eq, E] Val::map4(
a : Val[A],
b : Val[B],
c : Val[C],
d : Val[D],
map : (A, B, C, D) -> E,
) -> Val[E] {
Val(@val.Val::map4(a.0, b.0, c.0, d.0, map))
}
///|
pub fn[A : Eq, B : Eq, C : Eq, D : Eq, E : Eq, F] Val::map5(
a : Val[A],
b : Val[B],
c : Val[C],
d : Val[D],
e : Val[E],
map : (A, B, C, D, E) -> F,
) -> Val[F] {
Val(@val.Val::map5(a.0, b.0, c.0, d.0, e.0, map))
}
///|
pub fn[A : Eq, B : Eq, C : Eq, D : Eq, E : Eq, F : Eq, G] Val::map6(
a : Val[A],
b : Val[B],
c : Val[C],
d : Val[D],
e : Val[E],
f : Val[F],
map : (A, B, C, D, E, F) -> G,
) -> Val[G] {
Val(@val.Val::map6(a.0, b.0, c.0, d.0, e.0, f.0, map))
}
///|
pub fn[A : Eq, B : Eq, C : Eq, D : Eq, E : Eq, F : Eq, G : Eq, H] Val::map7(
a : Val[A],
b : Val[B],
c : Val[C],
d : Val[D],
e : Val[E],
f : Val[F],
g : Val[G],
map : (A, B, C, D, E, F, G) -> H,
) -> Val[H] {
Val(@val.Val::map7(a.0, b.0, c.0, d.0, e.0, f.0, g.0, map))
}
///|
pub fn[A : Eq, B : Eq, C : Eq, D : Eq, E : Eq, F : Eq, G : Eq, H : Eq, I] Val::map8(
a : Val[A],
b : Val[B],
c : Val[C],
d : Val[D],
e : Val[E],
f : Val[F],
g : Val[G],
h : Val[H],
map : (A, B, C, D, E, F, G, H) -> I,
) -> Val[I] {
Val(@val.Val::map8(a.0, b.0, c.0, d.0, e.0, f.0, g.0, h.0, map))
}
///|
pub fn[
A : Eq,
B : Eq,
C : Eq,
D : Eq,
E : Eq,
F : Eq,
G : Eq,
H : Eq,
I : Eq,
J,
] Val::map9(
a : Val[A],
b : Val[B],
c : Val[C],
d : Val[D],
e : Val[E],
f : Val[F],
g : Val[G],
h : Val[H],
i : Val[I],
map : (A, B, C, D, E, F, G, H, I) -> J,
) -> Val[J] {
Val(@val.Val::map9(a.0, b.0, c.0, d.0, e.0, f.0, g.0, h.0, i.0, map))
}
///|
pub fn[A : Eq] Val::view(self : Val[A], render : (A) -> Node) -> Val[Node] {
self.map(render)
}
///|
pub fn[A : Eq, B : Eq] Val::view2(
a : Val[A],
b : Val[B],
render : (A, B) -> Node,
) -> Val[Node] {
Val::map2(a, b, render)
}
///|
pub fn[A : Eq, B : Eq, C : Eq] Val::view3(
a : Val[A],
b : Val[B],
c : Val[C],
render : (A, B, C) -> Node,
) -> Val[Node] {
Val::map3(a, b, c, render)
}
///|
pub fn[A : Eq, B : Eq, C : Eq, D : Eq] Val::view4(
a : Val[A],
b : Val[B],
c : Val[C],
d : Val[D],
render : (A, B, C, D) -> Node,
) -> Val[Node] {
Val::map4(a, b, c, d, render)
}
///|
pub fn[A : Eq, B : Eq, C : Eq, D : Eq, E : Eq] Val::view5(
a : Val[A],
b : Val[B],
c : Val[C],
d : Val[D],
e : Val[E],
render : (A, B, C, D, E) -> Node,
) -> Val[Node] {
Val::map5(a, b, c, d, e, render)
}
///|
pub fn[A : Eq, B : Eq, C : Eq, D : Eq, E : Eq, F : Eq] Val::view6(
a : Val[A],
b : Val[B],
c : Val[C],
d : Val[D],
e : Val[E],
f : Val[F],
render : (A, B, C, D, E, F) -> Node,
) -> Val[Node] {
Val::map6(a, b, c, d, e, f, render)
}
///|
pub fn[A : Eq, B : Eq, C : Eq, D : Eq, E : Eq, F : Eq, G : Eq] Val::view7(
a : Val[A],
b : Val[B],
c : Val[C],
d : Val[D],
e : Val[E],
f : Val[F],
g : Val[G],
render : (A, B, C, D, E, F, G) -> Node,
) -> Val[Node] {
Val::map7(a, b, c, d, e, f, g, render)
}
///|
pub fn[A : Eq, B : Eq, C : Eq, D : Eq, E : Eq, F : Eq, G : Eq, H : Eq] Val::view8(
a : Val[A],
b : Val[B],
c : Val[C],
d : Val[D],
e : Val[E],
f : Val[F],
g : Val[G],
h : Val[H],
render : (A, B, C, D, E, F, G, H) -> Node,
) -> Val[Node] {
Val::map8(a, b, c, d, e, f, g, h, render)
}
///|
pub fn[A : Eq, B : Eq, C : Eq, D : Eq, E : Eq, F : Eq, G : Eq, H : Eq, I : Eq] Val::view9(
a : Val[A],
b : Val[B],
c : Val[C],
d : Val[D],
e : Val[E],
f : Val[F],
g : Val[G],
h : Val[H],
i : Val[I],
render : (A, B, C, D, E, F, G, H, I) -> Node,
) -> Val[Node] {
Val::map9(a, b, c, d, e, f, g, h, i, render)
}
///|
pub fn[K : Hash + Eq, V : Eq, C : Eq] Val::assoc(
self : Val[Vector[(K, V)]],
build : (K, Val[V]) -> Val[C],
) -> Val[Vector[C]] {
Val(self.0.assoc((key, value) => build(key, Val(value)).0))
}
///|
pub fn[K : Hash + Eq, V : Eq, C : Eq] Val::assoc_by(
self : Val[Vector[V]],
build : (K, Val[V]) -> Val[C],
by~ : (V) -> K,
) -> Val[Vector[C]] {
Val(self.0.assoc_by((key, value) => build(key, Val(value)).0, by~))
}
///|
pub trait Enumerate {
fn tag(Self) -> String
}
///|
pub fn[E : Enumerate + Eq, C : Eq] Val::switch(
self : Val[E],
build : (E, Val[E]) -> Val[C],
) -> Val[C] {
self.switch_by(build, by=value => Enumerate::tag(value))
}
///|
pub fn[E : Eq, C : Eq] Val::switch_by(
self : Val[E],
build : (E, Val[E]) -> Val[C],
by~ : (E) -> String,
) -> Val[C] {
Val(self.0.switch_by((initial, input) => build(initial, Val(input)).0, by~))
}
///|
pub fn[E : Enumerate + Eq, C : Eq] Val::enumerate(
self : Val[E],
build : (E, Val[E]) -> Val[C],
) -> Val[C] {
self.enumerate_by(build, by=value => Enumerate::tag(value))
}
///|
pub fn[E : Eq, C : Eq] Val::enumerate_by(
self : Val[E],
build : (E, Val[E]) -> Val[C],
by~ : (E) -> String,
) -> Val[C] {
Val(
self.0.enumerate_by((initial, input) => build(initial, Val(input)).0, by~),
)
}
///|
/// Cache at most `capacity` dynamic branches, evicting the least-recently used
/// inactive branch after a successful candidate commit.
pub fn[E : Eq, C : Eq] Val::enumerate_bounded_by(
self : Val[E],
capacity~ : Int,
build~ : (E, Val[E]) -> Val[C],
by~ : (E) -> String,
) -> Val[C] {
Val(
self.0.enumerate_bounded_by(
capacity~,
build=(initial, input) => build(initial, Val(input)).0,
by~,
),
)
}
///|
struct Sub(@val.Sub)
///|
pub fn Sub::none() -> Sub {
Sub(@val.no_sub)
}
///|
pub fn Sub::batch(subscriptions : Array[Sub]) -> Sub {
Sub(@val.sub_batch(subscriptions.map(subscription => subscription.0)))
}
///|
fn[Msg] wrap_emit(emit : @val.Emit[Msg]) -> Emit[Msg] {
Emit(message => Cmd(emit.emit(message)))
}
///|
pub fn[Model : Eq, Msg] create_pure_state(
initial : Model,
update~ : (Model, Msg) -> Model,
) -> (Val[Model], Emit[Msg]) {
let (value, emit) = @val.create_pure_state(initial, update~)
(Val(value), wrap_emit(emit))
}
///|
pub fn[Model : Eq, Msg] create_state(
initial : Model,
update~ : (Model, Msg, Emit[Msg]) -> (Model, Cmd),
subscriptions? : (Model, Emit[Msg]) -> Sub,
) -> (Val[Model], Emit[Msg]) {
let mapped_subscriptions = match subscriptions {
Some(build) =>
Some((model : Model, emit : @val.Emit[Msg]) => {
build(model, wrap_emit(emit)).0
})
None => None
}
let (value, emit) = @val.create_state(
initial,
update=(model, message, emit) => {
let (next, command) = update(model, message, wrap_emit(emit))
(next, command.0)
},
subscriptions?=mapped_subscriptions,
)
(Val(value), wrap_emit(emit))
}
///|
pub fn[Model : Eq, Msg] create_state_with_init(
init~ : (Emit[Msg]) -> (Model, Cmd),
update~ : (Model, Msg, Emit[Msg]) -> (Model, Cmd),
subscriptions? : (Model, Emit[Msg]) -> Sub,
) -> (Val[Model], Emit[Msg]) {
let mapped_subscriptions = match subscriptions {
Some(build) =>
Some((model : Model, emit : @val.Emit[Msg]) => {
build(model, wrap_emit(emit)).0
})
None => None
}
let (value, emit) = @val.create_state_with_init(
init=emit => {
let (model, command) = init(wrap_emit(emit))
(model, command.0)
},
update=(model, message, emit) => {
let (next, command) = update(model, message, wrap_emit(emit))
(next, command.0)
},
subscriptions?=mapped_subscriptions,
)
(Val(value), wrap_emit(emit))
}
///|
pub fn[Model : Eq, Msg, Input : Eq] create_state_with_input(
init~ : (Emit[Msg], Input) -> (Model, Cmd),
update~ : (Model, Input, Msg, Emit[Msg]) -> (Model, Cmd),
subscriptions? : (Model, Input, Emit[Msg]) -> Sub,
input~ : Val[Input],
) -> (Val[Model], Emit[Msg]) {
let mapped_subscriptions = match subscriptions {
Some(build) =>
Some((model : Model, current_input : Input, emit : @val.Emit[Msg]) => {
build(model, current_input, wrap_emit(emit)).0
})
None => None
}
let (value, emit) = @val.create_state_with_input(
input=input.0,
init=(emit, current_input) => {
let (model, command) = init(wrap_emit(emit), current_input)
(model, command.0)
},
update=(model, current_input, message, emit) => {
let (next, command) = update(
model,
current_input,
message,
wrap_emit(emit),
)
(next, command.0)
},
subscriptions?=mapped_subscriptions,
)
(Val(value), wrap_emit(emit))
}
///|
pub fn[Model : Eq] create_variable(
initial : Model,
) -> (Val[Model], Emit[(Model) -> Model]) {
let (value, emit) = @val.create_variable(initial)
(Val(value), wrap_emit(emit))
}
///|
pub fn[T : Eq] create_resource(
load : (Emit[Result[T, Error]]) -> Cmd,
) -> Val[Status[T]] {
Val(
@val.create_resource(emit => load(wrap_emit(emit)).0).map(status => {
match status {
@val.Pending => Pending
@val.Loaded(value) => Loaded(value)
@val.Failed(error) => Failed(error)
}
}),
)
}
///|
pub impl[T] Enumerate for Status[T] with fn tag(self) {
match self {
Pending => "pending"
Loaded(_) => "loaded"
Failed(_) => "failed"
}
}