1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#[doc(hidden)]
pub use std::sync::{Once, ONCE_INIT};

#[macro_export]
macro_rules! ifunky {
    ($(pub fn $fun:ident($($arg:ident: $kind:ty),*) -> $ret:ty { $($selector:expr);* })*) => (
        $(
            pub fn $fun($($arg: $kind),*) -> $ret {
                static SYNCHRO_START: $crate::Once = $crate::ONCE_INIT;
                static mut INDIRECT: fn( $($kind),* ) -> $ret = dispatch;

                fn dispatch($($arg: $kind),*) -> $ret {
                    fn select() -> fn( $($kind),* ) -> $ret {
                        $($selector);*
                    }

                    SYNCHRO_START.call_once(
                        || unsafe { INDIRECT = select() }
                    );


                    $fun($($arg),*)
                }

                (unsafe { INDIRECT })( $($arg),* )
            }
        )*
    );
}

#[cfg(test)]
mod test {
    extern crate rand;
    use std::iter::repeat;

    ifunky! {
        pub fn foo(x: u32) -> u32 {
            if rand::random::<bool>() {
                foo_big as fn(u32) -> u32
            } else {
                foo_bigger as fn(u32) -> u32
            }
        }

        pub fn bar(x: u32) -> u32 {
            if rand::random::<bool>() {
                bar_small as fn(u32) -> u32
            } else {
                bar_smaller as fn(u32) -> u32
            }
        }
    }

    fn foo_big(x: u32) -> u32 {
        x + 1
    }

    fn foo_bigger(x: u32) -> u32 {
        (x + 1) * 2
    }

    fn bar_small(x: u32) -> u32 {
        x - 1
    }

    fn bar_smaller(x: u32) -> u32 {
        (x - 1) / 2
    }

    #[test]
    fn memoizes_foo() {
        let gold_master_foo = foo(3);
        assert!(
            repeat( 3 )
            .map( foo )
            .take( 999 )
            .all(|x| x == gold_master_foo)
        );
    }

    #[test]
    fn memoizes_bar() {
        let gold_master_bar = bar(3);
        assert!(
            repeat( 3 )
            .map( bar )
            .take( 999 )
            .all(|x| x == gold_master_bar)
        );
    }
}