dvcompute_branch/simulation/composite/
mod.rs

1// Copyright (c) 2020-2022  David Sorokin <davsor@mail.ru>, based in Yoshkar-Ola, Russia
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
7use std::marker::PhantomData;
8
9use crate::simulation;
10use crate::simulation::Point;
11use crate::simulation::event::*;
12use crate::simulation::observable::disposable::*;
13
14/// Return a new `Composite` computation by the specified pure value.
15#[inline]
16pub fn return_composite<T>(val: T) -> Return<T> {
17    Return { val: val }
18}
19
20/// Delay the `Composite` computation.
21#[inline]
22pub fn delay_composite<F, M>(f: F) -> Delay<F, M>
23    where F: FnOnce() -> M,
24          M: Composite
25{
26    Delay { f: f, _phantom: PhantomData }
27}
28
29/// Construct a new `Composite` computation by the specified function.
30#[inline]
31pub fn cons_composite<F, T>(f: F) -> Cons<F, T>
32    where F: FnOnce(DisposableBox, &Point) -> simulation::Result<(T, DisposableBox)>
33{
34     Cons { f: f, _phantom: PhantomData }
35}
36
37/// Allows embedding arbitrary `Disposable` actions in `Composite` computation.
38#[inline]
39pub fn disposable_composite<D>(action: D) -> DisposableComposite<D>
40    where D: Disposable + Clone + 'static
41{
42    DisposableComposite { action: action }
43}
44
45/// Create a sequence of computations.
46#[inline]
47pub fn composite_sequence<I, M>(comps: I) -> Sequence<I::IntoIter, M>
48    where I: IntoIterator<Item = M>,
49          M: Composite
50{
51    Sequence { comps: comps.into_iter(), _phantom: PhantomData }
52}
53
54/// Create a sequence of computations, where the result is ignored.
55#[inline]
56pub fn composite_sequence_<I, M>(comps: I) -> Sequence_<I::IntoIter, M>
57    where I: IntoIterator<Item = M>,
58          M: Composite
59{
60    Sequence_ { comps: comps.into_iter(), _phantom: PhantomData }
61}
62
63/// Trace the computation.
64#[inline]
65pub fn trace_composite<M>(msg: String, comp: M) -> Trace<M>
66    where M: Composite
67{
68    Trace { comp: comp, msg: msg}
69}
70
71/// The computation synchronized with the composite queue.
72pub trait Composite {
73
74    /// The type of the item that is returned in the current modeling time.
75    type Item;
76
77    /// Call the `Composite` computation in the current modeling time by updating the `Disposable` object.
78    #[doc(hidden)]
79    fn call_composite(self, disposable: DisposableBox, p: &Point) -> simulation::Result<(Self::Item, DisposableBox)>;
80
81    /// Bind the current computation with its continuation within the resulting computation.
82    #[inline]
83    fn and_then<U, F>(self, f: F) -> AndThen<Self, U, F>
84        where Self: Sized,
85              U: Composite,
86              F: FnOnce(Self::Item) -> U,
87    {
88        AndThen { comp: self, f: f, _phantom: PhantomData }
89    }
90
91    /// Map the current computation using the specified transform.
92    #[inline]
93    fn map<B, F>(self, f: F) -> Map<Self, B, F>
94        where Self: Sized,
95              F: FnOnce(Self::Item) -> B,
96    {
97        Map { comp: self, f: f, _phantom: PhantomData }
98    }
99
100    /// Zip the current computation with another one within the resulting computation.
101    #[inline]
102    fn zip<U>(self, other: U) -> Zip<Self, U>
103        where Self: Sized,
104              U: Composite
105    {
106        Zip { comp: self, other: other }
107    }
108
109    /// The function application.
110    #[inline]
111    fn ap<U, B>(self, other: U) -> Ap<Self, U, B>
112        where Self: Sized,
113              Self::Item: FnOnce(U::Item) -> B,
114              U: Composite
115    {
116        Ap { comp: self, other: other, _phantom: PhantomData }
117    }
118
119    /// Convert into a boxed value.
120    #[inline]
121    fn into_boxed(self) -> CompositeBox<Self::Item>
122        where Self: Sized + Clone + 'static
123    {
124        CompositeBox::new(move |disposable, p: &Point| { self.call_composite(disposable, p) })
125    }
126
127    /// Run the computation by the specified initial `Disposable` object.
128    #[inline]
129    fn run<D>(self, disposable: D) -> Run<Self, D>
130        where Self: Sized,
131              D: Disposable + Clone + 'static
132    {
133        Run { comp: self, disposable: disposable }
134    }
135
136    /// Run the computation by ignoring `Disposable` objects.
137    #[inline]
138    fn run_(self) -> Run_<Self>
139        where Self: Sized
140    {
141        Run_ { comp: self }
142    }
143}
144
145/// Allows converting to `Composite` computations.
146pub trait IntoComposite {
147
148    /// The target computation.
149    type Composite: Composite<Item = Self::Item>;
150
151    /// The type of item that is returned by the computation.
152    type Item;
153
154    /// Convert to the `Composite` computation.
155    fn into_composite(self) -> Self::Composite;
156}
157
158impl<M: Composite> IntoComposite for M {
159
160    type Composite = M;
161
162    type Item = M::Item;
163
164    #[inline]
165    fn into_composite(self) -> Self::Composite {
166        self
167    }
168}
169
170/// It represents the boxed `Composite` computation.
171#[must_use = "computations are lazy and do nothing unless to be run"]
172pub struct CompositeBox<T> {
173    f: Box<dyn CompositeFnBoxClone<T>>
174}
175
176impl<T> CompositeBox<T> {
177
178    /// Create a new boxed computation.
179    #[doc(hidden)]
180    #[inline]
181    fn new<F>(f: F) -> Self
182        where F: FnOnce(DisposableBox, &Point) -> simulation::Result<(T, DisposableBox)> + Clone + 'static
183    {
184        CompositeBox {
185            f: Box::new(f)
186        }
187    }
188
189    /// Call the boxed function.
190    #[doc(hidden)]
191    #[inline]
192    pub fn call_box(self, arg: (DisposableBox, &Point,)) -> simulation::Result<(T, DisposableBox)> {
193        let CompositeBox { f } = self;
194        f.call_box(arg)
195    }
196}
197
198impl<T> Clone for CompositeBox<T> {
199
200    fn clone(&self) -> Self {
201        CompositeBox {
202            f: self.f.call_clone()
203        }
204    }
205}
206
207impl<T> Composite for CompositeBox<T> {
208
209    type Item = T;
210
211    #[inline]
212    fn call_composite(self, disposable: DisposableBox, p: &Point) -> simulation::Result<(Self::Item, DisposableBox)> {
213        self.call_box((disposable, p,))
214    }
215
216    #[inline]
217    fn into_boxed(self) -> CompositeBox<Self::Item>
218        where Self: Sized + 'static
219    {
220        self
221    }
222}
223
224/// A trait to support the stable version of Rust, where there is no `FnBox`.
225trait CompositeFnBox<T> {
226
227    /// Call the corresponding function.
228    fn call_box(self: Box<Self>, args: (DisposableBox, &Point,)) -> simulation::Result<(T, DisposableBox)>;
229}
230
231impl<T, F> CompositeFnBox<T> for F
232    where F: for<'a> FnOnce(DisposableBox, &'a Point) -> simulation::Result<(T, DisposableBox)>
233{
234    fn call_box(self: Box<Self>, args: (DisposableBox, &Point,)) -> simulation::Result<(T, DisposableBox)> {
235        let this: Self = *self;
236        this(args.0, args.1)
237    }
238}
239
240/// A trait to implement a cloneable `FnBox`.
241trait CompositeFnBoxClone<T>: CompositeFnBox<T> {
242
243    /// Clone the function.
244    fn call_clone(&self) -> Box<dyn CompositeFnBoxClone<T>>;
245}
246
247impl<T, F> CompositeFnBoxClone<T> for F
248    where F: for<'a> FnOnce(DisposableBox, &'a Point) -> simulation::Result<(T, DisposableBox)> + Clone + 'static
249{
250    fn call_clone(&self) -> Box<dyn CompositeFnBoxClone<T>> {
251        Box::new(self.clone())
252    }
253}
254
255/// Allows creating the `Composite` computation from a pure value.
256#[must_use = "computations are lazy and do nothing unless to be run"]
257#[derive(Clone)]
258pub struct Return<T> {
259
260    /// Return a pure value, which is then transformed to the computation.
261    val: T
262}
263
264impl<T> Composite for Return<T> {
265
266    type Item = T;
267
268    #[doc(hidden)]
269    #[inline]
270    fn call_composite(self, disposable: DisposableBox, _: &Point) -> simulation::Result<(T, DisposableBox)> {
271        let Return { val } = self;
272        Result::Ok((val, disposable))
273    }
274}
275
276/// Allows delaying the `Composite` computation by the specified function.
277#[must_use = "computations are lazy and do nothing unless to be run"]
278#[derive(Clone)]
279pub struct Delay<F, M> {
280
281    /// Return the computation.
282    f: F,
283
284    /// To keep the type parameter.
285    _phantom: PhantomData<M>
286}
287
288impl<F, M> Composite for Delay<F, M>
289    where F: FnOnce() -> M,
290          M: Composite
291{
292    type Item = M::Item;
293
294    #[doc(hidden)]
295    #[inline]
296    fn call_composite(self, disposable: DisposableBox, p: &Point) -> simulation::Result<(M::Item, DisposableBox)> {
297        let Delay { f, _phantom } = self;
298        f().call_composite(disposable, p)
299    }
300}
301
302/// Allows constructing the `Composite` computation by the specified function.
303#[must_use = "computations are lazy and do nothing unless to be run"]
304#[derive(Clone)]
305pub struct Cons<F, T> {
306
307    /// The function of time point.
308    f: F,
309
310    /// To keep the type parameter.
311    _phantom: PhantomData<T>
312}
313
314impl<F, T> Composite for Cons<F, T>
315    where F: FnOnce(DisposableBox, &Point) -> simulation::Result<(T, DisposableBox)>
316{
317    type Item = T;
318
319    #[doc(hidden)]
320    #[inline]
321    fn call_composite(self, disposable: DisposableBox, p: &Point) -> simulation::Result<(T, DisposableBox)> {
322        let Cons { f, _phantom } = self;
323        f(disposable, p)
324    }
325}
326
327/// The monadic bind for the `Composite` computation.
328#[must_use = "computations are lazy and do nothing unless to be run"]
329#[derive(Clone)]
330pub struct AndThen<M, U, F> {
331
332    /// The current computation.
333    comp: M,
334
335    /// The continuation of the current computation.
336    f: F,
337
338    /// To keep the type parameter.
339    _phantom: PhantomData<U>
340}
341
342impl<M, U, F> Composite for AndThen<M, U, F>
343    where M: Composite,
344          U: Composite,
345          F: FnOnce(M::Item) -> U,
346{
347    type Item = U::Item;
348
349    #[doc(hidden)]
350    #[inline]
351    fn call_composite(self, disposable: DisposableBox, p: &Point) -> simulation::Result<(U::Item, DisposableBox)> {
352        let AndThen { comp, f, _phantom } = self;
353        match comp.call_composite(disposable, p) {
354            Result::Ok((a, disposable)) => {
355                let m = f(a);
356                m.call_composite(disposable, p)
357            },
358            Result::Err(e) => {
359                Result::Err(e)
360            }
361        }
362    }
363}
364
365/// The functor for the `Composite` computation.
366#[must_use = "computations are lazy and do nothing unless to be run"]
367#[derive(Clone)]
368pub struct Map<M, B, F> {
369
370    /// The current computation.
371    comp: M,
372
373    /// The transform.
374    f: F,
375
376    /// To keep the type parameter.
377    _phantom: PhantomData<B>
378}
379
380impl<M, B, F> Composite for Map<M, B, F>
381    where M: Composite,
382          F: FnOnce(M::Item) -> B,
383{
384    type Item = B;
385
386    #[doc(hidden)]
387    #[inline]
388    fn call_composite(self, disposable: DisposableBox, p: &Point) -> simulation::Result<(B, DisposableBox)> {
389        let Map { comp, f, _phantom } = self;
390        match comp.call_composite(disposable, p) {
391            Result::Ok((a, disposable)) => Result::Ok((f(a), disposable)),
392            Result::Err(e) => Result::Err(e)
393        }
394    }
395}
396
397/// The zip of two `Composite` computations.
398#[must_use = "computations are lazy and do nothing unless to be run"]
399#[derive(Clone)]
400pub struct Zip<M, U> {
401
402    /// The current computation.
403    comp: M,
404
405    /// Another computation.
406    other: U,
407}
408
409impl<M, U> Composite for Zip<M, U>
410    where M: Composite,
411          U: Composite
412{
413    type Item = (M::Item, U::Item);
414
415    #[doc(hidden)]
416    #[inline]
417    fn call_composite(self, disposable: DisposableBox, p: &Point) -> simulation::Result<((M::Item, U::Item), DisposableBox)> {
418        let Zip { comp, other } = self;
419        comp.and_then(move |a| {
420            other.map(move |b| (a, b))
421        }).call_composite(disposable, p)
422    }
423}
424
425/// The function application for the `Composite` computation.
426#[must_use = "computations are lazy and do nothing unless to be run"]
427#[derive(Clone)]
428pub struct Ap<M, U, B> {
429
430    /// The current computation.
431    comp: M,
432
433    /// The continuation of the current computation.
434    other: U,
435
436    /// To keep the type parameter.
437    _phantom: PhantomData<B>
438}
439
440impl<M, U, B> Composite for Ap<M, U, B>
441    where M: Composite,
442          U: Composite,
443          M::Item: FnOnce(U::Item) -> B,
444{
445    type Item = B;
446
447    #[doc(hidden)]
448    #[inline]
449    fn call_composite(self, disposable: DisposableBox, p: &Point) -> simulation::Result<(B, DisposableBox)> {
450        let Ap { comp, other, _phantom } = self;
451        comp.and_then(move |f| {
452            other.map(move |a| { f(a) })
453        }).call_composite(disposable, p)
454    }
455}
456
457/// The sequence of computations.
458#[must_use = "computations are lazy and do nothing unless to be run"]
459#[derive(Clone)]
460pub struct Sequence<I, M> {
461
462    /// The computations.
463    comps: I,
464
465    /// To keep the type parameter.
466    _phantom: PhantomData<M>
467}
468
469impl<I, M> Composite for Sequence<I, M>
470    where I: Iterator<Item = M>,
471          M: Composite
472{
473    type Item = Vec<M::Item>;
474
475    #[doc(hidden)]
476    fn call_composite(self, disposable: DisposableBox, p: &Point) -> simulation::Result<(Self::Item, DisposableBox)> {
477        let Sequence { comps, _phantom } = self;
478        let mut v = {
479            match comps.size_hint() {
480                (_, Some(n)) => Vec::with_capacity(n),
481                (_, None) => Vec::new()
482            }
483        };
484        let mut disposable = disposable;
485        for m in comps {
486            match m.call_composite(disposable, p) {
487                Result::Ok((a, x)) => {
488                    v.push(a);
489                    disposable = x;
490                },
491                Result::Err(e) => {
492                    return Result::Err(e)
493                }
494            }
495        }
496        Result::Ok((v, disposable))
497    }
498}
499
500/// The sequence of computations with ignored result.
501#[must_use = "computations are lazy and do nothing unless to be run"]
502#[derive(Clone)]
503pub struct Sequence_<I, M> {
504
505    /// The computations.
506    comps: I,
507
508    /// To keep the type parameter.
509    _phantom: PhantomData<M>
510}
511
512impl<I, M> Composite for Sequence_<I, M>
513    where I: Iterator<Item = M>,
514          M: Composite
515{
516    type Item = ();
517
518    #[doc(hidden)]
519    fn call_composite(self, disposable: DisposableBox, p: &Point) -> simulation::Result<(Self::Item, DisposableBox)> {
520        let Sequence_ { comps, _phantom } = self;
521        let mut disposable = disposable;
522        for m in comps {
523            match m.call_composite(disposable, p) {
524                Result::Ok((_, x)) => { disposable = x; },
525                Result::Err(e) => return Result::Err(e)
526            }
527        }
528        Result::Ok(((), disposable))
529    }
530}
531
532/// The run function for `Composite` computation.
533#[must_use = "computations are lazy and do nothing unless to be run"]
534#[derive(Clone)]
535pub struct Run<M, D> {
536
537    /// The computation.
538    comp: M,
539
540    /// The initial `Disposable` action.
541    disposable: D
542}
543
544impl<M, D> Event for Run<M, D>
545    where M: Composite,
546          D: Disposable + Clone + 'static
547{
548    type Item = (M::Item, DisposableBox);
549
550    #[doc(hidden)]
551    fn call_event(self, p: &Point) -> simulation::Result<Self::Item> {
552        let Run { comp, disposable } = self;
553        let disposable = disposable.into_boxed();
554        comp.call_composite(disposable, p)
555    }
556}
557
558/// Another version of the run function for `Composite` computation.
559#[must_use = "computations are lazy and do nothing unless to be run"]
560#[derive(Clone)]
561pub struct Run_<M> {
562
563    /// The computation.
564    comp: M
565}
566
567impl<M> Event for Run_<M>
568    where M: Composite
569{
570    type Item = M::Item;
571
572    #[doc(hidden)]
573    fn call_event(self, p: &Point) -> simulation::Result<Self::Item> {
574        let Run_ { comp } = self;
575        let disposable = empty_disposable().into_boxed();
576        let (a, _) = comp.call_composite(disposable, p)?;
577        Result::Ok(a)
578    }
579}
580
581/// Computation that allows embedding `Disposable` actions.
582#[must_use = "computations are lazy and do nothing unless to be run"]
583#[derive(Clone)]
584pub struct DisposableComposite<D> {
585
586    /// The action.
587    action: D
588}
589
590impl<D> Composite for DisposableComposite<D>
591    where D: Disposable + Clone + 'static
592{
593    type Item = ();
594
595    #[doc(hidden)]
596    fn call_composite(self, disposable: DisposableBox, _p: &Point) -> simulation::Result<(Self::Item, DisposableBox)> {
597        let DisposableComposite { action } = self;
598        let disposable = disposable.merge(action).into_boxed();
599        Result::Ok(((), disposable))
600    }
601}
602
603/// Trace the computation.
604#[must_use = "computations are lazy and do nothing unless to be run"]
605#[derive(Clone)]
606pub struct Trace<M> {
607
608    /// The computation.
609    comp: M,
610
611    /// The message to print.
612    msg: String
613}
614
615impl<M> Composite for Trace<M>
616    where M: Composite
617{
618    type Item = M::Item;
619
620    #[doc(hidden)]
621    #[inline]
622    fn call_composite(self, disposable: DisposableBox, p: &Point) -> simulation::Result<(Self::Item, DisposableBox)> {
623        let Trace { comp, msg } = self;
624        p.trace(&msg);
625        comp.call_composite(disposable, p)
626    }
627}