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}