Skip to content

Commit

Permalink
Merge pull request #2 from jacobaraujo7/main
Browse files Browse the repository at this point in the history
Added new functions and wrapper (bind, bindFuture, id, fold, left, right)
  • Loading branch information
SandroMaglione authored Jun 20, 2021
2 parents 2680b7c + 2b3964a commit fcc1422
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 2 deletions.
2 changes: 1 addition & 1 deletion .packages
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#
# For more info see: https://dart.dev/go/dot-packages-deprecation
#
# Generated by pub on 2021-06-15 19:06:22.469619.
# Generated by pub on 2021-06-20 14:14:38.902319.
_fe_analyzer_shared:file:///C:/Users/Sandro%20Maglione/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/_fe_analyzer_shared-22.0.0/lib/
analyzer:file:///C:/Users/Sandro%20Maglione/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/analyzer-1.7.0/lib/
args:file:///C:/Users/Sandro%20Maglione/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/args-2.1.0/lib/
Expand Down
13 changes: 13 additions & 0 deletions example/src/function/identity.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import 'package:fpdart/fpdart.dart';

void main() {
final either = Either<String, int>.of(10);

/// Without using `identity`, you must write a function to return
/// the input parameter `(l) => l`.
final noId = either.match((l) => l, (r) => '$r');

/// Using `identity`/`id`, the function just returns its input parameter.
final withIdentity = either.match(identity, (r) => '$r');
final withId = either.match(id, (r) => '$r');
}
45 changes: 45 additions & 0 deletions lib/src/either.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
import 'function.dart';
import 'option.dart';
import 'task_either.dart';
import 'tuple.dart';
import 'typeclass/typeclass.export.dart';

/// Return a `Right(r)`.
///
/// Shortcut for `Either.of(r)`.
Either<L, R> right<L, R>(R r) => Right<L, R>(r);

/// Return a `Left(l)`.
///
/// Shortcut for `Either.left(l)`.
Either<L, R> left<L, R>(L l) => Left<L, R>(l);

/// Tag the [HKT2] interface for the actual [Either].
abstract class _EitherHKT {}

Expand Down Expand Up @@ -92,6 +103,8 @@ abstract class Either<L, R> extends HKT2<_EitherHKT, L, R>
/// You can extract the value of every [Right] in the chain without
/// handling all possible missing cases.
/// If any of the functions in the chain returns [Left], the result is [Left].
///
/// Same as `bind`.
@override
Either<L, C> flatMap<C>(covariant Either<L, C> Function(R a) f);

Expand Down Expand Up @@ -135,6 +148,22 @@ abstract class Either<L, R> extends HKT2<_EitherHKT, L, R>
Either<L, R> filterOrElse(bool Function(R r) f, L Function(R r) onFalse) =>
flatMap((r) => f(r) ? Either.of(r) : Either.left(onFalse(r)));

/// Used to chain multiple functions that return a [Either].
///
/// You can extract the value of every [Right] in the chain without
/// handling all possible missing cases.
/// If any of the functions in the chain returns [Left], the result is [Left].
///
/// Same as `flatMap`.
Either<L, R2> bind<R2>(Either<L, R2> Function(R r) f) => flatMap(f);

/// Used to chain multiple functions that return a `Future<Either>`.
///
/// When this value is [Right], it returns a [TaskEither] that will resolve to
/// the result of calling `f`.
/// Otherwise, if this value is [Left], it returns `TaskEither.left()`.
TaskEither<L, R2> bindFuture<R2>(Future<Either<L, R2>> Function(R r) f);

/// If the [Either] is [Left], then change its value from type `L` to
/// type `C` using function `f`.
Either<C, R> mapLeft<C>(C Function(L a) f);
Expand Down Expand Up @@ -176,8 +205,16 @@ abstract class Either<L, R> extends HKT2<_EitherHKT, L, R>
R getOrElse(R Function(L l) orElse);

/// Execute `onLeft` when value is [Left], otherwise execute `onRight`.
///
/// Same as `fold`.
C match<C>(C Function(L l) onLeft, C Function(R r) onRight);

/// Execute `onLeft` when value is [Left], otherwise execute `onRight`.
///
/// Same as `match`.
C fold<C>(C Function(L l) onLeft, C Function(R r) onRight) =>
match<C>(onLeft, onRight);

/// Return `true` when value of `r` is equal to the value inside this [Either].
/// If this [Either] is [Left], then return `false`.
bool elem(R r, Eq<R> eq);
Expand Down Expand Up @@ -320,6 +357,10 @@ class Right<L, R> extends Either<L, R> {

@override
String toString() => 'Right($_value)';

@override
TaskEither<L, R2> bindFuture<R2>(Future<Either<L, R2>> Function(R r) f) =>
TaskEither(() async => f(_value));
}

class Left<L, R> extends Either<L, R> {
Expand Down Expand Up @@ -396,4 +437,8 @@ class Left<L, R> extends Either<L, R> {

@override
String toString() => 'Left($_value)';

@override
TaskEither<L, R2> bindFuture<R2>(Future<Either<L, R2>> Function(R r) f) =>
TaskEither.left(_value);
}
33 changes: 33 additions & 0 deletions lib/src/function.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,41 @@
typedef Endo<A> = A Function(A a);

/// Returns the given `a`.
///
/// Same as `id`.
///
/// Shortcut function to return the input parameter:
/// ```dart
/// final either = Either<String, int>.of(10);
///
/// /// Without using `identity`, you must write a function to return
/// /// the input parameter `(l) => l`.
/// final noId = either.match((l) => l, (r) => '$r');
///
/// /// Using `identity`/`id`, the function just returns its input parameter.
/// final withIdentity = either.match(identity, (r) => '$r');
/// final withId = either.match(id, (r) => '$r');
/// ```
T identity<T>(T a) => a;

/// Returns the given `a`.
///
/// Same as `identity`.
///
/// Shortcut function to return the input parameter:
/// ```dart
/// final either = Either<String, int>.of(10);
///
/// /// Without using `identity`, you must write a function to return
/// /// the input parameter `(l) => l`.
/// final noId = either.match((l) => l, (r) => '$r');
///
/// /// Using `identity`/`id`, the function just returns its input parameter.
/// final withIdentity = either.match(identity, (r) => '$r');
/// final withId = either.match(id, (r) => '$r');
/// ```
T id<T>(T a) => a;

/// Converts a binary function into a unary function that returns a unary function.
///
/// Enables the use of partial application of functions.
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: fpdart
version: 0.0.4
version: 0.0.5
homepage: https://www.sandromaglione.com/
repository: https://github.com/SandroMaglione/fpdart
description: Functional programming in Dart and Flutter. All the main functional programming types and patterns fully documented, tested, and with examples.
Expand Down
32 changes: 32 additions & 0 deletions test/src/either_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -721,4 +721,36 @@ void main() {
});
});
});

group('bind', () {
test('Right', () {
final either1 = Either<String, int>.of(10);
final result = either1.bind((r) => Either<String, int>.of(r + 10));
expect(result.getOrElse((l) => 0), 20);
});

test('Left', () {
final either1 = Either<String, int>.left('String');
final result = either1.bind((r) => Either<String, int>.of(r + 10));
expect(result.getOrElse((l) => 0), 0);
expect(result.getLeft().getOrElse(() => ''), 'String');
});
});

group('bindFuture', () {
test('Right', () async {
final either1 = Either<String, int>.of(10);
final asyncEither = either1.bindFuture((r) async => Either.of(r + 10));
final result = await asyncEither.run();
expect(result.getOrElse((l) => 0), 20);
});

test('Left', () async {
final either1 = Either<String, int>.left('String');
final asyncEither = either1.bindFuture((r) async => Either.of(r + 10));
final result = await asyncEither.run();
expect(result.getOrElse((l) => 0), 0);
expect(result.getLeft().getOrElse(() => ''), 'String');
});
});
}

0 comments on commit fcc1422

Please sign in to comment.