Skip to content

Commit

Permalink
Added new traversing functions for the state monad
Browse files Browse the repository at this point in the history
Also added list extensions for the following functions:
* traverseListWithIndex
* traverseList
* sequenceList
  • Loading branch information
michaelzingg authored and SandroMaglione committed Jan 18, 2024
1 parent caef8dd commit e7455f0
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 0 deletions.
18 changes: 18 additions & 0 deletions packages/fpdart/lib/src/extension/list_extension.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import '../io.dart';
import '../io_either.dart';
import '../io_option.dart';
import '../option.dart';
import '../state.dart';
import '../task.dart';
import '../task_either.dart';
import '../task_option.dart';
Expand Down Expand Up @@ -178,6 +179,18 @@ extension FpdartTraversableIterable<T> on Iterable<T> {
IOEither<E, B> Function(T a) f,
) =>
IOEither.traverseList(toList(), f);

/// {@macro fpdart_traverse_list_state}
State<S, List<B>> traverseStateWithIndex<S, B>(
State<S, B> Function(T a, int i) f,
) =>
State.traverseListWithIndex(toList(), f);

/// {@macro fpdart_traverse_list_state}
State<S, List<B>> traverseState<S, B>(
State<S, B> Function(T a) f,
) =>
State.traverseList(toList(), f);
}

extension FpdartSequenceIterableOption<T> on Iterable<Option<T>> {
Expand Down Expand Up @@ -241,3 +254,8 @@ extension FpdartSequenceIterableIOEither<E, T> on Iterable<IOEither<E, T>> {
/// {@macro fpdart_sequence_list_io_either}
IOEither<E, List<T>> sequenceIOEither() => IOEither.sequenceList(toList());
}

/// {@macro fpdart_sequence_list_state}
extension FpdartSequenceIterableState<S, A> on Iterable<State<S, A>> {
State<S, Iterable<A>> sequenceState() => State.sequenceList(toList());
}
33 changes: 33 additions & 0 deletions packages/fpdart/lib/src/state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,39 @@ final class State<S, A> extends HKT2<_StateHKT, S, A>
/// To extract only the state `S` use `execute`.
(A, S) run(S state) => _run(state);

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

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

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

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

Expand Down
53 changes: 53 additions & 0 deletions packages/fpdart/test/src/state_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -210,4 +210,57 @@ void main() {
expect(result.$2, 'zabca');
expect(sideEffect, 100);
});

test('traverseListWithIndex', () {
var sideEffect = 0;
final list = ['a', 'b'];

final traverse = State.traverseListWithIndex(
list,
(a, i) => State((state) {
sideEffect++;
return (a + i.toString(), state);
}));
expect(sideEffect, 0);
final (resultList, resultState) = traverse.run(1);
expect(resultList, ['a0', 'b1']);
expect(resultState, 1);
expect(sideEffect, list.length);
});

test('traverseList', () {
var sideEffect = 0;
final list = ['a', 'b'];
final traverse = State.traverseList(
list,
(a) => State((state) {
sideEffect++;
return (a, state);
}));
expect(sideEffect, 0);
final (resultList, resultState) = traverse.run(1);
expect(resultList, ['a', 'b']);
expect(resultState, 1);
expect(sideEffect, list.length);
});

test('sequenceList', () {
var sideEffect = 0;
final list = [
State((int state) {
sideEffect++;
return ('a', state);
}),
State((String state) {
sideEffect++;
return ('b', state);
})
];
final sequence = State.sequenceList(list);
expect(sideEffect, 0);
final (resultList, resultState) = sequence.run(1);
expect(resultList, ['a', 'b']);
expect(resultState, 1);
expect(sideEffect, list.length);
});
}

0 comments on commit e7455f0

Please sign in to comment.