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