Skip to content

Commit

Permalink
Added new traversing functions for the state monad (#141)
Browse files Browse the repository at this point in the history
  • Loading branch information
SandroMaglione committed Jan 18, 2024
2 parents caef8dd + 5001da1 commit d1f5f15
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<int, String>((s) {
sideEffect++;
return (a + i.toString(), s);
}));
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<int, String>((s) {
sideEffect++;
return (a, s);
}));
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, String>((s) {
sideEffect++;
return ('a', s);
}),
State<int, String>((s) {
sideEffect++;
return ('b', s);
})
];
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 d1f5f15

Please sign in to comment.