//// The type `Or` is an type union with no wrapping. //// `Or2(Int, String, Float)` is the same as `Int | String | Float`, //// not `{Left, Int} | {Right, {Left, String} | {Right, Float}}` //// //// You can either treat it as a dynamic type that gives you some //// options on what it might be, or as a group of types that can //// be whittled down with decoders. //// //// There are functions for (example) //// ``` //// * Encoding: tail_3rd t3 -> Or2(t1,t2,t3) //// * Decoding: dehead Or2(t1,t2,t3) -> t1? //// * Moving: flip Or(a,b) -> Or(b,a) //// ``` //// //// The repeated functions with numbers are used to move around //// the types, so you dont have to make an external casting function. //// For example, `rev4_with_tail` just returns the same value with //// a different, equivalent type. It asserts that //// `Or(k, Or(l, Or(m, n)))` is equivalent to `Or(n, Or(m, Or(l, k)))`. //// //// head_4th puts a type in the 4th position in a union. //// ``` //// V------------------------->V //// fn(k) -> Or(l, Or(m, Or(n, Or(k, o)))) //// ``` //// //// Using tail_4th puts the type in the 4th position at the //// very end of the union, terminating the list of types. //// ``` //// V-----------------------V //// fn(k) -> Or(l, Or(m, Or(n, k))) //// ``` import gleam/dynamic import gleam/dynamic/decode.{type Decoder} as dec import gleam/result @external(erlang, "untag_ffi", "id") @external(javascript, "untag_ffi.mjs", "id") @internal pub fn id(x: a) -> b pub fn to_dynamic(or: Or(a, b)) -> dynamic.Dynamic { id(or) } pub fn decode(this or, with decoder: Decoder(_)) { or |> to_dynamic |> dec.run(decoder) } /// Tries to decode the head. if it fails, it will return the value as the tail type. /// /// This function is unsound when the generic type of the decoder /// does not directly match with the data it wants to decode. /// For example, /// ``` /// "string" /// // String -> Or(String, b) /// |> head /// |> dehead_unsafe(dec.int |> dec.map(int.to_string)) /// ``` /// The value of the Or(String, b) is "string", not any integer. /// pub fn dehead_unsafe( this union: Or(head, tail), with decoder: Decoder(head), ) -> Result(head, #(tail, List(dec.DecodeError))) { union |> to_dynamic |> dec.run(decoder) |> result.map_error(fn(errs) { #(id(union), errs) }) } /// Unsafe. See [dehead_unsafe] pub fn detail_unsafe( this union: Or(head, tail), with decoder: Decoder(tail), ) -> Result(tail, #(head, List(dec.DecodeError))) { union |> to_dynamic |> dec.run(decoder) |> result.map_error(fn(errs) { #(id(union), errs) }) } pub type Or(t1, t2) pub type Or2(t1, t2, t3) = Or(t1, Or(t2, t3)) pub type Or3(a, b, c, d) = Or(a, Or2(b, c, d)) pub type Or4(a, b, c, d, e) = Or(a, Or3(b, c, d, e)) pub type Or5(a, b, c, d, e, f) = Or(a, Or4(b, c, d, e, f)) pub type Or6(a, b, c, d, e, f, g) = Or(a, Or5(b, c, d, e, f, g)) pub type Or7(a, b, c, d, e, f, g, h) = Or(a, Or6(b, c, d, e, f, g, h)) pub type Or8(a, b, c, d, e, f, g, h, i) = Or(a, Or7(b, c, d, e, f, g, h, i)) pub type Or9(a, b, c, d, e, f, g, h, i, j) = Or(a, Or8(b, c, d, e, f, g, h, i, j)) pub const head: fn(h) -> Or(h, _) = id pub const tail: fn(t) -> Or(_, t) = id fn tail_in(tn, x) { tn(tail(x)) } pub const tail_2nd = tail pub fn tail_3rd(x) { tail_2nd |> tail_in(x) } pub fn tail_4th(x) { tail_3rd |> tail_in(x) } pub fn tail_5th(x) { tail_4th |> tail_in(x) } pub fn tail_6th(x) { tail_5th |> tail_in(x) } pub fn tail_7th(x) { tail_6th |> tail_in(x) } pub fn tail_8th(x) { tail_7th |> tail_in(x) } pub fn tail_9th(x) { tail_8th |> tail_in(x) } pub fn tail_10th(x) { tail_9th |> tail_in(x) } fn head_in(tn, x) { tn(head(x)) } /// h_th = become nth head pub const head_1st = head pub fn head_2nd(x) { tail_2nd |> head_in(x) } pub fn head_3th(x) { tail_3rd |> head_in(x) } pub fn head_4th(x) { tail_4th |> head_in(x) } pub fn head_5th(x) { tail_5th |> head_in(x) } pub fn head_6th(x) { tail_6th |> head_in(x) } pub fn head_7th(x) { tail_7th |> head_in(x) } pub fn head_8th(x) { tail_8th |> head_in(x) } pub fn head_9th(x) { tail_9th |> head_in(x) } pub fn head_10th(x) { tail_10th |> head_in(x) } pub fn flip(or: Or(a, b)) -> Or(b, a) { id(or) } pub fn flip_tail(or: Or(t1, Or(t2, t3))) -> Or(t1, Or(t3, t2)) { id(or) } pub fn flip_head(or) { or |> flipped(flip_tail) } pub fn flipped(or, f) { or |> flip |> f |> flip } pub fn give(or: Or(Or(t1, t2), t3)) -> Or(t2, Or(t1, t3)) { id(or) } pub fn give2(or) { or |> give |> give } pub fn give3(or) { or |> give |> give2 } pub fn give4(or) { or |> give |> give3 } pub fn give5(or) { or |> give |> give4 } pub fn give6(or) { or |> give |> give5 } pub fn give7(or) { or |> give |> give6 } pub fn give8(or) { or |> give |> give7 } pub fn take(or: Or(t1, Or(t2, t3))) -> Or(Or(t2, t1), t3) { or |> flipped(give) } pub fn take2(or) { or |> take |> take } pub fn take3(or) { or |> take |> take2 } pub fn take4(or) { or |> take |> take3 } pub fn take5(or) { or |> take |> take4 } pub fn take6(or) { or |> take |> take5 } pub fn take7(or) { or |> take |> take6 } pub fn take8(or) { or |> take |> take7 } pub fn rev3_with_tail(or) { or |> flip |> give } pub fn rev4_with_tail(or) { or |> rev3_with_tail |> give } pub fn rev5_with_tail(or) { or |> rev4_with_tail |> give } pub fn rev6_with_tail(or) { or |> rev5_with_tail |> give } pub fn rev7_with_tail(or) { or |> rev6_with_tail |> give } pub fn rev8_with_tail(or) { or |> rev7_with_tail |> give } pub fn rev9_with_tail(or) { or |> rev8_with_tail |> give } pub fn rev10_with_tail(or) { or |> rev9_with_tail |> give }