Skip to main content

oxilean_std/option/
functions.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use crate::env_builder::{app, sort, var, EnvBuilder};
6use oxilean_kernel::{BinderInfo, Declaration, Environment, Level};
7use oxilean_kernel::{Expr, Name};
8
9use super::types::{OptionCache, OptionIter, OptionMap, OptionMemo, WeightedOption};
10
11/// Register the core `Option` declarations into the environment.
12///
13/// Adds `Option`, `Option.none`, `Option.some`, and `Option.rec`.
14pub fn build_option_env(env: &mut EnvBuilder) {
15    env.add_axiom(Name::from_str("Option"), sort(1));
16    env.add_axiom(Name::from_str("Option.none"), sort(1));
17    env.add_axiom(Name::from_str("Option.some"), sort(1));
18    env.add_axiom(Name::from_str("Option.rec"), sort(1));
19}
20/// Register `Option.getD` (get-with-default) into the environment.
21///
22/// `Option.getD : Option α → α → α`
23pub fn build_option_getd(env: &mut EnvBuilder) {
24    env.add_axiom(Name::from_str("Option.getD"), sort(1));
25}
26/// Register `Option.map` into the environment.
27///
28/// `Option.map : (α → β) → Option α → Option β`
29pub fn build_option_map(env: &mut EnvBuilder) {
30    env.add_axiom(Name::from_str("Option.map"), sort(1));
31}
32/// Register `Option.bind` (monadic bind) into the environment.
33///
34/// `Option.bind : Option α → (α → Option β) → Option β`
35pub fn build_option_bind(env: &mut EnvBuilder) {
36    env.add_axiom(Name::from_str("Option.bind"), sort(1));
37}
38/// Register `Option.filter` into the environment.
39pub fn build_option_filter(env: &mut EnvBuilder) {
40    env.add_axiom(Name::from_str("Option.filter"), sort(1));
41}
42/// Register the full `Option` API into the environment in one call.
43pub fn build_full_option_env(env: &mut EnvBuilder) {
44    build_option_env(env);
45    build_option_getd(env);
46    build_option_map(env);
47    build_option_bind(env);
48    build_option_filter(env);
49    env.add_axiom(Name::from_str("Option.isSome"), sort(1));
50    env.add_axiom(Name::from_str("Option.isNone"), sort(1));
51    env.add_axiom(Name::from_str("Option.get!"), sort(1));
52    env.add_axiom(Name::from_str("Option.toList"), sort(1));
53    env.add_axiom(Name::from_str("Option.all"), sort(1));
54    env.add_axiom(Name::from_str("Option.any"), sort(1));
55    env.add_axiom(Name::from_str("Option.zip"), sort(1));
56    env.add_axiom(Name::from_str("Option.unzip"), sort(1));
57    env.add_axiom(Name::from_str("Option.sequence"), sort(1));
58}
59/// Build `Option.some x`.
60pub fn mk_some(x: Expr) -> Expr {
61    app(var("Option.some"), x)
62}
63/// Build `Option.none`.
64pub fn mk_none() -> Expr {
65    var("Option.none")
66}
67/// Build `Option.getD opt default`.
68pub fn mk_option_getd(opt: Expr, default: Expr) -> Expr {
69    app(app(var("Option.getD"), opt), default)
70}
71/// Build `Option.map f opt`.
72pub fn mk_option_map(f: Expr, opt: Expr) -> Expr {
73    app(app(var("Option.map"), f), opt)
74}
75/// Build `Option.bind opt f`.
76pub fn mk_option_bind(opt: Expr, f: Expr) -> Expr {
77    app(app(var("Option.bind"), opt), f)
78}
79/// Return `default` if `opt` is `None`, otherwise apply `f` to the value.
80pub fn option_get_or_else<T, U>(opt: Option<T>, default: U, f: impl FnOnce(T) -> U) -> U {
81    match opt {
82        Some(x) => f(x),
83        None => default,
84    }
85}
86/// Convert `Option<T>` to `Option<U>` by applying `f` only when `Some`.
87pub fn option_map<T, U>(opt: Option<T>, f: impl FnOnce(T) -> U) -> Option<U> {
88    opt.map(f)
89}
90/// Monadic bind: apply `f` to the inner value if `Some`.
91pub fn option_bind<T, U>(opt: Option<T>, f: impl FnOnce(T) -> Option<U>) -> Option<U> {
92    opt.and_then(f)
93}
94/// Applicative apply: if both `f` and `a` are `Some`, apply `f` to `a`.
95pub fn option_ap<T, U>(f: Option<impl FnOnce(T) -> U>, a: Option<T>) -> Option<U> {
96    match (f, a) {
97        (Some(func), Some(val)) => Some(func(val)),
98        _ => None,
99    }
100}
101/// Lift a binary function into `Option`.
102pub fn option_lift2<A, B, C>(f: impl FnOnce(A, B) -> C, a: Option<A>, b: Option<B>) -> Option<C> {
103    match (a, b) {
104        (Some(x), Some(y)) => Some(f(x, y)),
105        _ => None,
106    }
107}
108/// Lift a ternary function into `Option`.
109pub fn option_lift3<A, B, C, D>(
110    f: impl FnOnce(A, B, C) -> D,
111    a: Option<A>,
112    b: Option<B>,
113    c: Option<C>,
114) -> Option<D> {
115    match (a, b, c) {
116        (Some(x), Some(y), Some(z)) => Some(f(x, y, z)),
117        _ => None,
118    }
119}
120/// Zip two options into an option of a pair.
121pub fn option_zip<A, B>(a: Option<A>, b: Option<B>) -> Option<(A, B)> {
122    match (a, b) {
123        (Some(x), Some(y)) => Some((x, y)),
124        _ => None,
125    }
126}
127/// Unzip an `Option<(A, B)>` into `(Option<A>, Option<B>)`.
128pub fn option_unzip<A, B>(p: Option<(A, B)>) -> (Option<A>, Option<B>) {
129    match p {
130        Some((a, b)) => (Some(a), Some(b)),
131        None => (None, None),
132    }
133}
134/// Convert `Option<Option<T>>` to `Option<T>` (join/flatten).
135pub fn option_join<T>(opt: Option<Option<T>>) -> Option<T> {
136    opt.flatten()
137}
138/// Sequence a list of options — `None` if any is `None`.
139pub fn option_sequence<T>(opts: Vec<Option<T>>) -> Option<Vec<T>> {
140    let mut result = Vec::with_capacity(opts.len());
141    for opt in opts {
142        result.push(opt?);
143    }
144    Some(result)
145}
146/// Traverse a slice with a fallible function, collecting results.
147///
148/// Returns `None` if any call returns `None`.
149pub fn option_traverse<A, B>(xs: &[A], f: impl Fn(&A) -> Option<B>) -> Option<Vec<B>> {
150    let mut result = Vec::with_capacity(xs.len());
151    for x in xs {
152        result.push(f(x)?);
153    }
154    Some(result)
155}
156/// Return the first `Some` value from an iterator of options.
157pub fn option_first<T>(opts: impl IntoIterator<Item = Option<T>>) -> Option<T> {
158    opts.into_iter().flatten().next()
159}
160/// Return the last `Some` value from a vector of options.
161pub fn option_last<T>(opts: Vec<Option<T>>) -> Option<T> {
162    opts.into_iter().flatten().last()
163}
164/// Collect all `Some` values from an iterator, ignoring `None`.
165pub fn option_catsome<T>(opts: impl IntoIterator<Item = Option<T>>) -> Vec<T> {
166    opts.into_iter().flatten().collect()
167}
168/// Retry `f` up to `n` times until it returns `Some`.
169pub fn option_retry<T>(n: usize, mut f: impl FnMut() -> Option<T>) -> Option<T> {
170    for _ in 0..n {
171        if let Some(v) = f() {
172            return Some(v);
173        }
174    }
175    None
176}
177/// Transpose `Option<Result<T, E>>` to `Result<Option<T>, E>`.
178pub fn option_transpose<T, E>(opt: Option<Result<T, E>>) -> Result<Option<T>, E> {
179    match opt {
180        Some(Ok(v)) => Ok(Some(v)),
181        Some(Err(e)) => Err(e),
182        None => Ok(None),
183    }
184}
185/// Combine two options: if both are `Some`, apply `f`; otherwise return the other.
186pub fn option_combine<T: Clone>(
187    a: Option<T>,
188    b: Option<T>,
189    f: impl FnOnce(T, T) -> T,
190) -> Option<T> {
191    match (a, b) {
192        (Some(x), Some(y)) => Some(f(x, y)),
193        (Some(x), None) => Some(x),
194        (None, Some(y)) => Some(y),
195        (None, None) => None,
196    }
197}
198/// Alternative operator: return `a` if `Some`, else `b`.
199pub fn option_alt<T>(a: Option<T>, b: Option<T>) -> Option<T> {
200    a.or(b)
201}
202/// Filter an option by a predicate.
203pub fn option_filter<T>(opt: Option<T>, pred: impl FnOnce(&T) -> bool) -> Option<T> {
204    opt.filter(pred)
205}
206/// Convert an option to a `Result<T, E>` with a given error.
207pub fn option_ok_or<T, E>(opt: Option<T>, err: E) -> Result<T, E> {
208    opt.ok_or(err)
209}
210/// Convert a `Result<T, E>` to `Option<T>`, discarding errors.
211pub fn result_to_option<T, E>(r: Result<T, E>) -> Option<T> {
212    r.ok()
213}
214/// Apply a side effect to the inner value if `Some`.
215pub fn option_tap<T: Clone>(opt: &Option<T>, f: impl FnOnce(&T)) -> &Option<T> {
216    if let Some(ref v) = opt {
217        f(v);
218    }
219    opt
220}
221/// Convert `Option<&str>` to `Option<String>`.
222pub fn option_to_owned(opt: Option<&str>) -> Option<String> {
223    opt.map(|s| s.to_owned())
224}
225/// Convert `&Option<T>` to `Option<&T>`.
226pub fn option_as_ref<T>(opt: &Option<T>) -> Option<&T> {
227    opt.as_ref()
228}
229/// Partition a vector of options into (somes, count_of_nones).
230pub fn option_partition<T>(opts: Vec<Option<T>>) -> (Vec<T>, usize) {
231    let mut somes = Vec::new();
232    let mut none_count = 0;
233    for opt in opts {
234        match opt {
235            Some(v) => somes.push(v),
236            None => none_count += 1,
237        }
238    }
239    (somes, none_count)
240}
241/// Fold over an option.
242///
243/// If `None`, returns `init`. If `Some(x)`, returns `f(init, x)`.
244pub fn option_fold<T, U>(opt: Option<T>, init: U, f: impl FnOnce(U, T) -> U) -> U {
245    match opt {
246        Some(x) => f(init, x),
247        None => init,
248    }
249}
250/// Return a default value if the option is `None`, consuming it.
251pub fn option_unwrap_or_default<T: Default>(opt: Option<T>) -> T {
252    opt.unwrap_or_default()
253}
254/// Count `Some` values in an iterator of options.
255pub fn option_count_some<T>(opts: impl IntoIterator<Item = Option<T>>) -> usize {
256    opts.into_iter().filter(|o| o.is_some()).count()
257}
258/// Build a `Some` chain from a vector: the first `Some` wins.
259pub fn option_find<T>(candidates: Vec<T>, pred: impl Fn(&T) -> bool) -> Option<T> {
260    candidates.into_iter().find(|x| pred(x))
261}
262/// Pair each element of a slice with its option-mapped value.
263pub fn option_annotate<A: Clone, B>(xs: &[A], f: impl Fn(&A) -> Option<B>) -> Vec<(A, Option<B>)> {
264    xs.iter().map(|x| (x.clone(), f(x))).collect()
265}
266#[cfg(test)]
267mod tests {
268    use super::*;
269    #[test]
270    fn test_option_map() {
271        assert_eq!(option_map(Some(2u32), |x| x * 3), Some(6));
272        assert_eq!(option_map::<u32, u32>(None, |x| x * 3), None);
273    }
274    #[test]
275    fn test_option_bind() {
276        let f = |x: u32| if x > 2 { Some(x * 10) } else { None };
277        assert_eq!(option_bind(Some(5), f), Some(50));
278        assert_eq!(option_bind(Some(1), f), None);
279        assert_eq!(option_bind(None, f), None);
280    }
281    #[test]
282    fn test_option_lift2() {
283        let r = option_lift2(|a: u32, b: u32| a + b, Some(3), Some(4));
284        assert_eq!(r, Some(7));
285        let r2 = option_lift2(|a: u32, b: u32| a + b, None, Some(4));
286        assert_eq!(r2, None);
287    }
288    #[test]
289    fn test_option_zip() {
290        assert_eq!(option_zip(Some(1u32), Some("a")), Some((1, "a")));
291        assert_eq!(option_zip::<u32, &str>(None, Some("a")), None);
292    }
293    #[test]
294    fn test_option_unzip() {
295        let (a, b) = option_unzip(Some((1u32, 2u32)));
296        assert_eq!(a, Some(1));
297        assert_eq!(b, Some(2));
298        let (c, d) = option_unzip::<u32, u32>(None);
299        assert_eq!(c, None);
300        assert_eq!(d, None);
301    }
302    #[test]
303    fn test_option_sequence() {
304        let v = vec![Some(1u32), Some(2), Some(3)];
305        assert_eq!(option_sequence(v), Some(vec![1, 2, 3]));
306        let v2 = vec![Some(1u32), None, Some(3)];
307        assert_eq!(option_sequence(v2), None);
308    }
309    #[test]
310    fn test_option_traverse() {
311        let xs = vec![2u32, 4, 6];
312        let r = option_traverse(&xs, |x| if x % 2 == 0 { Some(x / 2) } else { None });
313        assert_eq!(r, Some(vec![1, 2, 3]));
314        let xs2 = vec![2u32, 3, 6];
315        let r2 = option_traverse(&xs2, |x| if x % 2 == 0 { Some(x / 2) } else { None });
316        assert_eq!(r2, None);
317    }
318    #[test]
319    fn test_option_catsome() {
320        let opts = vec![Some(1u32), None, Some(3), None, Some(5)];
321        assert_eq!(option_catsome(opts), vec![1, 3, 5]);
322    }
323    #[test]
324    fn test_option_retry() {
325        let mut count = 0u32;
326        let result = option_retry(5, || {
327            count += 1;
328            if count >= 3 {
329                Some(count)
330            } else {
331                None
332            }
333        });
334        assert_eq!(result, Some(3));
335    }
336    #[test]
337    fn test_option_combine() {
338        let r = option_combine(Some(3u32), Some(5), |a, b| a + b);
339        assert_eq!(r, Some(8));
340        let r2 = option_combine(Some(3u32), None, |a, b| a + b);
341        assert_eq!(r2, Some(3));
342        let r3 = option_combine::<u32>(None, None, |a, b| a + b);
343        assert_eq!(r3, None);
344    }
345    #[test]
346    fn test_option_partition() {
347        let opts = vec![Some(1u32), None, Some(3), None];
348        let (somes, none_count) = option_partition(opts);
349        assert_eq!(somes, vec![1, 3]);
350        assert_eq!(none_count, 2);
351    }
352    #[test]
353    fn test_option_fold() {
354        let r = option_fold(Some(5u32), 0u32, |acc, x| acc + x);
355        assert_eq!(r, 5);
356        let r2 = option_fold::<u32, u32>(None, 42, |acc, x| acc + x);
357        assert_eq!(r2, 42);
358    }
359    #[test]
360    fn test_option_first_last() {
361        let opts = vec![None, Some(2u32), Some(3)];
362        assert_eq!(option_first(opts.clone()), Some(2));
363        assert_eq!(option_last(opts), Some(3));
364    }
365    #[test]
366    fn test_option_map_struct() {
367        let mut map: OptionMap<&str, u32> = OptionMap::new();
368        map.set("a", Some(1));
369        map.set("b", None);
370        assert_eq!(map.get(&"a"), Some(&1));
371        assert_eq!(map.get(&"b"), None);
372        assert_eq!(map.some_keys(), vec![&"a"]);
373        assert_eq!(map.none_keys(), vec![&"b"]);
374    }
375    #[test]
376    fn test_option_join() {
377        let nested: Option<Option<u32>> = Some(Some(42));
378        assert_eq!(option_join(nested), Some(42));
379        let nested2: Option<Option<u32>> = Some(None);
380        assert_eq!(option_join(nested2), None);
381    }
382    #[test]
383    fn test_option_transpose() {
384        let r: Option<Result<u32, &str>> = Some(Ok(5));
385        assert_eq!(option_transpose(r), Ok(Some(5)));
386        let r2: Option<Result<u32, &str>> = Some(Err("err"));
387        assert!(option_transpose(r2).is_err());
388        let r3: Option<Result<u32, &str>> = None;
389        assert_eq!(option_transpose(r3), Ok(None));
390    }
391}
392/// Extension methods for `Option<T>`.
393///
394/// Provides a fluent API for common option operations used throughout the
395/// OxiLean elaborator.
396#[allow(clippy::wrong_self_convention)]
397pub trait OptionExt<T> {
398    /// Map with a fallible function.
399    fn try_map<U, E>(self, f: impl FnOnce(T) -> Result<U, E>) -> Result<Option<U>, E>;
400    /// Return `true` if `Some` and the predicate holds.
401    fn is_some_and(self, pred: impl FnOnce(&T) -> bool) -> bool;
402    /// Return `true` if `None` or the predicate holds.
403    fn is_none_or(self, pred: impl FnOnce(&T) -> bool) -> bool;
404    /// Chain two options: if `self` is `Some`, also check `other`.
405    fn and_also(self, other: Option<T>) -> Option<(T, T)>;
406    /// Inspect the inner value without consuming.
407    fn inspect_opt(self, f: impl FnOnce(&T)) -> Self;
408    /// Convert to a vector (0 or 1 elements).
409    fn to_vec(self) -> Vec<T>;
410    /// Unwrap with a custom panic message.
411    fn expect_msg(self, msg: &str) -> T;
412}
413impl<T> OptionExt<T> for Option<T> {
414    fn try_map<U, E>(self, f: impl FnOnce(T) -> Result<U, E>) -> Result<Option<U>, E> {
415        match self {
416            Some(v) => f(v).map(Some),
417            None => Ok(None),
418        }
419    }
420    fn is_some_and(self, pred: impl FnOnce(&T) -> bool) -> bool {
421        match self {
422            Some(ref v) => pred(v),
423            None => false,
424        }
425    }
426    fn is_none_or(self, pred: impl FnOnce(&T) -> bool) -> bool {
427        match self {
428            Some(ref v) => pred(v),
429            None => true,
430        }
431    }
432    fn and_also(self, other: Option<T>) -> Option<(T, T)> {
433        match (self, other) {
434            (Some(a), Some(b)) => Some((a, b)),
435            _ => None,
436        }
437    }
438    fn inspect_opt(self, f: impl FnOnce(&T)) -> Self {
439        if let Some(ref v) = self {
440            f(v);
441        }
442        self
443    }
444    fn to_vec(self) -> Vec<T> {
445        match self {
446            Some(v) => vec![v],
447            None => vec![],
448        }
449    }
450    fn expect_msg(self, msg: &str) -> T {
451        match self {
452            Some(v) => v,
453            None => panic!("{msg}"),
454        }
455    }
456}
457/// Look up a key in multiple maps (slices), returning the first match.
458pub fn option_chain_lookup<'a, K: PartialEq, V>(key: &K, maps: &[&'a [(K, V)]]) -> Option<&'a V> {
459    for map in maps {
460        if let Some(v) = map.iter().find(|(k, _)| k == key).map(|(_, v)| v) {
461            return Some(v);
462        }
463    }
464    None
465}
466/// Apply a series of transformations, stopping at the first `Some`.
467pub fn option_first_of<T>(value: T, transforms: &[&dyn Fn(T) -> (T, Option<T>)]) -> Option<T>
468where
469    T: Clone,
470{
471    let mut current = value;
472    for transform in transforms {
473        let (next, result) = transform(current.clone());
474        if result.is_some() {
475            return result;
476        }
477        current = next;
478    }
479    None
480}
481/// Select the best option from a list of weighted options.
482pub fn best_option<T>(options: Vec<WeightedOption<T>>) -> Option<T> {
483    options
484        .into_iter()
485        .filter(|o| o.value.is_some())
486        .max_by(|a, b| {
487            a.weight
488                .partial_cmp(&b.weight)
489                .unwrap_or(std::cmp::Ordering::Equal)
490        })
491        .and_then(|o| o.value)
492}
493#[cfg(test)]
494mod extra_tests {
495    use super::*;
496    #[test]
497    fn test_option_ext_try_map() {
498        let r: Result<Option<u32>, &str> = Some(5u32).try_map(|x| Ok::<u32, &str>(x * 2));
499        assert_eq!(r, Ok(Some(10)));
500        let r2: Result<Option<u32>, &str> = None.try_map(|x: u32| Ok::<u32, &str>(x * 2));
501        assert_eq!(r2, Ok(None));
502    }
503    #[test]
504    fn test_option_ext_is_some_and() {
505        assert!(Some(5u32).is_some_and(|x| x > 3));
506        assert!(!Some(2u32).is_some_and(|x| x > 3));
507        assert!(!None::<u32>.is_some_and(|x| x > 3));
508    }
509    #[test]
510    fn test_option_ext_to_vec() {
511        assert_eq!(Some(42u32).to_vec(), vec![42]);
512        assert_eq!(None::<u32>.to_vec(), Vec::<u32>::new());
513    }
514    #[test]
515    fn test_option_ext_and_also() {
516        let r = Some(1u32).and_also(Some(2u32));
517        assert_eq!(r, Some((1, 2)));
518        let r2 = None::<u32>.and_also(Some(2));
519        assert!(r2.is_none());
520    }
521    #[test]
522    fn test_weighted_option() {
523        let opts = vec![
524            WeightedOption::some(0.8, "high"),
525            WeightedOption::some(0.5, "low"),
526            WeightedOption::none(0.9),
527        ];
528        let best = best_option(opts);
529        assert_eq!(best, Some("high"));
530    }
531    #[test]
532    fn test_option_cache() {
533        let mut cache: OptionCache<&str, u32> = OptionCache::new();
534        let v = cache.get_or_insert_with("a", || Some(42));
535        assert_eq!(v, Some(42));
536        let v2 = cache.get_or_insert_with("a", || Some(999));
537        assert_eq!(v2, Some(42));
538        assert_eq!(cache.len(), 1);
539    }
540    #[test]
541    fn test_option_cache_invalidate() {
542        let mut cache: OptionCache<&str, u32> = OptionCache::new();
543        cache.insert("x", Some(10));
544        cache.invalidate(&"x");
545        assert!(cache.is_empty());
546    }
547    #[test]
548    fn test_option_count_some() {
549        let opts = vec![Some(1u32), None, Some(3), None, Some(5)];
550        assert_eq!(option_count_some(opts), 3);
551    }
552    #[test]
553    fn test_option_annotate() {
554        let xs = vec![2u32, 3, 4];
555        let ann = option_annotate(&xs, |x| if x % 2 == 0 { Some(*x / 2) } else { None });
556        assert_eq!(ann[0].1, Some(1));
557        assert_eq!(ann[1].1, None);
558        assert_eq!(ann[2].1, Some(2));
559    }
560    #[test]
561    fn test_option_find() {
562        let v = vec![1u32, 2, 3, 4, 5];
563        let found = option_find(v, |x| *x > 3);
564        assert_eq!(found, Some(4));
565    }
566}
567/// Convert `Option<T>` to `Result<T, &'static str>`.
568#[allow(dead_code)]
569pub fn option_to_result<T>(opt: Option<T>, msg: &'static str) -> Result<T, &'static str> {
570    opt.ok_or(msg)
571}
572/// Convert `Result<T, E>` to `Option<T>`.
573#[allow(dead_code)]
574pub fn result_ok<T, E>(r: Result<T, E>) -> Option<T> {
575    r.ok()
576}
577/// Apply `f` and `g` to the inner value if `Some`, returning `Some` only if both succeed.
578#[allow(dead_code)]
579pub fn option_both<T: Clone, U, V>(
580    opt: Option<T>,
581    f: impl FnOnce(T) -> Option<U>,
582    g: impl FnOnce(T) -> Option<V>,
583) -> Option<(U, V)> {
584    let v = opt?;
585    let u = f(v.clone())?;
586    let w = g(v)?;
587    Some((u, w))
588}
589/// Return `Some(default)` if `opt` is `None`.
590#[allow(dead_code)]
591pub fn option_or_default<T: Default>(opt: Option<T>) -> Option<T> {
592    Some(opt.unwrap_or_default())
593}
594/// Unwrap or compute a fallback using a closure.
595#[allow(dead_code)]
596pub fn option_unwrap_or_compute<T>(opt: Option<T>, f: impl FnOnce() -> T) -> T {
597    opt.unwrap_or_else(f)
598}
599/// Map over an option, threading an accumulator.
600#[allow(dead_code)]
601pub fn option_map_with<T, U, S>(
602    opt: Option<T>,
603    state: S,
604    f: impl FnOnce(S, T) -> (S, U),
605) -> (S, Option<U>) {
606    match opt {
607        Some(v) => {
608            let (s2, u) = f(state, v);
609            (s2, Some(u))
610        }
611        None => (state, None),
612    }
613}
614/// Build an `Option` from a boolean: `true` → `Some(value)`, `false` → `None`.
615#[allow(dead_code)]
616pub fn option_from_bool<T>(cond: bool, value: T) -> Option<T> {
617    if cond {
618        Some(value)
619    } else {
620        None
621    }
622}
623/// Build an `Option<T>` by calling `f` and catching panics.
624#[allow(dead_code)]
625pub fn option_catch<T>(f: impl FnOnce() -> T + std::panic::UnwindSafe) -> Option<T> {
626    std::panic::catch_unwind(f).ok()
627}
628/// Zip three options.
629#[allow(dead_code)]
630pub fn option_zip3<A, B, C>(a: Option<A>, b: Option<B>, c: Option<C>) -> Option<(A, B, C)> {
631    match (a, b, c) {
632        (Some(x), Some(y), Some(z)) => Some((x, y, z)),
633        _ => None,
634    }
635}
636/// Swap the type inside an `Option<(A, B)>`.
637#[allow(dead_code)]
638pub fn option_swap<A, B>(opt: Option<(A, B)>) -> Option<(B, A)> {
639    opt.map(|(a, b)| (b, a))
640}
641/// Return the value inside an `Option`, or insert `value` if `None`.
642#[allow(dead_code)]
643pub fn option_get_or_insert<T: Clone>(opt: &mut Option<T>, value: T) -> T {
644    if opt.is_none() {
645        *opt = Some(value.clone());
646        value
647    } else {
648        opt.clone().expect("opt is Some: checked by is_none guard")
649    }
650}
651/// A simple priority-based option selection.
652///
653/// Returns the first `Some` that has a priority greater than or equal to `min_priority`.
654#[allow(dead_code)]
655pub fn option_select_priority<T>(
656    candidates: Vec<(u32, Option<T>)>,
657    min_priority: u32,
658) -> Option<T> {
659    candidates
660        .into_iter()
661        .filter(|(p, o)| *p >= min_priority && o.is_some())
662        .max_by_key(|(p, _)| *p)
663        .and_then(|(_, o)| o)
664}
665#[cfg(test)]
666mod option_bridge_tests {
667    use super::*;
668    #[test]
669    fn test_option_to_result_some() {
670        let r = option_to_result(Some(5u32), "missing");
671        assert_eq!(r, Ok(5));
672    }
673    #[test]
674    fn test_option_to_result_none() {
675        let r = option_to_result(None::<u32>, "missing");
676        assert_eq!(r, Err("missing"));
677    }
678    #[test]
679    fn test_option_iter_some() {
680        let iter = OptionIter::new(Some(42u32));
681        let v: Vec<u32> = iter.collect();
682        assert_eq!(v, vec![42]);
683    }
684    #[test]
685    fn test_option_iter_none() {
686        let iter = OptionIter::<u32>::new(None);
687        let v: Vec<u32> = iter.collect();
688        assert!(v.is_empty());
689    }
690    #[test]
691    fn test_option_zip3() {
692        assert_eq!(
693            option_zip3(Some(1u32), Some(2u32), Some(3u32)),
694            Some((1, 2, 3))
695        );
696        assert!(option_zip3(Some(1u32), None::<u32>, Some(3u32)).is_none());
697    }
698    #[test]
699    fn test_option_swap() {
700        let swapped = option_swap(Some((1u32, "hello")));
701        assert_eq!(swapped, Some(("hello", 1)));
702    }
703    #[test]
704    fn test_option_from_bool() {
705        assert_eq!(option_from_bool(true, 42u32), Some(42));
706        assert_eq!(option_from_bool(false, 42u32), None);
707    }
708    #[test]
709    fn test_option_select_priority() {
710        let candidates = vec![(1, Some(10u32)), (3, Some(30)), (2, Some(20))];
711        let v = option_select_priority(candidates, 2);
712        assert_eq!(v, Some(30));
713    }
714    #[test]
715    fn test_option_select_priority_none_below_min() {
716        let candidates = vec![(1, Some(10u32))];
717        let v = option_select_priority(candidates, 2);
718        assert!(v.is_none());
719    }
720    #[test]
721    fn test_option_memo() {
722        let mut memo: OptionMemo<u32, String> = OptionMemo::new();
723        let v = memo.get_or_compute(5, |k| Some(k.to_string()));
724        assert_eq!(v, Some("5".to_string()));
725        let v2 = memo.get_or_compute(5, |_| panic!("should not be called"));
726        assert_eq!(v2, Some("5".to_string()));
727        assert_eq!(memo.len(), 1);
728    }
729    #[test]
730    fn test_option_memo_clear() {
731        let mut memo: OptionMemo<u32, u32> = OptionMemo::new();
732        memo.get_or_compute(1, |k| Some(*k));
733        memo.clear();
734        assert!(memo.is_empty());
735    }
736    #[test]
737    fn test_option_both() {
738        let r = option_both(
739            Some(5u32),
740            |x| if x > 3 { Some(x * 2) } else { None },
741            |x| Some(x + 1),
742        );
743        assert_eq!(r, Some((10, 6)));
744        let r2 = option_both(
745            Some(1u32),
746            |x| if x > 3 { Some(x * 2) } else { None },
747            |x| Some(x + 1),
748        );
749        assert!(r2.is_none());
750    }
751    #[test]
752    fn test_option_map_with() {
753        let (state, result) = option_map_with(Some(5u32), 0u32, |s, v| (s + v, v * 2));
754        assert_eq!(state, 5);
755        assert_eq!(result, Some(10));
756        let (s2, r2) = option_map_with(None::<u32>, 0u32, |s, v| (s + v, v * 2));
757        assert_eq!(s2, 0);
758        assert!(r2.is_none());
759    }
760}
761pub(super) fn opt_ext_type0() -> Expr {
762    Expr::Sort(Level::succ(Level::zero()))
763}
764pub(super) fn opt_ext_prop() -> Expr {
765    Expr::Sort(Level::zero())
766}
767pub fn opt_ext_cst(name: &str) -> Expr {
768    Expr::Const(Name::str(name), vec![])
769}
770pub fn opt_ext_app(f: Expr, a: Expr) -> Expr {
771    Expr::App(Box::new(f), Box::new(a))
772}
773pub fn opt_ext_app2(f: Expr, a: Expr, b: Expr) -> Expr {
774    opt_ext_app(opt_ext_app(f, a), b)
775}
776pub fn opt_ext_app3(f: Expr, a: Expr, b: Expr, c: Expr) -> Expr {
777    opt_ext_app(opt_ext_app2(f, a, b), c)
778}
779pub(super) fn opt_ext_pi(name: &str, dom: Expr, body: Expr) -> Expr {
780    Expr::Pi(
781        BinderInfo::Default,
782        Name::str(name),
783        Box::new(dom),
784        Box::new(body),
785    )
786}
787pub(super) fn opt_ext_pi_imp(name: &str, dom: Expr, body: Expr) -> Expr {
788    Expr::Pi(
789        BinderInfo::Implicit,
790        Name::str(name),
791        Box::new(dom),
792        Box::new(body),
793    )
794}
795pub(super) fn opt_ext_arrow(a: Expr, b: Expr) -> Expr {
796    opt_ext_pi("_", a, b)
797}
798pub(super) fn opt_ext_option_ty(alpha: Expr) -> Expr {
799    opt_ext_app(opt_ext_cst("Option"), alpha)
800}
801pub(super) fn opt_ext_axiom(env: &mut Environment, name: &str, ty: Expr) -> Result<(), String> {
802    env.add(Declaration::Axiom {
803        name: Name::str(name),
804        univ_params: vec![],
805        ty,
806    })
807    .map_err(|e| e.to_string())
808}
809/// Option.monad_left_id: pure a >>= f = f a
810pub fn opt_ext_monad_left_id(env: &mut Environment) -> Result<(), String> {
811    let t = opt_ext_type0();
812    let ty = opt_ext_pi_imp(
813        "α",
814        t.clone(),
815        opt_ext_pi_imp(
816            "β",
817            t.clone(),
818            opt_ext_pi(
819                "a",
820                Expr::BVar(1),
821                opt_ext_pi(
822                    "f",
823                    opt_ext_arrow(Expr::BVar(1), opt_ext_option_ty(Expr::BVar(1))),
824                    opt_ext_prop(),
825                ),
826            ),
827        ),
828    );
829    opt_ext_axiom(env, "Option.monad_left_id", ty)
830}
831/// Option.monad_right_id: m >>= pure = m
832pub fn opt_ext_monad_right_id(env: &mut Environment) -> Result<(), String> {
833    let t = opt_ext_type0();
834    let opt_a = opt_ext_option_ty(Expr::BVar(0));
835    let ty = opt_ext_pi_imp(
836        "α",
837        t.clone(),
838        opt_ext_pi("m", opt_a.clone(), opt_ext_prop()),
839    );
840    opt_ext_axiom(env, "Option.monad_right_id", ty)
841}
842/// Option.monad_assoc: (m >>= f) >>= g = m >>= (fun x => f x >>= g)
843pub fn opt_ext_monad_assoc(env: &mut Environment) -> Result<(), String> {
844    let t = opt_ext_type0();
845    let ty = opt_ext_pi_imp(
846        "α",
847        t.clone(),
848        opt_ext_pi_imp(
849            "β",
850            t.clone(),
851            opt_ext_pi_imp(
852                "γ",
853                t.clone(),
854                opt_ext_pi(
855                    "m",
856                    opt_ext_option_ty(Expr::BVar(2)),
857                    opt_ext_pi(
858                        "f",
859                        opt_ext_arrow(Expr::BVar(2), opt_ext_option_ty(Expr::BVar(2))),
860                        opt_ext_pi(
861                            "g",
862                            opt_ext_arrow(Expr::BVar(2), opt_ext_option_ty(Expr::BVar(2))),
863                            opt_ext_prop(),
864                        ),
865                    ),
866                ),
867            ),
868        ),
869    );
870    opt_ext_axiom(env, "Option.monad_assoc", ty)
871}
872/// Option.functor_id: fmap id = id
873pub fn opt_ext_functor_id(env: &mut Environment) -> Result<(), String> {
874    let t = opt_ext_type0();
875    let ty = opt_ext_pi_imp(
876        "α",
877        t.clone(),
878        opt_ext_pi("m", opt_ext_option_ty(Expr::BVar(0)), opt_ext_prop()),
879    );
880    opt_ext_axiom(env, "Option.functor_id", ty)
881}
882/// Option.functor_comp: fmap (f . g) = fmap f . fmap g
883pub fn opt_ext_functor_comp(env: &mut Environment) -> Result<(), String> {
884    let t = opt_ext_type0();
885    let ty = opt_ext_pi_imp(
886        "α",
887        t.clone(),
888        opt_ext_pi_imp(
889            "β",
890            t.clone(),
891            opt_ext_pi_imp(
892                "γ",
893                t.clone(),
894                opt_ext_pi(
895                    "f",
896                    opt_ext_arrow(Expr::BVar(1), Expr::BVar(1)),
897                    opt_ext_pi(
898                        "g",
899                        opt_ext_arrow(Expr::BVar(3), Expr::BVar(3)),
900                        opt_ext_prop(),
901                    ),
902                ),
903            ),
904        ),
905    );
906    opt_ext_axiom(env, "Option.functor_comp", ty)
907}
908/// Option.ap_identity: pure id <*> v = v
909pub fn opt_ext_ap_identity(env: &mut Environment) -> Result<(), String> {
910    let t = opt_ext_type0();
911    let ty = opt_ext_pi_imp(
912        "α",
913        t.clone(),
914        opt_ext_pi("v", opt_ext_option_ty(Expr::BVar(0)), opt_ext_prop()),
915    );
916    opt_ext_axiom(env, "Option.ap_identity", ty)
917}
918/// Option.ap_homomorphism: pure f <*> pure x = pure (f x)
919pub fn opt_ext_ap_homomorphism(env: &mut Environment) -> Result<(), String> {
920    let t = opt_ext_type0();
921    let ty = opt_ext_pi_imp(
922        "α",
923        t.clone(),
924        opt_ext_pi_imp(
925            "β",
926            t.clone(),
927            opt_ext_pi(
928                "f",
929                opt_ext_arrow(Expr::BVar(1), Expr::BVar(1)),
930                opt_ext_pi("x", Expr::BVar(2), opt_ext_prop()),
931            ),
932        ),
933    );
934    opt_ext_axiom(env, "Option.ap_homomorphism", ty)
935}
936/// Option.ap_interchange: u <*> pure y = pure ($ y) <*> u
937pub fn opt_ext_ap_interchange(env: &mut Environment) -> Result<(), String> {
938    let t = opt_ext_type0();
939    let ty = opt_ext_pi_imp(
940        "α",
941        t.clone(),
942        opt_ext_pi_imp(
943            "β",
944            t.clone(),
945            opt_ext_pi(
946                "u",
947                opt_ext_option_ty(opt_ext_arrow(Expr::BVar(1), Expr::BVar(1))),
948                opt_ext_pi("y", Expr::BVar(2), opt_ext_prop()),
949            ),
950        ),
951    );
952    opt_ext_axiom(env, "Option.ap_interchange", ty)
953}
954/// Option.ap_composition: pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
955pub fn opt_ext_ap_composition(env: &mut Environment) -> Result<(), String> {
956    let t = opt_ext_type0();
957    let ty = opt_ext_pi_imp(
958        "α",
959        t.clone(),
960        opt_ext_pi_imp(
961            "β",
962            t.clone(),
963            opt_ext_pi_imp("γ", t.clone(), opt_ext_prop()),
964        ),
965    );
966    opt_ext_axiom(env, "Option.ap_composition", ty)
967}
968/// Option.alt_first_some: Some x <|> _ = Some x
969pub fn opt_ext_alt_first_some(env: &mut Environment) -> Result<(), String> {
970    let t = opt_ext_type0();
971    let ty = opt_ext_pi_imp(
972        "α",
973        t.clone(),
974        opt_ext_pi(
975            "x",
976            Expr::BVar(0),
977            opt_ext_pi("other", opt_ext_option_ty(Expr::BVar(1)), opt_ext_prop()),
978        ),
979    );
980    opt_ext_axiom(env, "Option.alt_first_some", ty)
981}
982/// Option.alt_none_left: None <|> m = m
983pub fn opt_ext_alt_none_left(env: &mut Environment) -> Result<(), String> {
984    let t = opt_ext_type0();
985    let ty = opt_ext_pi_imp(
986        "α",
987        t.clone(),
988        opt_ext_pi("m", opt_ext_option_ty(Expr::BVar(0)), opt_ext_prop()),
989    );
990    opt_ext_axiom(env, "Option.alt_none_left", ty)
991}
992/// Option.alt_assoc: (a <|> b) <|> c = a <|> (b <|> c)
993pub fn opt_ext_alt_assoc(env: &mut Environment) -> Result<(), String> {
994    let t = opt_ext_type0();
995    let oa = opt_ext_option_ty(Expr::BVar(0));
996    let ty = opt_ext_pi_imp(
997        "α",
998        t.clone(),
999        opt_ext_pi(
1000            "a",
1001            oa.clone(),
1002            opt_ext_pi("b", oa.clone(), opt_ext_pi("c", oa.clone(), opt_ext_prop())),
1003        ),
1004    );
1005    opt_ext_axiom(env, "Option.alt_assoc", ty)
1006}
1007/// Option.iso_to_either: Option α ≅ Either Unit α (to direction)
1008pub fn opt_ext_iso_to_either(env: &mut Environment) -> Result<(), String> {
1009    let t = opt_ext_type0();
1010    let opt_a = opt_ext_option_ty(Expr::BVar(0));
1011    let either_unit_a = opt_ext_app2(opt_ext_cst("Either"), opt_ext_cst("Unit"), Expr::BVar(0));
1012    let ty = opt_ext_pi_imp("α", t.clone(), opt_ext_arrow(opt_a, either_unit_a));
1013    opt_ext_axiom(env, "Option.iso_to_either", ty)
1014}
1015/// Option.iso_from_either: Option α ≅ Either Unit α (from direction)
1016pub fn opt_ext_iso_from_either(env: &mut Environment) -> Result<(), String> {
1017    let t = opt_ext_type0();
1018    let opt_a = opt_ext_option_ty(Expr::BVar(0));
1019    let either_unit_a = opt_ext_app2(opt_ext_cst("Either"), opt_ext_cst("Unit"), Expr::BVar(0));
1020    let ty = opt_ext_pi_imp("α", t.clone(), opt_ext_arrow(either_unit_a, opt_a));
1021    opt_ext_axiom(env, "Option.iso_from_either", ty)
1022}
1023/// Option.iso_roundtrip_to: from_either (to_either x) = x
1024pub fn opt_ext_iso_roundtrip_to(env: &mut Environment) -> Result<(), String> {
1025    let t = opt_ext_type0();
1026    let ty = opt_ext_pi_imp(
1027        "α",
1028        t.clone(),
1029        opt_ext_pi("x", opt_ext_option_ty(Expr::BVar(0)), opt_ext_prop()),
1030    );
1031    opt_ext_axiom(env, "Option.iso_roundtrip_to", ty)
1032}
1033/// Option.iso_roundtrip_from: to_either (from_either y) = y
1034pub fn opt_ext_iso_roundtrip_from(env: &mut Environment) -> Result<(), String> {
1035    let t = opt_ext_type0();
1036    let either_unit_a = opt_ext_app2(opt_ext_cst("Either"), opt_ext_cst("Unit"), Expr::BVar(0));
1037    let ty = opt_ext_pi_imp(
1038        "α",
1039        t.clone(),
1040        opt_ext_pi("y", either_unit_a, opt_ext_prop()),
1041    );
1042    opt_ext_axiom(env, "Option.iso_roundtrip_from", ty)
1043}
1044/// Option.cata: catamorphism (fold)
1045pub fn opt_ext_cata(env: &mut Environment) -> Result<(), String> {
1046    let t = opt_ext_type0();
1047    let ty = opt_ext_pi_imp(
1048        "α",
1049        t.clone(),
1050        opt_ext_pi_imp(
1051            "β",
1052            t.clone(),
1053            opt_ext_pi(
1054                "n",
1055                Expr::BVar(0),
1056                opt_ext_pi(
1057                    "s",
1058                    opt_ext_arrow(Expr::BVar(2), Expr::BVar(1)),
1059                    opt_ext_arrow(opt_ext_option_ty(Expr::BVar(3)), Expr::BVar(2)),
1060                ),
1061            ),
1062        ),
1063    );
1064    opt_ext_axiom(env, "Option.cata", ty)
1065}
1066/// Option.ana: anamorphism (unfold)
1067pub fn opt_ext_ana(env: &mut Environment) -> Result<(), String> {
1068    let t = opt_ext_type0();
1069    let ty = opt_ext_pi_imp(
1070        "α",
1071        t.clone(),
1072        opt_ext_pi_imp(
1073            "β",
1074            t.clone(),
1075            opt_ext_pi(
1076                "coalg",
1077                opt_ext_arrow(Expr::BVar(0), opt_ext_option_ty(Expr::BVar(1))),
1078                opt_ext_arrow(Expr::BVar(1), opt_ext_option_ty(Expr::BVar(2))),
1079            ),
1080        ),
1081    );
1082    opt_ext_axiom(env, "Option.ana", ty)
1083}
1084/// Option.traverse_id: traverse Id = Id
1085pub fn opt_ext_traverse_id(env: &mut Environment) -> Result<(), String> {
1086    let t = opt_ext_type0();
1087    let ty = opt_ext_pi_imp(
1088        "α",
1089        t.clone(),
1090        opt_ext_pi("m", opt_ext_option_ty(Expr::BVar(0)), opt_ext_prop()),
1091    );
1092    opt_ext_axiom(env, "Option.traverse_id", ty)
1093}
1094/// Option.traverse_comp: traverse (f . g) = traverse f . traverse g
1095pub fn opt_ext_traverse_comp(env: &mut Environment) -> Result<(), String> {
1096    let t = opt_ext_type0();
1097    let ty = opt_ext_pi_imp(
1098        "α",
1099        t.clone(),
1100        opt_ext_pi_imp(
1101            "β",
1102            t.clone(),
1103            opt_ext_pi_imp("γ", t.clone(), opt_ext_prop()),
1104        ),
1105    );
1106    opt_ext_axiom(env, "Option.traverse_comp", ty)
1107}
1108/// Option.sequence_traverse: sequence = traverse id
1109pub fn opt_ext_sequence_traverse(env: &mut Environment) -> Result<(), String> {
1110    let t = opt_ext_type0();
1111    let ty = opt_ext_pi_imp("α", t.clone(), opt_ext_prop());
1112    opt_ext_axiom(env, "Option.sequence_traverse", ty)
1113}
1114/// Option.foldable_foldl: foldl f z (Some x) = f z x
1115pub fn opt_ext_foldable_foldl(env: &mut Environment) -> Result<(), String> {
1116    let t = opt_ext_type0();
1117    let ty = opt_ext_pi_imp(
1118        "α",
1119        t.clone(),
1120        opt_ext_pi_imp(
1121            "β",
1122            t.clone(),
1123            opt_ext_pi(
1124                "f",
1125                opt_ext_arrow(Expr::BVar(0), opt_ext_arrow(Expr::BVar(1), Expr::BVar(1))),
1126                opt_ext_pi(
1127                    "z",
1128                    Expr::BVar(1),
1129                    opt_ext_pi("x", Expr::BVar(3), opt_ext_prop()),
1130                ),
1131            ),
1132        ),
1133    );
1134    opt_ext_axiom(env, "Option.foldable_foldl", ty)
1135}
1136/// Option.foldable_foldr: foldr f z (Some x) = f x z
1137pub fn opt_ext_foldable_foldr(env: &mut Environment) -> Result<(), String> {
1138    let t = opt_ext_type0();
1139    let ty = opt_ext_pi_imp(
1140        "α",
1141        t.clone(),
1142        opt_ext_pi_imp(
1143            "β",
1144            t.clone(),
1145            opt_ext_pi(
1146                "f",
1147                opt_ext_arrow(Expr::BVar(1), opt_ext_arrow(Expr::BVar(1), Expr::BVar(1))),
1148                opt_ext_pi(
1149                    "z",
1150                    Expr::BVar(1),
1151                    opt_ext_pi("x", Expr::BVar(3), opt_ext_prop()),
1152                ),
1153            ),
1154        ),
1155    );
1156    opt_ext_axiom(env, "Option.foldable_foldr", ty)
1157}
1158/// Option.foldable_fold_none: foldl f z None = z
1159pub fn opt_ext_foldable_fold_none(env: &mut Environment) -> Result<(), String> {
1160    let t = opt_ext_type0();
1161    let ty = opt_ext_pi_imp(
1162        "α",
1163        t.clone(),
1164        opt_ext_pi_imp(
1165            "β",
1166            t.clone(),
1167            opt_ext_pi(
1168                "f",
1169                opt_ext_arrow(Expr::BVar(0), opt_ext_arrow(Expr::BVar(1), Expr::BVar(1))),
1170                opt_ext_pi("z", Expr::BVar(1), opt_ext_prop()),
1171            ),
1172        ),
1173    );
1174    opt_ext_axiom(env, "Option.foldable_fold_none", ty)
1175}
1176/// OptionT.run: run the OptionT monad transformer
1177pub fn opt_ext_optiont_run(env: &mut Environment) -> Result<(), String> {
1178    let t = opt_ext_type0();
1179    let ty = opt_ext_pi_imp(
1180        "m",
1181        opt_ext_arrow(t.clone(), t.clone()),
1182        opt_ext_pi_imp(
1183            "α",
1184            t.clone(),
1185            opt_ext_arrow(
1186                opt_ext_app(Expr::BVar(1), opt_ext_option_ty(Expr::BVar(0))),
1187                opt_ext_app(Expr::BVar(1), opt_ext_option_ty(Expr::BVar(0))),
1188            ),
1189        ),
1190    );
1191    opt_ext_axiom(env, "OptionT.run", ty)
1192}
1193/// OptionT.lift: lift m into OptionT
1194pub fn opt_ext_optiont_lift(env: &mut Environment) -> Result<(), String> {
1195    let t = opt_ext_type0();
1196    let ty = opt_ext_pi_imp(
1197        "m",
1198        opt_ext_arrow(t.clone(), t.clone()),
1199        opt_ext_pi_imp(
1200            "α",
1201            t.clone(),
1202            opt_ext_arrow(
1203                opt_ext_app(Expr::BVar(1), Expr::BVar(0)),
1204                opt_ext_app(Expr::BVar(1), opt_ext_option_ty(Expr::BVar(0))),
1205            ),
1206        ),
1207    );
1208    opt_ext_axiom(env, "OptionT.lift", ty)
1209}
1210/// OptionT.bind: monadic bind for OptionT
1211pub fn opt_ext_optiont_bind(env: &mut Environment) -> Result<(), String> {
1212    let t = opt_ext_type0();
1213    let ty = opt_ext_pi_imp(
1214        "m",
1215        opt_ext_arrow(t.clone(), t.clone()),
1216        opt_ext_pi_imp(
1217            "α",
1218            t.clone(),
1219            opt_ext_pi_imp("β", t.clone(), opt_ext_prop()),
1220        ),
1221    );
1222    opt_ext_axiom(env, "OptionT.bind", ty)
1223}
1224/// Option.pointed_pure: pure x = Some x
1225pub fn opt_ext_pointed_pure(env: &mut Environment) -> Result<(), String> {
1226    let t = opt_ext_type0();
1227    let ty = opt_ext_pi_imp(
1228        "α",
1229        t.clone(),
1230        opt_ext_pi("x", Expr::BVar(0), opt_ext_prop()),
1231    );
1232    opt_ext_axiom(env, "Option.pointed_pure", ty)
1233}
1234/// Option.monoid_empty: empty = None
1235pub fn opt_ext_monoid_empty(env: &mut Environment) -> Result<(), String> {
1236    let t = opt_ext_type0();
1237    let ty = opt_ext_pi_imp("α", t.clone(), opt_ext_option_ty(Expr::BVar(0)));
1238    opt_ext_axiom(env, "Option.monoid_empty", ty)
1239}
1240/// Option.monoid_append: append (first Some wins)
1241pub fn opt_ext_monoid_append(env: &mut Environment) -> Result<(), String> {
1242    let t = opt_ext_type0();
1243    let oa = opt_ext_option_ty(Expr::BVar(0));
1244    let ty = opt_ext_pi_imp(
1245        "α",
1246        t.clone(),
1247        opt_ext_arrow(oa.clone(), opt_ext_arrow(oa.clone(), oa.clone())),
1248    );
1249    opt_ext_axiom(env, "Option.monoid_append", ty)
1250}
1251/// Option.monoid_left_id: None `append` m = m
1252pub fn opt_ext_monoid_left_id(env: &mut Environment) -> Result<(), String> {
1253    let t = opt_ext_type0();
1254    let ty = opt_ext_pi_imp(
1255        "α",
1256        t.clone(),
1257        opt_ext_pi("m", opt_ext_option_ty(Expr::BVar(0)), opt_ext_prop()),
1258    );
1259    opt_ext_axiom(env, "Option.monoid_left_id", ty)
1260}
1261/// Option.monoid_right_id: m `append` None = m
1262pub fn opt_ext_monoid_right_id(env: &mut Environment) -> Result<(), String> {
1263    let t = opt_ext_type0();
1264    let ty = opt_ext_pi_imp(
1265        "α",
1266        t.clone(),
1267        opt_ext_pi("m", opt_ext_option_ty(Expr::BVar(0)), opt_ext_prop()),
1268    );
1269    opt_ext_axiom(env, "Option.monoid_right_id", ty)
1270}
1271/// Option.bimap_some: bimap f (Some x) = Some (f x)
1272pub fn opt_ext_bimap_some(env: &mut Environment) -> Result<(), String> {
1273    let t = opt_ext_type0();
1274    let ty = opt_ext_pi_imp(
1275        "α",
1276        t.clone(),
1277        opt_ext_pi_imp(
1278            "β",
1279            t.clone(),
1280            opt_ext_pi(
1281                "f",
1282                opt_ext_arrow(Expr::BVar(1), Expr::BVar(1)),
1283                opt_ext_pi("x", Expr::BVar(2), opt_ext_prop()),
1284            ),
1285        ),
1286    );
1287    opt_ext_axiom(env, "Option.bimap_some", ty)
1288}
1289/// Option.bimap_none: bimap f None = None
1290pub fn opt_ext_bimap_none(env: &mut Environment) -> Result<(), String> {
1291    let t = opt_ext_type0();
1292    let ty = opt_ext_pi_imp(
1293        "α",
1294        t.clone(),
1295        opt_ext_pi_imp(
1296            "β",
1297            t.clone(),
1298            opt_ext_pi(
1299                "f",
1300                opt_ext_arrow(Expr::BVar(1), Expr::BVar(1)),
1301                opt_ext_prop(),
1302            ),
1303        ),
1304    );
1305    opt_ext_axiom(env, "Option.bimap_none", ty)
1306}
1307/// Option.zip_some: zip (Some a) (Some b) = Some (a, b)
1308pub fn opt_ext_zip_some(env: &mut Environment) -> Result<(), String> {
1309    let t = opt_ext_type0();
1310    let ty = opt_ext_pi_imp(
1311        "α",
1312        t.clone(),
1313        opt_ext_pi_imp(
1314            "β",
1315            t.clone(),
1316            opt_ext_pi(
1317                "a",
1318                Expr::BVar(1),
1319                opt_ext_pi("b", Expr::BVar(1), opt_ext_prop()),
1320            ),
1321        ),
1322    );
1323    opt_ext_axiom(env, "Option.zip_some", ty)
1324}
1325/// Option.zip_none: zip None _ = None
1326pub fn opt_ext_zip_none(env: &mut Environment) -> Result<(), String> {
1327    let t = opt_ext_type0();
1328    let ty = opt_ext_pi_imp(
1329        "α",
1330        t.clone(),
1331        opt_ext_pi_imp(
1332            "β",
1333            t.clone(),
1334            opt_ext_pi("b", opt_ext_option_ty(Expr::BVar(0)), opt_ext_prop()),
1335        ),
1336    );
1337    opt_ext_axiom(env, "Option.zip_none", ty)
1338}
1339/// Option.filter_some_true: filter p (Some x) = Some x when p x = true
1340pub fn opt_ext_filter_some_true(env: &mut Environment) -> Result<(), String> {
1341    let t = opt_ext_type0();
1342    let ty = opt_ext_pi_imp(
1343        "α",
1344        t.clone(),
1345        opt_ext_pi(
1346            "p",
1347            opt_ext_arrow(Expr::BVar(0), opt_ext_cst("Bool")),
1348            opt_ext_pi("x", Expr::BVar(1), opt_ext_prop()),
1349        ),
1350    );
1351    opt_ext_axiom(env, "Option.filter_some_true", ty)
1352}
1353/// Option.filter_some_false: filter p (Some x) = None when p x = false
1354pub fn opt_ext_filter_some_false(env: &mut Environment) -> Result<(), String> {
1355    let t = opt_ext_type0();
1356    let ty = opt_ext_pi_imp(
1357        "α",
1358        t.clone(),
1359        opt_ext_pi(
1360            "p",
1361            opt_ext_arrow(Expr::BVar(0), opt_ext_cst("Bool")),
1362            opt_ext_pi("x", Expr::BVar(1), opt_ext_prop()),
1363        ),
1364    );
1365    opt_ext_axiom(env, "Option.filter_some_false", ty)
1366}
1367/// Option.filter_none: filter p None = None
1368pub fn opt_ext_filter_none(env: &mut Environment) -> Result<(), String> {
1369    let t = opt_ext_type0();
1370    let ty = opt_ext_pi_imp(
1371        "α",
1372        t.clone(),
1373        opt_ext_pi(
1374            "p",
1375            opt_ext_arrow(Expr::BVar(0), opt_ext_cst("Bool")),
1376            opt_ext_prop(),
1377        ),
1378    );
1379    opt_ext_axiom(env, "Option.filter_none", ty)
1380}
1381/// Option.get_or_else_some: getOrElse (Some x) d = x
1382pub fn opt_ext_get_or_else_some(env: &mut Environment) -> Result<(), String> {
1383    let t = opt_ext_type0();
1384    let ty = opt_ext_pi_imp(
1385        "α",
1386        t.clone(),
1387        opt_ext_pi(
1388            "x",
1389            Expr::BVar(0),
1390            opt_ext_pi("d", Expr::BVar(1), opt_ext_prop()),
1391        ),
1392    );
1393    opt_ext_axiom(env, "Option.get_or_else_some", ty)
1394}
1395/// Option.get_or_else_none: getOrElse None d = d
1396pub fn opt_ext_get_or_else_none(env: &mut Environment) -> Result<(), String> {
1397    let t = opt_ext_type0();
1398    let ty = opt_ext_pi_imp(
1399        "α",
1400        t.clone(),
1401        opt_ext_pi("d", Expr::BVar(0), opt_ext_prop()),
1402    );
1403    opt_ext_axiom(env, "Option.get_or_else_none", ty)
1404}
1405/// Option.or_else_some: orElse (Some x) _ = Some x
1406pub fn opt_ext_or_else_some(env: &mut Environment) -> Result<(), String> {
1407    let t = opt_ext_type0();
1408    let oa = opt_ext_option_ty(Expr::BVar(0));
1409    let ty = opt_ext_pi_imp(
1410        "α",
1411        t.clone(),
1412        opt_ext_pi(
1413            "x",
1414            Expr::BVar(0),
1415            opt_ext_pi("other", oa.clone(), opt_ext_prop()),
1416        ),
1417    );
1418    opt_ext_axiom(env, "Option.or_else_some", ty)
1419}
1420/// Option.or_else_none: orElse None m = m
1421pub fn opt_ext_or_else_none(env: &mut Environment) -> Result<(), String> {
1422    let t = opt_ext_type0();
1423    let ty = opt_ext_pi_imp(
1424        "α",
1425        t.clone(),
1426        opt_ext_pi("m", opt_ext_option_ty(Expr::BVar(0)), opt_ext_prop()),
1427    );
1428    opt_ext_axiom(env, "Option.or_else_none", ty)
1429}
1430/// Option.dimap: profunctor dimap
1431pub fn opt_ext_dimap(env: &mut Environment) -> Result<(), String> {
1432    let t = opt_ext_type0();
1433    let ty = opt_ext_pi_imp(
1434        "α",
1435        t.clone(),
1436        opt_ext_pi_imp(
1437            "β",
1438            t.clone(),
1439            opt_ext_pi_imp(
1440                "γ",
1441                t.clone(),
1442                opt_ext_pi_imp(
1443                    "δ",
1444                    t.clone(),
1445                    opt_ext_pi(
1446                        "pre",
1447                        opt_ext_arrow(Expr::BVar(2), Expr::BVar(3)),
1448                        opt_ext_pi(
1449                            "post",
1450                            opt_ext_arrow(Expr::BVar(2), Expr::BVar(2)),
1451                            opt_ext_pi(
1452                                "f",
1453                                opt_ext_arrow(Expr::BVar(5), opt_ext_option_ty(Expr::BVar(3))),
1454                                opt_ext_arrow(Expr::BVar(5), opt_ext_option_ty(Expr::BVar(4))),
1455                            ),
1456                        ),
1457                    ),
1458                ),
1459            ),
1460        ),
1461    );
1462    opt_ext_axiom(env, "Option.dimap", ty)
1463}
1464/// Option.comonad_extract: extract (pure x) = x with fixed default
1465pub fn opt_ext_comonad_extract(env: &mut Environment) -> Result<(), String> {
1466    let t = opt_ext_type0();
1467    let ty = opt_ext_pi_imp(
1468        "α",
1469        t.clone(),
1470        opt_ext_pi(
1471            "m",
1472            opt_ext_option_ty(Expr::BVar(0)),
1473            opt_ext_pi("d", Expr::BVar(1), opt_ext_prop()),
1474        ),
1475    );
1476    opt_ext_axiom(env, "Option.comonad_extract", ty)
1477}
1478/// Option.join_some_some: join (Some (Some x)) = Some x
1479pub fn opt_ext_join_some_some(env: &mut Environment) -> Result<(), String> {
1480    let t = opt_ext_type0();
1481    let inner = opt_ext_option_ty(Expr::BVar(0));
1482    let outer = opt_ext_option_ty(inner);
1483    let ty = opt_ext_pi_imp(
1484        "α",
1485        t.clone(),
1486        opt_ext_pi("x", Expr::BVar(0), opt_ext_pi("m", outer, opt_ext_prop())),
1487    );
1488    opt_ext_axiom(env, "Option.join_some_some", ty)
1489}
1490/// Option.join_some_none: join (Some None) = None
1491pub fn opt_ext_join_some_none(env: &mut Environment) -> Result<(), String> {
1492    let t = opt_ext_type0();
1493    let ty = opt_ext_pi_imp("α", t.clone(), opt_ext_prop());
1494    opt_ext_axiom(env, "Option.join_some_none", ty)
1495}
1496/// Option.join_none: join None = None
1497pub fn opt_ext_join_none(env: &mut Environment) -> Result<(), String> {
1498    let t = opt_ext_type0();
1499    let ty = opt_ext_pi_imp("α", t.clone(), opt_ext_prop());
1500    opt_ext_axiom(env, "Option.join_none", ty)
1501}
1502/// Option.flatmap_is_join_map: m >>= f = join (map f m)
1503pub fn opt_ext_flatmap_is_join_map(env: &mut Environment) -> Result<(), String> {
1504    let t = opt_ext_type0();
1505    let ty = opt_ext_pi_imp(
1506        "α",
1507        t.clone(),
1508        opt_ext_pi_imp(
1509            "β",
1510            t.clone(),
1511            opt_ext_pi(
1512                "m",
1513                opt_ext_option_ty(Expr::BVar(1)),
1514                opt_ext_pi(
1515                    "f",
1516                    opt_ext_arrow(Expr::BVar(2), opt_ext_option_ty(Expr::BVar(2))),
1517                    opt_ext_prop(),
1518                ),
1519            ),
1520        ),
1521    );
1522    opt_ext_axiom(env, "Option.flatmap_is_join_map", ty)
1523}
1524/// Option.nullable_iso_to: Option α ≅ nullable α (to)
1525pub fn opt_ext_nullable_iso_to(env: &mut Environment) -> Result<(), String> {
1526    let t = opt_ext_type0();
1527    let ty = opt_ext_pi_imp(
1528        "α",
1529        t.clone(),
1530        opt_ext_arrow(
1531            opt_ext_option_ty(Expr::BVar(0)),
1532            opt_ext_option_ty(Expr::BVar(0)),
1533        ),
1534    );
1535    opt_ext_axiom(env, "Option.nullable_iso_to", ty)
1536}
1537/// Option.nullable_iso_from: nullable α ≅ Option α (from)
1538pub fn opt_ext_nullable_iso_from(env: &mut Environment) -> Result<(), String> {
1539    let t = opt_ext_type0();
1540    let ty = opt_ext_pi_imp(
1541        "α",
1542        t.clone(),
1543        opt_ext_arrow(
1544            opt_ext_option_ty(Expr::BVar(0)),
1545            opt_ext_option_ty(Expr::BVar(0)),
1546        ),
1547    );
1548    opt_ext_axiom(env, "Option.nullable_iso_from", ty)
1549}
1550/// Option.liftA2_def: liftA2 f u v = f <$> u <*> v
1551pub fn opt_ext_lift_a2(env: &mut Environment) -> Result<(), String> {
1552    let t = opt_ext_type0();
1553    let ty = opt_ext_pi_imp(
1554        "α",
1555        t.clone(),
1556        opt_ext_pi_imp(
1557            "β",
1558            t.clone(),
1559            opt_ext_pi_imp(
1560                "γ",
1561                t.clone(),
1562                opt_ext_pi(
1563                    "f",
1564                    opt_ext_arrow(Expr::BVar(2), opt_ext_arrow(Expr::BVar(2), Expr::BVar(2))),
1565                    opt_ext_pi(
1566                        "u",
1567                        opt_ext_option_ty(Expr::BVar(3)),
1568                        opt_ext_pi("v", opt_ext_option_ty(Expr::BVar(3)), opt_ext_prop()),
1569                    ),
1570                ),
1571            ),
1572        ),
1573    );
1574    opt_ext_axiom(env, "Option.liftA2_def", ty)
1575}
1576/// Register all extended Option axioms into `env`.
1577///
1578/// Covers monad laws, functor laws, applicative laws, alternative laws,
1579/// Either isomorphism, catamorphism/anamorphism, traversal, foldable laws,
1580/// OptionT transformer, pointed functor, monoid laws, bimap, zip, filter,
1581/// getOrElse/orElse laws, profunctor, comonad, join/flatten, flatmap,
1582/// nullable isomorphism, and liftA2.
1583pub fn register_option_extended_axioms(env: &mut Environment) -> Result<(), String> {
1584    let t = opt_ext_type0();
1585    for name in ["Bool", "Option", "Either", "Unit", "Nat", "List", "Prod"] {
1586        let _ = env.add(Declaration::Axiom {
1587            name: Name::str(name),
1588            univ_params: vec![],
1589            ty: t.clone(),
1590        });
1591    }
1592    opt_ext_monad_left_id(env)?;
1593    opt_ext_monad_right_id(env)?;
1594    opt_ext_monad_assoc(env)?;
1595    opt_ext_functor_id(env)?;
1596    opt_ext_functor_comp(env)?;
1597    opt_ext_ap_identity(env)?;
1598    opt_ext_ap_homomorphism(env)?;
1599    opt_ext_ap_interchange(env)?;
1600    opt_ext_ap_composition(env)?;
1601    opt_ext_alt_first_some(env)?;
1602    opt_ext_alt_none_left(env)?;
1603    opt_ext_alt_assoc(env)?;
1604    opt_ext_iso_to_either(env)?;
1605    opt_ext_iso_from_either(env)?;
1606    opt_ext_iso_roundtrip_to(env)?;
1607    opt_ext_iso_roundtrip_from(env)?;
1608    opt_ext_cata(env)?;
1609    opt_ext_ana(env)?;
1610    opt_ext_traverse_id(env)?;
1611    opt_ext_traverse_comp(env)?;
1612    opt_ext_sequence_traverse(env)?;
1613    opt_ext_foldable_foldl(env)?;
1614    opt_ext_foldable_foldr(env)?;
1615    opt_ext_foldable_fold_none(env)?;
1616    opt_ext_optiont_run(env)?;
1617    opt_ext_optiont_lift(env)?;
1618    opt_ext_optiont_bind(env)?;
1619    opt_ext_pointed_pure(env)?;
1620    opt_ext_monoid_empty(env)?;
1621    opt_ext_monoid_append(env)?;
1622    opt_ext_monoid_left_id(env)?;
1623    opt_ext_monoid_right_id(env)?;
1624    opt_ext_bimap_some(env)?;
1625    opt_ext_bimap_none(env)?;
1626    opt_ext_zip_some(env)?;
1627    opt_ext_zip_none(env)?;
1628    opt_ext_filter_some_true(env)?;
1629    opt_ext_filter_some_false(env)?;
1630    opt_ext_filter_none(env)?;
1631    opt_ext_get_or_else_some(env)?;
1632    opt_ext_get_or_else_none(env)?;
1633    opt_ext_or_else_some(env)?;
1634    opt_ext_or_else_none(env)?;
1635    opt_ext_dimap(env)?;
1636    opt_ext_comonad_extract(env)?;
1637    opt_ext_join_some_some(env)?;
1638    opt_ext_join_some_none(env)?;
1639    opt_ext_join_none(env)?;
1640    opt_ext_flatmap_is_join_map(env)?;
1641    opt_ext_nullable_iso_to(env)?;
1642    opt_ext_nullable_iso_from(env)?;
1643    opt_ext_lift_a2(env)?;
1644    Ok(())
1645}