summaryrefslogtreecommitdiff
path: root/dhall/src/semantics/nze/lazy.rs
blob: d3b5c8d23d3289bc543b18057617185738b3d43b (plain)
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
use once_cell::unsync::OnceCell;
use std::cell::Cell;
use std::fmt::Debug;
use std::ops::Deref;

pub trait Eval<Tgt> {
    fn eval(self) -> Tgt;
}

/// A value which is initialized from a `Src` on the first access.
pub struct Lazy<Src, Tgt> {
    /// Exactly one of `src` of `tgt` must be set at a given time.
    /// Once `src` is unset and `tgt` is set, we never go back.
    src: Cell<Option<Src>>,
    tgt: OnceCell<Tgt>,
}

impl<Src, Tgt> Lazy<Src, Tgt>
where
    Src: Eval<Tgt>,
{
    /// Creates a new lazy value with the given initializing value.
    pub fn new(src: Src) -> Self {
        Lazy {
            src: Cell::new(Some(src)),
            tgt: OnceCell::new(),
        }
    }
    /// Creates a new lazy value with the given already-initialized value.
    pub fn new_completed(tgt: Tgt) -> Self {
        let lazy = Lazy {
            src: Cell::new(None),
            tgt: OnceCell::new(),
        };
        let _ = lazy.tgt.set(tgt);
        lazy
    }

    pub fn force(&self) -> &Tgt {
        self.tgt.get_or_init(|| {
            let src = self.src.take().unwrap();
            src.eval()
        })
    }

    pub fn get_mut(&mut self) -> &mut Tgt {
        self.force();
        self.tgt.get_mut().unwrap()
    }
    pub fn into_inner(self) -> Tgt {
        self.force();
        self.tgt.into_inner().unwrap()
    }
}

impl<Src, Tgt> Deref for Lazy<Src, Tgt>
where
    Src: Eval<Tgt>,
{
    type Target = Tgt;
    fn deref(&self) -> &Self::Target {
        self.force()
    }
}

/// This implementation evaluates before cloning, because we can't clone the contents of a `Cell`.
impl<Src, Tgt> Clone for Lazy<Src, Tgt>
where
    Src: Eval<Tgt>,
    Tgt: Clone,
{
    fn clone(&self) -> Self {
        Self::new_completed(self.force().clone())
    }
}

impl<Src, Tgt> Debug for Lazy<Src, Tgt>
where
    Tgt: Debug,
{
    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if let Some(tgt) = self.tgt.get() {
            fmt.debug_tuple("Lazy@Init").field(tgt).finish()
        } else {
            fmt.debug_tuple("Lazy@Uninit").finish()
        }
    }
}