sodium_rust/
cell.rs

1use crate::impl_::cell::Cell as CellImpl;
2use crate::impl_::lambda::IsLambda1;
3use crate::impl_::lambda::IsLambda2;
4use crate::impl_::lambda::IsLambda3;
5use crate::impl_::lambda::IsLambda4;
6use crate::impl_::lambda::IsLambda5;
7use crate::impl_::lambda::IsLambda6;
8use crate::impl_::lazy::Lazy;
9use crate::listener::Listener;
10use crate::sodium_ctx::SodiumCtx;
11use crate::stream::Stream;
12use crate::Dep;
13
14/// Represents a value of type `A` that changes over time.
15///
16/// In other Functional Reactive Programming (FRP) systems this is
17/// also called a _behavior_, _property_, or a _signal_. A `Cell`
18/// should be used for modeling any pieces of mutable state in an FRP
19/// application.
20pub struct Cell<A> {
21    pub impl_: CellImpl<A>,
22}
23
24impl<A> Clone for Cell<A> {
25    fn clone(&self) -> Self {
26        Cell {
27            impl_: self.impl_.clone(),
28        }
29    }
30}
31
32impl<A: Clone + Send + 'static> Cell<A> {
33    /// Create a `Cell` with a constant value.
34    pub fn new(sodium_ctx: &SodiumCtx, value: A) -> Cell<A> {
35        Cell {
36            impl_: CellImpl::new(&sodium_ctx.impl_, value),
37        }
38    }
39
40    /// Sample the `Cell`'s current value.
41    ///
42    /// `Cell::sample` may be used in the functions passed to
43    /// primitives that apply them to [`Stream`]s, including
44    /// [`Stream::map`], [`Stream::snapshot`], [`Stream::filter`], and
45    /// [`Stream::merge`].
46    ///
47    /// When called within a function passed to [`Stream::map`] using
48    /// `sample` is equivalent to [snapshotting][Stream::snapshot]
49    /// this `Cell` with that [`Stream`].
50    pub fn sample(&self) -> A {
51        self.impl_.sample()
52    }
53
54    /// Sample the `Cell`'s current value lazily.
55    ///
56    /// When it is necessary to use `sample` while implementing more
57    /// general abstractions, `sample_lazy` should be preferred in
58    /// case a [`CellLoop`][crate::CellLoop] is passed rather than a
59    /// `Cell`.
60    ///
61    /// See [`Cell::sample`] for more details.
62    pub fn sample_lazy(&self) -> Lazy<A> {
63        self.impl_.sample_lazy()
64    }
65
66    // use as dependency to lambda1, lambda2, etc.
67    #[doc(hidden)]
68    pub fn to_dep(&self) -> Dep {
69        self.impl_.to_dep()
70    }
71
72    /// Return a [`Stream`] that gives the updates/steps for a `Cell`.
73    ///
74    /// ## Important
75    ///
76    /// This is an operational primitive, which isn't part of the main
77    /// Sodium API. It breaks the property of non-detectability of
78    /// cell updates/steps. The rule with this primitive is that you
79    /// should only use it in functions that don't allow the caller to
80    /// detect the `Cell` updates.
81    pub fn updates(&self) -> Stream<A> {
82        Stream {
83            impl_: self.impl_.updates(),
84        }
85    }
86
87    /// Return a [`Stream`] that is guaranteed to fire at least once.
88    ///
89    /// When `value` is called, the returned `Stream` will fire once
90    /// in the current transaction with the current value of this
91    /// `Cell` and thereafter behaves like [`Cell::updates`].
92    ///
93    /// ## Important
94    ///
95    /// This is an operational primitive, which isn't part of the main
96    /// Sodium API. It breaks the property of non-detectability of
97    /// cell updates/steps. The rule with this primitive is that you
98    /// should only use it in functions that don't allow the caller to
99    /// detect the `Cell` updates.
100    pub fn value(&self) -> Stream<A> {
101        Stream {
102            impl_: self.impl_.value(),
103        }
104    }
105
106    /// Transform the `Cell`s value with the supplied function.
107    ///
108    /// The returned `Cell` always reflects the value produced by the
109    /// function applied to the input `Cell`s value. The given
110    /// function _must_ be referentially transparent.
111    pub fn map<B: Clone + Send + 'static, FN: IsLambda1<A, B> + Send + Sync + 'static>(
112        &self,
113        f: FN,
114    ) -> Cell<B> {
115        Cell {
116            impl_: self.impl_.map(f),
117        }
118    }
119
120    /// Lift a binary function into cells so the returned [`Cell`]
121    /// always reflects the specified function applied to the input
122    /// cells' values.
123    pub fn lift2<
124        B: Clone + Send + 'static,
125        C: Clone + Send + 'static,
126        FN: IsLambda2<A, B, C> + Send + 'static,
127    >(
128        &self,
129        cb: &Cell<B>,
130        f: FN,
131    ) -> Cell<C> {
132        Cell {
133            impl_: self.impl_.lift2(&cb.impl_, f),
134        }
135    }
136
137    /// Lift a ternary function into cells so the returned [`Cell`]
138    /// always reflects the specified function applied to the input
139    /// cells' values.
140    pub fn lift3<
141        B: Clone + Send + 'static,
142        C: Clone + Send + 'static,
143        D: Clone + Send + 'static,
144        FN: IsLambda3<A, B, C, D> + Send + 'static,
145    >(
146        &self,
147        cb: &Cell<B>,
148        cc: &Cell<C>,
149        f: FN,
150    ) -> Cell<D> {
151        Cell {
152            impl_: self.impl_.lift3(&cb.impl_, &cc.impl_, f),
153        }
154    }
155
156    /// Lift a quaternary function into cells so the returned [`Cell`]
157    /// always reflects the specified function applied to the input
158    /// cells' values.
159    pub fn lift4<
160        B: Clone + Send + 'static,
161        C: Clone + Send + 'static,
162        D: Clone + Send + 'static,
163        E: Clone + Send + 'static,
164        FN: IsLambda4<A, B, C, D, E> + Send + 'static,
165    >(
166        &self,
167        cb: &Cell<B>,
168        cc: &Cell<C>,
169        cd: &Cell<D>,
170        f: FN,
171    ) -> Cell<E> {
172        Cell {
173            impl_: self.impl_.lift4(&cb.impl_, &cc.impl_, &cd.impl_, f),
174        }
175    }
176
177    /// Lift a five-argument function into cells so the returned
178    /// [`Cell`] always reflects the specified function applied to the
179    /// input cells' values.
180    pub fn lift5<
181        B: Clone + Send + 'static,
182        C: Clone + Send + 'static,
183        D: Clone + Send + 'static,
184        E: Clone + Send + 'static,
185        F: Clone + Send + 'static,
186        FN: IsLambda5<A, B, C, D, E, F> + Send + 'static,
187    >(
188        &self,
189        cb: &Cell<B>,
190        cc: &Cell<C>,
191        cd: &Cell<D>,
192        ce: &Cell<E>,
193        f: FN,
194    ) -> Cell<F> {
195        Cell {
196            impl_: self
197                .impl_
198                .lift5(&cb.impl_, &cc.impl_, &cd.impl_, &ce.impl_, f),
199        }
200    }
201
202    /// Lift a six argument function into cells so the returned
203    /// [`Cell`] always reflects the specified function applied to the
204    /// input cells' values.
205    pub fn lift6<
206        B: Clone + Send + 'static,
207        C: Clone + Send + 'static,
208        D: Clone + Send + 'static,
209        E: Clone + Send + 'static,
210        F: Clone + Send + 'static,
211        G: Clone + Send + 'static,
212        FN: IsLambda6<A, B, C, D, E, F, G> + Send + 'static,
213    >(
214        &self,
215        cb: &Cell<B>,
216        cc: &Cell<C>,
217        cd: &Cell<D>,
218        ce: &Cell<E>,
219        cf: &Cell<F>,
220        f: FN,
221    ) -> Cell<G> {
222        Cell {
223            impl_: self
224                .impl_
225                .lift6(&cb.impl_, &cc.impl_, &cd.impl_, &ce.impl_, &cf.impl_, f),
226        }
227    }
228
229    /// Unwrap a [`Stream`] in a `Cell` to give a time-varying stream implementation.
230    pub fn switch_s(csa: &Cell<Stream<A>>) -> Stream<A> {
231        Stream {
232            impl_: CellImpl::switch_s(&csa.map(|sa: &Stream<A>| sa.impl_.clone()).impl_),
233        }
234    }
235
236    /// Unwrap a `Cell` in another `Cell` to give a time-varying cell implementation.
237    pub fn switch_c(cca: &Cell<Cell<A>>) -> Cell<A> {
238        Cell {
239            impl_: CellImpl::switch_c(&cca.map(|ca: &Cell<A>| ca.impl_.clone()).impl_),
240        }
241    }
242
243    /// A variant of [`listen`][Cell::listen] that will deregister the
244    /// listener automatically if the listener is garbage-collected.
245    pub fn listen_weak<K: FnMut(&A) + Send + Sync + 'static>(&self, k: K) -> Listener {
246        Listener {
247            impl_: self.impl_.listen_weak(k),
248        }
249    }
250
251    /// Listen for updates to the value of this `Cell`.
252    ///
253    /// This is the observer pattern. The returned [`Listener`] has an
254    /// [`unlisten`][Listener::unlisten] method to cause the listener
255    /// to be removed.
256    ///
257    /// This is an operational mechanism for interfacing between the
258    /// world of I/O and FRP.
259    pub fn listen<K: IsLambda1<A, ()> + Send + Sync + 'static>(&self, k: K) -> Listener {
260        Listener {
261            impl_: self.impl_.listen(k),
262        }
263    }
264}