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