Skip to main content

oxilean_std/either/
functions.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use oxilean_kernel::{BinderInfo, Declaration, Environment, Expr, Level, Name};
6
7use super::types::{EitherLeftIter, EitherRightIter, LeftIter, OxiEither, RightIter, TripleSum};
8
9pub fn type1() -> Expr {
10    Expr::Sort(Level::succ(Level::zero()))
11}
12pub fn type2() -> Expr {
13    Expr::Sort(Level::succ(Level::succ(Level::zero())))
14}
15pub fn either_of(alpha: Expr, beta: Expr) -> Expr {
16    Expr::App(
17        Box::new(Expr::App(
18            Box::new(Expr::Const(Name::str("Either"), vec![])),
19            Box::new(alpha),
20        )),
21        Box::new(beta),
22    )
23}
24pub fn option_of(alpha: Expr) -> Expr {
25    Expr::App(
26        Box::new(Expr::Const(Name::str("Option"), vec![])),
27        Box::new(alpha),
28    )
29}
30pub fn bool_ty() -> Expr {
31    Expr::Const(Name::str("Bool"), vec![])
32}
33pub fn list_of(alpha: Expr) -> Expr {
34    Expr::App(
35        Box::new(Expr::Const(Name::str("List"), vec![])),
36        Box::new(alpha),
37    )
38}
39pub fn prod_of(alpha: Expr, beta: Expr) -> Expr {
40    Expr::App(
41        Box::new(Expr::App(
42            Box::new(Expr::Const(Name::str("Prod"), vec![])),
43            Box::new(alpha),
44        )),
45        Box::new(beta),
46    )
47}
48pub fn axiom(env: &mut Environment, name: &str, ty: Expr) -> Result<(), String> {
49    env.add(Declaration::Axiom {
50        name: Name::str(name),
51        univ_params: vec![],
52        ty,
53    })
54    .map_err(|e| e.to_string())
55}
56pub fn ab_implicit(inner: Expr) -> Expr {
57    Expr::Pi(
58        BinderInfo::Implicit,
59        Name::str("α"),
60        Box::new(type1()),
61        Box::new(Expr::Pi(
62            BinderInfo::Implicit,
63            Name::str("β"),
64            Box::new(type1()),
65            Box::new(inner),
66        )),
67    )
68}
69/// Build Either type in the environment.
70pub fn build_either_env(env: &mut Environment) -> Result<(), String> {
71    let either_ty = Expr::Pi(
72        BinderInfo::Default,
73        Name::str("α"),
74        Box::new(type1()),
75        Box::new(Expr::Pi(
76            BinderInfo::Default,
77            Name::str("β"),
78            Box::new(type1()),
79            Box::new(type2()),
80        )),
81    );
82    env.add(Declaration::Axiom {
83        name: Name::str("Either"),
84        univ_params: vec![],
85        ty: either_ty,
86    })
87    .map_err(|e| e.to_string())?;
88    add_left(env)?;
89    add_right(env)?;
90    add_is_left(env)?;
91    add_is_right(env)?;
92    add_get_left(env)?;
93    add_get_right(env)?;
94    add_cases(env)?;
95    add_map(env)?;
96    add_map_left(env)?;
97    add_bimap(env)?;
98    add_swap(env)?;
99    add_fold(env)?;
100    add_to_option(env)?;
101    add_from_option(env)?;
102    add_sequence(env)?;
103    add_partition_eithers(env)?;
104    Ok(())
105}
106pub fn add_left(env: &mut Environment) -> Result<(), String> {
107    let ty = ab_implicit(Expr::Pi(
108        BinderInfo::Default,
109        Name::str("a"),
110        Box::new(Expr::BVar(1)),
111        Box::new(either_of(Expr::BVar(2), Expr::BVar(1))),
112    ));
113    axiom(env, "Either.left", ty)
114}
115pub fn add_right(env: &mut Environment) -> Result<(), String> {
116    let ty = ab_implicit(Expr::Pi(
117        BinderInfo::Default,
118        Name::str("b"),
119        Box::new(Expr::BVar(0)),
120        Box::new(either_of(Expr::BVar(2), Expr::BVar(1))),
121    ));
122    axiom(env, "Either.right", ty)
123}
124pub fn add_is_left(env: &mut Environment) -> Result<(), String> {
125    let ty = ab_implicit(Expr::Pi(
126        BinderInfo::Default,
127        Name::str("e"),
128        Box::new(either_of(Expr::BVar(1), Expr::BVar(0))),
129        Box::new(bool_ty()),
130    ));
131    axiom(env, "Either.isLeft", ty)
132}
133pub fn add_is_right(env: &mut Environment) -> Result<(), String> {
134    let ty = ab_implicit(Expr::Pi(
135        BinderInfo::Default,
136        Name::str("e"),
137        Box::new(either_of(Expr::BVar(1), Expr::BVar(0))),
138        Box::new(bool_ty()),
139    ));
140    axiom(env, "Either.isRight", ty)
141}
142pub fn add_get_left(env: &mut Environment) -> Result<(), String> {
143    let ty = ab_implicit(Expr::Pi(
144        BinderInfo::Default,
145        Name::str("e"),
146        Box::new(either_of(Expr::BVar(1), Expr::BVar(0))),
147        Box::new(option_of(Expr::BVar(2))),
148    ));
149    axiom(env, "Either.getLeft", ty)
150}
151pub fn add_get_right(env: &mut Environment) -> Result<(), String> {
152    let ty = ab_implicit(Expr::Pi(
153        BinderInfo::Default,
154        Name::str("e"),
155        Box::new(either_of(Expr::BVar(1), Expr::BVar(0))),
156        Box::new(option_of(Expr::BVar(1))),
157    ));
158    axiom(env, "Either.getRight", ty)
159}
160pub fn add_cases(env: &mut Environment) -> Result<(), String> {
161    let fl = Expr::Pi(
162        BinderInfo::Default,
163        Name::str("_"),
164        Box::new(Expr::BVar(2)),
165        Box::new(Expr::BVar(2)),
166    );
167    let fr = Expr::Pi(
168        BinderInfo::Default,
169        Name::str("_"),
170        Box::new(Expr::BVar(2)),
171        Box::new(Expr::BVar(2)),
172    );
173    let ty = Expr::Pi(
174        BinderInfo::Implicit,
175        Name::str("α"),
176        Box::new(type1()),
177        Box::new(Expr::Pi(
178            BinderInfo::Implicit,
179            Name::str("β"),
180            Box::new(type1()),
181            Box::new(Expr::Pi(
182                BinderInfo::Implicit,
183                Name::str("γ"),
184                Box::new(type1()),
185                Box::new(Expr::Pi(
186                    BinderInfo::Default,
187                    Name::str("e"),
188                    Box::new(either_of(Expr::BVar(2), Expr::BVar(1))),
189                    Box::new(Expr::Pi(
190                        BinderInfo::Default,
191                        Name::str("l"),
192                        Box::new(fl),
193                        Box::new(Expr::Pi(
194                            BinderInfo::Default,
195                            Name::str("r"),
196                            Box::new(fr),
197                            Box::new(Expr::BVar(3)),
198                        )),
199                    )),
200                )),
201            )),
202        )),
203    );
204    axiom(env, "Either.cases", ty)
205}
206pub fn add_map(env: &mut Environment) -> Result<(), String> {
207    let fn_ty = Expr::Pi(
208        BinderInfo::Default,
209        Name::str("_"),
210        Box::new(Expr::BVar(1)),
211        Box::new(Expr::BVar(1)),
212    );
213    let ty = Expr::Pi(
214        BinderInfo::Implicit,
215        Name::str("α"),
216        Box::new(type1()),
217        Box::new(Expr::Pi(
218            BinderInfo::Implicit,
219            Name::str("β"),
220            Box::new(type1()),
221            Box::new(Expr::Pi(
222                BinderInfo::Implicit,
223                Name::str("γ"),
224                Box::new(type1()),
225                Box::new(Expr::Pi(
226                    BinderInfo::Default,
227                    Name::str("f"),
228                    Box::new(fn_ty),
229                    Box::new(Expr::Pi(
230                        BinderInfo::Default,
231                        Name::str("e"),
232                        Box::new(either_of(Expr::BVar(3), Expr::BVar(2))),
233                        Box::new(either_of(Expr::BVar(4), Expr::BVar(2))),
234                    )),
235                )),
236            )),
237        )),
238    );
239    axiom(env, "Either.map", ty)
240}
241pub fn add_map_left(env: &mut Environment) -> Result<(), String> {
242    let fn_ty = Expr::Pi(
243        BinderInfo::Default,
244        Name::str("_"),
245        Box::new(Expr::BVar(2)),
246        Box::new(Expr::BVar(2)),
247    );
248    let ty = Expr::Pi(
249        BinderInfo::Implicit,
250        Name::str("α"),
251        Box::new(type1()),
252        Box::new(Expr::Pi(
253            BinderInfo::Implicit,
254            Name::str("β"),
255            Box::new(type1()),
256            Box::new(Expr::Pi(
257                BinderInfo::Implicit,
258                Name::str("γ"),
259                Box::new(type1()),
260                Box::new(Expr::Pi(
261                    BinderInfo::Default,
262                    Name::str("f"),
263                    Box::new(fn_ty),
264                    Box::new(Expr::Pi(
265                        BinderInfo::Default,
266                        Name::str("e"),
267                        Box::new(either_of(Expr::BVar(3), Expr::BVar(2))),
268                        Box::new(either_of(Expr::BVar(2), Expr::BVar(3))),
269                    )),
270                )),
271            )),
272        )),
273    );
274    axiom(env, "Either.mapLeft", ty)
275}
276pub fn add_bimap(env: &mut Environment) -> Result<(), String> {
277    let fl = Expr::Pi(
278        BinderInfo::Default,
279        Name::str("_"),
280        Box::new(Expr::BVar(3)),
281        Box::new(Expr::BVar(3)),
282    );
283    let fr = Expr::Pi(
284        BinderInfo::Default,
285        Name::str("_"),
286        Box::new(Expr::BVar(3)),
287        Box::new(Expr::BVar(3)),
288    );
289    let ty = Expr::Pi(
290        BinderInfo::Implicit,
291        Name::str("α"),
292        Box::new(type1()),
293        Box::new(Expr::Pi(
294            BinderInfo::Implicit,
295            Name::str("β"),
296            Box::new(type1()),
297            Box::new(Expr::Pi(
298                BinderInfo::Implicit,
299                Name::str("γ"),
300                Box::new(type1()),
301                Box::new(Expr::Pi(
302                    BinderInfo::Implicit,
303                    Name::str("δ"),
304                    Box::new(type1()),
305                    Box::new(Expr::Pi(
306                        BinderInfo::Default,
307                        Name::str("fl"),
308                        Box::new(fl),
309                        Box::new(Expr::Pi(
310                            BinderInfo::Default,
311                            Name::str("fr"),
312                            Box::new(fr),
313                            Box::new(Expr::Pi(
314                                BinderInfo::Default,
315                                Name::str("e"),
316                                Box::new(either_of(Expr::BVar(5), Expr::BVar(4))),
317                                Box::new(either_of(Expr::BVar(4), Expr::BVar(4))),
318                            )),
319                        )),
320                    )),
321                )),
322            )),
323        )),
324    );
325    axiom(env, "Either.bimap", ty)
326}
327pub fn add_swap(env: &mut Environment) -> Result<(), String> {
328    let ty = ab_implicit(Expr::Pi(
329        BinderInfo::Default,
330        Name::str("e"),
331        Box::new(either_of(Expr::BVar(1), Expr::BVar(0))),
332        Box::new(either_of(Expr::BVar(1), Expr::BVar(2))),
333    ));
334    axiom(env, "Either.swap", ty)
335}
336pub fn add_fold(env: &mut Environment) -> Result<(), String> {
337    let fl = Expr::Pi(
338        BinderInfo::Default,
339        Name::str("_"),
340        Box::new(Expr::BVar(2)),
341        Box::new(Expr::BVar(2)),
342    );
343    let fr = Expr::Pi(
344        BinderInfo::Default,
345        Name::str("_"),
346        Box::new(Expr::BVar(2)),
347        Box::new(Expr::BVar(2)),
348    );
349    let ty = Expr::Pi(
350        BinderInfo::Implicit,
351        Name::str("α"),
352        Box::new(type1()),
353        Box::new(Expr::Pi(
354            BinderInfo::Implicit,
355            Name::str("β"),
356            Box::new(type1()),
357            Box::new(Expr::Pi(
358                BinderInfo::Implicit,
359                Name::str("γ"),
360                Box::new(type1()),
361                Box::new(Expr::Pi(
362                    BinderInfo::Default,
363                    Name::str("fl"),
364                    Box::new(fl),
365                    Box::new(Expr::Pi(
366                        BinderInfo::Default,
367                        Name::str("fr"),
368                        Box::new(fr),
369                        Box::new(Expr::Pi(
370                            BinderInfo::Default,
371                            Name::str("e"),
372                            Box::new(either_of(Expr::BVar(4), Expr::BVar(3))),
373                            Box::new(Expr::BVar(3)),
374                        )),
375                    )),
376                )),
377            )),
378        )),
379    );
380    axiom(env, "Either.fold", ty)
381}
382pub fn add_to_option(env: &mut Environment) -> Result<(), String> {
383    let ty = ab_implicit(Expr::Pi(
384        BinderInfo::Default,
385        Name::str("e"),
386        Box::new(either_of(Expr::BVar(1), Expr::BVar(0))),
387        Box::new(option_of(Expr::BVar(1))),
388    ));
389    axiom(env, "Either.toOption", ty)
390}
391pub fn add_from_option(env: &mut Environment) -> Result<(), String> {
392    let ty = ab_implicit(Expr::Pi(
393        BinderInfo::Default,
394        Name::str("err"),
395        Box::new(Expr::BVar(1)),
396        Box::new(Expr::Pi(
397            BinderInfo::Default,
398            Name::str("opt"),
399            Box::new(option_of(Expr::BVar(1))),
400            Box::new(either_of(Expr::BVar(3), Expr::BVar(2))),
401        )),
402    ));
403    axiom(env, "Either.fromOption", ty)
404}
405pub fn add_sequence(env: &mut Environment) -> Result<(), String> {
406    let ty = ab_implicit(Expr::Pi(
407        BinderInfo::Default,
408        Name::str("xs"),
409        Box::new(list_of(either_of(Expr::BVar(1), Expr::BVar(0)))),
410        Box::new(either_of(Expr::BVar(2), list_of(Expr::BVar(1)))),
411    ));
412    axiom(env, "Either.sequence", ty)
413}
414pub fn add_partition_eithers(env: &mut Environment) -> Result<(), String> {
415    let ty = ab_implicit(Expr::Pi(
416        BinderInfo::Default,
417        Name::str("xs"),
418        Box::new(list_of(either_of(Expr::BVar(1), Expr::BVar(0)))),
419        Box::new(prod_of(list_of(Expr::BVar(2)), list_of(Expr::BVar(1)))),
420    ));
421    axiom(env, "Either.partitionEithers", ty)
422}
423pub fn setup_base_env() -> Environment {
424    let mut env = Environment::new();
425    let t1 = type1();
426    for name in ["Bool", "Option", "List", "Prod"] {
427        env.add(Declaration::Axiom {
428            name: Name::str(name),
429            univ_params: vec![],
430            ty: t1.clone(),
431        })
432        .unwrap_or(());
433    }
434    env
435}
436#[cfg(test)]
437mod tests {
438    use super::*;
439    #[test]
440    fn test_build_either_env() {
441        let mut env = setup_base_env();
442        assert!(build_either_env(&mut env).is_ok());
443        assert!(env.get(&Name::str("Either")).is_some());
444        assert!(env.get(&Name::str("Either.left")).is_some());
445        assert!(env.get(&Name::str("Either.right")).is_some());
446    }
447    #[test]
448    fn test_either_is_left() {
449        let mut env = setup_base_env();
450        build_either_env(&mut env).expect("build_either_env should succeed");
451        assert!(matches!(
452            env.get(&Name::str("Either.isLeft"))
453                .expect("declaration 'Either.isLeft' should exist in env"),
454            Declaration::Axiom { .. }
455        ));
456    }
457    #[test]
458    fn test_either_is_right() {
459        let mut env = setup_base_env();
460        build_either_env(&mut env).expect("build_either_env should succeed");
461        assert!(matches!(
462            env.get(&Name::str("Either.isRight"))
463                .expect("declaration 'Either.isRight' should exist in env"),
464            Declaration::Axiom { .. }
465        ));
466    }
467    #[test]
468    fn test_either_get_left() {
469        let mut env = setup_base_env();
470        build_either_env(&mut env).expect("build_either_env should succeed");
471        assert!(env.get(&Name::str("Either.getLeft")).is_some());
472    }
473    #[test]
474    fn test_either_get_right() {
475        let mut env = setup_base_env();
476        build_either_env(&mut env).expect("build_either_env should succeed");
477        assert!(env.get(&Name::str("Either.getRight")).is_some());
478    }
479    #[test]
480    fn test_either_cases() {
481        let mut env = setup_base_env();
482        build_either_env(&mut env).expect("build_either_env should succeed");
483        assert!(env.get(&Name::str("Either.cases")).is_some());
484    }
485    #[test]
486    fn test_either_map() {
487        let mut env = setup_base_env();
488        build_either_env(&mut env).expect("build_either_env should succeed");
489        assert!(env.get(&Name::str("Either.map")).is_some());
490    }
491    #[test]
492    fn test_either_map_left() {
493        let mut env = setup_base_env();
494        build_either_env(&mut env).expect("build_either_env should succeed");
495        assert!(env.get(&Name::str("Either.mapLeft")).is_some());
496    }
497    #[test]
498    fn test_either_bimap() {
499        let mut env = setup_base_env();
500        build_either_env(&mut env).expect("build_either_env should succeed");
501        assert!(env.get(&Name::str("Either.bimap")).is_some());
502    }
503    #[test]
504    fn test_either_swap() {
505        let mut env = setup_base_env();
506        build_either_env(&mut env).expect("build_either_env should succeed");
507        assert!(env.get(&Name::str("Either.swap")).is_some());
508    }
509    #[test]
510    fn test_either_fold() {
511        let mut env = setup_base_env();
512        build_either_env(&mut env).expect("build_either_env should succeed");
513        assert!(env.get(&Name::str("Either.fold")).is_some());
514    }
515    #[test]
516    fn test_either_to_option() {
517        let mut env = setup_base_env();
518        build_either_env(&mut env).expect("build_either_env should succeed");
519        assert!(env.get(&Name::str("Either.toOption")).is_some());
520    }
521    #[test]
522    fn test_either_from_option() {
523        let mut env = setup_base_env();
524        build_either_env(&mut env).expect("build_either_env should succeed");
525        assert!(env.get(&Name::str("Either.fromOption")).is_some());
526    }
527    #[test]
528    fn test_either_sequence() {
529        let mut env = setup_base_env();
530        build_either_env(&mut env).expect("build_either_env should succeed");
531        assert!(env.get(&Name::str("Either.sequence")).is_some());
532    }
533    #[test]
534    fn test_either_partition_eithers() {
535        let mut env = setup_base_env();
536        build_either_env(&mut env).expect("build_either_env should succeed");
537        assert!(env.get(&Name::str("Either.partitionEithers")).is_some());
538    }
539}
540/// Extension methods for iterators of `OxiEither`.
541pub trait EitherIterExt<A, B>: Iterator<Item = OxiEither<A, B>> + Sized {
542    /// Yield only the `Left` values.
543    fn lefts(self) -> LeftIter<A, B, Self> {
544        LeftIter { inner: self }
545    }
546    /// Yield only the `Right` values.
547    fn rights(self) -> RightIter<A, B, Self> {
548        RightIter { inner: self }
549    }
550    /// Partition into (lefts, rights).
551    fn partition_either(self) -> (Vec<A>, Vec<B>) {
552        let mut ls = Vec::new();
553        let mut rs = Vec::new();
554        for e in self {
555            match e {
556                OxiEither::Left(a) => ls.push(a),
557                OxiEither::Right(b) => rs.push(b),
558            }
559        }
560        (ls, rs)
561    }
562}
563impl<A, B, I: Iterator<Item = OxiEither<A, B>>> EitherIterExt<A, B> for I {}
564/// Validate a list of items, collecting either all successes or the first error.
565///
566/// Returns `Right(errors)` if any item validates to an `OxiEither::Right(error)`;
567/// otherwise returns `Left(successes)`.
568pub fn validate_all<A: Clone, E: Clone, I: IntoIterator<Item = OxiEither<A, E>>>(
569    items: I,
570) -> OxiEither<Vec<A>, Vec<E>> {
571    let mut successes = Vec::new();
572    let mut errors = Vec::new();
573    for item in items {
574        match item {
575            OxiEither::Left(a) => successes.push(a),
576            OxiEither::Right(e) => errors.push(e),
577        }
578    }
579    if errors.is_empty() {
580        OxiEither::Left(successes)
581    } else {
582        OxiEither::Right(errors)
583    }
584}
585/// Sequence a list of `OxiEither` values, stopping at the first `Right`.
586pub fn sequence_either<A: Clone, E: Clone, I: IntoIterator<Item = OxiEither<A, E>>>(
587    items: I,
588) -> OxiEither<Vec<A>, E> {
589    let mut results = Vec::new();
590    for item in items {
591        match item {
592            OxiEither::Left(a) => results.push(a),
593            OxiEither::Right(e) => return OxiEither::Right(e),
594        }
595    }
596    OxiEither::Left(results)
597}
598/// Apply a function to each element, collecting all results.
599pub fn traverse_either<A, B, E, F, I>(items: I, f: F) -> OxiEither<Vec<B>, E>
600where
601    I: IntoIterator<Item = A>,
602    F: Fn(A) -> OxiEither<B, E>,
603{
604    let mut results = Vec::new();
605    for item in items {
606        match f(item) {
607            OxiEither::Left(b) => results.push(b),
608            OxiEither::Right(e) => return OxiEither::Right(e),
609        }
610    }
611    OxiEither::Left(results)
612}
613/// Zip two `OxiEither` values, pairing their contents.
614pub fn zip_either<A, B, C>(ea: OxiEither<A, C>, eb: OxiEither<B, C>) -> OxiEither<(A, B), C> {
615    match (ea, eb) {
616        (OxiEither::Left(a), OxiEither::Left(b)) => OxiEither::Left((a, b)),
617        (OxiEither::Right(e), _) | (_, OxiEither::Right(e)) => OxiEither::Right(e),
618    }
619}
620/// Merge two `OxiEither` values with a combining function.
621pub fn merge_either<A, B, C, F>(ea: OxiEither<A, B>, eb: OxiEither<A, B>, f: F) -> OxiEither<A, B>
622where
623    F: Fn(A, A) -> A,
624    A: Clone,
625    B: Clone,
626{
627    match (ea, eb) {
628        (OxiEither::Left(a1), OxiEither::Left(a2)) => OxiEither::Left(f(a1, a2)),
629        (OxiEither::Right(e), _) | (_, OxiEither::Right(e)) => OxiEither::Right(e),
630    }
631}
632/// Convert `Option<A>` to `OxiEither<A, ()>`.
633pub fn option_to_either<A>(opt: Option<A>) -> OxiEither<A, ()> {
634    match opt {
635        Some(a) => OxiEither::Left(a),
636        None => OxiEither::Right(()),
637    }
638}
639/// Convert `OxiEither<A, ()>` back to `Option<A>`.
640pub fn either_to_option<A>(e: OxiEither<A, ()>) -> Option<A> {
641    match e {
642        OxiEither::Left(a) => Some(a),
643        OxiEither::Right(()) => None,
644    }
645}
646/// Convert `Result<A, E>` to `OxiEither<A, E>`.
647pub fn result_to_either<A, E>(r: Result<A, E>) -> OxiEither<A, E> {
648    match r {
649        Ok(a) => OxiEither::Left(a),
650        Err(e) => OxiEither::Right(e),
651    }
652}
653/// Convert `OxiEither<A, E>` to `Result<A, E>`.
654pub fn either_to_result<A, E>(e: OxiEither<A, E>) -> Result<A, E> {
655    match e {
656        OxiEither::Left(a) => Ok(a),
657        OxiEither::Right(e) => Err(e),
658    }
659}
660/// Count `Left` and `Right` values in a collection.
661pub fn count_lefts_rights<A, B, I: IntoIterator<Item = OxiEither<A, B>>>(
662    items: I,
663) -> (usize, usize) {
664    items.into_iter().fold((0, 0), |(ls, rs), e| match e {
665        OxiEither::Left(_) => (ls + 1, rs),
666        OxiEither::Right(_) => (ls, rs + 1),
667    })
668}
669/// Build an `OxiEither` from a predicate.
670pub fn either_from_pred<A: Clone, B: Clone, F: Fn(&A) -> Option<B>>(a: A, f: F) -> OxiEither<A, B> {
671    match f(&a) {
672        Some(b) => OxiEither::Right(b),
673        None => OxiEither::Left(a),
674    }
675}
676#[cfg(test)]
677mod either_extended_tests {
678    use super::*;
679    #[test]
680    fn test_lefts_iter() {
681        let items: Vec<OxiEither<i32, &str>> = vec![
682            OxiEither::Left(1),
683            OxiEither::Right("no"),
684            OxiEither::Left(2),
685        ];
686        let lefts: Vec<i32> = items.into_iter().lefts().collect();
687        assert_eq!(lefts, vec![1, 2]);
688    }
689    #[test]
690    fn test_rights_iter() {
691        let items: Vec<OxiEither<i32, &str>> = vec![
692            OxiEither::Left(1),
693            OxiEither::Right("yes"),
694            OxiEither::Right("also"),
695        ];
696        let rights: Vec<&str> = items.into_iter().rights().collect();
697        assert_eq!(rights, vec!["yes", "also"]);
698    }
699    #[test]
700    fn test_partition_either_iter() {
701        let items: Vec<OxiEither<i32, &str>> = vec![
702            OxiEither::Left(1),
703            OxiEither::Right("e"),
704            OxiEither::Left(2),
705        ];
706        let (ls, rs) = items.into_iter().partition_either();
707        assert_eq!(ls, vec![1, 2]);
708        assert_eq!(rs, vec!["e"]);
709    }
710    #[test]
711    fn test_validate_all_ok() {
712        let items: Vec<OxiEither<i32, &str>> = vec![OxiEither::Left(1), OxiEither::Left(2)];
713        let result = validate_all(items);
714        assert_eq!(result, OxiEither::Left(vec![1, 2]));
715    }
716    #[test]
717    fn test_validate_all_errors() {
718        let items: Vec<OxiEither<i32, &str>> = vec![OxiEither::Left(1), OxiEither::Right("bad")];
719        let result = validate_all(items);
720        assert_eq!(result, OxiEither::Right(vec!["bad"]));
721    }
722    #[test]
723    fn test_sequence_either_ok() {
724        let items: Vec<OxiEither<i32, &str>> = vec![OxiEither::Left(1), OxiEither::Left(2)];
725        assert_eq!(sequence_either(items), OxiEither::Left(vec![1, 2]));
726    }
727    #[test]
728    fn test_sequence_either_fail() {
729        let items: Vec<OxiEither<i32, &str>> = vec![OxiEither::Left(1), OxiEither::Right("err")];
730        assert_eq!(sequence_either(items), OxiEither::Right("err"));
731    }
732    #[test]
733    fn test_traverse_either_ok() {
734        let result = traverse_either(vec![1i32, 2, 3], |x| OxiEither::Left(x * 2));
735        assert_eq!(result, OxiEither::<Vec<i32>, &str>::Left(vec![2, 4, 6]));
736    }
737    #[test]
738    fn test_option_to_either() {
739        let e = option_to_either(Some(42i32));
740        assert_eq!(e, OxiEither::Left(42));
741        let e2: OxiEither<i32, ()> = option_to_either(None);
742        assert_eq!(e2, OxiEither::Right(()));
743    }
744    #[test]
745    fn test_result_to_either() {
746        let e: OxiEither<i32, &str> = result_to_either(Ok(1));
747        assert_eq!(e, OxiEither::Left(1));
748        let e2: OxiEither<i32, &str> = result_to_either(Err("oops"));
749        assert_eq!(e2, OxiEither::Right("oops"));
750    }
751    #[test]
752    fn test_count_lefts_rights() {
753        let items: Vec<OxiEither<i32, &str>> = vec![
754            OxiEither::Left(1),
755            OxiEither::Right("a"),
756            OxiEither::Left(2),
757        ];
758        assert_eq!(count_lefts_rights(items), (2, 1));
759    }
760    #[test]
761    fn test_triple_sum_to_nested() {
762        let t: TripleSum<i32, &str, f64> = TripleSum::First(1);
763        let nested = t.to_nested();
764        assert!(nested.is_left());
765        let t2: TripleSum<i32, &str, f64> = TripleSum::Second("hi");
766        let nested2 = t2.to_nested();
767        assert!(nested2.is_right());
768    }
769    #[test]
770    fn test_triple_sum_variants() {
771        let a: TripleSum<i32, i32, i32> = TripleSum::First(1);
772        let b: TripleSum<i32, i32, i32> = TripleSum::Second(2);
773        let c: TripleSum<i32, i32, i32> = TripleSum::Third(3);
774        assert!(a.is_first() && !a.is_second() && !a.is_third());
775        assert!(b.is_second());
776        assert!(c.is_third());
777    }
778    #[test]
779    fn test_zip_either_both_left() {
780        let ea: OxiEither<i32, &str> = OxiEither::Left(1);
781        let eb: OxiEither<i32, &str> = OxiEither::Left(2);
782        let result = zip_either(ea, eb);
783        assert_eq!(result, OxiEither::Left((1, 2)));
784    }
785    #[test]
786    fn test_zip_either_one_right() {
787        let ea: OxiEither<i32, &str> = OxiEither::Left(1);
788        let eb: OxiEither<i32, &str> = OxiEither::Right("err");
789        let result = zip_either(ea, eb);
790        assert!(result.is_right());
791    }
792}
793/// Unwrap a `Left` or compute a default from a `Right`.
794pub fn left_or_else<A, B, F: FnOnce(B) -> A>(e: OxiEither<A, B>, f: F) -> A {
795    match e {
796        OxiEither::Left(a) => a,
797        OxiEither::Right(b) => f(b),
798    }
799}
800/// Unwrap a `Right` or compute a default from a `Left`.
801pub fn right_or_else<A, B, F: FnOnce(A) -> B>(e: OxiEither<A, B>, f: F) -> B {
802    match e {
803        OxiEither::Right(b) => b,
804        OxiEither::Left(a) => f(a),
805    }
806}
807/// Filter a collection, returning `Left(v)` if predicate holds, `Right(v)` otherwise.
808pub fn filter_with_either<T, F: Fn(&T) -> bool>(
809    items: impl IntoIterator<Item = T>,
810    pred: F,
811) -> Vec<OxiEither<T, T>> {
812    items
813        .into_iter()
814        .map(|item| {
815            if pred(&item) {
816                OxiEither::Left(item)
817            } else {
818                OxiEither::Right(item)
819            }
820        })
821        .collect()
822}
823/// Flatten a nested `OxiEither<OxiEither<A, B>, B>` to `OxiEither<A, B>`.
824pub fn flatten_either<A, B>(e: OxiEither<OxiEither<A, B>, B>) -> OxiEither<A, B> {
825    match e {
826        OxiEither::Left(inner) => inner,
827        OxiEither::Right(b) => OxiEither::Right(b),
828    }
829}
830/// Transpose `OxiEither<Option<A>, B>` to `Option<OxiEither<A, B>>`.
831pub fn transpose_option_either<A, B>(e: OxiEither<Option<A>, B>) -> Option<OxiEither<A, B>> {
832    match e {
833        OxiEither::Left(Some(a)) => Some(OxiEither::Left(a)),
834        OxiEither::Left(None) => None,
835        OxiEither::Right(b) => Some(OxiEither::Right(b)),
836    }
837}
838/// Collect `OxiEither` values from an iterator, accumulating errors.
839pub fn collect_errors<A, E, I: IntoIterator<Item = OxiEither<A, E>>>(items: I) -> (Vec<A>, Vec<E>) {
840    items
841        .into_iter()
842        .fold((Vec::new(), Vec::new()), |(mut ls, mut rs), e| {
843            match e {
844                OxiEither::Left(a) => ls.push(a),
845                OxiEither::Right(e) => rs.push(e),
846            }
847            (ls, rs)
848        })
849}
850/// Apply a mapping that may fail, short-circuiting on the first error.
851pub fn try_map<A, B, E, F, I>(items: I, f: F) -> OxiEither<Vec<B>, E>
852where
853    I: IntoIterator<Item = A>,
854    F: Fn(A) -> OxiEither<B, E>,
855{
856    traverse_either(items, f)
857}
858/// An `OxiEither` where both sides have the same type (a homogeneous sum).
859pub type Homo<T> = OxiEither<T, T>;
860#[cfg(test)]
861mod either_further_tests {
862    use super::*;
863    #[test]
864    fn test_left_or_else() {
865        let e: OxiEither<i32, &str> = OxiEither::Left(5);
866        assert_eq!(left_or_else(e, |_| 99), 5);
867        let e2: OxiEither<i32, &str> = OxiEither::Right("err");
868        assert_eq!(left_or_else(e2, |_| 99), 99);
869    }
870    #[test]
871    fn test_right_or_else() {
872        let e: OxiEither<i32, &str> = OxiEither::Right("ok");
873        assert_eq!(right_or_else(e, |_| "default"), "ok");
874        let e2: OxiEither<i32, &str> = OxiEither::Left(1);
875        assert_eq!(right_or_else(e2, |_| "default"), "default");
876    }
877    #[test]
878    fn test_filter_with_either() {
879        let items = vec![1i32, 2, 3, 4];
880        let result = filter_with_either(items, |x| x % 2 == 0);
881        let (evens, odds): (Vec<_>, Vec<_>) = result.into_iter().partition(|e| e.is_left());
882        assert_eq!(evens.len(), 2);
883        assert_eq!(odds.len(), 2);
884    }
885    #[test]
886    fn test_flatten_either() {
887        let e: OxiEither<OxiEither<i32, &str>, &str> = OxiEither::Left(OxiEither::Left(1));
888        assert_eq!(flatten_either(e), OxiEither::Left(1));
889        let e2: OxiEither<OxiEither<i32, &str>, &str> = OxiEither::Right("err");
890        assert_eq!(flatten_either(e2), OxiEither::Right("err"));
891    }
892    #[test]
893    fn test_transpose_option_either_some() {
894        let e: OxiEither<Option<i32>, &str> = OxiEither::Left(Some(42));
895        assert_eq!(transpose_option_either(e), Some(OxiEither::Left(42)));
896    }
897    #[test]
898    fn test_transpose_option_either_none() {
899        let e: OxiEither<Option<i32>, &str> = OxiEither::Left(None);
900        assert_eq!(transpose_option_either(e), None);
901    }
902    #[test]
903    fn test_collect_errors() {
904        let items: Vec<OxiEither<i32, &str>> = vec![
905            OxiEither::Left(1),
906            OxiEither::Right("e1"),
907            OxiEither::Left(2),
908            OxiEither::Right("e2"),
909        ];
910        let (ls, rs) = collect_errors(items);
911        assert_eq!(ls, vec![1, 2]);
912        assert_eq!(rs, vec!["e1", "e2"]);
913    }
914    #[test]
915    fn test_homo_into_inner() {
916        let e: OxiEither<i32, i32> = OxiEither::Left(5);
917        assert_eq!(e.into_inner(), 5);
918        let e2: OxiEither<i32, i32> = OxiEither::Right(7);
919        assert_eq!(e2.into_inner(), 7);
920    }
921    #[test]
922    fn test_try_map_ok() {
923        let result = try_map(vec![1i32, 2, 3], |x| {
924            if x > 0 {
925                OxiEither::Left(x * 2)
926            } else {
927                OxiEither::Right("neg")
928            }
929        });
930        assert_eq!(result, OxiEither::Left(vec![2, 4, 6]));
931    }
932    #[test]
933    fn test_try_map_fail() {
934        let result: OxiEither<Vec<i32>, &str> = try_map(vec![1i32, -1, 2], |x| {
935            if x > 0 {
936                OxiEither::Left(x)
937            } else {
938                OxiEither::Right("neg")
939            }
940        });
941        assert_eq!(result, OxiEither::Right("neg"));
942    }
943}
944/// Separate a slice of `OxiEither`s into lefts and rights.
945pub fn separate<A: Clone, B: Clone>(items: &[OxiEither<A, B>]) -> (Vec<A>, Vec<B>) {
946    let mut lefts = Vec::new();
947    let mut rights = Vec::new();
948    for item in items {
949        match item {
950            OxiEither::Left(a) => lefts.push(a.clone()),
951            OxiEither::Right(b) => rights.push(b.clone()),
952        }
953    }
954    (lefts, rights)
955}
956/// Count the number of `Left` values in a slice.
957pub fn count_lefts<A, B>(items: &[OxiEither<A, B>]) -> usize {
958    items.iter().filter(|e| e.is_left()).count()
959}
960/// Count the number of `Right` values in a slice.
961pub fn count_rights<A, B>(items: &[OxiEither<A, B>]) -> usize {
962    items.iter().filter(|e| e.is_right()).count()
963}
964/// Map a function over all `Right` values in a slice, leaving `Left`s unchanged.
965pub fn map_rights<A: Clone, B: Clone, C>(
966    items: &[OxiEither<A, B>],
967    f: impl Fn(B) -> C,
968) -> Vec<OxiEither<A, C>> {
969    items.iter().map(|e| e.clone().map_right(&f)).collect()
970}
971/// Map a function over all `Left` values in a slice, leaving `Right`s unchanged.
972pub fn map_lefts<A: Clone, B: Clone, C>(
973    items: &[OxiEither<A, B>],
974    f: impl Fn(A) -> C,
975) -> Vec<OxiEither<C, B>> {
976    items.iter().map(|e| e.clone().map_left(&f)).collect()
977}
978/// Collect only the `Right` values from a slice.
979pub fn collect_rights<A, B: Clone>(items: &[OxiEither<A, B>]) -> Vec<B> {
980    items.iter().filter_map(|e| e.as_right().cloned()).collect()
981}
982/// Collect only the `Left` values from a slice.
983pub fn collect_lefts<A: Clone, B>(items: &[OxiEither<A, B>]) -> Vec<A> {
984    items.iter().filter_map(|e| e.as_left().cloned()).collect()
985}
986#[cfg(test)]
987mod extra_either_tests {
988    use super::*;
989    #[test]
990    fn test_either_right_iter_some() {
991        let e: OxiEither<i32, &str> = OxiEither::Right("hello");
992        let mut it = EitherRightIter::new(e);
993        assert_eq!(it.next(), Some("hello"));
994        assert_eq!(it.next(), None);
995    }
996    #[test]
997    fn test_either_right_iter_none() {
998        let e: OxiEither<i32, &str> = OxiEither::Left(42);
999        let mut it = EitherRightIter::new(e);
1000        assert_eq!(it.next(), None);
1001    }
1002    #[test]
1003    fn test_either_left_iter_some() {
1004        let e: OxiEither<i32, &str> = OxiEither::Left(99);
1005        let mut it = EitherLeftIter::new(e);
1006        assert_eq!(it.next(), Some(99));
1007        assert_eq!(it.next(), None);
1008    }
1009    #[test]
1010    fn test_either_left_iter_none() {
1011        let e: OxiEither<i32, &str> = OxiEither::Right("r");
1012        let mut it = EitherLeftIter::new(e);
1013        assert_eq!(it.next(), None);
1014    }
1015    #[test]
1016    fn test_separate() {
1017        let items: Vec<OxiEither<i32, &str>> = vec![
1018            OxiEither::Left(1),
1019            OxiEither::Right("a"),
1020            OxiEither::Left(2),
1021        ];
1022        let (ls, rs) = separate(&items);
1023        assert_eq!(ls, vec![1, 2]);
1024        assert_eq!(rs, vec!["a"]);
1025    }
1026    #[test]
1027    fn test_count_lefts_rights() {
1028        let items: Vec<OxiEither<i32, &str>> = vec![
1029            OxiEither::Left(1),
1030            OxiEither::Right("x"),
1031            OxiEither::Left(2),
1032            OxiEither::Right("y"),
1033        ];
1034        assert_eq!(count_lefts(&items), 2);
1035        assert_eq!(count_rights(&items), 2);
1036    }
1037    #[test]
1038    fn test_map_rights() {
1039        let items: Vec<OxiEither<i32, i32>> = vec![OxiEither::Left(1), OxiEither::Right(2)];
1040        let mapped = map_rights(&items, |x| x * 10);
1041        assert_eq!(mapped[0], OxiEither::Left(1));
1042        assert_eq!(mapped[1], OxiEither::Right(20));
1043    }
1044    #[test]
1045    fn test_map_lefts() {
1046        let items: Vec<OxiEither<i32, i32>> = vec![OxiEither::Left(3), OxiEither::Right(4)];
1047        let mapped = map_lefts(&items, |x| x + 100);
1048        assert_eq!(mapped[0], OxiEither::Left(103));
1049        assert_eq!(mapped[1], OxiEither::Right(4));
1050    }
1051    #[test]
1052    fn test_collect_rights() {
1053        let items: Vec<OxiEither<i32, &str>> = vec![
1054            OxiEither::Left(1),
1055            OxiEither::Right("a"),
1056            OxiEither::Right("b"),
1057        ];
1058        assert_eq!(collect_rights(&items), vec!["a", "b"]);
1059    }
1060    #[test]
1061    fn test_collect_lefts() {
1062        let items: Vec<OxiEither<i32, &str>> = vec![
1063            OxiEither::Left(10),
1064            OxiEither::Right("x"),
1065            OxiEither::Left(20),
1066        ];
1067        assert_eq!(collect_lefts(&items), vec![10, 20]);
1068    }
1069}
1070pub fn ei_ext_app(f: Expr, a: Expr) -> Expr {
1071    Expr::App(Box::new(f), Box::new(a))
1072}
1073pub fn ei_ext_app2(f: Expr, a: Expr, b: Expr) -> Expr {
1074    ei_ext_app(ei_ext_app(f, a), b)
1075}
1076pub fn ei_ext_cst(s: &str) -> Expr {
1077    Expr::Const(Name::str(s), vec![])
1078}
1079pub fn ei_ext_prop() -> Expr {
1080    Expr::Sort(Level::zero())
1081}
1082pub fn ei_ext_type0() -> Expr {
1083    Expr::Sort(Level::succ(Level::zero()))
1084}
1085pub fn ei_ext_bvar(n: u32) -> Expr {
1086    Expr::BVar(n)
1087}
1088pub fn ei_ext_nat_ty() -> Expr {
1089    ei_ext_cst("Nat")
1090}
1091pub fn ei_ext_arrow(dom: Expr, cod: Expr) -> Expr {
1092    Expr::Pi(
1093        BinderInfo::Default,
1094        Name::Anonymous,
1095        Box::new(dom),
1096        Box::new(cod),
1097    )
1098}
1099pub fn ei_ext_pi(binfo: BinderInfo, nm: &str, dom: Expr, cod: Expr) -> Expr {
1100    Expr::Pi(binfo, Name::str(nm), Box::new(dom), Box::new(cod))
1101}
1102pub fn ei_ext_ipi(nm: &str, dom: Expr, cod: Expr) -> Expr {
1103    ei_ext_pi(BinderInfo::Implicit, nm, dom, cod)
1104}
1105pub fn ei_ext_dpi(nm: &str, dom: Expr, cod: Expr) -> Expr {
1106    ei_ext_pi(BinderInfo::Default, nm, dom, cod)
1107}
1108pub fn ei_ext_either(a: Expr, b: Expr) -> Expr {
1109    ei_ext_app2(ei_ext_cst("Either"), a, b)
1110}
1111pub fn ei_ext_prod(a: Expr, b: Expr) -> Expr {
1112    ei_ext_app2(ei_ext_cst("Prod"), a, b)
1113}
1114pub fn ei_ext_list(a: Expr) -> Expr {
1115    ei_ext_app(ei_ext_cst("List"), a)
1116}
1117pub fn ei_ext_option(a: Expr) -> Expr {
1118    ei_ext_app(ei_ext_cst("Option"), a)
1119}
1120pub fn ei_ext_result(a: Expr, e: Expr) -> Expr {
1121    ei_ext_app2(ei_ext_cst("Result"), a, e)
1122}
1123pub fn ei_ext_sum(a: Expr, b: Expr) -> Expr {
1124    ei_ext_app2(ei_ext_cst("Sum"), a, b)
1125}
1126pub fn ei_ext_eq(a: Expr, x: Expr, y: Expr) -> Expr {
1127    ei_ext_app2(ei_ext_app(ei_ext_cst("Eq"), a), x, y)
1128}
1129pub fn ei_ext_axiom(env: &mut Environment, name: &str, ty: Expr) -> Result<(), String> {
1130    env.add(Declaration::Axiom {
1131        name: Name::str(name),
1132        univ_params: vec![],
1133        ty,
1134    })
1135    .map_err(|e| e.to_string())
1136}
1137pub fn ei_ext_forall_ab(body: Expr) -> Expr {
1138    ei_ext_ipi("α", ei_ext_type0(), ei_ext_ipi("β", ei_ext_type0(), body))
1139}
1140pub fn ei_ext_forall_abg(body: Expr) -> Expr {
1141    ei_ext_ipi(
1142        "α",
1143        ei_ext_type0(),
1144        ei_ext_ipi("β", ei_ext_type0(), ei_ext_ipi("γ", ei_ext_type0(), body)),
1145    )
1146}
1147pub fn ei_ext_forall_abgd(body: Expr) -> Expr {
1148    ei_ext_ipi(
1149        "α",
1150        ei_ext_type0(),
1151        ei_ext_ipi(
1152            "β",
1153            ei_ext_type0(),
1154            ei_ext_ipi("γ", ei_ext_type0(), ei_ext_ipi("δ", ei_ext_type0(), body)),
1155        ),
1156    )
1157}
1158/// Either as coproduct: left injection inl : α → Either α β
1159pub fn ei_coproduct_inl(env: &mut Environment) -> Result<(), String> {
1160    let ty = ei_ext_forall_ab(ei_ext_arrow(
1161        ei_ext_bvar(1),
1162        ei_ext_either(ei_ext_bvar(2), ei_ext_bvar(1)),
1163    ));
1164    ei_ext_axiom(env, "Either.inl", ty)
1165}
1166/// Either as coproduct: right injection inr : β → Either α β
1167pub fn ei_coproduct_inr(env: &mut Environment) -> Result<(), String> {
1168    let ty = ei_ext_forall_ab(ei_ext_arrow(
1169        ei_ext_bvar(0),
1170        ei_ext_either(ei_ext_bvar(2), ei_ext_bvar(1)),
1171    ));
1172    ei_ext_axiom(env, "Either.inr", ty)
1173}
1174/// Coproduct universal property: unique mediating morphism
1175/// Either.coprod : {α β γ : Type} → (α → γ) → (β → γ) → Either α β → γ
1176pub fn ei_coproduct_universal(env: &mut Environment) -> Result<(), String> {
1177    let ty = ei_ext_forall_abg(ei_ext_dpi(
1178        "fl",
1179        ei_ext_arrow(ei_ext_bvar(2), ei_ext_bvar(0)),
1180        ei_ext_dpi(
1181            "fr",
1182            ei_ext_arrow(ei_ext_bvar(2), ei_ext_bvar(1)),
1183            ei_ext_dpi(
1184                "e",
1185                ei_ext_either(ei_ext_bvar(4), ei_ext_bvar(3)),
1186                ei_ext_bvar(3),
1187            ),
1188        ),
1189    ));
1190    ei_ext_axiom(env, "Either.coprod", ty)
1191}
1192/// Functor law: bimap identity
1193/// Either.bimap_id : {α β : Type} → ∀ e : Either α β, bimap id id e = e
1194pub fn ei_bimap_id(env: &mut Environment) -> Result<(), String> {
1195    let ty = ei_ext_forall_ab(ei_ext_dpi(
1196        "e",
1197        ei_ext_either(ei_ext_bvar(1), ei_ext_bvar(0)),
1198        ei_ext_eq(
1199            ei_ext_either(ei_ext_bvar(3), ei_ext_bvar(2)),
1200            ei_ext_bvar(0),
1201            ei_ext_bvar(0),
1202        ),
1203    ));
1204    ei_ext_axiom(env, "Either.bimap_id", ty)
1205}
1206/// Functor law: bimap composition (propositional)
1207pub fn ei_bimap_comp(env: &mut Environment) -> Result<(), String> {
1208    ei_ext_axiom(env, "Either.bimap_comp", ei_ext_prop())
1209}
1210/// Either as bifunctor: left map preserves composition
1211pub fn ei_bifunctor_left_comp(env: &mut Environment) -> Result<(), String> {
1212    ei_ext_axiom(env, "Either.mapLeft_comp", ei_ext_prop())
1213}
1214/// Either as bifunctor: right map preserves composition
1215pub fn ei_bifunctor_right_comp(env: &mut Environment) -> Result<(), String> {
1216    ei_ext_axiom(env, "Either.map_comp", ei_ext_prop())
1217}
1218/// Monad return (pure): right injection
1219/// Either.pure : {ε β : Type} → β → Either ε β
1220pub fn ei_monad_pure(env: &mut Environment) -> Result<(), String> {
1221    let ty = ei_ext_forall_ab(ei_ext_arrow(
1222        ei_ext_bvar(0),
1223        ei_ext_either(ei_ext_bvar(2), ei_ext_bvar(1)),
1224    ));
1225    ei_ext_axiom(env, "Either.pure", ty)
1226}
1227/// Monad bind: sequence with short-circuit on Left
1228/// Either.bind : {ε α β : Type} → Either ε α → (α → Either ε β) → Either ε β
1229pub fn ei_monad_bind(env: &mut Environment) -> Result<(), String> {
1230    let ty = ei_ext_forall_abg(ei_ext_dpi(
1231        "e",
1232        ei_ext_either(ei_ext_bvar(2), ei_ext_bvar(1)),
1233        ei_ext_dpi(
1234            "f",
1235            ei_ext_arrow(
1236                ei_ext_bvar(1),
1237                ei_ext_either(ei_ext_bvar(4), ei_ext_bvar(2)),
1238            ),
1239            ei_ext_either(ei_ext_bvar(4), ei_ext_bvar(2)),
1240        ),
1241    ));
1242    ei_ext_axiom(env, "Either.bind", ty)
1243}
1244/// Monad left identity: bind (pure a) f = f a
1245pub fn ei_monad_left_id(env: &mut Environment) -> Result<(), String> {
1246    ei_ext_axiom(env, "Either.bind_pure_left", ei_ext_prop())
1247}
1248/// Monad right identity: bind m pure = m
1249pub fn ei_monad_right_id(env: &mut Environment) -> Result<(), String> {
1250    ei_ext_axiom(env, "Either.bind_pure_right", ei_ext_prop())
1251}
1252/// Monad associativity
1253pub fn ei_monad_assoc(env: &mut Environment) -> Result<(), String> {
1254    ei_ext_axiom(env, "Either.bind_assoc", ei_ext_prop())
1255}
1256/// Applicative ap: Either.ap : {ε α β : Type} → Either ε (α → β) → Either ε α → Either ε β
1257pub fn ei_applicative_ap(env: &mut Environment) -> Result<(), String> {
1258    let ty = ei_ext_forall_abg(ei_ext_dpi(
1259        "ef",
1260        ei_ext_either(ei_ext_bvar(2), ei_ext_arrow(ei_ext_bvar(1), ei_ext_bvar(0))),
1261        ei_ext_dpi(
1262            "ea",
1263            ei_ext_either(ei_ext_bvar(3), ei_ext_bvar(2)),
1264            ei_ext_either(ei_ext_bvar(4), ei_ext_bvar(2)),
1265        ),
1266    ));
1267    ei_ext_axiom(env, "Either.ap", ty)
1268}
1269/// Applicative homomorphism law
1270pub fn ei_applicative_hom(env: &mut Environment) -> Result<(), String> {
1271    ei_ext_axiom(env, "Either.ap_hom", ei_ext_prop())
1272}
1273/// Applicative interchange law
1274pub fn ei_applicative_interchange(env: &mut Environment) -> Result<(), String> {
1275    ei_ext_axiom(env, "Either.ap_interchange", ei_ext_prop())
1276}
1277/// Alternative: first Right wins combinator
1278/// Either.alt : {α β : Type} → Either α β → Either α β → Either α β
1279pub fn ei_alternative_alt(env: &mut Environment) -> Result<(), String> {
1280    let ty = ei_ext_forall_ab(ei_ext_dpi(
1281        "e1",
1282        ei_ext_either(ei_ext_bvar(1), ei_ext_bvar(0)),
1283        ei_ext_dpi(
1284            "e2",
1285            ei_ext_either(ei_ext_bvar(2), ei_ext_bvar(1)),
1286            ei_ext_either(ei_ext_bvar(3), ei_ext_bvar(2)),
1287        ),
1288    ));
1289    ei_ext_axiom(env, "Either.alt", ty)
1290}
1291/// Isomorphism with Result: Either.toResult : {α β : Type} → Either α β → Result β α
1292pub fn ei_iso_result_to(env: &mut Environment) -> Result<(), String> {
1293    let ty = ei_ext_forall_ab(ei_ext_arrow(
1294        ei_ext_either(ei_ext_bvar(1), ei_ext_bvar(0)),
1295        ei_ext_result(ei_ext_bvar(1), ei_ext_bvar(2)),
1296    ));
1297    ei_ext_axiom(env, "Either.toResult", ty)
1298}
1299/// Isomorphism with Result: Either.fromResult : {α β : Type} → Result β α → Either α β
1300pub fn ei_iso_result_from(env: &mut Environment) -> Result<(), String> {
1301    let ty = ei_ext_forall_ab(ei_ext_arrow(
1302        ei_ext_result(ei_ext_bvar(0), ei_ext_bvar(1)),
1303        ei_ext_either(ei_ext_bvar(2), ei_ext_bvar(1)),
1304    ));
1305    ei_ext_axiom(env, "Either.fromResult", ty)
1306}
1307/// Isomorphism with Sum: Either.toSum : {α β : Type} → Either α β → Sum α β
1308pub fn ei_iso_sum_to(env: &mut Environment) -> Result<(), String> {
1309    let ty = ei_ext_forall_ab(ei_ext_arrow(
1310        ei_ext_either(ei_ext_bvar(1), ei_ext_bvar(0)),
1311        ei_ext_sum(ei_ext_bvar(2), ei_ext_bvar(1)),
1312    ));
1313    ei_ext_axiom(env, "Either.toSum", ty)
1314}
1315/// Isomorphism with Sum: Either.fromSum : {α β : Type} → Sum α β → Either α β
1316pub fn ei_iso_sum_from(env: &mut Environment) -> Result<(), String> {
1317    let ty = ei_ext_forall_ab(ei_ext_arrow(
1318        ei_ext_sum(ei_ext_bvar(1), ei_ext_bvar(0)),
1319        ei_ext_either(ei_ext_bvar(2), ei_ext_bvar(1)),
1320    ));
1321    ei_ext_axiom(env, "Either.fromSum", ty)
1322}
1323/// Traversable: traverse over the Right value (propositional)
1324pub fn ei_traversable_traverse(env: &mut Environment) -> Result<(), String> {
1325    ei_ext_axiom(env, "Either.traverse", ei_ext_prop())
1326}
1327/// Traversable law: traverse (pure ∘ f) = pure ∘ map f
1328pub fn ei_traversable_law_pure(env: &mut Environment) -> Result<(), String> {
1329    ei_ext_axiom(env, "Either.traverse_pure", ei_ext_prop())
1330}
1331/// Traversable law: naturality
1332pub fn ei_traversable_law_naturality(env: &mut Environment) -> Result<(), String> {
1333    ei_ext_axiom(env, "Either.traverse_naturality", ei_ext_prop())
1334}
1335/// Foldable: foldl over Either (only folds Right values)
1336/// Either.foldl : {α β γ : Type} → (γ → β → γ) → γ → Either α β → γ
1337pub fn ei_foldable_foldl(env: &mut Environment) -> Result<(), String> {
1338    let ty = ei_ext_forall_abg(ei_ext_dpi(
1339        "f",
1340        ei_ext_arrow(ei_ext_bvar(0), ei_ext_arrow(ei_ext_bvar(1), ei_ext_bvar(1))),
1341        ei_ext_dpi(
1342            "z",
1343            ei_ext_bvar(1),
1344            ei_ext_dpi(
1345                "e",
1346                ei_ext_either(ei_ext_bvar(4), ei_ext_bvar(3)),
1347                ei_ext_bvar(2),
1348            ),
1349        ),
1350    ));
1351    ei_ext_axiom(env, "Either.foldl", ty)
1352}
1353/// Foldable: foldr over Either
1354/// Either.foldr : {α β γ : Type} → (β → γ → γ) → γ → Either α β → γ
1355pub fn ei_foldable_foldr(env: &mut Environment) -> Result<(), String> {
1356    let ty = ei_ext_forall_abg(ei_ext_dpi(
1357        "f",
1358        ei_ext_arrow(ei_ext_bvar(1), ei_ext_arrow(ei_ext_bvar(1), ei_ext_bvar(1))),
1359        ei_ext_dpi(
1360            "z",
1361            ei_ext_bvar(1),
1362            ei_ext_dpi(
1363                "e",
1364                ei_ext_either(ei_ext_bvar(4), ei_ext_bvar(3)),
1365                ei_ext_bvar(2),
1366            ),
1367        ),
1368    ));
1369    ei_ext_axiom(env, "Either.foldr", ty)
1370}
1371/// Profunctor dimap: dimap a function on the left and right sides
1372/// Either.dimap : {α β γ δ : Type} → (γ → α) → (β → δ) → Either α β → Either γ δ
1373pub fn ei_profunctor_dimap(env: &mut Environment) -> Result<(), String> {
1374    let ty = ei_ext_forall_abgd(ei_ext_dpi(
1375        "fl",
1376        ei_ext_arrow(ei_ext_bvar(1), ei_ext_bvar(3)),
1377        ei_ext_dpi(
1378            "fr",
1379            ei_ext_arrow(ei_ext_bvar(3), ei_ext_bvar(1)),
1380            ei_ext_dpi(
1381                "e",
1382                ei_ext_either(ei_ext_bvar(5), ei_ext_bvar(4)),
1383                ei_ext_either(ei_ext_bvar(4), ei_ext_bvar(4)),
1384            ),
1385        ),
1386    ));
1387    ei_ext_axiom(env, "Either.dimap", ty)
1388}
1389/// Partitioning: lefts extracts all left values from a list
1390/// Either.lefts : {α β : Type} → List (Either α β) → List α
1391pub fn ei_partition_lefts(env: &mut Environment) -> Result<(), String> {
1392    let ty = ei_ext_forall_ab(ei_ext_arrow(
1393        ei_ext_list(ei_ext_either(ei_ext_bvar(1), ei_ext_bvar(0))),
1394        ei_ext_list(ei_ext_bvar(2)),
1395    ));
1396    ei_ext_axiom(env, "Either.lefts", ty)
1397}
1398/// Partitioning: rights extracts all right values from a list
1399/// Either.rights : {α β : Type} → List (Either α β) → List β
1400pub fn ei_partition_rights(env: &mut Environment) -> Result<(), String> {
1401    let ty = ei_ext_forall_ab(ei_ext_arrow(
1402        ei_ext_list(ei_ext_either(ei_ext_bvar(1), ei_ext_bvar(0))),
1403        ei_ext_list(ei_ext_bvar(1)),
1404    ));
1405    ei_ext_axiom(env, "Either.rights", ty)
1406}
1407/// Either fold (elimination)
1408/// Either.elim : {α β γ : Type} → (α → γ) → (β → γ) → Either α β → γ
1409pub fn ei_elim(env: &mut Environment) -> Result<(), String> {
1410    let ty = ei_ext_forall_abg(ei_ext_dpi(
1411        "fl",
1412        ei_ext_arrow(ei_ext_bvar(2), ei_ext_bvar(0)),
1413        ei_ext_dpi(
1414            "fr",
1415            ei_ext_arrow(ei_ext_bvar(2), ei_ext_bvar(1)),
1416            ei_ext_dpi(
1417                "e",
1418                ei_ext_either(ei_ext_bvar(4), ei_ext_bvar(3)),
1419                ei_ext_bvar(3),
1420            ),
1421        ),
1422    ));
1423    ei_ext_axiom(env, "Either.elim", ty)
1424}
1425/// Swap involution: swap (swap e) = e
1426pub fn ei_swap_involution(env: &mut Environment) -> Result<(), String> {
1427    let ty = ei_ext_forall_ab(ei_ext_dpi(
1428        "e",
1429        ei_ext_either(ei_ext_bvar(1), ei_ext_bvar(0)),
1430        ei_ext_eq(
1431            ei_ext_either(ei_ext_bvar(3), ei_ext_bvar(2)),
1432            ei_ext_bvar(0),
1433            ei_ext_bvar(0),
1434        ),
1435    ));
1436    ei_ext_axiom(env, "Either.swap_involution", ty)
1437}
1438/// Either as tagged union: tag accessor
1439/// Either.tag : {α β : Type} → Either α β → Bool
1440pub fn ei_tagged_union_tag(env: &mut Environment) -> Result<(), String> {
1441    let ty = ei_ext_forall_ab(ei_ext_arrow(
1442        ei_ext_either(ei_ext_bvar(1), ei_ext_bvar(0)),
1443        ei_ext_cst("Bool"),
1444    ));
1445    ei_ext_axiom(env, "Either.tag", ty)
1446}
1447/// Error handling: catchLeft maps Left to a new Either
1448/// Either.catchLeft : {ε α δ : Type} → Either ε α → (ε → Either δ α) → Either δ α
1449pub fn ei_error_catch_left(env: &mut Environment) -> Result<(), String> {
1450    let ty = ei_ext_forall_abg(ei_ext_dpi(
1451        "e",
1452        ei_ext_either(ei_ext_bvar(2), ei_ext_bvar(1)),
1453        ei_ext_dpi(
1454            "handler",
1455            ei_ext_arrow(
1456                ei_ext_bvar(3),
1457                ei_ext_either(ei_ext_bvar(3), ei_ext_bvar(3)),
1458            ),
1459            ei_ext_either(ei_ext_bvar(3), ei_ext_bvar(4)),
1460        ),
1461    ));
1462    ei_ext_axiom(env, "Either.catchLeft", ty)
1463}
1464/// Commutativity iso: Either α β ≃ Either β α (via swap)
1465pub fn ei_commutativity_iso(env: &mut Environment) -> Result<(), String> {
1466    ei_ext_axiom(env, "Either.comm_iso", ei_ext_prop())
1467}
1468/// Associativity iso: Either (Either α β) γ ≃ Either α (Either β γ)
1469pub fn ei_associativity_iso(env: &mut Environment) -> Result<(), String> {
1470    ei_ext_axiom(env, "Either.assoc_iso", ei_ext_prop())
1471}
1472/// Either.assocLeft : {α β γ : Type} → Either (Either α β) γ → Either α (Either β γ)
1473pub fn ei_assoc_left(env: &mut Environment) -> Result<(), String> {
1474    let either_ab = ei_ext_either(ei_ext_bvar(2), ei_ext_bvar(1));
1475    let ty = ei_ext_forall_abg(ei_ext_dpi(
1476        "e",
1477        ei_ext_either(either_ab, ei_ext_bvar(0)),
1478        ei_ext_either(
1479            ei_ext_bvar(3),
1480            ei_ext_either(ei_ext_bvar(3), ei_ext_bvar(2)),
1481        ),
1482    ));
1483    ei_ext_axiom(env, "Either.assocLeft", ty)
1484}
1485/// Either.assocRight : {α β γ : Type} → Either α (Either β γ) → Either (Either α β) γ
1486pub fn ei_assoc_right(env: &mut Environment) -> Result<(), String> {
1487    let either_bg = ei_ext_either(ei_ext_bvar(1), ei_ext_bvar(0));
1488    let ty = ei_ext_forall_abg(ei_ext_dpi(
1489        "e",
1490        ei_ext_either(ei_ext_bvar(2), either_bg),
1491        ei_ext_either(
1492            ei_ext_either(ei_ext_bvar(4), ei_ext_bvar(3)),
1493            ei_ext_bvar(2),
1494        ),
1495    ));
1496    ei_ext_axiom(env, "Either.assocRight", ty)
1497}
1498/// Either with Void: Either Void β ≃ β
1499/// Either.elimVoidLeft : {β : Type} → Either Void β → β
1500pub fn ei_void_elim_left(env: &mut Environment) -> Result<(), String> {
1501    let ty = ei_ext_ipi(
1502        "β",
1503        ei_ext_type0(),
1504        ei_ext_arrow(
1505            ei_ext_either(ei_ext_cst("Void"), ei_ext_bvar(0)),
1506            ei_ext_bvar(1),
1507        ),
1508    );
1509    ei_ext_axiom(env, "Either.elimVoidLeft", ty)
1510}
1511/// Either.introVoidLeft : {β : Type} → β → Either Void β
1512pub fn ei_void_intro_left(env: &mut Environment) -> Result<(), String> {
1513    let ty = ei_ext_ipi(
1514        "β",
1515        ei_ext_type0(),
1516        ei_ext_arrow(
1517            ei_ext_bvar(0),
1518            ei_ext_either(ei_ext_cst("Void"), ei_ext_bvar(1)),
1519        ),
1520    );
1521    ei_ext_axiom(env, "Either.introVoidLeft", ty)
1522}
1523/// Distributivity over product (propositional)
1524pub fn ei_distrib_over_prod(env: &mut Environment) -> Result<(), String> {
1525    ei_ext_axiom(env, "Either.distrib_prod", ei_ext_prop())
1526}
1527/// Either.distribLeft : {α β γ : Type} → Either α (β × γ) → (Either α β) × (Either α γ)
1528pub fn ei_distrib_left(env: &mut Environment) -> Result<(), String> {
1529    let prod_bg = ei_ext_prod(ei_ext_bvar(1), ei_ext_bvar(0));
1530    let ty = ei_ext_forall_abg(ei_ext_dpi(
1531        "e",
1532        ei_ext_either(ei_ext_bvar(2), prod_bg),
1533        ei_ext_prod(
1534            ei_ext_either(ei_ext_bvar(4), ei_ext_bvar(3)),
1535            ei_ext_either(ei_ext_bvar(4), ei_ext_bvar(3)),
1536        ),
1537    ));
1538    ei_ext_axiom(env, "Either.distribLeft", ty)
1539}
1540/// do-notation bind alias: >>= operator type
1541/// Either.seqBind : {ε α β : Type} → Either ε α → (α → Either ε β) → Either ε β
1542pub fn ei_do_seq_bind(env: &mut Environment) -> Result<(), String> {
1543    let ty = ei_ext_forall_abg(ei_ext_dpi(
1544        "m",
1545        ei_ext_either(ei_ext_bvar(2), ei_ext_bvar(1)),
1546        ei_ext_dpi(
1547            "f",
1548            ei_ext_arrow(
1549                ei_ext_bvar(1),
1550                ei_ext_either(ei_ext_bvar(4), ei_ext_bvar(2)),
1551            ),
1552            ei_ext_either(ei_ext_bvar(4), ei_ext_bvar(2)),
1553        ),
1554    ));
1555    ei_ext_axiom(env, "Either.seqBind", ty)
1556}
1557/// Kleisli composition: (>=>) for Either monad
1558/// Either.kleisliComp : {ε α β γ : Type} →
1559///   (α → Either ε β) → (β → Either ε γ) → α → Either ε γ
1560pub fn ei_kleisli_comp(env: &mut Environment) -> Result<(), String> {
1561    let ty = ei_ext_forall_abgd(ei_ext_dpi(
1562        "f",
1563        ei_ext_arrow(
1564            ei_ext_bvar(3),
1565            ei_ext_either(ei_ext_bvar(3), ei_ext_bvar(2)),
1566        ),
1567        ei_ext_dpi(
1568            "g",
1569            ei_ext_arrow(
1570                ei_ext_bvar(3),
1571                ei_ext_either(ei_ext_bvar(4), ei_ext_bvar(2)),
1572            ),
1573            ei_ext_arrow(
1574                ei_ext_bvar(5),
1575                ei_ext_either(ei_ext_bvar(5), ei_ext_bvar(3)),
1576            ),
1577        ),
1578    ));
1579    ei_ext_axiom(env, "Either.kleisliComp", ty)
1580}
1581/// Kleisli identity law
1582pub fn ei_kleisli_id(env: &mut Environment) -> Result<(), String> {
1583    ei_ext_axiom(env, "Either.kleisli_id_law", ei_ext_prop())
1584}
1585/// EitherT monad transformer: run function type (propositional)
1586pub fn ei_eithert_run(env: &mut Environment) -> Result<(), String> {
1587    ei_ext_axiom(env, "EitherT.run", ei_ext_prop())
1588}
1589/// EitherT.lift (propositional)
1590pub fn ei_eithert_lift(env: &mut Environment) -> Result<(), String> {
1591    ei_ext_axiom(env, "EitherT.lift", ei_ext_prop())
1592}
1593/// EitherT.bind (propositional)
1594pub fn ei_eithert_bind(env: &mut Environment) -> Result<(), String> {
1595    ei_ext_axiom(env, "EitherT.bind", ei_ext_prop())
1596}
1597/// Either.sequenceList : {α β : Type} → List (Either α β) → Either α (List β)
1598pub fn ei_sequence_list(env: &mut Environment) -> Result<(), String> {
1599    let ty = ei_ext_forall_ab(ei_ext_arrow(
1600        ei_ext_list(ei_ext_either(ei_ext_bvar(1), ei_ext_bvar(0))),
1601        ei_ext_either(ei_ext_bvar(2), ei_ext_list(ei_ext_bvar(1))),
1602    ));
1603    ei_ext_axiom(env, "Either.sequenceList", ty)
1604}
1605/// Either.traverseList : {ε α β : Type} → (α → Either ε β) → List α → Either ε (List β)
1606pub fn ei_traverse_list(env: &mut Environment) -> Result<(), String> {
1607    let ty = ei_ext_forall_abg(ei_ext_dpi(
1608        "f",
1609        ei_ext_arrow(
1610            ei_ext_bvar(1),
1611            ei_ext_either(ei_ext_bvar(2), ei_ext_bvar(1)),
1612        ),
1613        ei_ext_dpi(
1614            "xs",
1615            ei_ext_list(ei_ext_bvar(2)),
1616            ei_ext_either(ei_ext_bvar(4), ei_ext_list(ei_ext_bvar(2))),
1617        ),
1618    ));
1619    ei_ext_axiom(env, "Either.traverseList", ty)
1620}
1621/// Either select combinator (Selective)
1622/// Either.select : {ε α β : Type} → Either ε α → Either ε (α → β) → Either ε β
1623pub fn ei_select_combinator(env: &mut Environment) -> Result<(), String> {
1624    let ty = ei_ext_forall_abg(ei_ext_dpi(
1625        "e",
1626        ei_ext_either(ei_ext_bvar(2), ei_ext_bvar(1)),
1627        ei_ext_dpi(
1628            "f",
1629            ei_ext_either(ei_ext_bvar(3), ei_ext_arrow(ei_ext_bvar(2), ei_ext_bvar(1))),
1630            ei_ext_either(ei_ext_bvar(4), ei_ext_bvar(2)),
1631        ),
1632    ));
1633    ei_ext_axiom(env, "Either.select", ty)
1634}
1635/// Select law: select (Right x) f = map ($ x) f
1636pub fn ei_select_law_right(env: &mut Environment) -> Result<(), String> {
1637    ei_ext_axiom(env, "Either.select_right_law", ei_ext_prop())
1638}
1639/// Select law: select (Left e) (Left h) = Left e
1640pub fn ei_select_law_left(env: &mut Environment) -> Result<(), String> {
1641    ei_ext_axiom(env, "Either.select_left_law", ei_ext_prop())
1642}
1643/// Nat-indexed sum type via iterated Either
1644/// Either.natSum : Nat → Type → Type → Type
1645pub fn ei_nat_sum(env: &mut Environment) -> Result<(), String> {
1646    let ty = ei_ext_arrow(
1647        ei_ext_nat_ty(),
1648        ei_ext_arrow(ei_ext_type0(), ei_ext_arrow(ei_ext_type0(), ei_ext_type0())),
1649    );
1650    ei_ext_axiom(env, "Either.natSum", ty)
1651}
1652/// Either.mapBoth : {α β γ : Type} → (α → γ) → (β → γ) → Either α β → γ
1653pub fn ei_map_both(env: &mut Environment) -> Result<(), String> {
1654    let ty = ei_ext_forall_abg(ei_ext_dpi(
1655        "fl",
1656        ei_ext_arrow(ei_ext_bvar(2), ei_ext_bvar(0)),
1657        ei_ext_dpi(
1658            "fr",
1659            ei_ext_arrow(ei_ext_bvar(2), ei_ext_bvar(1)),
1660            ei_ext_dpi(
1661                "e",
1662                ei_ext_either(ei_ext_bvar(4), ei_ext_bvar(3)),
1663                ei_ext_bvar(3),
1664            ),
1665        ),
1666    ));
1667    ei_ext_axiom(env, "Either.mapBoth", ty)
1668}
1669/// Either.joinWith : {α β : Type} → (α → β) → Either α β → β
1670pub fn ei_join_with(env: &mut Environment) -> Result<(), String> {
1671    let ty = ei_ext_forall_ab(ei_ext_dpi(
1672        "f",
1673        ei_ext_arrow(ei_ext_bvar(1), ei_ext_bvar(0)),
1674        ei_ext_dpi(
1675            "e",
1676            ei_ext_either(ei_ext_bvar(2), ei_ext_bvar(1)),
1677            ei_ext_bvar(1),
1678        ),
1679    ));
1680    ei_ext_axiom(env, "Either.joinWith", ty)
1681}