fral/
lib.rs

1//! # Functional random-access lists
2//!
3//! A functional random access list is an efficient data structure with _lookup_ and _update_
4//! operations in O(log n), and _cons_ and _uncons_ operations in O(1) while preserving the
5//! original list. It was introduced in Chris Okasaki's 1995 _ACM FPCA_ paper [Purely Functional
6//! Random-Access Lists].
7//!
8//! We provide [`Arc`]-based and [`Rc`]-based implementations in [`Fral`] and [`rc::Fral`],
9//! depending on your use-case. Because [`Arc`] is the most versatile, it is the "primary"
10//! implementation for this crate. However, if you don't need thread-safety, [`rc::Fral`] has
11//! less overhead and should be used instead — it is a drop-in replacement for [`Fral`].
12//!
13//! ### Comparison with `im`
14//!
15//! The following are benchmark results against [`Fral`], [`im::Vector`], [`im::CatList`], and
16//! [`im::ConsList`] (`im` version `10.0.0`) with the `get`, `cons`, and `uncons` operations (which
17//! are `push_front` and `pop_front` for `im::Vector`). Results are sorted by efficiency:
18//!
19//! ```plain
20//! test get_im_vector      ... bench:      25,968 ns/iter (+/- 113)
21//! test get_fral           ... bench:      37,356 ns/iter (+/- 124)
22//! test get_im_catlist     ... bench:  15,397,279 ns/iter (+/- 375,877)
23//! test get_im_conslist    ... bench:  36,834,300 ns/iter (+/- 1,073,303)
24//!
25//! test cons_im_conslist   ... bench:     170,603 ns/iter (+/- 461)
26//! test cons_fral          ... bench:     294,475 ns/iter (+/- 195)
27//! test cons_im_catlist    ... bench:     641,423 ns/iter (+/- 2,625)
28//! test cons_im_vector     ... bench:     949,886 ns/iter (+/- 6,663)
29//!
30//! test uncons_im_conslist ... bench:          17 ns/iter (+/- 0)
31//! test uncons_fral        ... bench:          52 ns/iter (+/- 0)
32//! test uncons_im_catlist  ... bench:         149 ns/iter (+/- 0)
33//! test uncons_im_vector   ... bench:         454 ns/iter (+/- 2)
34//! ```
35//!
36//! # Examples
37//!
38//! ```
39//! # use std::sync::Arc;
40//! use fral::Fral;
41//!
42//! // cons is O(1)
43//! let mut f = Fral::new();
44//! for item in vec![1, 2, 3, 4, 5] {
45//!     f = f.cons(item);
46//! }
47//!
48//! // lookup is O(log n)
49//! if let Some(x) = f.get(4) {
50//!     assert_eq!(*x, 1);
51//! } else { unreachable!() }
52//! // lookup out of bounds is O(1)
53//! assert_eq!(f.get(5), None);
54//!
55//! // uncons is O(1)
56//! if let Some((head, tail)) = f.uncons() {
57//!     assert_eq!(*head, 5);
58//!     assert_eq!(tail.len(), 4);
59//! } else { unreachable!() }
60//!
61//! // in this scope, we want f to have an extra item in front.
62//! // we can do this in O(1), without cloning any items.
63//! {
64//!     let f = f.cons(42);
65//!
66//!     assert_eq!(*f.get(0).unwrap(), 42);
67//!     assert_eq!(*f.get(5).unwrap(), 1);
68//! }
69//!
70//! // our original Fral is still here
71//! assert_eq!(
72//!     f.iter().take(2).collect::<Vec<_>>(),
73//!     vec![Arc::new(5), Arc::new(4)]
74//! );
75//! ```
76//!
77//! [Purely Functional Random-Access Lists]: https://www.westpoint.edu/eecs/SiteAssets/SitePages/Faculty%20Publication%20Documents/Okasaki/fpca95.pdf
78//! [`Arc`]: https://doc.rust-lang.org/stable/std/sync/struct.Arc.html
79//! [`Rc`]: https://doc.rust-lang.org/stable/std/rc/struct.Rc.html
80//! [`Fral`]: struct.Fral.html
81//! [`rc::Fral`]: rc/struct.Fral.html
82//! [`im::Vector`]: https://docs.rs/im/~10.0/im/vector/struct.Vector.html
83//! [`im::CatList`]: https://docs.rs/im/~10.0/im/catlist/struct.CatList.html
84//! [`im::ConsList`]: https://docs.rs/im/~10.0/im/conslist/struct.ConsList.html
85
86mod arc;
87pub mod rc;
88
89pub use arc::*;