//! Implementation of the `Alt` effect let { Eff, inject_rest, ? } = import! std.effect let { map } = import! std.functor let { wrap } = import! std.applicative let { Alternative } = import! std.alternative let { (<<), id } = import! std.function /// The `Alt` effect lets `Eff` implement `Alternative` type Alt r a = | Empty .. r let extract_alt x : forall s . [| alt : Alt | r |] a -> Alt r a = convert_variant! x let send_alt f : Alt r a -> Eff [| alt : Alt | r |] a = Impure (convert_effect! alt f) Pure let run_alt_inner transform fail eff_1 eff_2 : (a -> b) -> (() -> Eff [| | s |] b) -> Eff [| alt : Alt | r |] a -> Eff [| alt : Alt | r |] a -> Eff [| | s |] b = let loop next ve : (() -> Eff [| | s |] b) -> Eff [| alt : Alt | r |] a -> _ = match ve with | Pure value -> wrap (transform value) | Impure e f -> match extract_alt e with | Empty -> next () | rest -> Impure (inject_rest rest) (loop next << f) let loop_2 _ = loop fail eff_2 loop loop_2 eff_1 let empty : forall s . Eff [| alt : Alt | r |] s = send_alt Empty let alternative : Alternative (Eff [| alt : Alt | r |]) = { applicative = (import! std.effect).applicative, empty = empty, or = \l r -> run_alt_inner id (\_ -> empty) l r, } /// Eliminates the `Alt` effect returning `None` if the `Alt` effect is `empty`, otherwise returns `Some a` with the value /// /// ``` /// let { assert_eq, ? } = import! std.test /// let alt @ { ? } = import! std.effect.alt /// let state = import! std.effect.state /// let { (*>) } = import! std.applicative /// let { empty } = import! std.alternative /// let { Eff, run_pure, ? } = import! std.effect /// /// let incr = state.modify (\x -> x + 1) /// /// seq assert_eq (run_pure (state.exec_state 0 (alt.run_alt incr incr))) (1) /// seq assert_eq (run_pure (state.exec_state 0 (alt.run_alt (incr *> incr) incr))) (2) /// seq assert_eq (run_pure (state.exec_state 0 (alt.run_alt (incr *> empty *> incr) incr))) (2) /// assert_eq (run_pure (state.exec_state 0 (alt.run_alt empty incr))) (1) /// /// ``` let run_alt eff_1 eff_2 : Eff [| alt : Alt | r |] a -> Eff [| alt : Alt | r |] a -> Eff [| | r |] (Option a) = let fail _ = wrap None run_alt_inner Some fail eff_1 eff_2 { Alt, alternative, run_alt, }