/* * Copyright 2020 Magnus Madsen * * Use of this source code is governed by the Apache 2.0 license * that can be found in the LICENSE.md file. */pubmod Validation {////// The Validation type.///pubenumValidation[e, t] withEq, Order, ToString {case Success(t)case Failure(Nec[e]) }instanceHash[Validation[e, t]] withHash[e], Hash[t] {pubdefhash(x: Validation[e, t]): Int32 = matchx {case Validation.Success(v) => 5407+197*Hash.hash(v)case Validation.Failure(v) => 5413+199*Hash.hash(v) } }instanceSemiGroup[Validation[e, t]] withSemiGroup[t] {pubdefcombine(x: Validation[e, t], y: Validation[e, t]): Validation[e, t] = match (x, y) {case (Validation.Success(x1), Validation.Success(y1)) => Validation.Success(SemiGroup.combine(x1, y1))case (Validation.Failure(es1), Validation.Failure(es2)) => Validation.Failure(Nec.append(es1, es2))case (Validation.Failure(_), _) => xcase (_, Validation.Failure(_)) => y } }instanceMonoid[Validation[e, t]] withMonoid[t] {pubdefempty(): Validation[e, t] = Validation.Success(Monoid.empty()) }instanceFunctor[Validation[e]] {pubdefmap(f: a -> b \ ef, x: Validation[e, a]): Validation[e, b] \ ef = Validation.map(f, x) }instanceApplicative[Validation[e]] {pubdefpoint(a: a): Validation[e, a] = Validation.Success(a)pubdefap(f: Validation[e, a -> b \ ef], x: Validation[e, a]): Validation[e, b] \ ef = Validation.ap(f, x) }instanceFormattable[Validation[e, t]] withFormattable[e], Formattable[t] {typeAef = Formattable.Aef[e] + Formattable.Aef[t]pubdefformat(x: Validation[e, t]): RichString \ (Formattable.Aef[e] + Formattable.Aef[t]) = matchx {case Validation.Success(v) => RichString.fromString("Success(") +Formattable.format(v) +RichString.fromString(")")case Validation.Failure(e) => RichString.fromString("Failure(") +Formattable.format(e) +RichString.fromString(")") } }////// Returns `Success(t)`.///pubdefsuccess(t: t): Validation[e, t] = Success(t)////// Returns `Failure(Nec.singleton(err))`.///pubdeffailure(err: e): Validation[e, t] = Failure(Nec.singleton(err))////// Applies the function in `v1` to the value in `v2`.///pubdefap(v1: Validation[e, t -> u \ ef], v2: Validation[e, t]): Validation[e, u] \ ef = match (v1, v2) {case (Success(f), Success(v)) => Success(f(v))case (Success(_), Failure(e)) => Failure(e)case (Failure(e), Success(_)) => Failure(e)case (Failure(x), Failure(y)) => Failure(Nec.append(x, y)) }////// Chain two functions, returns the product of their results.///pubdefproduct(fa: Validation[e, t1], fb: Validation[e, t2]): Validation[e, (t1, t2)] =ap(map((a, b) -> (a, b), fa), fb)////// Chain three functions, returns the product of their results.///pubdefproduct3(fa: Validation[e, t1], fb: Validation[e, t2], fc: Validation[e, t3]): Validation[e, (t1, t2, t3)] =ap(ap(map((a, b, c) -> (a, b, c), fa), fb), fc)////// Chain four functions, returns the product of their results.///pubdefproduct4(fa: Validation[e, t1], fb: Validation[e, t2], fc: Validation[e, t3], fd: Validation[e, t4]): Validation[e, (t1, t2, t3, t4)] =ap(ap(ap(map((a, b, c, d) -> (a, b, c, d), fa), fb), fc), fd)////// Chain five functions, returns the product of their results.///pubdefproduct5(fa: Validation[e, t1], fb: Validation[e, t2], fc: Validation[e, t3], fd: Validation[e, t4], fe: Validation[e, t5]): Validation[e, (t1, t2, t3, t4, t5)] =ap(ap(ap(ap(map((a, b, c, d, e) -> (a, b, c, d, e), fa), fb), fc), fd), fe)////// Returns `t` if `v` is `Success(t).` Otherwise returns `d`.///pubdefgetWithDefault(d: t, v: Validation[e, t]): t = matchv {case Success(t) => tcase Failure(_) => d }////// Returns `v` if it is `Success(v)`. Otherwise returns `default`.///pubdefwithDefault(default: {default = Validation[e, t]}, v: Validation[e, t]): Validation[e, t] = matchv {case Success(_) => vcase Failure(_) => default#default }////// Returns `true` if and only if `v` is `Success(t)` and `f(t)` is true.////// Returns `false` if `v` is `Failure`.///pubdefexists(f: t -> Bool \ ef, v: Validation[e, t]): Bool \ ef = matchv {case Success(t) => f(t)case Failure(_) => false }////// Returns `true` if `v` is `Success(t)` and `f(t)` is true or if `v` is `Failure`.///pubdefforAll(f: t -> Bool \ ef, v: Validation[e, t]): Bool \ ef = matchv {case Success(t) => f(t)case Failure(_) => true }////// Returns `Success(f(t))` if `v` is `Success(t)`. Otherwise returns `v`.///pubdefmap(f: t -> u \ ef, v: Validation[e, t]): Validation[e, u] \ ef = matchv {case Success(t) => Success(f(t))case Failure(e) => Failure(e) }////// Returns `Success(v1 :: v2 :: ... :: vn)` if each of `l_i` is `Success(v_i)`.////// Otherwise returns `Failure(e1 :: ... :: en)` with all of the failures concatenated.///pubdefsequence(l: List[Validation[e, t]]): Validation[e, List[t]] =defloop(ll, k) = matchll {case Nil => k(Nil)case Success(x) :: xs => loop(xs, ks -> k(x :: ks))case Failure(e) :: xs => collectFailuresWith(identity, xs, e) };loop(l, ks -> Success(ks))////// Returns `Success(v1 :: v2 :: ... v :: vn)` if each of `f(l_i)` is `Success(v_i)`.////// Otherwise returns `Failure(e1 :: ... :: en)` with all of the failures concatenated.///pubdeftraverse(f: a -> Validation[e, b] \ ef, l: List[a]): Validation[e, List[b]] \ ef =defloop(ll, k) = matchll {case Nil => k(Nil)casex :: xs => matchf(x) {case Success(y) => loop(xs, ks -> k(y :: ks))case Failure(y) => collectFailuresWith(f, xs, y) } };loop(l, ks -> Success(ks))////// Returns `Success()` if each of `f(l_i)` is `Success(_)`.////// Otherwise returns `Failure(e1 :: ... :: en)` with all of the failures concatenated.////// This function is the "forgetful" version of `traverse`, use it when you want the effect/// of applying `f` to each element but do not care about collecting the results.///pubdeftraverseX(f: a -> Validation[e, b] \ ef, l: List[a]): Validation[e, Unit] \ ef = matchl {case Nil => Success()casex :: xs => matchf(x) {case Success(_) => traverseX(f, xs)case Failure(e) => collectFailuresWith(f, xs, e) } }////// Helper function for `sequence`, `traverse` and `traverseX`. Collects a chain of failures.///defcollectFailuresWith(f: a -> Validation[e, b] \ ef, l: List[a], acc: Nec[e]): Validation[e, c] \ ef = matchl {case Nil => Failure(acc)casex :: xs => matchf(x) {case Success(_) => collectFailuresWith(f, xs, acc)case Failure(e) => collectFailuresWith(f, xs, Nec.append(acc, e)) } }////// Converts a Validation to an Option.////// Returns `Some(t)` if `v` is `Success(t)`./// Returns `None` otherwise.///pubdeftoOption(v: Validation[e, t]): Option[t] = matchv {case Success(t) => Some(t)case Failure(_) => None }////// Converts a Validation to a Result.////// Returns `Ok(t)` if `v` is `Success(t)`./// Returns `Err(e)` if `v` is `Failure(e)`.///pubdeftoResult(v: Validation[e, t]): Result[Nec[e], t] = matchv {case Success(t) => Ok(t)case Failure(e) => Err(e) }////// Converts a Validation to a List.////// Returns `t :: Nil` if `v` is `Success(t)`./// Returns `Nil` if `v` is `Failure(e)`.///pubdeftoList(v: Validation[e, t]): List[t] = matchv {case Success(t) => t :: Nilcase Failure(_) => Nil }////// Applies the binary function `f` to the values in `v1` and `v2`.////// Returns the concatenation of all the failures as `Failure(xs)` if either or both of `v1` or `v2` are `Failure(xs1)`.///pubdefmap2(f: (t1, t2) -> u \ ef, v1: Validation[e, t1], v2: Validation[e, t2]): Validation[e, u] \ ef =ap(map(f, v1), v2)////// Applies the ternary function `f` to the values in `v1`, `v2` and `v3`.////// Returns the concatenation of all the failures as `Failure(xs)` if any of `v1`, `v2` and `v3` are `Failure(xs1)`.///pubdefmap3(f: (t1, t2, t3) -> u \ ef, v1: Validation[e, t1], v2: Validation[e, t2], v3: Validation[e, t3]): Validation[e, u] \ ef =ap(map2(f, v1, v2), v3)////// Applies the 4-ary function `f` to the values in `v1`, `v2`, `v3` and `v4`.////// Returns the concatenation of all the failures as `Failure(xs)` if any of `v1`, `v2`, `v3` and `v4` are `Failure(xs1)`.///pubdefmap4(f: (t1, t2, t3, t4) -> u \ ef, v1: Validation[e, t1], v2: Validation[e, t2], v3: Validation[e, t3], v4: Validation[e, t4]): Validation[e, u] \ ef =ap(map3(f, v1, v2, v3), v4)////// Applies the 5-ary function `f` to the values in `v1`, `v2`, ... `v5`.////// Returns the concatenation of all the failures as `Failure(xs)` if any of `v1`, `v2`, ... `v5` are `Failure(xs1)`.///pubdefmap5(f: (t1, t2, t3, t4, t5) -> u \ ef, v1: Validation[e, t1], v2: Validation[e, t2], v3: Validation[e, t3], v4: Validation[e, t4], v5: Validation[e, t5]): Validation[e, u] \ ef =ap(map4(f, v1, v2, v3, v4), v5)////// Applies the 6-ary function `f` to the values in `v1`, `v2`, ... `v6`.////// Returns the concatenation of all the failures as `Failure(xs)` if any of `v1`, `v2`, ... `v6` are `Failure(xs1)`.///pubdefmap6(f: (t1, t2, t3, t4, t5, t6) -> u \ ef, v1: Validation[e, t1], v2: Validation[e, t2], v3: Validation[e, t3], v4: Validation[e, t4], v5: Validation[e, t5], v6: Validation[e, t6]): Validation[e, u] \ ef =ap(map5(f, v1, v2, v3, v4, v5), v6)////// Applies the 7-ary function `f` to the values in `v1`, `v2`, ... `v7`.////// Returns the concatenation of all the failures as `Failure(xs)` if any of `v1`, `v2`, ... `v7` are `Failure(xs1)`.///pubdefmap7(f: (t1, t2, t3, t4, t5, t6, t7) -> u \ ef, v1: Validation[e, t1], v2: Validation[e, t2], v3: Validation[e, t3], v4: Validation[e, t4], v5: Validation[e, t5], v6: Validation[e, t6], v7: Validation[e, t7]): Validation[e, u] \ ef =ap(map6(f, v1, v2, v3, v4, v5, v6), v7)////// Applies the 8-ary function `f` to the values in `v1`, `v2`, ... `v8`.////// Returns the concatenation of all the failures as `Failure(xs)` if any of `v1`, `v2`, ... `v8` are `Failure(xs1)`.///pubdefmap8(f: (t1, t2, t3, t4, t5, t6, t7, t8) -> u \ ef, v1: Validation[e, t1], v2: Validation[e, t2], v3: Validation[e, t3], v4: Validation[e, t4], v5: Validation[e, t5], v6: Validation[e, t6], v7: Validation[e, t7], v8: Validation[e, t8]): Validation[e, u] \ ef =ap(map7(f, v1, v2, v3, v4, v5, v6, v7), v8)////// Applies the 9-ary function `f` to the values in `v1`, `v2`, ... `v9`.////// Returns the concatenation of all the failures as `Failure(xs)` if any of `v1`, `v2`, ... `v9` are `Failure(xs1)`.///pubdefmap9(f: (t1, t2, t3, t4, t5, t6, t7, t8, t9) -> u \ ef, v1: Validation[e, t1], v2: Validation[e, t2], v3: Validation[e, t3], v4: Validation[e, t4], v5: Validation[e, t5], v6: Validation[e, t6], v7: Validation[e, t7], v8: Validation[e, t8], v9: Validation[e, t9]): Validation[e, u] \ ef =ap(map8(f, v1, v2, v3, v4, v5, v6, v7, v8), v9)////// Applies the 10-ary function `f` to the values in `v1`, `v2`, ... `v10`.////// Returns the concatenation of all the failures as `Failure(xs)` if any of `v1`, `v2`, ... `v10` are `Failure(xs1)`.///pubdefmap10(f: (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) -> u \ ef, v1: Validation[e, t1], v2: Validation[e, t2], v3: Validation[e, t3], v4: Validation[e, t4], v5: Validation[e, t5], v6: Validation[e, t6], v7: Validation[e, t7], v8: Validation[e, t8], v9: Validation[e, t9], v10: Validation[e, t10]): Validation[e, u] \ ef =ap(map9(f, v1, v2, v3, v4, v5, v6, v7, v8, v9), v10)}