Skip to content

Commit

Permalink
Traverse & Sequence (#55)
Browse files Browse the repository at this point in the history
  • Loading branch information
SandroMaglione authored Oct 7, 2022
2 parents 9ce70de + 3914bdf commit 8772ab1
Show file tree
Hide file tree
Showing 19 changed files with 3,174 additions and 17 deletions.
18 changes: 18 additions & 0 deletions example/src/traverse/option.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import 'package:fpdart/fpdart.dart';

void main() {
/// "a40" is invalid 💥
final inputValues = ["10", "20", "30", "a40"];

/// Verify that all the values can be converted to [int] 🔐
///
/// If **any** of them is invalid, then the result is [None] 🙅‍♂️
final traverseOption = inputValues.traverseOption(
(a) => Option.tryCatch(
/// If `a` does not contain a valid integer literal a [FormatException] is thrown
() => int.parse(a),
),
);

print(traverseOption);
}
21 changes: 21 additions & 0 deletions example/src/traverse/sequnce_traverse.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import 'package:fpdart/fpdart.dart';

void main() {
final inputValues = ["10", "20", "30", "40"];

/// Using `traverse` = `map` + `sequence` 🪄
final traverseOption = inputValues.traverseOption(
(a) => Option.tryCatch(() => int.parse(a)),
);

/// Using `sequence`, same as the above with `traverse` 🪄
final sequenceOption = inputValues
.map((a) => Option.tryCatch(() => int.parse(a)))
.sequenceOption();

/// `Some([10, 20, 30, 40])` - Same ☑️
print(traverseOption);

/// `Some([10, 20, 30, 40])` - Same ☑️
print(sequenceOption);
}
49 changes: 49 additions & 0 deletions lib/src/either.dart
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,55 @@ abstract class Either<L, R> extends HKT2<_EitherHKT, L, R>
/// Otherwise return `false`.
bool exists(bool Function(R r) predicate);

/// {@template fpdart_traverse_list_either}
/// Map each element in the list to an [Either] using the function `f`,
/// and collect the result in an `Either<E, List<B>>`.
///
/// If any mapped element of the list is [Left], then the final result
/// will be [Left].
/// {@endtemplate}
///
/// Same as `Either.traverseList` but passing `index` in the map function.
static Either<E, List<B>> traverseListWithIndex<E, A, B>(
List<A> list,
Either<E, B> Function(A a, int i) f,
) {
final resultList = <B>[];
for (var i = 0; i < list.length; i++) {
final e = f(list[i], i);
if (e is Left<E, B>) {
return left(e._value);
} else if (e is Right<E, B>) {
resultList.add(e._value);
} else {
throw Exception(
"[fpdart]: Error when mapping Either, it should be either Left or Right.",
);
}
}

return right(resultList);
}

/// {@macro fpdart_traverse_list_either}
///
/// Same as `Either.traverseListWithIndex` but without `index` in the map function.
static Either<E, List<B>> traverseList<E, A, B>(
List<A> list,
Either<E, B> Function(A a) f,
) =>
traverseListWithIndex<E, A, B>(list, (a, _) => f(a));

/// {@template fpdart_sequence_list_either}
/// Convert a `List<Either<E, A>>` to a single `Either<E, List<A>>`.
///
/// If any of the [Either] in the [List] is [Left], then the result is [Left].
/// {@endtemplate}
static Either<E, List<A>> sequenceList<E, A>(
List<Either<E, A>> list,
) =>
traverseList(list, identity);

/// Flat a [Either] contained inside another [Either] to be a single [Either].
factory Either.flatten(Either<L, Either<L, R>> e) => e.flatMap(identity);

Expand Down
35 changes: 35 additions & 0 deletions lib/src/io.dart
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,41 @@ class IO<A> extends HKT<_IOHKT, A>
/// Execute the IO function.
A run() => _run();

/// {@template fpdart_traverse_list_io}
/// Map each element in the list to an [IO] using the function `f`,
/// and collect the result in an `IO<List<B>>`.
/// {@endtemplate}
///
/// Same as `IO.traverseList` but passing `index` in the map function.
static IO<List<B>> traverseListWithIndex<A, B>(
List<A> list,
IO<B> Function(A a, int i) f,
) =>
IO<List<B>>(() {
final resultList = <B>[];
for (var i = 0; i < list.length; i++) {
resultList.add(f(list[i], i).run());
}
return resultList;
});

/// {@macro fpdart_traverse_list_io}
///
/// Same as `IO.traverseListWithIndex` but without `index` in the map function.
static IO<List<B>> traverseList<A, B>(
List<A> list,
IO<B> Function(A a) f,
) =>
traverseListWithIndex<A, B>(list, (a, _) => f(a));

/// {@template fpdart_sequence_list_io}
/// Convert a `List<IO<A>>` to a single `IO<List<A>>`.
/// {@endtemplate}
static IO<List<A>> sequenceList<A>(
List<IO<A>> list,
) =>
traverseList(list, identity);

@override
bool operator ==(Object other) => (other is IO) && other._run == _run;

Expand Down
38 changes: 38 additions & 0 deletions lib/src/io_either.dart
Original file line number Diff line number Diff line change
Expand Up @@ -208,4 +208,42 @@ class IOEither<L, R> extends HKT2<_IOEitherHKT, L, R>
return Left<L, R>(onError(error, stack));
}
});

/// {@template fpdart_traverse_list_io_either}
/// Map each element in the list to a [IOEither] using the function `f`,
/// and collect the result in an `IOEither<E, List<B>>`.
/// {@endtemplate}
///
/// Same as `IOEither.traverseList` but passing `index` in the map function.
static IOEither<E, List<B>> traverseListWithIndex<E, A, B>(
List<A> list,
IOEither<E, B> Function(A a, int i) f,
) =>
IOEither<E, List<B>>(
() => Either.sequenceList(
IO
.traverseListWithIndex<A, Either<E, B>>(
list,
(a, i) => IO(() => f(a, i).run()),
)
.run(),
),
);

/// {@macro fpdart_traverse_list_io_either}
///
/// Same as `IOEither.traverseListWithIndex` but without `index` in the map function.
static IOEither<E, List<B>> traverseList<E, A, B>(
List<A> list,
IOEither<E, B> Function(A a) f,
) =>
traverseListWithIndex<E, A, B>(list, (a, _) => f(a));

/// {@template fpdart_sequence_list_io_either}
/// Convert a `List<IOEither<E, A>>` to a single `IOEither<E, List<A>>`.
/// {@endtemplate}
static IOEither<E, List<A>> sequenceList<E, A>(
List<IOEither<E, A>> list,
) =>
traverseList(list, identity);
}
175 changes: 175 additions & 0 deletions lib/src/list_extension.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import 'date.dart';
import 'either.dart';
import 'io.dart';
import 'io_either.dart';
import 'option.dart';
import 'task.dart';
import 'task_either.dart';
import 'task_option.dart';
import 'tuple.dart';
import 'typeclass/order.dart';

Expand Down Expand Up @@ -334,3 +340,172 @@ extension FpdartOnMutableIterableOfIterable<T> on Iterable<Iterable<T>> {
/// From a container of `Iterable<Iterable<T>>` return a `Iterable<T>` of their concatenation.
Iterable<T> get concat => foldRight([], (a, e) => [...a, ...e]);
}

extension FpdartTraversableIterable<T> on Iterable<T> {
/// {@macro fpdart_traverse_list_option}
Option<List<B>> traverseOptionWithIndex<B>(
Option<B> Function(T a, int i) f,
) =>
Option.traverseListWithIndex(toList(), f);

/// {@macro fpdart_traverse_list_option}
Option<List<B>> traverseOption<B>(
Option<B> Function(T a) f,
) =>
Option.traverseList(toList(), f);

/// {@macro fpdart_traverse_list_task_option}
TaskOption<List<B>> traverseTaskOptionWithIndex<B>(
TaskOption<B> Function(T a, int i) f,
) =>
TaskOption.traverseListWithIndex(toList(), f);

/// {@macro fpdart_traverse_list_task_option}
TaskOption<List<B>> traverseTaskOption<B>(
TaskOption<B> Function(T a) f,
) =>
TaskOption.traverseList(toList(), f);

/// {@macro fpdart_traverse_list_seq_task_option}
TaskOption<List<B>> traverseTaskOptionWithIndexSeq<B>(
TaskOption<B> Function(T a, int i) f,
) =>
TaskOption.traverseListWithIndexSeq(toList(), f);

/// {@macro fpdart_traverse_list_seq_task_option}
TaskOption<List<B>> traverseTaskOptionSeq<B>(
TaskOption<B> Function(T a) f,
) =>
TaskOption.traverseListSeq(toList(), f);

/// {@macro fpdart_traverse_list_io}
IO<List<B>> traverseIOWithIndex<B>(
IO<B> Function(T a, int i) f,
) =>
IO.traverseListWithIndex(toList(), f);

/// {@macro fpdart_traverse_list_io}
IO<List<B>> traverseIO<B>(
IO<B> Function(T a) f,
) =>
IO.traverseList(toList(), f);

/// {@macro fpdart_traverse_list_task}
Task<List<B>> traverseTaskWithIndex<B>(
Task<B> Function(T a, int i) f,
) =>
Task.traverseListWithIndex(toList(), f);

/// {@macro fpdart_traverse_list_task}
Task<List<B>> traverseTask<B>(
Task<B> Function(T a) f,
) =>
Task.traverseList(toList(), f);

/// {@macro fpdart_traverse_list_seq_task}
Task<List<B>> traverseTaskWithIndexSeq<B>(
Task<B> Function(T a, int i) f,
) =>
Task.traverseListWithIndexSeq(toList(), f);

/// {@macro fpdart_traverse_list_seq_task}
Task<List<B>> traverseTaskSeq<B>(
Task<B> Function(T a) f,
) =>
Task.traverseListSeq(toList(), f);

/// {@macro fpdart_traverse_list_either}
Either<E, List<B>> traverseEitherWithIndex<E, B>(
Either<E, B> Function(T a, int i) f,
) =>
Either.traverseListWithIndex(toList(), f);

/// {@macro fpdart_traverse_list_either}
Either<E, List<B>> traverseEither<E, B>(
Either<E, B> Function(T a) f,
) =>
Either.traverseList(toList(), f);

/// {@macro fpdart_traverse_list_task_either}
TaskEither<E, List<B>> traverseTaskEitherWithIndex<E, B>(
TaskEither<E, B> Function(T a, int i) f,
) =>
TaskEither.traverseListWithIndex(toList(), f);

/// {@macro fpdart_traverse_list_task_either}
TaskEither<E, List<B>> traverseTaskEither<E, B>(
TaskEither<E, B> Function(T a) f,
) =>
TaskEither.traverseList(toList(), f);

/// {@macro fpdart_traverse_list_seq_task_either}
TaskEither<E, List<B>> traverseTaskEitherWithIndexSeq<E, B>(
TaskEither<E, B> Function(T a, int i) f,
) =>
TaskEither.traverseListWithIndexSeq(toList(), f);

/// {@macro fpdart_traverse_list_seq_task_either}
TaskEither<E, List<B>> traverseTaskEitherSeq<E, B>(
TaskEither<E, B> Function(T a) f,
) =>
TaskEither.traverseListSeq(toList(), f);

/// {@macro fpdart_traverse_list_io_either}
IOEither<E, List<B>> traverseIOEitherWithIndex<E, B>(
IOEither<E, B> Function(T a, int i) f,
) =>
IOEither.traverseListWithIndex(toList(), f);

/// {@macro fpdart_traverse_list_io_either}
IOEither<E, List<B>> traverseIOEither<E, B>(
IOEither<E, B> Function(T a) f,
) =>
IOEither.traverseList(toList(), f);
}

extension FpdartSequenceIterableOption<T> on Iterable<Option<T>> {
/// {@macro fpdart_sequence_list_option}
Option<List<T>> sequenceOption() => Option.sequenceList(toList());
}

extension FpdartSequenceIterableTaskOption<T> on Iterable<TaskOption<T>> {
/// {@macro fpdart_sequence_list_task_option}
TaskOption<List<T>> sequenceTaskOption() => TaskOption.sequenceList(toList());

/// {@macro fpdart_sequence_list_seq_task_option}
TaskOption<List<T>> sequenceTaskOptionSeq() =>
TaskOption.sequenceListSeq(toList());
}

extension FpdartSequenceIterableIO<T> on Iterable<IO<T>> {
/// {@macro fpdart_sequence_list_io}
IO<List<T>> sequenceIO() => IO.sequenceList(toList());
}

extension FpdartSequenceIterableTask<T> on Iterable<Task<T>> {
/// {@macro fpdart_sequence_list_task}
Task<List<T>> sequenceTask() => Task.sequenceList(toList());

/// {@macro fpdart_sequence_list_seq_task}
Task<List<T>> sequenceTaskSeq() => Task.sequenceListSeq(toList());
}

extension FpdartSequenceIterableEither<E, T> on Iterable<Either<E, T>> {
/// {@macro fpdart_sequence__io}
Either<E, List<T>> sequenceEither() => Either.sequenceList(toList());
}

extension FpdartSequenceIterableTaskEither<E, T> on Iterable<TaskEither<E, T>> {
/// {@macro fpdart_sequence_list_task_either}
TaskEither<E, List<T>> sequenceTaskEither() =>
TaskEither.sequenceList(toList());

/// {@macro fpdart_sequence_list_seq_task_either}
TaskEither<E, List<T>> sequenceTaskEitherSeq() =>
TaskEither.sequenceListSeq(toList());
}

extension FpdartSequenceIterableIOEither<E, T> on Iterable<IOEither<E, T>> {
/// {@macro fpdart_sequence_list_io_either}
IOEither<E, List<T>> sequenceIOEither() => IOEither.sequenceList(toList());
}
Loading

0 comments on commit 8772ab1

Please sign in to comment.