adapton/macros.rs
1/*! Macros to make using the `engine` module's interface more
2 ergonomic.
3
4*/
5
6// Adapton uses memoization under the covers, which needs an efficient
7// mechanism to search for function pointers and compare them for
8// equality.
9//
10// Meanwhile, Rust does not provide Eq and Hash implementations for
11// trait Fn. So, to identify Rust functions as values that we can
12// hash and compare, we need to bundle additional static information
13// along with the function pointer as use this data as a proxy for the
14// function itself. The idea is that this information uniquely
15// identifies the function pointer (i.e., two distinct functions will
16// always have two distinct identities).
17//
18
19use std::cell::RefCell;
20use std::fmt::{Formatter,Result,Debug};
21
22#[doc(hidden)]
23pub use std::rc::Rc;
24
25thread_local!(static NAME_COUNTER: RefCell<usize> = RefCell::new(0));
26
27//#[doc(hidden)]
28/// Program points: used by the Adapton engine to distinguish
29/// different memoized Rust functions, which cannot be directly
30/// compared for equality, nor hashed.
31#[derive(PartialEq,Eq,Clone,Hash)]
32pub struct ProgPt {
33 // Symbolic identity, in Rust semantics:
34 pub symbol:&'static str, // via stringify!(...)
35 // module:Rc<String>, // via module!()
36
37 // Location in local filesystem:
38 //pub file:&'static str, // via file!()
39 //pub line:u32, // via line!()
40 //pub column:u32, // via column!()
41}
42
43impl Debug for ProgPt {
44 fn fmt(&self, f: &mut Formatter) -> Result { self.symbol.fmt(f) }
45}
46
47#[doc(hidden)]
48/// Convenience function: A global counter for creating unique names,
49/// e.g., in unit tests. Avoid using this outside of unit tests.
50pub fn bump_name_counter() -> usize {
51 NAME_COUNTER.with(|ctr|{let c = *ctr.borrow(); *ctr.borrow_mut() = c + 1; c})
52}
53
54#[doc(hidden)]
55/// Generate a "program point", used as a unique ID for memoized functions.
56#[macro_export]
57macro_rules! prog_pt {
58 ($symbol:expr) => {{
59 ProgPt{
60 symbol:$symbol,
61 //file:file!(),
62 //line:line!(),
63 //column:column!(),
64 }
65 }}
66}
67
68/**
69Convenience wrapper for `engine::force`
70
71Example usage:
72
73```
74# #[macro_use] extern crate adapton;
75# fn main() {
76# use adapton::macros::*;
77# use adapton::engine::*;
78# manage::init_dcg();
79let c = cell!(123);
80assert_eq!(get!(c), 123);
81assert_eq!(get!(c), force(&c));
82# }
83```
84
85*/
86#[macro_export]
87macro_rules! get {
88 ($art:expr) => {{
89 force(&($art))
90 }}
91 ;
92 ($art:expr, $cycle_out:expr) => {{
93 force_cycle(&($art), Some($cycle_out))
94 }}
95}
96
97/**
98Convenience wrappers for `engine::cell`.
99
100## Optional-name version
101
102In this verion, supply an expression `optional_name` of type
103`Option<Name>` to specify the name for the cell, created by either
104`cell` or `put`, in the case that `optional_name` is `Some(name)` or
105`None`, respectively:
106
107```
108# #[macro_use] extern crate adapton;
109# fn main() {
110# use adapton::macros::*;
111# use adapton::engine::*;
112# manage::init_dcg();
113let n = name_of_str(stringify!(c));
114let c = cell!([Some(n)]? 123);
115
116assert_eq!(get!(c), 123);
117assert_eq!(get!(c), force(&c));
118
119let c = cell!([None]? 123);
120
121assert_eq!(get!(c), 123);
122assert_eq!(get!(c), force(&c));
123# }
124```
125
126## Explicit-names version:
127
128In this verion, use `[ name ]` to specify the cell's name is `name`:
129
130```
131# #[macro_use] extern crate adapton;
132# fn main() {
133# use adapton::macros::*;
134# use adapton::engine::*;
135# manage::init_dcg();
136let c = cell!([c] 123);
137
138assert_eq!(get!(c), 123);
139assert_eq!(get!(c), force(&c));
140# }
141```
142
143## Global counter version:
144
145Uses a global counter to choose a unique name. Important note: This
146_may_ be appopriate for the Editor role, but is _never appropriate for
147the Archivist role_.
148
149```
150# #[macro_use] extern crate adapton;
151# fn main() {
152# use adapton::macros::*;
153# use adapton::engine::*;
154# manage::init_dcg();
155let c = cell!( 123 );
156
157assert_eq!( get!(c), 123 );
158assert_eq!( get!(c), force(&c) );
159# }
160```
161
162*/
163
164#[macro_export]
165macro_rules! cell {
166 ( $value:expr ) => {{
167 cell(name_of_usize(bump_name_counter()), $value)
168 }}
169 ;
170 ( [ $nm:expr ] ? $value:expr ) => {{
171 match $nm { Some(n) => cell(n, $value), None => put($value) }
172 }}
173 ;
174 ( [ $nm:ident ] $value:expr ) => {{
175 cell(name_of_str(stringify!($nm)), $value)
176 }}
177}
178
179
180/** Wrappers for `engine::thunk`.
181
182Thunks
183=======================
184
185The following form is preferred:
186
187`thunk!( [ optional_name ]? fnexpr ; lab1 : arg1, ..., labk : argk )`
188
189It accepts an optional name, of type `Option<Name>`, and a function
190`fnexpr`, of type `Fn(A1,...,Ak) -> B`.
191
192The arguments `arg1,...,argk` have types `A1,...,Ak`. Like the other
193thunk and memoization forms, this form requires that the programmer
194provide a label `labi` for each argument `argi`.
195
196Example 1
197----------
198
199The programmer specifies the optional name `opnm`, function expression
200`max`, and two labeled arguments `x` and `y`:
201
202```
203# #[macro_use] extern crate adapton;
204# fn main() {
205# use adapton::macros::*;
206# use adapton::engine::*;
207# manage::init_dcg();
208fn max(a:usize,b:usize) -> usize {
209 if a > b { a } else { b }
210};
211let opnm : Option<Name> = Some(name_unit());
212let t : Art<usize> = thunk!([opnm]? max ; x:10, y:20 );
213assert_eq!(get!(t), 20);
214# }
215```
216
217Example 2
218----------
219
220The function expression need not be pre-declared:
221
222```
223# #[macro_use] extern crate adapton;
224# fn main() {
225# use adapton::macros::*;
226# use adapton::engine::*;
227# manage::init_dcg();
228let opnm : Option<Name> = Some(name_unit());
229let t : Art<usize> = thunk!([opnm]?
230 |x,y| if x > y { x } else { y };
231 x:10, y:20 );
232assert_eq!(get!(t), 20);
233# }
234```
235
236Example 3
237----------
238Sometimes thunks just consist of a body expression, without separated arguments:
239
240```
241# #[macro_use] extern crate adapton;
242# fn main() {
243# use adapton::macros::*;
244# use adapton::engine::*;
245# manage::init_dcg();
246let x : Art<usize> = cell!([x] 10);
247let y : Art<usize> = cell!([y] 20);
248let t : Art<usize> = thunk![[Some(name_unit())]?
249 if get!(x) > get!(y) { get!(x) } else { get!(y) } ];
250assert_eq!(get!(t), 20);
251# }
252```
253
254Convenience forms, for examples
255================================
256
257**Example 4**:
258
259We can use the Rust symbol `t` as the name to repeat Example 2
260above, as follows:
261
262```
263# #[macro_use] extern crate adapton;
264# fn main() {
265# use adapton::macros::*;
266# use adapton::engine::*;
267# manage::init_dcg();
268let t : Art<usize> = thunk!([t]
269 |x,y| if x > y { x } else { y };
270 x:10, y:20 );
271assert_eq!(get!(t), 20);
272# }
273```
274
275**Example 5**
276
277We can use the Rust symbol `t` as the name to repeat Example 3
278above, as follows:
279
280```
281# #[macro_use] extern crate adapton;
282# fn main() {
283# use adapton::macros::*;
284# use adapton::engine::*;
285# manage::init_dcg();
286let x : Art<usize> = cell!([x] 10);
287let y : Art<usize> = cell!([y] 20);
288let t : Art<usize> = thunk![[t] if get!(x) > get!(y) { get!(x) } else { get!(y) } ];
289assert_eq!(get!(t), 20);
290# }
291```
292
293**Example 6**
294
295Implicit name-counter version (not suitable for archivist role):
296
297```
298# #[macro_use] extern crate adapton;
299# fn main() {
300# use adapton::macros::*;
301# use adapton::engine::*;
302# manage::init_dcg();
303let x : Art<usize> = cell!(10);
304let y : Art<usize> = cell!(20);
305let t : Art<usize> = thunk![ if get!(x) > get!(y) { get!(x) } else { get!(y) } ];
306assert_eq!(get!(t), 20);
307# }
308```
309
310Spurious arguments
311======================
312
313Sometimes, we need to pass arguments that do not admit the traits
314required by Adapton thunks `Eq + Hash + Debug + Clone`.
315
316For instance, suppose that we want to pass `Fn`s around, which are not
317`Debug`, `Eq` or `Hash`. Though generally unsound, we can use the
318`;;` syntax below to append arguments to thunks that do not admit
319these traits. For soundness, it is critical that the name and/or
320other arguments (before the `;;`) [functionally
321determine](https://en.wikipedia.org/wiki/Functional_dependency) the
322arguments that follow the `;;`, which are stored and never updated,
323nor tested for changes.
324
325```
326# #[macro_use] extern crate adapton;
327# fn main() {
328# use adapton::macros::*;
329# use adapton::engine::*;
330# manage::init_dcg();
331let t : Art<usize> = thunk!(
332 [Some(name_unit())]?
333 |x:usize,y:usize,
334 choose:Rc<Fn(usize,usize)->bool>|{
335 if choose(x,y) { x } else { y }
336 };
337 x:10, y:20 ;;
338 f:Rc::new(|x,y| if x > y { true } else { false })
339 );
340
341assert_eq!(get!(t), 20);
342# }
343```
344
345
346*/
347#[macro_export]
348macro_rules! thunk {
349 ([ $nmop:expr ] ? $fun:expr ; $( $lab:ident :$arg:expr ),* ) => {{
350 thunk(
351 match $nmop {
352 None => { NameChoice::Eager },
353 Some(n) => { NameChoice::Nominal(n) }},
354 prog_pt!(stringify!($fun)),
355 Rc::new(Box::new(
356 move |($($lab),*,()),()| $fun ( $($lab),* )
357 )),
358 ( $( $arg ),*,()),
359 () )
360 }}
361 ;
362 ([ $nmop:expr ] ? $fun:expr ; $( $lab:ident :$arg:expr ),* ;; $( $lab2:ident :$arg2:expr ),* ) => {{
363 thunk(
364 match $nmop {
365 None => { NameChoice::Eager },
366 Some(n) => { NameChoice::Nominal(n) }},
367 prog_pt!(stringify!($fun)),
368 Rc::new(Box::new(
369 move | arg1 , arg2 | {
370 let ( $( $lab ),*, () ) = arg1 ;
371 let ( $( $lab2 ),*, () ) = arg2 ;
372 $fun ( $( $lab ),*, $( $lab2 ),* )
373 }
374 )),
375 ( $( $arg ),*, () ),
376 ( $( $arg2 ),*, () )
377 )
378 }}
379 ;
380 ( [ $name:ident ] $fun:expr ; $( $lab:ident :$arg:expr ),* ) => {{
381 thunk!([Some(name_of_str(stringify!($name)))]?
382 $fun ;
383 $( $lab:$arg ),*
384 )
385 }}
386 ;
387 ( $nm:expr =>> $fun:expr , $( $lab:ident : $arg:expr ),* ) => {{
388 thunk!([Some($nm)]?
389 $fun ;
390 $( $lab:$arg ),*
391 )
392 }}
393 ;
394 ( $nm:expr =>> $fun:expr , $( $lab:ident : $arg:expr ),* ;; $( $lab2:ident : $arg2:expr ),* ) => {{
395 thunk!([Some($nm)]?
396 $fun ;
397 $( $lab:$arg ),* ;;
398 $( $lab2:$arg2 ),*
399 )
400 }}
401 ;
402 [ [ $nmop:expr ] ? $body:expr ] => {{
403 thunk(
404 match $nmop {
405 None => { NameChoice::Eager },
406 Some(n) => { NameChoice::Nominal(n) }},
407 prog_pt!(stringify!($body)),
408 Rc::new(Box::new( move |(),()| { $body } )),
409 () ,
410 () )
411 }}
412 ;
413 [ [ $name:ident ] $body:expr ] => {{
414 thunk(
415 NameChoice::Nominal(name_of_str(stringify!($name))),
416 prog_pt!(stringify!($fun)),
417 Rc::new(Box::new( move |(),()| { $body } )),
418 () ,
419 () )
420 }}
421 ;
422 [ $body:expr ] => {{
423 thunk![ [Some(name_of_usize(bump_name_counter()))]?
424 $body ]
425 }}
426}
427
428/** Wrappers for `engine::fork_name`.
429
430Name forking
431-----------------
432
433Sometimes one has a single name but wants more _that are determined by
434it, deterministically_:
435
436```
437# #[macro_use] extern crate adapton;
438# fn main() {
439use adapton::macros::*;
440use adapton::engine::*;
441manage::init_dcg();
442
443// Run 1, start with `name_unit()`:
444let u : Name = name_unit();
445let (u1,u2) = fork!( u );
446let (u3,u4) = fork!( u1 );
447
448// Run 2, start with the same name, `name_unit()`:
449let w : Name = name_unit();
450let (w1,w2) = fork!( w );
451let (w3,w4) = fork!( w1 );
452
453// The name forks consist of the same names between runs 1 and 2:
454assert_eq!(u2, w2);
455assert_eq!(u3, w3);
456assert_eq!(u4, w4);
457# }
458```
459
460In the context of incremental computation, the archivist names the
461output of their computation by the names of its input.
462
463
464*/
465#[macro_export]
466macro_rules! fork {
467 ( $nm:expr ) => {{
468 name_fork($nm)
469 }}
470}
471
472
473/** Optional name forking.
474
475Name forking
476=============
477
478Sometimes one has a single name but wants more _that are determined by
479it, deterministically_:
480
481```
482# #[macro_use] extern crate adapton;
483# fn main() {
484use adapton::macros::*;
485use adapton::engine::*;
486manage::init_dcg();
487
488// Run 1, start with `name_unit()`:
489let u : Name = name_unit();
490let (u1,u2) = fork!( u );
491let (u3,u4) = fork!( u1 );
492
493// Run 2, start with the same name, `name_unit()`:
494let w : Name = name_unit();
495let (w1,w2) = fork!( w );
496let (w3,w4) = fork!( w1 );
497
498// The name forks consist of the same names between runs 1 and 2:
499assert_eq!(u2, w2);
500assert_eq!(u3, w3);
501assert_eq!(u4, w4);
502# }
503```
504
505In the context of incremental computation, the archivist names the
506output of their computation by the names of its input.
507
508
509Optional-name forking
510------------------------
511
512Sometimes it's natural to work with _optional_ names, and in these
513contexts, one may want to fork optional names:
514
515```
516# #[macro_use] extern crate adapton;
517# fn main() {
518use adapton::macros::*;
519use adapton::engine::*;
520manage::init_dcg();
521
522let u : Option<Name> = Some(name_unit());
523let (u1,u2) = forko!(u);
524let (w1,w2) = forko!(u1);
525# }
526```
527
528When the name is `None`, one gets more `None`s, as expected:
529
530```
531# #[macro_use] extern crate adapton;
532# fn main() {
533# use adapton::macros::*;
534# use adapton::engine::*;
535# manage::init_dcg();
536#
537let u : Option<Name> = None;
538let (u1,u2) = forko!(u);
539let (w1,w2) = forko!(u1);
540
541assert_eq!(w1, None);
542assert_eq!(w2, None);
543# }
544```
545
546*/
547#[macro_export]
548macro_rules! forko {
549 ( $nmop:expr ) => {{
550 match $nmop {
551 None => (None,None),
552 Some(n) => {
553 let (l,r) = name_fork(n);
554 (Some(l),Some(r))
555 }
556 }
557 }}
558}
559
560
561/** Wrappers for creating and forcing thunks (`engine::thunk` and `engine::force`).
562
563Memoization
564============
565
566Memoization provides a mechanism for caching the results of
567subcomputations; it is a crtical feature of Adapton's approach to
568incremental computation.
569
570In Adapton, each _memoization point_ has three ingredients:
571
572- A function expression (of type `Fn`)
573
574- Zero or more arguments. Each argument type must have an
575 implementation for the traits `Eq + Clone + Hash + Debug`. The
576 traits `Eq` and `Clone` are both critical to Adapton's caching and
577 change propagation engine. The trait `Hash` is required when
578 Adapton's naming strategy is _structural_ (e.g., where function
579 names are based on the hashes of their arguments). The trait
580 `Debug` is useful for debugging, and reflection.
581
582- An optional _name_, which identifies the function call for reuse later.
583
584 - When this optional name is `None`, the memoization point may be
585 treated in one of two ways: either as just an ordinary, uncached
586 function call, or as a cached function call that is identified
587 _structurally_, by its function pointer and arguments. Adapton
588 permits structural subcomputations via the engine's
589 [structural](https://docs.rs/adapton/0/adapton/engine/fn.structural.html)
590 function.
591
592 - When this is `Some(name)`, the memoization point uses `name` to
593 identify the work performed by the function call, and its
594 result. Critically, in future incremental runs, it is possible
595 for `name` to associate with different functions and/or argument
596 values.
597
598
599Optional name version
600----------------------
601
602The following form is preferred:
603
604`memo!( [ optional_name ]? fnexp ; lab1 : arg1, ..., labk : argk )`
605
606It accepts an optional name, of type `Option<Name>`, and an arbitrary
607function expression `fnexp` (closure or function pointer). Like the
608other forms, it requires that the programmer label each argument.
609
610Example 1
611---------
612
613**Optional name:**
614
615```
616# #[macro_use] extern crate adapton;
617# fn main() {
618# use adapton::macros::*;
619# use adapton::engine::*;
620# manage::init_dcg();
621// Choose an optional name:
622let opnm : Option<Name> = Some(name_unit());
623
624let (t,z) : (Art<usize>, usize) =
625 memo!([opnm]?
626 |x:usize,y:usize|{ if x > y { x } else { y }};
627 x:10, y:20 );
628
629assert_eq!(z, 20);
630assert_eq!(force(&t), 20);
631# }
632```
633
634Example 2
635---------------
636
637**Function pointers as arguments:**
638
639```
640# #[macro_use] extern crate adapton;
641# fn main() {
642# use adapton::macros::*;
643# use adapton::engine::*;
644# manage::init_dcg();
645fn max(x:usize,y:usize) -> bool {
646 if x > y { true } else { false }
647};
648
649let (t,z) : (Art<usize>, usize) =
650 memo!([Some(name_unit())]?
651 |x:usize,y:usize,choose:fn(usize,usize)->bool|{
652 if choose(x,y) { x } else { y }
653 }; x:10, y:20, f:max );
654
655assert_eq!(z, 20);
656assert_eq!(force(&t), 20);
657# }
658```
659
660Spurious arguments
661===================
662
663Sometimes, we need to pass arguments that do not admit the traits
664required by Adapton thunks `Eq + Hash + Debug + Clone`.
665
666For instance, suppose that we want to pass `Fn`s around, which are not
667`Debug`, `Eq` or `Hash`. Though generally unsound, we can use the
668`;;` syntax below to append arguments to thunks that do not admit
669these traits. For soundness, it is critical that the name and/or
670other arguments (before the `;;`) [functionally
671determine](https://en.wikipedia.org/wiki/Functional_dependency) the
672arguments that follow the `;;`, which are stored and never updated,
673nor tested for changes.
674
675```
676# #[macro_use] extern crate adapton;
677# fn main() {
678# use adapton::macros::*;
679# use adapton::engine::*;
680# manage::init_dcg();
681let (t,z) : (Art<usize>, usize) = memo!(
682 [Some(name_unit())]?
683 |x:usize,y:usize,
684 choose:Rc<Fn(usize,usize)->bool>|{
685 if choose(x,y) { x } else { y }
686 };
687 x:10, y:20 ;;
688 f:Rc::new(|x,y| if x > y { true } else { false })
689 );
690
691assert_eq!(z, 20);
692assert_eq!(get!(t), 20);
693# }
694```
695
696
697*/
698#[macro_export]
699macro_rules! memo {
700 ( [ $nmop:expr ] ? $fun:expr ; $( $lab:ident :$arg:expr ),* ) => {{
701 { let t = thunk!( [$nmop]? $fun ; $( $lab:$arg ),* ); let x = get!(t); (t, x) }
702 }}
703 ;
704 ( [ $nmop:expr ] ? $fun:expr ; $( $lab:ident :$arg:expr ),* ;; $( $lab2:ident : $arg2:expr ),* ) => {{
705 { let t = thunk!( [$nmop]? $fun ; $( $lab:$arg ),* ;; $( $lab2:$arg2 ),* ); let x = get!(t); (t, x) }
706 }}
707 ;
708 ( $nm:expr =>> $fun:expr , $( $lab:ident : $arg:expr ),* ) => {{
709 get!(thunk!( [Some($nm)]? $fun ; $( $lab:$arg ),* ))
710 }}
711 ;
712 ( $nm:expr =>> $fun:expr , $( $lab1:ident : $arg1:expr ),* ;; $( $lab2:ident : $arg2:expr ),* ) => {{
713 get!(thunk!( [Some($nm)]? $fun ; $( $lab1:$arg1 ),* ;; $( $lab2:$arg2 ),* ))
714 }}
715}
716
717
718/// Similar to `memo!`, except return both the thunk and its observed (`force`d) value.
719#[macro_export]
720#[doc(hidden)]
721macro_rules! eager {
722 ( $nm:expr =>> $f:ident :: < $( $ty:ty ),* > , $( $lab:ident : $arg:expr ),* ) => {{
723 let t = thunk
724 (NameChoice::Nominal($nm),
725 prog_pt!(stringify!($f)),
726 Rc::new(Box::new(
727 |args, _|{
728 let ($( $lab ),*) = args ;
729 $f :: < $( $ty ),* >( $( $lab ),* )
730 })),
731 ( $( $arg ),*, ),
732 ()
733 );
734 let res = force(&t) ;
735 (t, res)
736 }}
737 ;
738 ( $nm:expr =>> $f:path , $( $lab:ident : $arg:expr ),* ) => {{
739 let t = thunk
740 (NameChoice::Nominal($nm),
741 prog_pt!(stringify!($f)),
742 Rc::new(Box::new(
743 |args, _|{
744 let ($( $lab ),*) = args ;
745 $f ( $( $lab ),* )
746 })),
747 ( $( $arg ),* ),
748 ()
749 );
750 let res = force(&t) ;
751 (t, res)
752 }}
753 ;
754 ( $f:ident :: < $( $ty:ty ),* > , $( $lab:ident : $arg:expr ),* ) => {{
755 let t = thunk
756 (NameChoice::Structural,
757 prog_pt!(stringify!($f)),
758 Rc::new(Box::new(
759 |args, _|{
760 let ($( $lab ),*) = args ;
761 $f :: < $( $ty ),* >( $( $lab ),* )
762 })),
763 ( $( $arg ),* ),
764 ()
765 );
766 let res = force(&t) ;
767 (t, res)
768 }}
769 ;
770 ( $f:path , $( $lab:ident : $arg:expr ),* ) => {{
771 let t = thunk
772 (NameChoice::Structural,
773 prog_pt!(stringify!($f)),
774 Rc::new(Box::new(
775 |args, _|{
776 let ($( $lab ),*, _) = args ;
777 $f ( $( $lab ),* )
778 })),
779 ( $( $arg ),*, () ),
780 ()
781 );
782 let res = force(&t) ;
783 (t, res)
784 }}
785 ;
786 ( $nm:expr =>> $f:ident =>> < $( $ty:ty ),* > , $( $lab1:ident : $arg1:expr ),* ;; $( $lab2:ident : $arg2:expr ),* ) => {{
787 let t = thunk
788 (NameChoice::Nominal($nm),
789 prog_pt!(stringify!($f)),
790 Rc::new(Box::new(
791 |args1, args2|{
792 let ($( $lab1 ),*, _) = args1 ;
793 let ($( $lab2 ),*, _) = args2 ;
794 $f :: < $( $ty ),* > ( $( $lab1 ),* , $( $lab2 ),* )
795 })),
796 ( $( $arg1 ),*, () ),
797 ( $( $arg2 ),*, () ),
798 );
799 let res = force(&t) ;
800 (t, res)
801 }}
802 ;
803}
804
805/// Convenience wrapper: Call a function and place the result into an `engine::cell`.
806#[doc(hidden)]
807#[macro_export]
808macro_rules! cell_call {
809 ( $nm:expr =>> $f:ident :: < $( $ty:ty ),* > , $( $lab:ident : $arg:expr ),* ) => {{
810 let res = {
811 $f :: < $( $ty ),* >( $( $arg ),*, )
812 } ;
813 let cell = cell($nm, res) ;
814 cell
815 }}
816 ;
817 ( $nm:expr =>> $f:ident , $( $lab:ident : $arg:expr ),* ) => {{
818 let res = {
819 $f ( $( $arg ),*, )
820 } ;
821 let cell = cell($nm, res) ;
822 cell
823 }}
824}
825
826
827/**
828Let-bind a nominal ref cell via `cell`, using the let-bound variable identifier as its name. Permits sequences of bindings.
829
830Example usage: [Adapton Example: Nominal firewalls](https://docs.rs/adapton/0/adapton/index.html#nominal-firewalls).
831*/
832#[macro_export]
833macro_rules! let_cell {
834 { $var:ident = $rhs:expr; $body:expr } => {{ {
835 let name = name_of_str(stringify!($var));
836 let value = $rhs;
837 let $var = cell(name, value); $body }
838 }};
839 { $var1:ident = $rhs1:expr ; $( $var2:ident = $rhs2:expr ),+ ; $body:expr} => {{
840 let_cell!($var1 = $rhs1;
841 let_cell!( $( $var2 = $rhs2 ),+ ; $body ))
842 }};
843}
844
845/**
846Let-bind a nominal thunk via `thunk!`, without forcing it. Permits sequences of bindings.
847
848Example usage: [Adapton Example: Nominal firewalls](https://docs.rs/adapton/0/adapton/index.html#nominal-firewalls).
849*/
850#[macro_export]
851macro_rules! let_thunk {
852 { $var:ident = $rhs:expr; $body:expr } => {{
853 let name = name_of_str(stringify!($var));
854 let $var = thunk!([Some(name)]?{$rhs}); $body
855 }};
856 { $var1:ident = $rhs1:expr ; $( $var2:ident = $rhs2:expr ),+ ; $body:expr} => {{
857 let_thunk!($var1 = $rhs1;
858 let_thunk!( $( $var2 = $rhs2 ),+ ; $body ))
859 }};
860}
861
862
863#[test]
864fn test_let_cell_let_thunk_macros() {
865 use crate::adapton::macros::*;
866 use crate::adapton::engine::*;
867
868 fn demand_graph(a: Art<i32>) -> Art<i32> {
869 let c : Art<i32> = get!(let_thunk!{f = {
870 let a = a.clone();
871 let b : Art<i32> = get!(let_thunk!{g = {let x = get!(a); let_cell!{b = x * x; b}}; g});
872 let c : Art<i32> = get!(let_thunk!{h = {let x = get!(b); let_cell!{c = if x < 100 { x } else { 100 }; c}}; h});
873 c}; f});
874 return c
875 };
876
877 manage::init_dcg();
878
879 // 1. Initialize input cell "a" to hold 2, and do the computation illustrated above:
880 let _ = demand_graph(cell(name_of_str("a"), 2));
881
882 // 2. Change input cell "a" to hold -2, and do the computation illustrated above:
883 let _ = demand_graph(cell(name_of_str("a"), -2));
884
885 // 3. Change input cell "a" to hold 3, and do the computation illustrated above:
886 let _ = demand_graph(cell(name_of_str("a"), 3));
887}
888
889/**
890Let-bind a nominal thunk, force it, and let-bind its result. Permits sequences of bindings.
891
892Example usage and expansion:
893
894- [Nominal firewall, example usage](https://docs.rs/adapton/0/adapton/index.html#example-nominal-firewall).
895- [Nominal firewall, expanded](https://docs.rs/adapton/0/adapton/index.html#let_memo-example).
896*/
897#[macro_export]
898macro_rules! let_memo {
899 { $var:ident = ( $thkvar1:ident ) = $rhs:expr; $body:expr } => {{
900 let $thkvar1 = thunk!([$thkvar1]{$rhs});
901 let $var = get!($thkvar1);
902 $body
903 }};
904 { $var1:ident = ( $thkvar1:ident ) = $rhs1:expr ; $( $var2:ident = ( $thkvar2:ident ) = $rhs2:expr ),+ ; $body:expr} => {{
905 let_memo!($var1 = ( $thkvar1 ) = $rhs1;
906 let_memo!( $( $var2 = ( $thkvar2 ) = $rhs2 ),+ ; $body ))
907 }};
908}
909
910
911#[test]
912fn test_memo_macros() {
913 use crate::adapton::macros::*;
914 use crate::adapton::engine::*;
915
916 fn demand_graph(a: Art<i32>) -> Art<i32> {
917 let_memo!{c =(f)= {
918 let a = a.clone();
919 let_memo!{b =(g)= {let x = get!(a); let_cell!{b = x * x; b}};
920 c =(h)= {let x = get!(b); let_cell!{c = if x < 100 { x } else { 100 }; c}};
921 c}};
922 c}
923 }
924
925 manage::init_dcg();
926
927 // 1. Initialize input cell "a" to hold 2, and do the computation illustrated above:
928 let _ = demand_graph(cell(name_of_str("a"), 2));
929
930 // 2. Change input cell "a" to hold -2, and do the computation illustrated above:
931 let _ = demand_graph(cell(name_of_str("a"), -2));
932
933 // 3. Change input cell "a" to hold 3, and do the computation illustrated above:
934 let _ = demand_graph(cell(name_of_str("a"), 3));
935}
936