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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
use crate::prelude::*;

/// Sequence extends [`Traversable`] with a function that inverts collection
/// of Applicatives into an Applicative of a collection.
///
/// e.g. `Vec<Result<T, E>>` to `Result<Vec<T>, E>`
///
/// ```
/// use std::fs::{DirEntry, File, FileType, ReadDir};
/// use std::io;
/// use std::path::{Path, PathBuf};
///
/// use naan::prelude::*;
///
/// fn is_dir(ent: &DirEntry) -> bool {
///   ent.file_type()
///      .fmap(|ft: FileType| ft.is_dir())
///      .unwrap_or(false)
/// }
///
/// fn is_file(ent: &DirEntry) -> bool {
///   ent.file_type()
///      .fmap(|ft: FileType| ft.is_file())
///      .unwrap_or(false)
/// }
///
/// /// Recursively search directories and their children
/// /// for all files that match a predicate
/// fn find_all_rec<P: AsRef<Path>, F: Fn(PathBuf) -> io::Result<bool>>(
///   path: P,
///   f: F)
///   -> io::Result<Vec<PathBuf>> {
///   let find_matches = |matches: Vec<io::Result<Vec<PathBuf>>>, ent: DirEntry| {
///     let path_if_found = |found| {
///       if found {
///         vec![ent.path()]
///       } else {
///         vec![]
///       }
///     };
///
///     if is_file(&ent) {
///       matches.append_one(f(ent.path()).fmap(path_if_found))
///     } else if is_dir(&ent) {
///       matches.append_one(find_all_rec(ent.path(), &f))
///     } else {
///       matches
///     }
///   };
///
///   std::fs::read_dir(path).bind1(|dir: ReadDir| {
///                            dir.into_iter().collect::<io::Result<Vec<DirEntry>>>()
///                          })
///                          .bind1(|ents: Vec<DirEntry>| {
///                            ents.foldl(find_matches, vec![])
///                                .sequence::<hkt::ResultOk<_>>()
///                          })
///                          .fmap(|vv: Vec<Vec<_>>| vv.concat())
/// }
///
/// /// ...or using try syntax:
/// fn find_all_rec2<P: AsRef<Path>, F: Fn(PathBuf) -> io::Result<bool>>(
///   path: P,
///   f: F)
///   -> io::Result<Vec<PathBuf>> {
///   let find_matches = |matches: Vec<io::Result<Vec<PathBuf>>>, ent: DirEntry| {
///     let path_if_found = |found| {
///       if found {
///         vec![ent.path()]
///       } else {
///         vec![]
///       }
///     };
///
///     if is_file(&ent) {
///       matches.append_one(f(ent.path()).fmap(path_if_found))
///     } else if is_dir(&ent) {
///       matches.append_one(find_all_rec(ent.path(), &f))
///     } else {
///       matches
///     }
///   };
///
///   let dir = std::fs::read_dir(path)?;
///   let ents = dir.into_iter().collect::<io::Result<Vec<DirEntry>>>()?;
///   let out: Vec<Vec<PathBuf>> = ents.foldl(find_matches, Vec::<io::Result<Vec<PathBuf>>>::new())
///                                    .sequence::<hkt::ResultOk<_>>()?;
///
///   Ok(out.concat())
/// }
/// ```
pub trait Sequence<F, A, TF> {
  /// See [`Sequence`]
  fn sequence<Ap>(self) -> Ap::T<F::T<A>>
    where Self: Sized + Traversable<F, Ap::T<A>, A, TF> + Foldable<F, Ap::T<A>>,
          Ap: HKT1,
          Ap::T<A>: Applicative<Ap, A> + ApplyOnce<Ap, A>,
          Ap::T<TF>: Applicative<Ap, TF> + ApplyOnce<Ap, TF>,
          Ap::T<F::T<A>>: Applicative<Ap, F::T<A>> + ApplyOnce<Ap, F::T<A>>,
          F: HKT1<T<Ap::T<A>> = Self>
  {
    self.traversem1::<Ap, _>(|a| a)
  }
}

impl<F, A, TF, T> Sequence<F, A, TF> for T {}

/// [`Traversable`]'s signatures that guarantee the traversal
/// function will only ever be called once.
pub trait TraversableOnce<F, A, B, TF>: Traversable<F, A, B, TF> {
  /// Traverse a structure with 0 or 1 elements, collecting into
  /// an Applicative of 0 or 1 elements.
  ///
  /// ```
  /// use std::fs::File;
  ///
  /// use naan::prelude::*;
  ///
  /// fn maybe_path() -> Option<&'static str> {
  ///   None
  /// }
  ///
  /// let tried_read: std::io::Result<Option<File>> =
  ///   maybe_path().traverse11::<hkt::std::io::Result, _>(File::open);
  ///
  /// assert!(matches!(tried_read, Ok(None)))
  /// ```
  fn traverse11<Ap, AtoApOfB>(self, f: AtoApOfB) -> Ap::T<F::T<B>>
    where Ap: HKT1,
          Self: FoldableOnce<F, A>,
          Ap::T<B>: Applicative<Ap, B> + ApplyOnce<Ap, B>,
          Ap::T<TF>: Applicative<Ap, TF> + ApplyOnce<Ap, TF>,
          Ap::T<F::T<B>>: Applicative<Ap, F::T<B>> + ApplyOnce<Ap, F::T<B>>,
          AtoApOfB: F1Once<A, Ret = Ap::T<B>>,
          F: HKT1<T<A> = Self>;

  /// Traverse a structure with 0 or 1 elements, collecting into
  /// an Applicative of 0 or more elements.
  fn traverse1m<Ap, AtoApOfB>(self, f: AtoApOfB) -> Ap::T<F::T<B>>
    where Self: FoldableOnce<F, A>,
          Ap: HKT1,
          Ap::T<B>: Applicative<Ap, B>,
          Ap::T<TF>: Applicative<Ap, TF>,
          Ap::T<F::T<B>>: Applicative<Ap, F::T<B>>,
          AtoApOfB: F1Once<A, Ret = Ap::T<B>>,
          F: HKT1<T<A> = Self>;

  /// Aliases [`TraversableOnce::traverse11`]
  fn traverse_swap<Ap, AtoApOfB>(self, f: AtoApOfB) -> Ap::T<F::T<B>>
    where Ap: HKT1,
          Self: Sized + FoldableOnce<F, A>,
          Ap::T<B>: Applicative<Ap, B> + ApplyOnce<Ap, B>,
          Ap::T<TF>: Applicative<Ap, TF> + ApplyOnce<Ap, TF>,
          Ap::T<F::T<B>>: Applicative<Ap, F::T<B>> + ApplyOnce<Ap, F::T<B>>,
          AtoApOfB: F1Once<A, Ret = Ap::T<B>>,
          F: HKT1<T<A> = Self>
  {
    self.traverse11::<Ap, AtoApOfB>(f)
  }

  /// Aliases [`TraversableOnce::traverse1m`]
  fn traverse_replicate<Ap, AtoApOfB>(self, f: AtoApOfB) -> Ap::T<F::T<B>>
    where Ap: HKT1,
          Self: Sized + FoldableOnce<F, A>,
          Ap::T<B>: Applicative<Ap, B>,
          Ap::T<TF>: Applicative<Ap, TF>,
          Ap::T<F::T<B>>: Applicative<Ap, F::T<B>>,
          AtoApOfB: F1Once<A, Ret = Ap::T<B>>,
          F: HKT1<T<A> = Self>
  {
    self.traverse1m::<Ap, AtoApOfB>(f)
  }
}

/// A Traversable structure is one that can be folded and collected into some [`Applicative`].
///
/// Practically, this allows you to swap nested types, e.g.
///  * apply `FnOnce(T) -> Option<R>` to `Result<T, E>`, yielding `Option<Result<R, E>>`
///  * apply `Fn(T) -> Result<R, E>` to `Vec<T>`, yielding `Result<Vec<R>, E>`
///
/// # Use-cases
/// ## Traverse a structure of length 1 into an Ap of length 1
/// In effect, this swaps the order of types after applying the function.
///
/// e.g. traversing `Option<A>` with a function `Fn(A) -> Result<B, E>` will result in
/// `Result<Option<B>, E>`.
///
/// * `None` -> `Ok(None)` -> `Ok(None)`
/// * `Some(a)` -> `Ok(b)` -> `Ok(Some(b))`
/// * `Some(a)` -> `Err(e)` -> `Err(e)`
///
/// ## Traverse a structure of length 1 into an Ap of length > 1
/// In effect, this will perform a sort of "replication" of the variant wrapping `A`:
///
/// e.g. traversing `Option<A>` with a function `Fn(A) -> Vec<B>` will result in
/// `Vec<Option<B>>`.
///
/// * `None` -> `vec![1, 2, 3]` -> `vec![None, None, None]`
/// * `Some(a)` -> `vec![1, 2, 3]` -> `vec![Some(1), Some(2), Some(3)]`
///
/// ## Traverse a structure of length > 1 into an Ap of length 1
/// By far the most practically useful `traverse` usecase,
/// this allows you to perform an action that returns `Result`, `Option`, etc. and
/// fail as soon as the first `Err`, `None` is encountered.
///
/// e.g. traversing `Vec<A>` with a function `Fn(A) -> Result<B, E>` will result in
/// `Result<Vec<B>>`.
///
/// * `vec![1, 2, 3]` -> `vec![Ok(1), Err(e), ..]` -> `Err(e)`
/// * `vec![a, b, c]` -> `vec![Ok(a), Ok(b), Ok(c)]` -> `Ok(vec![a, b, c])`
///
/// **Note**: the traversal function must be callable multiple times. (must implement [`F1`])
///
/// ## Traverse a structure of length > 1 into an Ap of length > 1
/// Arguably the least useful `traverse` usecase, this performs a cross-product between
/// the two collections.
///
/// <details>
///
/// e.g. traversing `Vec<A>` with a function `Fn(A) -> Vec<B>` will result in
/// a cross-product `Vec<Vec<B>>`.
///
/// 1. Input `vec![2, 4]`
/// 2. Traverse function `f` of `|n| vec![n * 10, n * 20]`
/// 3. Initial output of `Vec::<Vec<B>>::new(Vec::<B>::new())`
/// 4. Intermediate value of `vec![20, 40]` after applying `f` to `2`
/// 5. Map the intermediate to be a vec of _appending actions_ to run against each element of output `vec![append(20), append(40)]`
/// 6. Invoke each append fn on each element in output (currently `vec![vec![]]`) -> `vec![vec![20], vec![40]]`
/// 7. Repeat for each element
/// 8. Observed output is `vec![vec![20, 40], vec![20, 80], vec![40, 40], vec![40, 80]]`
/// </details>
///
/// **Note**: the traversal function must be callable multiple times. (must implement [`F1`])
///
/// **Note**: each value of type `B` appears many times in the return value, so it must be [`Clone`].
///
/// # `traverse` `m1`/`mm`/`1m`/`11`
/// In other languages and environments, `traverse` makes no distinction between the
/// lengths of the Traversable or the [`Applicative`] output, and uses the final usecase mentioned above
/// (_Traverse a structure of length > 1 into an Ap of length > 1_) as the general `traverse`
/// implementation.
///
/// A major consideration is that in Rust is that we must tell the compiler:
///  * when we need to deep-clone values
///  * when we need a function to be callable more than once
///
/// Using a single general traverse implementation would require you to clone
/// values captured in closures (because they must be callable many times) and
/// prevent you from returning many useful types that are not [`Clone`], e.g. [`std::fs::File`].
///
/// In order to provide the best UX around FnOnce-ness and Clone-ness, naan provides
/// specific signatures for each usecase:
///
/// |                         | Traversable length == 1 | Traversable length > 1 |
/// | - | - | - |
/// | Applicative length == 1 | [`traverse11`](TraversableOnce::traverse11)  | [`traversem1`](Traversable::traversem1)|
/// | Applicative length > 1  | [`traverse1m`](TraversableOnce::traverse1m)  | [`traversemm`](Traversable::traversemm)|
///
/// Which translates to extra trait requirements:
///
/// |                         | Traversable length == 1 | Traversable length > 1 |
/// | - | - | - |
/// | Applicative length == 1 | None                    | function must be [`F1`] |
/// | Applicative length > 1  | None                    | function must be [`F1`], `B` must be [`Clone`] |
///
/// Additionally, we provide aliases to better describe the problem each signature solves:
///  * `traverse11` -> `traverse_swap`
///  * `traverse1m` -> `traverse_replicate`
///  * `traversem1` -> `traverse`
pub trait Traversable<F, A, B, TF> {
  /// Traverse a structure with 0 or more elements, collecting into
  /// an Applicative of 0 or 1 elements.
  ///
  /// ```
  /// use naan::prelude::*;
  ///
  /// // Mock std::fs::File
  /// pub struct File;
  /// impl File {
  ///   pub fn open(path: &str) -> std::io::Result<Self> {
  ///     if path == "doesnt-exist.txt" {
  ///       Err(std::io::Error::new(std::io::ErrorKind::NotFound, ""))
  ///     } else {
  ///       Ok(Self)
  ///     }
  ///   }
  /// }
  ///
  /// let paths = vec!["a.txt", "b.txt", "c.txt"];
  /// let files: std::io::Result<Vec<File>> = paths.traversem1::<hkt::std::io::Result, _>(File::open);
  /// assert!(matches!(files.as_ref().map(|v| v.as_slice()), Ok(&[_, _, _])));
  ///
  /// let paths = vec!["a.txt", "doesnt-exist.txt", "c.txt"];
  /// let files: std::io::Result<Vec<File>> = paths.traversem1::<hkt::std::io::Result, _>(File::open);
  /// assert!(matches!(files.map_err(|e| e.kind()),
  ///                  Err(std::io::ErrorKind::NotFound)));
  /// ```
  fn traversem1<Ap, AtoApOfB>(self, f: AtoApOfB) -> Ap::T<F::T<B>>
    where Ap: HKT1,
          Self: Foldable<F, A>,
          Ap::T<B>: Applicative<Ap, B> + ApplyOnce<Ap, B>,
          Ap::T<TF>: Applicative<Ap, TF> + ApplyOnce<Ap, TF>,
          Ap::T<F::T<B>>: Applicative<Ap, F::T<B>> + ApplyOnce<Ap, F::T<B>>,
          AtoApOfB: F1<A, Ret = Ap::T<B>>,
          F: HKT1<T<A> = Self>;

  /// Traverse a structure with 0 or more elements, collecting into
  /// an Applicative of 0 or more elements.
  fn traversemm<Ap, AtoApOfB>(self, f: AtoApOfB) -> Ap::T<F::T<B>>
    where Ap: HKT1,
          B: Clone,
          Self: Foldable<F, A>,
          Ap::T<B>: Applicative<Ap, B>,
          Ap::T<TF>: Applicative<Ap, TF>,
          Ap::T<F::T<B>>: Applicative<Ap, F::T<B>>,
          AtoApOfB: F1<A, Ret = Ap::T<B>>,
          F: HKT1<T<A> = Self>;

  /// Aliases [`Traversable::traversem1`]
  fn traverse<Ap, AtoApOfB>(self, f: AtoApOfB) -> Ap::T<F::T<B>>
    where Ap: HKT1,
          Self: Sized + Foldable<F, A>,
          Ap::T<B>: Applicative<Ap, B> + ApplyOnce<Ap, B>,
          Ap::T<TF>: Applicative<Ap, TF> + ApplyOnce<Ap, TF>,
          Ap::T<F::T<B>>: Applicative<Ap, F::T<B>> + ApplyOnce<Ap, F::T<B>>,
          AtoApOfB: F1<A, Ret = Ap::T<B>>,
          F: HKT1<T<A> = Self>
  {
    self.traversem1::<Ap, AtoApOfB>(f)
  }
}