1use oxilean_kernel::{BinderInfo, Declaration, Environment, Expr, Level, Name};
6
7use super::types::{
8 CofreeStream, ComonadCtx, ComputeNode, Deferred, DeferredPool, Delayed, GameArena, ITreeNode,
9 LazyList, LazyStream, Memo, MemoThunk, OnceCellThunk, Suspension, ThunkApp, ThunkKleisli,
10 ThunkSeq, ThunkState, ThunkTree, TryThunk,
11};
12
13pub fn type1() -> Expr {
14 Expr::Sort(Level::succ(Level::zero()))
15}
16pub fn type2() -> Expr {
17 Expr::Sort(Level::succ(Level::succ(Level::zero())))
18}
19pub fn thunk_of(alpha: Expr) -> Expr {
20 Expr::App(
21 Box::new(Expr::Const(Name::str("Thunk"), vec![])),
22 Box::new(alpha),
23 )
24}
25pub fn unit_ty() -> Expr {
26 Expr::Const(Name::str("Unit"), vec![])
27}
28pub fn bool_ty() -> Expr {
29 Expr::Const(Name::str("Bool"), vec![])
30}
31pub fn nat_ty() -> Expr {
32 Expr::Const(Name::str("Nat"), vec![])
33}
34pub fn list_of(alpha: Expr) -> Expr {
35 Expr::App(
36 Box::new(Expr::Const(Name::str("List"), vec![])),
37 Box::new(alpha),
38 )
39}
40pub fn unit_to(ret_ty: Expr) -> Expr {
41 Expr::Pi(
42 BinderInfo::Default,
43 Name::str("_"),
44 Box::new(unit_ty()),
45 Box::new(ret_ty),
46 )
47}
48pub fn option_of(alpha: Expr) -> Expr {
49 Expr::App(
50 Box::new(Expr::Const(Name::str("Option"), vec![])),
51 Box::new(alpha),
52 )
53}
54pub fn axiom(env: &mut Environment, name: &str, ty: Expr) -> Result<(), String> {
55 env.add(Declaration::Axiom {
56 name: Name::str(name),
57 univ_params: vec![],
58 ty,
59 })
60 .map_err(|e| e.to_string())
61}
62pub fn alpha_implicit(inner: Expr) -> Expr {
63 Expr::Pi(
64 BinderInfo::Implicit,
65 Name::str("α"),
66 Box::new(type1()),
67 Box::new(inner),
68 )
69}
70pub fn ab_implicit(inner: Expr) -> Expr {
71 Expr::Pi(
72 BinderInfo::Implicit,
73 Name::str("α"),
74 Box::new(type1()),
75 Box::new(Expr::Pi(
76 BinderInfo::Implicit,
77 Name::str("β"),
78 Box::new(type1()),
79 Box::new(inner),
80 )),
81 )
82}
83pub fn build_thunk_env(env: &mut Environment) -> Result<(), String> {
85 let thunk_ty = Expr::Pi(
86 BinderInfo::Default,
87 Name::str("α"),
88 Box::new(type1()),
89 Box::new(type2()),
90 );
91 env.add(Declaration::Axiom {
92 name: Name::str("Thunk"),
93 univ_params: vec![],
94 ty: thunk_ty,
95 })
96 .map_err(|e| e.to_string())?;
97 add_mk(env)?;
98 add_get(env)?;
99 add_pure(env)?;
100 add_map(env)?;
101 add_bind(env)?;
102 add_join(env)?;
103 add_zip(env)?;
104 add_and(env)?;
105 add_or(env)?;
106 add_is_forced(env)?;
107 add_ap(env)?;
108 add_sequence(env)?;
109 add_delay(env)?;
110 add_count(env)?;
111 add_get_or(env)?;
112 Ok(())
113}
114pub fn add_mk(env: &mut Environment) -> Result<(), String> {
115 let ty = alpha_implicit(Expr::Pi(
116 BinderInfo::Default,
117 Name::str("f"),
118 Box::new(unit_to(Expr::BVar(1))),
119 Box::new(thunk_of(Expr::BVar(1))),
120 ));
121 axiom(env, "Thunk.mk", ty)
122}
123pub fn add_get(env: &mut Environment) -> Result<(), String> {
124 let ty = alpha_implicit(Expr::Pi(
125 BinderInfo::Default,
126 Name::str("t"),
127 Box::new(thunk_of(Expr::BVar(0))),
128 Box::new(Expr::BVar(1)),
129 ));
130 axiom(env, "Thunk.get", ty)
131}
132pub fn add_pure(env: &mut Environment) -> Result<(), String> {
133 let ty = alpha_implicit(Expr::Pi(
134 BinderInfo::Default,
135 Name::str("x"),
136 Box::new(Expr::BVar(0)),
137 Box::new(thunk_of(Expr::BVar(1))),
138 ));
139 axiom(env, "Thunk.pure", ty)
140}
141pub fn add_map(env: &mut Environment) -> Result<(), String> {
142 let fn_ty = Expr::Pi(
143 BinderInfo::Default,
144 Name::str("_"),
145 Box::new(Expr::BVar(1)),
146 Box::new(Expr::BVar(1)),
147 );
148 let ty = ab_implicit(Expr::Pi(
149 BinderInfo::Default,
150 Name::str("f"),
151 Box::new(fn_ty),
152 Box::new(Expr::Pi(
153 BinderInfo::Default,
154 Name::str("t"),
155 Box::new(thunk_of(Expr::BVar(2))),
156 Box::new(thunk_of(Expr::BVar(2))),
157 )),
158 ));
159 axiom(env, "Thunk.map", ty)
160}
161pub fn add_bind(env: &mut Environment) -> Result<(), String> {
162 let fn_ty = Expr::Pi(
163 BinderInfo::Default,
164 Name::str("_"),
165 Box::new(Expr::BVar(1)),
166 Box::new(thunk_of(Expr::BVar(1))),
167 );
168 let ty = ab_implicit(Expr::Pi(
169 BinderInfo::Default,
170 Name::str("t"),
171 Box::new(thunk_of(Expr::BVar(1))),
172 Box::new(Expr::Pi(
173 BinderInfo::Default,
174 Name::str("f"),
175 Box::new(fn_ty),
176 Box::new(thunk_of(Expr::BVar(2))),
177 )),
178 ));
179 axiom(env, "Thunk.bind", ty)
180}
181pub fn add_join(env: &mut Environment) -> Result<(), String> {
182 let ty = alpha_implicit(Expr::Pi(
183 BinderInfo::Default,
184 Name::str("t"),
185 Box::new(thunk_of(thunk_of(Expr::BVar(0)))),
186 Box::new(thunk_of(Expr::BVar(1))),
187 ));
188 axiom(env, "Thunk.join", ty)
189}
190pub fn add_zip(env: &mut Environment) -> Result<(), String> {
191 let prod_ty = Expr::App(
192 Box::new(Expr::App(
193 Box::new(Expr::Const(Name::str("Prod"), vec![])),
194 Box::new(Expr::BVar(1)),
195 )),
196 Box::new(Expr::BVar(0)),
197 );
198 let ty = ab_implicit(Expr::Pi(
199 BinderInfo::Default,
200 Name::str("ta"),
201 Box::new(thunk_of(Expr::BVar(1))),
202 Box::new(Expr::Pi(
203 BinderInfo::Default,
204 Name::str("tb"),
205 Box::new(thunk_of(Expr::BVar(1))),
206 Box::new(thunk_of(prod_ty)),
207 )),
208 ));
209 axiom(env, "Thunk.zip", ty)
210}
211pub fn add_bool_op(env: &mut Environment, name: &str) -> Result<(), String> {
212 let ty = Expr::Pi(
213 BinderInfo::Default,
214 Name::str("a"),
215 Box::new(thunk_of(bool_ty())),
216 Box::new(Expr::Pi(
217 BinderInfo::Default,
218 Name::str("b"),
219 Box::new(thunk_of(bool_ty())),
220 Box::new(thunk_of(bool_ty())),
221 )),
222 );
223 axiom(env, name, ty)
224}
225pub fn add_and(env: &mut Environment) -> Result<(), String> {
226 add_bool_op(env, "Thunk.and")
227}
228pub fn add_or(env: &mut Environment) -> Result<(), String> {
229 add_bool_op(env, "Thunk.or")
230}
231pub fn add_is_forced(env: &mut Environment) -> Result<(), String> {
232 let ty = alpha_implicit(Expr::Pi(
233 BinderInfo::Default,
234 Name::str("t"),
235 Box::new(thunk_of(Expr::BVar(0))),
236 Box::new(bool_ty()),
237 ));
238 axiom(env, "Thunk.isForced", ty)
239}
240pub fn add_ap(env: &mut Environment) -> Result<(), String> {
241 let fn_ty = Expr::Pi(
242 BinderInfo::Default,
243 Name::str("_"),
244 Box::new(Expr::BVar(1)),
245 Box::new(Expr::BVar(1)),
246 );
247 let ty = ab_implicit(Expr::Pi(
248 BinderInfo::Default,
249 Name::str("tf"),
250 Box::new(thunk_of(fn_ty)),
251 Box::new(Expr::Pi(
252 BinderInfo::Default,
253 Name::str("ta"),
254 Box::new(thunk_of(Expr::BVar(2))),
255 Box::new(thunk_of(Expr::BVar(2))),
256 )),
257 ));
258 axiom(env, "Thunk.ap", ty)
259}
260pub fn add_sequence(env: &mut Environment) -> Result<(), String> {
261 let ty = alpha_implicit(Expr::Pi(
262 BinderInfo::Default,
263 Name::str("ts"),
264 Box::new(list_of(thunk_of(Expr::BVar(0)))),
265 Box::new(thunk_of(list_of(Expr::BVar(1)))),
266 ));
267 axiom(env, "Thunk.sequence", ty)
268}
269pub fn add_delay(env: &mut Environment) -> Result<(), String> {
270 let ty = alpha_implicit(Expr::Pi(
271 BinderInfo::Default,
272 Name::str("x"),
273 Box::new(Expr::BVar(0)),
274 Box::new(thunk_of(Expr::BVar(1))),
275 ));
276 axiom(env, "Thunk.delay", ty)
277}
278pub fn add_count(env: &mut Environment) -> Result<(), String> {
279 let ty = alpha_implicit(Expr::Pi(
280 BinderInfo::Default,
281 Name::str("ts"),
282 Box::new(list_of(thunk_of(Expr::BVar(0)))),
283 Box::new(nat_ty()),
284 ));
285 axiom(env, "Thunk.count", ty)
286}
287pub fn add_get_or(env: &mut Environment) -> Result<(), String> {
288 let ty = alpha_implicit(Expr::Pi(
289 BinderInfo::Default,
290 Name::str("opt"),
291 Box::new(option_of(thunk_of(Expr::BVar(0)))),
292 Box::new(Expr::Pi(
293 BinderInfo::Default,
294 Name::str("def"),
295 Box::new(Expr::BVar(1)),
296 Box::new(Expr::BVar(2)),
297 )),
298 ));
299 axiom(env, "Thunk.getOr", ty)
300}
301pub fn setup_base_env() -> Environment {
302 let mut env = Environment::new();
303 let t1 = type1();
304 for name in ["Unit", "Bool", "Prod", "List", "Nat", "Option"] {
305 env.add(Declaration::Axiom {
306 name: Name::str(name),
307 univ_params: vec![],
308 ty: t1.clone(),
309 })
310 .unwrap_or(());
311 }
312 env
313}
314#[cfg(test)]
315mod tests {
316 use super::*;
317 #[test]
318 fn test_build_thunk_env() {
319 let mut env = setup_base_env();
320 assert!(build_thunk_env(&mut env).is_ok());
321 assert!(env.get(&Name::str("Thunk")).is_some());
322 assert!(env.get(&Name::str("Thunk.mk")).is_some());
323 assert!(env.get(&Name::str("Thunk.get")).is_some());
324 }
325 #[test]
326 fn test_thunk_pure() {
327 let mut env = setup_base_env();
328 build_thunk_env(&mut env).expect("build_thunk_env should succeed");
329 assert!(matches!(
330 env.get(&Name::str("Thunk.pure"))
331 .expect("declaration 'Thunk.pure' should exist in env"),
332 Declaration::Axiom { .. }
333 ));
334 }
335 #[test]
336 fn test_thunk_map() {
337 let mut env = setup_base_env();
338 build_thunk_env(&mut env).expect("build_thunk_env should succeed");
339 assert!(matches!(
340 env.get(&Name::str("Thunk.map"))
341 .expect("declaration 'Thunk.map' should exist in env"),
342 Declaration::Axiom { .. }
343 ));
344 }
345 #[test]
346 fn test_thunk_bind() {
347 let mut env = setup_base_env();
348 build_thunk_env(&mut env).expect("build_thunk_env should succeed");
349 assert!(env.get(&Name::str("Thunk.bind")).is_some());
350 }
351 #[test]
352 fn test_thunk_join() {
353 let mut env = setup_base_env();
354 build_thunk_env(&mut env).expect("build_thunk_env should succeed");
355 assert!(env.get(&Name::str("Thunk.join")).is_some());
356 }
357 #[test]
358 fn test_thunk_zip() {
359 let mut env = setup_base_env();
360 build_thunk_env(&mut env).expect("build_thunk_env should succeed");
361 assert!(env.get(&Name::str("Thunk.zip")).is_some());
362 }
363 #[test]
364 fn test_thunk_and() {
365 let mut env = setup_base_env();
366 build_thunk_env(&mut env).expect("build_thunk_env should succeed");
367 assert!(env.get(&Name::str("Thunk.and")).is_some());
368 }
369 #[test]
370 fn test_thunk_or() {
371 let mut env = setup_base_env();
372 build_thunk_env(&mut env).expect("build_thunk_env should succeed");
373 assert!(env.get(&Name::str("Thunk.or")).is_some());
374 }
375 #[test]
376 fn test_thunk_is_forced() {
377 let mut env = setup_base_env();
378 build_thunk_env(&mut env).expect("build_thunk_env should succeed");
379 assert!(env.get(&Name::str("Thunk.isForced")).is_some());
380 }
381 #[test]
382 fn test_thunk_mk_is_axiom() {
383 let mut env = setup_base_env();
384 build_thunk_env(&mut env).expect("build_thunk_env should succeed");
385 assert!(matches!(
386 env.get(&Name::str("Thunk.mk"))
387 .expect("declaration 'Thunk.mk' should exist in env"),
388 Declaration::Axiom { .. }
389 ));
390 }
391 #[test]
392 fn test_thunk_ap() {
393 let mut env = setup_base_env();
394 build_thunk_env(&mut env).expect("build_thunk_env should succeed");
395 assert!(env.get(&Name::str("Thunk.ap")).is_some());
396 }
397 #[test]
398 fn test_thunk_sequence() {
399 let mut env = setup_base_env();
400 build_thunk_env(&mut env).expect("build_thunk_env should succeed");
401 assert!(env.get(&Name::str("Thunk.sequence")).is_some());
402 }
403 #[test]
404 fn test_thunk_delay() {
405 let mut env = setup_base_env();
406 build_thunk_env(&mut env).expect("build_thunk_env should succeed");
407 assert!(env.get(&Name::str("Thunk.delay")).is_some());
408 }
409 #[test]
410 fn test_thunk_count() {
411 let mut env = setup_base_env();
412 build_thunk_env(&mut env).expect("build_thunk_env should succeed");
413 assert!(env.get(&Name::str("Thunk.count")).is_some());
414 }
415 #[test]
416 fn test_thunk_get_or() {
417 let mut env = setup_base_env();
418 build_thunk_env(&mut env).expect("build_thunk_env should succeed");
419 assert!(env.get(&Name::str("Thunk.getOr")).is_some());
420 }
421}
422#[cfg(test)]
423mod thunk_extended_tests {
424 use super::*;
425 #[test]
426 fn test_once_cell_thunk_forces_once() {
427 let count = std::sync::Arc::new(std::sync::Mutex::new(0));
428 let count_clone = count.clone();
429 let mut thunk = OnceCellThunk::new(move || {
430 *count_clone.lock().expect("lock should succeed") += 1;
431 42i32
432 });
433 assert!(!thunk.is_forced());
434 assert_eq!(*thunk.force(), 42);
435 assert!(thunk.is_forced());
436 assert_eq!(*thunk.force(), 42);
437 assert_eq!(*count.lock().expect("lock should succeed"), 1);
438 }
439 #[test]
440 fn test_thunk_seq_lazy() {
441 let mut seq: ThunkSeq<i32> = ThunkSeq::new();
442 seq.push(|| 10);
443 seq.push(|| 20);
444 seq.push(|| 30);
445 assert_eq!(seq.len(), 3);
446 assert_eq!(seq.forced_count(), 0);
447 assert_eq!(seq.get(1), Some(&20));
448 assert_eq!(seq.forced_count(), 1);
449 }
450 #[test]
451 fn test_thunk_seq_out_of_bounds() {
452 let mut seq: ThunkSeq<i32> = ThunkSeq::new();
453 seq.push(|| 1);
454 assert!(seq.get(5).is_none());
455 }
456 #[test]
457 fn test_memo_caches_result() {
458 let call_count = std::sync::Arc::new(std::sync::Mutex::new(0usize));
459 let cc = call_count.clone();
460 let mut memo = Memo::new(move |x: &i32| {
461 *cc.lock().expect("lock should succeed") += 1;
462 x * 2
463 });
464 assert_eq!(*memo.call(&5), 10);
465 assert_eq!(*memo.call(&5), 10);
466 assert_eq!(*call_count.lock().expect("lock should succeed"), 1);
467 assert_eq!(memo.cache_size(), 1);
468 }
469 #[test]
470 fn test_lazy_list_finite() {
471 let mut list = LazyList::from_fn(|i| if i < 5 { Some(i * i) } else { None });
472 let first3 = list.take(3);
473 assert_eq!(first3, vec![0, 1, 4]);
474 }
475 #[test]
476 fn test_lazy_list_get() {
477 let mut list = LazyList::from_fn(|i| Some(i as i32 + 1));
478 assert_eq!(list.get(0), Some(&1));
479 assert_eq!(list.get(4), Some(&5));
480 }
481 #[test]
482 fn test_thunk_tree_leaf() {
483 let t = ThunkTree::leaf(42i32);
484 assert!(t.is_leaf());
485 let leaves = t.leaves();
486 assert_eq!(leaves, vec![42]);
487 }
488 #[test]
489 fn test_thunk_tree_node() {
490 let t: ThunkTree<i32> = ThunkTree::node(|| vec![ThunkTree::leaf(1), ThunkTree::leaf(2)]);
491 assert!(!t.is_leaf());
492 let leaves = t.leaves();
493 assert_eq!(leaves.len(), 2);
494 }
495 #[test]
496 fn test_memo_clear_cache() {
497 let mut memo = Memo::new(|x: &i32| x + 1);
498 memo.call(&1);
499 memo.call(&2);
500 assert_eq!(memo.cache_size(), 2);
501 memo.clear_cache();
502 assert_eq!(memo.cache_size(), 0);
503 }
504}
505#[cfg(test)]
506mod thunk_deferred_tests {
507 use super::*;
508 #[test]
509 fn test_try_thunk_ok() {
510 let mut t: TryThunk<i32, &str> = TryThunk::new(|| Ok(42));
511 assert!(!t.is_forced());
512 assert_eq!(t.force(), &Ok(42));
513 assert!(t.is_forced());
514 }
515 #[test]
516 fn test_try_thunk_err() {
517 let mut t: TryThunk<i32, &str> = TryThunk::new(|| Err("fail"));
518 assert_eq!(t.force(), &Err("fail"));
519 }
520 #[test]
521 fn test_deferred_now_is_ready() {
522 let d = Deferred::now(10i32);
523 assert!(d.is_ready());
524 }
525 #[test]
526 fn test_deferred_later_not_ready() {
527 let d: Deferred<i32> = Deferred::later(0);
528 assert!(!d.is_ready());
529 }
530 #[test]
531 fn test_deferred_resolve_or() {
532 let d = Deferred::now(5i32);
533 assert_eq!(d.resolve_or(|_| 99), 5);
534 let d2: Deferred<i32> = Deferred::later(7);
535 assert_eq!(d2.resolve_or(|id| id as i32), 7);
536 }
537 #[test]
538 fn test_deferred_map() {
539 let d = Deferred::now(3i32);
540 let d2 = d.map(|x| x * 2);
541 assert_eq!(d2.resolve_or(|_| 0), 6);
542 }
543 #[test]
544 fn test_deferred_pool_submit_force() {
545 let mut pool = DeferredPool::new();
546 let id = pool.submit(|| 42i32);
547 assert_eq!(pool.pending_count(), 1);
548 let v = pool.force(id);
549 assert_eq!(v, Some(&42));
550 assert_eq!(pool.resolved_count(), 1);
551 assert_eq!(pool.pending_count(), 0);
552 }
553 #[test]
554 fn test_deferred_pool_force_all() {
555 let mut pool = DeferredPool::new();
556 pool.submit(|| 1i32);
557 pool.submit(|| 2i32);
558 pool.force_all();
559 assert_eq!(pool.pending_count(), 0);
560 assert_eq!(pool.resolved_count(), 2);
561 }
562 #[test]
563 fn test_delayed_steps() {
564 let mut d = Delayed::new(42i32, 3);
565 assert!(!d.is_ready());
566 assert_eq!(d.step(), None);
567 assert_eq!(d.step(), None);
568 assert_eq!(d.step(), Some(42));
569 assert!(d.is_ready());
570 }
571 #[test]
572 fn test_delayed_remaining() {
573 let d = Delayed::new(1i32, 5);
574 assert_eq!(d.remaining(), 5);
575 }
576}
577pub fn build_thunk_theorems(env: &mut Environment) -> Result<(), String> {
581 let prop = Expr::Sort(Level::zero());
582 let type1 = Expr::Sort(Level::succ(Level::zero()));
583 let get_pure_ty = Expr::Pi(
584 BinderInfo::Implicit,
585 Name::str("α"),
586 Box::new(type1.clone()),
587 Box::new(Expr::Pi(
588 BinderInfo::Default,
589 Name::str("x"),
590 Box::new(Expr::BVar(0)),
591 Box::new(prop.clone()),
592 )),
593 );
594 env.add(Declaration::Axiom {
595 name: Name::str("Thunk.get_pure"),
596 univ_params: vec![],
597 ty: get_pure_ty,
598 })
599 .map_err(|e| e.to_string())?;
600 let map_id_ty = Expr::Pi(
601 BinderInfo::Implicit,
602 Name::str("α"),
603 Box::new(type1.clone()),
604 Box::new(Expr::Pi(
605 BinderInfo::Default,
606 Name::str("t"),
607 Box::new(Expr::App(
608 Box::new(Expr::Const(Name::str("Thunk"), vec![])),
609 Box::new(Expr::BVar(0)),
610 )),
611 Box::new(prop.clone()),
612 )),
613 );
614 env.add(Declaration::Axiom {
615 name: Name::str("Thunk.map_id"),
616 univ_params: vec![],
617 ty: map_id_ty,
618 })
619 .map_err(|e| e.to_string())?;
620 let bind_pure_ty = Expr::Pi(
621 BinderInfo::Implicit,
622 Name::str("α"),
623 Box::new(type1.clone()),
624 Box::new(Expr::Pi(
625 BinderInfo::Default,
626 Name::str("x"),
627 Box::new(Expr::BVar(0)),
628 Box::new(prop.clone()),
629 )),
630 );
631 env.add(Declaration::Axiom {
632 name: Name::str("Thunk.bind_pure"),
633 univ_params: vec![],
634 ty: bind_pure_ty,
635 })
636 .map_err(|e| e.to_string())?;
637 Ok(())
638}
639pub fn count_thunk_decls(env: &Environment) -> usize {
641 let names = [
642 "Thunk",
643 "Thunk.mk",
644 "Thunk.get",
645 "Thunk.pure",
646 "Thunk.map",
647 "Thunk.bind",
648 "Thunk.join",
649 "Thunk.zip",
650 "Thunk.and",
651 "Thunk.or",
652 "Thunk.isForced",
653 "Thunk.ap",
654 "Thunk.sequence",
655 "Thunk.delay",
656 "Thunk.count",
657 "Thunk.getOr",
658 "Thunk.get_pure",
659 "Thunk.map_id",
660 "Thunk.bind_pure",
661 ];
662 names
663 .iter()
664 .filter(|&&n| env.get(&Name::str(n)).is_some())
665 .count()
666}
667#[cfg(test)]
668mod thunk_extended_extra_tests {
669 use super::*;
670 fn base() -> Environment {
671 let mut env = Environment::new();
672 let t1 = Expr::Sort(Level::succ(Level::zero()));
673 for name in ["Unit", "Bool", "Prod", "List", "Nat", "Option"] {
674 env.add(Declaration::Axiom {
675 name: Name::str(name),
676 univ_params: vec![],
677 ty: t1.clone(),
678 })
679 .unwrap_or(());
680 }
681 build_thunk_env(&mut env).expect("build_thunk_env should succeed");
682 env
683 }
684 #[test]
685 fn test_build_thunk_theorems() {
686 let mut env = base();
687 assert!(build_thunk_theorems(&mut env).is_ok());
688 assert!(env.get(&Name::str("Thunk.get_pure")).is_some());
689 }
690 #[test]
691 fn test_thunk_map_id() {
692 let mut env = base();
693 build_thunk_theorems(&mut env).expect("build_thunk_theorems should succeed");
694 assert!(env.get(&Name::str("Thunk.map_id")).is_some());
695 }
696 #[test]
697 fn test_thunk_bind_pure() {
698 let mut env = base();
699 build_thunk_theorems(&mut env).expect("build_thunk_theorems should succeed");
700 assert!(env.get(&Name::str("Thunk.bind_pure")).is_some());
701 }
702 #[test]
703 fn test_count_thunk_decls_base() {
704 let env = base();
705 let n = count_thunk_decls(&env);
706 assert!(n >= 16);
707 }
708 #[test]
709 fn test_count_thunk_decls_with_theorems() {
710 let mut env = base();
711 build_thunk_theorems(&mut env).expect("build_thunk_theorems should succeed");
712 let n = count_thunk_decls(&env);
713 assert!(n >= 19);
714 }
715 #[test]
716 fn test_deferred_pool_missing_id() {
717 let mut pool = DeferredPool::<i32>::new();
718 let v = pool.force(999);
719 assert!(v.is_none());
720 }
721 #[test]
722 fn test_thunk_seq_is_empty() {
723 let seq: ThunkSeq<i32> = ThunkSeq::new();
724 assert!(seq.is_empty());
725 }
726 #[test]
727 fn test_lazy_list_produced_count() {
728 let mut list = LazyList::from_fn(|i| Some(i as i32));
729 list.take(5);
730 assert_eq!(list.produced_count(), 5);
731 }
732}
733pub fn thk_ext_prop() -> Expr {
734 Expr::Sort(Level::zero())
735}
736pub fn thk_ext_ab_prop(body: Expr) -> Expr {
737 Expr::Pi(
738 BinderInfo::Implicit,
739 Name::str("α"),
740 Box::new(type1()),
741 Box::new(Expr::Pi(
742 BinderInfo::Implicit,
743 Name::str("β"),
744 Box::new(type1()),
745 Box::new(body),
746 )),
747 )
748}
749pub fn thk_ext_abc_implicit(body: Expr) -> Expr {
750 Expr::Pi(
751 BinderInfo::Implicit,
752 Name::str("α"),
753 Box::new(type1()),
754 Box::new(Expr::Pi(
755 BinderInfo::Implicit,
756 Name::str("β"),
757 Box::new(type1()),
758 Box::new(Expr::Pi(
759 BinderInfo::Implicit,
760 Name::str("γ"),
761 Box::new(type1()),
762 Box::new(body),
763 )),
764 )),
765 )
766}
767pub fn axiom_comonad_extract_ty() -> Expr {
771 alpha_implicit(Expr::Pi(
772 BinderInfo::Default,
773 Name::str("t"),
774 Box::new(thunk_of(Expr::BVar(0))),
775 Box::new(Expr::BVar(1)),
776 ))
777}
778pub fn axiom_comonad_duplicate_ty() -> Expr {
782 alpha_implicit(Expr::Pi(
783 BinderInfo::Default,
784 Name::str("t"),
785 Box::new(thunk_of(Expr::BVar(0))),
786 Box::new(thunk_of(thunk_of(Expr::BVar(1)))),
787 ))
788}
789pub fn axiom_comonad_extend_ty() -> Expr {
793 let coalg = Expr::Pi(
794 BinderInfo::Default,
795 Name::str("_"),
796 Box::new(thunk_of(Expr::BVar(1))),
797 Box::new(Expr::BVar(1)),
798 );
799 ab_implicit(Expr::Pi(
800 BinderInfo::Default,
801 Name::str("f"),
802 Box::new(coalg),
803 Box::new(Expr::Pi(
804 BinderInfo::Default,
805 Name::str("t"),
806 Box::new(thunk_of(Expr::BVar(2))),
807 Box::new(thunk_of(Expr::BVar(2))),
808 )),
809 ))
810}
811pub fn axiom_comonad_extract_duplicate_ty() -> Expr {
815 alpha_implicit(Expr::Pi(
816 BinderInfo::Default,
817 Name::str("t"),
818 Box::new(thunk_of(Expr::BVar(0))),
819 Box::new(thk_ext_prop()),
820 ))
821}
822pub fn axiom_comonad_duplicate_extract_ty() -> Expr {
826 alpha_implicit(Expr::Pi(
827 BinderInfo::Default,
828 Name::str("t"),
829 Box::new(thunk_of(Expr::BVar(0))),
830 Box::new(thk_ext_prop()),
831 ))
832}
833pub fn axiom_comonad_duplicate_duplicate_ty() -> Expr {
837 alpha_implicit(Expr::Pi(
838 BinderInfo::Default,
839 Name::str("t"),
840 Box::new(thunk_of(Expr::BVar(0))),
841 Box::new(thk_ext_prop()),
842 ))
843}
844pub fn axiom_cokleisli_compose_ty() -> Expr {
848 let f_ty = Expr::Pi(
849 BinderInfo::Default,
850 Name::str("_"),
851 Box::new(thunk_of(Expr::BVar(2))),
852 Box::new(Expr::BVar(2)),
853 );
854 let g_ty = Expr::Pi(
855 BinderInfo::Default,
856 Name::str("_"),
857 Box::new(thunk_of(Expr::BVar(2))),
858 Box::new(Expr::BVar(2)),
859 );
860 thk_ext_abc_implicit(Expr::Pi(
861 BinderInfo::Default,
862 Name::str("f"),
863 Box::new(f_ty),
864 Box::new(Expr::Pi(
865 BinderInfo::Default,
866 Name::str("g"),
867 Box::new(g_ty),
868 Box::new(Expr::Pi(
869 BinderInfo::Default,
870 Name::str("t"),
871 Box::new(thunk_of(Expr::BVar(4))),
872 Box::new(Expr::BVar(4)),
873 )),
874 )),
875 ))
876}
877pub fn axiom_functor_map_comp_ty() -> Expr {
881 let f_ty = Expr::Pi(
882 BinderInfo::Default,
883 Name::str("_"),
884 Box::new(Expr::BVar(2)),
885 Box::new(Expr::BVar(2)),
886 );
887 let g_ty = Expr::Pi(
888 BinderInfo::Default,
889 Name::str("_"),
890 Box::new(Expr::BVar(2)),
891 Box::new(Expr::BVar(2)),
892 );
893 thk_ext_abc_implicit(Expr::Pi(
894 BinderInfo::Default,
895 Name::str("g"),
896 Box::new(g_ty),
897 Box::new(Expr::Pi(
898 BinderInfo::Default,
899 Name::str("f"),
900 Box::new(f_ty),
901 Box::new(Expr::Pi(
902 BinderInfo::Default,
903 Name::str("t"),
904 Box::new(thunk_of(Expr::BVar(4))),
905 Box::new(thunk_of(Expr::BVar(4))),
906 )),
907 )),
908 ))
909}
910pub fn axiom_get_mk_ty() -> Expr {
914 alpha_implicit(Expr::Pi(
915 BinderInfo::Default,
916 Name::str("f"),
917 Box::new(unit_to(Expr::BVar(1))),
918 Box::new(thk_ext_prop()),
919 ))
920}
921pub fn axiom_mk_get_ty() -> Expr {
925 alpha_implicit(Expr::Pi(
926 BinderInfo::Default,
927 Name::str("t"),
928 Box::new(thunk_of(Expr::BVar(0))),
929 Box::new(thk_ext_prop()),
930 ))
931}
932pub fn axiom_sharing_ty() -> Expr {
936 alpha_implicit(Expr::Pi(
937 BinderInfo::Default,
938 Name::str("t"),
939 Box::new(thunk_of(Expr::BVar(0))),
940 Box::new(thk_ext_prop()),
941 ))
942}
943pub fn axiom_force_once_ty() -> Expr {
947 alpha_implicit(Expr::Pi(
948 BinderInfo::Default,
949 Name::str("t"),
950 Box::new(thunk_of(Expr::BVar(0))),
951 Box::new(Expr::Pi(
952 BinderInfo::Default,
953 Name::str("_h"),
954 Box::new(bool_ty()),
955 Box::new(thk_ext_prop()),
956 )),
957 ))
958}
959pub fn axiom_cbn_beta_ty() -> Expr {
963 alpha_implicit(Expr::Pi(
964 BinderInfo::Default,
965 Name::str("f"),
966 Box::new(unit_to(Expr::BVar(1))),
967 Box::new(thk_ext_prop()),
968 ))
969}
970pub fn axiom_presheaf_restriction_ty() -> Expr {
974 let f_ty = Expr::Pi(
975 BinderInfo::Default,
976 Name::str("_"),
977 Box::new(Expr::BVar(0)),
978 Box::new(Expr::BVar(1)),
979 );
980 ab_implicit(Expr::Pi(
981 BinderInfo::Default,
982 Name::str("r"),
983 Box::new(f_ty),
984 Box::new(Expr::Pi(
985 BinderInfo::Default,
986 Name::str("t"),
987 Box::new(thunk_of(Expr::BVar(2))),
988 Box::new(thunk_of(Expr::BVar(2))),
989 )),
990 ))
991}
992pub fn axiom_productive_step_ty() -> Expr {
996 let step_fn = Expr::Pi(
997 BinderInfo::Default,
998 Name::str("_"),
999 Box::new(nat_ty()),
1000 Box::new(thunk_of(Expr::BVar(1))),
1001 );
1002 alpha_implicit(Expr::Pi(
1003 BinderInfo::Default,
1004 Name::str("gen"),
1005 Box::new(step_fn),
1006 Box::new(Expr::Pi(
1007 BinderInfo::Default,
1008 Name::str("n"),
1009 Box::new(nat_ty()),
1010 Box::new(thunk_of(Expr::BVar(2))),
1011 )),
1012 ))
1013}
1014pub fn axiom_guarded_next_ty() -> Expr {
1018 alpha_implicit(Expr::Pi(
1019 BinderInfo::Default,
1020 Name::str("t"),
1021 Box::new(thunk_of(Expr::BVar(0))),
1022 Box::new(thunk_of(Expr::BVar(1))),
1023 ))
1024}
1025pub fn axiom_game_question_ty() -> Expr {
1029 alpha_implicit(Expr::Pi(
1030 BinderInfo::Default,
1031 Name::str("t"),
1032 Box::new(thunk_of(Expr::BVar(0))),
1033 Box::new(nat_ty()),
1034 ))
1035}
1036pub fn axiom_game_answer_ty() -> Expr {
1040 alpha_implicit(Expr::Pi(
1041 BinderInfo::Default,
1042 Name::str("t"),
1043 Box::new(thunk_of(Expr::BVar(0))),
1044 Box::new(Expr::BVar(1)),
1045 ))
1046}
1047pub fn axiom_itree_ret_ty() -> Expr {
1051 alpha_implicit(Expr::Pi(
1052 BinderInfo::Default,
1053 Name::str("x"),
1054 Box::new(Expr::BVar(0)),
1055 Box::new(thunk_of(Expr::BVar(1))),
1056 ))
1057}
1058pub fn axiom_itree_tau_ty() -> Expr {
1062 alpha_implicit(Expr::Pi(
1063 BinderInfo::Default,
1064 Name::str("t"),
1065 Box::new(thunk_of(Expr::BVar(0))),
1066 Box::new(thunk_of(Expr::BVar(1))),
1067 ))
1068}
1069pub fn axiom_itree_vis_ty() -> Expr {
1073 let cont = Expr::Pi(
1074 BinderInfo::Default,
1075 Name::str("_"),
1076 Box::new(nat_ty()),
1077 Box::new(thunk_of(Expr::BVar(1))),
1078 );
1079 alpha_implicit(Expr::Pi(
1080 BinderInfo::Default,
1081 Name::str("ev"),
1082 Box::new(nat_ty()),
1083 Box::new(Expr::Pi(
1084 BinderInfo::Default,
1085 Name::str("k"),
1086 Box::new(cont),
1087 Box::new(thunk_of(Expr::BVar(2))),
1088 )),
1089 ))
1090}
1091pub fn axiom_lazy_nat_stream_ty() -> Expr {
1095 Expr::Pi(
1096 BinderInfo::Default,
1097 Name::str("n"),
1098 Box::new(nat_ty()),
1099 Box::new(thunk_of(nat_ty())),
1100 )
1101}
1102pub fn axiom_memo_table_lookup_ty() -> Expr {
1106 alpha_implicit(Expr::Pi(
1107 BinderInfo::Default,
1108 Name::str("tbl"),
1109 Box::new(list_of(thunk_of(Expr::BVar(0)))),
1110 Box::new(Expr::Pi(
1111 BinderInfo::Default,
1112 Name::str("idx"),
1113 Box::new(nat_ty()),
1114 Box::new(option_of(Expr::BVar(2))),
1115 )),
1116 ))
1117}
1118pub fn axiom_memo_table_insert_ty() -> Expr {
1122 alpha_implicit(Expr::Pi(
1123 BinderInfo::Default,
1124 Name::str("tbl"),
1125 Box::new(list_of(thunk_of(Expr::BVar(0)))),
1126 Box::new(Expr::Pi(
1127 BinderInfo::Default,
1128 Name::str("idx"),
1129 Box::new(nat_ty()),
1130 Box::new(Expr::Pi(
1131 BinderInfo::Default,
1132 Name::str("v"),
1133 Box::new(thunk_of(Expr::BVar(2))),
1134 Box::new(list_of(thunk_of(Expr::BVar(3)))),
1135 )),
1136 )),
1137 ))
1138}
1139pub fn axiom_lazy_tree_branch_ty() -> Expr {
1143 alpha_implicit(Expr::Pi(
1144 BinderInfo::Default,
1145 Name::str("l"),
1146 Box::new(thunk_of(Expr::BVar(0))),
1147 Box::new(Expr::Pi(
1148 BinderInfo::Default,
1149 Name::str("r"),
1150 Box::new(thunk_of(Expr::BVar(1))),
1151 Box::new(thunk_of(Expr::BVar(2))),
1152 )),
1153 ))
1154}
1155pub fn axiom_lazy_tree_depth_ty() -> Expr {
1159 alpha_implicit(Expr::Pi(
1160 BinderInfo::Default,
1161 Name::str("t"),
1162 Box::new(thunk_of(Expr::BVar(0))),
1163 Box::new(nat_ty()),
1164 ))
1165}
1166pub fn axiom_cofree_out_ty() -> Expr {
1170 alpha_implicit(Expr::Pi(
1171 BinderInfo::Default,
1172 Name::str("t"),
1173 Box::new(thunk_of(Expr::BVar(0))),
1174 Box::new(Expr::BVar(1)),
1175 ))
1176}
1177pub fn axiom_cofree_tail_ty() -> Expr {
1181 alpha_implicit(Expr::Pi(
1182 BinderInfo::Default,
1183 Name::str("t"),
1184 Box::new(thunk_of(Expr::BVar(0))),
1185 Box::new(list_of(thunk_of(Expr::BVar(1)))),
1186 ))
1187}
1188pub fn axiom_whnf_step_ty() -> Expr {
1192 alpha_implicit(Expr::Pi(
1193 BinderInfo::Default,
1194 Name::str("t"),
1195 Box::new(thunk_of(Expr::BVar(0))),
1196 Box::new(thunk_of(Expr::BVar(1))),
1197 ))
1198}
1199pub fn axiom_ap_naturality_ty() -> Expr {
1203 thk_ext_ab_prop(thk_ext_prop())
1204}
1205pub fn axiom_bind_assoc_ty() -> Expr {
1209 thk_ext_abc_implicit(thk_ext_prop())
1210}
1211pub fn axiom_pure_map_ty() -> Expr {
1215 thk_ext_ab_prop(thk_ext_prop())
1216}
1217pub fn axiom_lazy_fix_ty() -> Expr {
1221 let coalg = Expr::Pi(
1222 BinderInfo::Default,
1223 Name::str("_"),
1224 Box::new(thunk_of(Expr::BVar(1))),
1225 Box::new(Expr::BVar(1)),
1226 );
1227 alpha_implicit(Expr::Pi(
1228 BinderInfo::Default,
1229 Name::str("f"),
1230 Box::new(coalg),
1231 Box::new(thunk_of(Expr::BVar(1))),
1232 ))
1233}
1234pub fn axiom_omega_limit_ty() -> Expr {
1238 let seq_fn = Expr::Pi(
1239 BinderInfo::Default,
1240 Name::str("_"),
1241 Box::new(nat_ty()),
1242 Box::new(thunk_of(Expr::BVar(1))),
1243 );
1244 alpha_implicit(Expr::Pi(
1245 BinderInfo::Default,
1246 Name::str("chain"),
1247 Box::new(seq_fn),
1248 Box::new(thunk_of(Expr::BVar(1))),
1249 ))
1250}
1251pub fn axiom_scott_continuity_ty() -> Expr {
1255 alpha_implicit(thk_ext_prop())
1256}
1257pub fn axiom_kleisli_compose_ty() -> Expr {
1261 let f_ty = Expr::Pi(
1262 BinderInfo::Default,
1263 Name::str("_"),
1264 Box::new(Expr::BVar(2)),
1265 Box::new(thunk_of(Expr::BVar(2))),
1266 );
1267 let g_ty = Expr::Pi(
1268 BinderInfo::Default,
1269 Name::str("_"),
1270 Box::new(Expr::BVar(2)),
1271 Box::new(thunk_of(Expr::BVar(2))),
1272 );
1273 thk_ext_abc_implicit(Expr::Pi(
1274 BinderInfo::Default,
1275 Name::str("f"),
1276 Box::new(f_ty),
1277 Box::new(Expr::Pi(
1278 BinderInfo::Default,
1279 Name::str("g"),
1280 Box::new(g_ty),
1281 Box::new(Expr::Pi(
1282 BinderInfo::Default,
1283 Name::str("x"),
1284 Box::new(Expr::BVar(4)),
1285 Box::new(thunk_of(Expr::BVar(4))),
1286 )),
1287 )),
1288 ))
1289}
1290pub fn axiom_force_deterministic_ty() -> Expr {
1294 alpha_implicit(Expr::Pi(
1295 BinderInfo::Default,
1296 Name::str("t"),
1297 Box::new(thunk_of(Expr::BVar(0))),
1298 Box::new(Expr::Pi(
1299 BinderInfo::Default,
1300 Name::str("n"),
1301 Box::new(nat_ty()),
1302 Box::new(thk_ext_prop()),
1303 )),
1304 ))
1305}
1306pub fn register_thunk_extended(env: &mut Environment) -> Result<(), String> {
1308 let entries: &[(&str, fn() -> Expr)] = &[
1309 ("Thunk.extract", axiom_comonad_extract_ty),
1310 ("Thunk.duplicate", axiom_comonad_duplicate_ty),
1311 ("Thunk.extend", axiom_comonad_extend_ty),
1312 (
1313 "Thunk.extract_duplicate",
1314 axiom_comonad_extract_duplicate_ty,
1315 ),
1316 (
1317 "Thunk.duplicate_extract",
1318 axiom_comonad_duplicate_extract_ty,
1319 ),
1320 (
1321 "Thunk.duplicate_duplicate",
1322 axiom_comonad_duplicate_duplicate_ty,
1323 ),
1324 ("Thunk.cokleisli_compose", axiom_cokleisli_compose_ty),
1325 ("Thunk.map_comp", axiom_functor_map_comp_ty),
1326 ("Thunk.get_mk", axiom_get_mk_ty),
1327 ("Thunk.mk_get", axiom_mk_get_ty),
1328 ("Thunk.sharing", axiom_sharing_ty),
1329 ("Thunk.force_once", axiom_force_once_ty),
1330 ("Thunk.cbn_beta", axiom_cbn_beta_ty),
1331 ("Thunk.presheaf_restriction", axiom_presheaf_restriction_ty),
1332 ("Thunk.productive_step", axiom_productive_step_ty),
1333 ("Thunk.guarded_next", axiom_guarded_next_ty),
1334 ("Thunk.game_question", axiom_game_question_ty),
1335 ("Thunk.game_answer", axiom_game_answer_ty),
1336 ("Thunk.itree_ret", axiom_itree_ret_ty),
1337 ("Thunk.itree_tau", axiom_itree_tau_ty),
1338 ("Thunk.itree_vis", axiom_itree_vis_ty),
1339 ("Thunk.lazy_nat_stream", axiom_lazy_nat_stream_ty),
1340 ("Thunk.memoTable_lookup", axiom_memo_table_lookup_ty),
1341 ("Thunk.memoTable_insert", axiom_memo_table_insert_ty),
1342 ("Thunk.lazyTree_branch", axiom_lazy_tree_branch_ty),
1343 ("Thunk.lazyTree_depth", axiom_lazy_tree_depth_ty),
1344 ("Thunk.cofree_out", axiom_cofree_out_ty),
1345 ("Thunk.cofree_tail", axiom_cofree_tail_ty),
1346 ("Thunk.whnf_step", axiom_whnf_step_ty),
1347 ("Thunk.ap_naturality", axiom_ap_naturality_ty),
1348 ("Thunk.bind_assoc", axiom_bind_assoc_ty),
1349 ("Thunk.pure_map", axiom_pure_map_ty),
1350 ("Thunk.lazy_fix", axiom_lazy_fix_ty),
1351 ("Thunk.omega_limit", axiom_omega_limit_ty),
1352 ("Thunk.scott_continuity", axiom_scott_continuity_ty),
1353 ("Thunk.kleisli_compose", axiom_kleisli_compose_ty),
1354 ("Thunk.force_deterministic", axiom_force_deterministic_ty),
1355 ];
1356 for (name, ty_fn) in entries {
1357 axiom(env, name, ty_fn())?;
1358 }
1359 Ok(())
1360}
1361#[cfg(test)]
1362mod thunk_comonad_tests {
1363 use super::*;
1364 fn base_env_for_ext() -> Environment {
1365 let mut env = Environment::new();
1366 let t1 = type1();
1367 for name in ["Unit", "Bool", "Prod", "List", "Nat", "Option"] {
1368 env.add(Declaration::Axiom {
1369 name: Name::str(name),
1370 univ_params: vec![],
1371 ty: t1.clone(),
1372 })
1373 .unwrap_or(());
1374 }
1375 build_thunk_env(&mut env).expect("build_thunk_env should succeed");
1376 env
1377 }
1378 #[test]
1379 fn test_register_thunk_extended_all_registered() {
1380 let mut env = base_env_for_ext();
1381 let result = register_thunk_extended(&mut env);
1382 assert!(
1383 result.is_ok(),
1384 "register_thunk_extended failed: {:?}",
1385 result
1386 );
1387 assert!(env.get(&Name::str("Thunk.extract")).is_some());
1388 assert!(env.get(&Name::str("Thunk.duplicate")).is_some());
1389 assert!(env.get(&Name::str("Thunk.extend")).is_some());
1390 assert!(env.get(&Name::str("Thunk.cokleisli_compose")).is_some());
1391 assert!(env.get(&Name::str("Thunk.itree_ret")).is_some());
1392 assert!(env.get(&Name::str("Thunk.lazy_fix")).is_some());
1393 assert!(env.get(&Name::str("Thunk.omega_limit")).is_some());
1394 }
1395 #[test]
1396 fn test_comonad_ctx_extract() {
1397 let ctx = ComonadCtx::new("env", 42i32);
1398 assert_eq!(ctx.extract(), 42);
1399 }
1400 #[test]
1401 fn test_comonad_ctx_map() {
1402 let ctx = ComonadCtx::new("env", 10i32);
1403 let ctx2 = ctx.map(|x| x * 2);
1404 assert_eq!(ctx2.extract(), 20);
1405 }
1406 #[test]
1407 fn test_comonad_ctx_extend() {
1408 let ctx = ComonadCtx::new(0u32, 5i32);
1409 let ctx2 = ctx.extend(|c| c.extract() + 1);
1410 assert_eq!(ctx2.extract(), 6);
1411 }
1412 #[test]
1413 fn test_game_arena_ask_answer() {
1414 let mut arena = GameArena::new();
1415 let q = arena.ask();
1416 arena.answer(q, "42");
1417 assert_eq!(arena.get_answer(q), Some("42"));
1418 assert!(arena.is_complete());
1419 }
1420 #[test]
1421 fn test_game_arena_incomplete() {
1422 let mut arena = GameArena::new();
1423 let _q = arena.ask();
1424 assert!(!arena.is_complete());
1425 }
1426 #[test]
1427 fn test_lazy_stream_take() {
1428 let stream: LazyStream<i32> = LazyStream::cons(1, || {
1429 LazyStream::cons(2, || LazyStream::cons(3, || LazyStream::nil()))
1430 });
1431 let elems = stream.take(3);
1432 assert_eq!(elems, vec![1, 2, 3]);
1433 }
1434 #[test]
1435 fn test_lazy_stream_nil() {
1436 let s: LazyStream<i32> = LazyStream::nil();
1437 assert!(s.is_nil());
1438 }
1439 #[test]
1440 fn test_thunk_kleisli_apply() {
1441 let k = ThunkKleisli::new(|x: i32| if x > 0 { Some(x * 2) } else { None });
1442 assert_eq!(k.apply(3), Some(6));
1443 assert_eq!(k.apply(-1), None);
1444 }
1445 #[test]
1446 fn test_thunk_kleisli_compose() {
1447 let k1 = ThunkKleisli::new(|x: i32| Some(x + 1));
1448 let k2 = ThunkKleisli::new(|x: i32| Some(x * 3));
1449 let composed = k1.compose(k2);
1450 assert_eq!(composed.apply(4), Some(15));
1451 }
1452 #[test]
1453 fn test_itree_node_ret() {
1454 let node: ITreeNode<(), i32> = ITreeNode::ret(99);
1455 assert_eq!(node.run(10), Some(99));
1456 }
1457 #[test]
1458 fn test_itree_node_tau() {
1459 let node: ITreeNode<(), i32> = ITreeNode::tau(|| ITreeNode::tau(|| ITreeNode::ret(7)));
1460 assert_eq!(node.run(10), Some(7));
1461 }
1462 #[test]
1463 fn test_itree_node_vis_blocks() {
1464 let node: ITreeNode<&str, i32> = ITreeNode::vis("read", |_| ITreeNode::ret(0));
1465 assert_eq!(node.run(10), None);
1466 }
1467 #[test]
1468 fn test_axiom_comonad_extract_ty_is_pi() {
1469 let ty = axiom_comonad_extract_ty();
1470 assert!(matches!(ty, Expr::Pi(..)));
1471 }
1472 #[test]
1473 fn test_axiom_itree_vis_ty_is_pi() {
1474 let ty = axiom_itree_vis_ty();
1475 assert!(matches!(ty, Expr::Pi(..)));
1476 }
1477}
1478#[cfg(test)]
1479mod tests_thunk_extra {
1480 use super::*;
1481 #[test]
1482 fn test_memo_thunk() {
1483 let mut t = MemoThunk::<i32>::new();
1484 assert!(!t.is_forced());
1485 let v = t.force_with(|| 42);
1486 assert_eq!(v, 42);
1487 assert!(t.is_forced());
1488 let v2 = t.force_with(|| 99);
1489 assert_eq!(v2, 42);
1490 t.reset();
1491 assert!(!t.is_forced());
1492 }
1493 #[test]
1494 fn test_cofree_stream() {
1495 let s = CofreeStream::from_periodic(vec![1, 2, 3]);
1496 assert_eq!(s.extract(), &1);
1497 assert_eq!(*s.nth(0), 1);
1498 assert_eq!(*s.nth(1), 2);
1499 assert_eq!(*s.nth(3), 1);
1500 let t = s.tail();
1501 assert_eq!(t.extract(), &2);
1502 let doubled = s.map(|x| x * 2);
1503 assert_eq!(*doubled.nth(0), 2);
1504 assert_eq!(*doubled.nth(1), 4);
1505 }
1506 #[test]
1507 fn test_compute_node() {
1508 let mut node = ComputeNode::<i32>::new("result");
1509 node.add_dep("input");
1510 assert!(node.is_dirty);
1511 assert!(node.get().is_none());
1512 node.set_value(100);
1513 assert!(!node.is_dirty);
1514 assert_eq!(*node.get().expect("get should succeed"), 100);
1515 node.invalidate();
1516 assert!(node.is_dirty);
1517 }
1518 #[test]
1519 fn test_thunk_app_functor() {
1520 let t = ThunkApp::pure(5);
1521 let t2 = t.map(|x| x * 2);
1522 assert_eq!(t2.val, 10);
1523 let tf = ThunkApp::pure(|x: i32| x + 1);
1524 let t3 = ThunkApp::pure(10);
1525 let result = t3.ap(tf);
1526 assert_eq!(result.val, 11);
1527 }
1528 #[test]
1529 fn test_thunk_state_blackhole() {
1530 let mut s = ThunkState::<i32>::Unevaluated;
1531 assert!(s.enter());
1532 assert!(s.is_blackhole());
1533 assert!(!s.enter());
1534 s.fill(42);
1535 assert!(s.is_value());
1536 assert_eq!(s.get_value(), Some(&42));
1537 }
1538 #[test]
1539 fn test_suspension_monad() {
1540 let s: Suspension<i32> = Suspension::pure(10);
1541 assert!(s.is_done());
1542 let s2 = s.map(|x| x + 5);
1543 assert!(s2.is_done());
1544 let pend: Suspension<i32> = Suspension::suspend("waiting");
1545 let pend2 = pend.map(|x| x * 2);
1546 assert!(!pend2.is_done());
1547 let chained = Suspension::pure(3).and_then(|x| Suspension::pure(x * x));
1548 assert!(chained.is_done());
1549 }
1550}