GithubHelp home page GithubHelp logo

sandromaglione / fpdart Goto Github PK

View Code? Open in Web Editor NEW
494.0 12.0 41.0 1.37 MB

Functional programming in Dart and Flutter. All the main functional programming types and patterns fully documented, tested, and with examples.

Home Page: https://pub.dev/packages/fpdart

License: MIT License

Dart 100.00%
dart functional-programming functional dartlang dart-package flutter flutter-package dart-functional-programming dart-functional flutter-functional

fpdart's Introduction

Functional programming in Dart and Flutter

All the main functional programming types and patterns fully documented, tested, and with examples

GitHub: SandroMaglione Twitter: SandroMaglione

Introduction

fpdart is fully documented. You do not need to have any previous experience with functional programming to start using fpdart. Give it a try!

fpdart is inspired by fp-ts, cats, and dartz.

Follow my Twitter for updates, or subscribe to the newsletter


๐Ÿ“– Learn functional programming and fpdart

Would you like to know more about functional programming, fpdart, and how to use the package?

๐Ÿ“š Collection of tutorials on fpdart

Are you new to fpdart and functional programming?

๐Ÿ‘จโ€๐Ÿ’ป Getting started with fpdart complete guide

Interested in what fpdart is and how it came to be?

๐Ÿšถ Full history of fpdart and functional programming in dart

โœ๏ธ Blog posts and tutorials

๐Ÿง‘โ€๐Ÿซ Getting started with functional programming

๐Ÿ’ป Installation

# pubspec.yaml
dependencies:
  fpdart: ^1.1.0

โœจ Examples

Step by step course on how to build a safe, maintainable, and testable Flutter app using fpdart and riverpod.

Flutter app that lets you search and view your favorite Pokemon:

  • API request
  • Response validation
  • JSON conversion
  • State management (riverpod)

Re-implementation using fpdart and functional programming of the Open Meteo API from the flutter_weather app example in the bloc package.

A 2 parts series explains step by step the Open Meteo API code:

Example of how to read and write a local file using functional programming.

Using fpdart with other libraries and noticing naming conflicts? Learn how to rename the classes that conflict with other SDK or third-party packages.

Used when a return value can be missing.

For example, when parsing a String to int, since not all String can be converted to int

/// Create an instance of [Some]
final option = Option.of(10);

/// Create an instance of [None]
final none = Option<int>.none();

/// Map [int] to [String]
final map = option.map((a) => '$a');

/// Extract the value from [Option]
final value = option.getOrElse(() => -1);

/// Pattern matching
final match = option.match(
  () => print('None'),
  (a) => print('Some($a)'),
);

/// or use Dart's pattern matching as well ๐Ÿค
final dartMatch = switch (option) {
  None() => 'None',
  Some(value: final a) => 'Some($a)',
};

/// Convert to [Either]
final either = option.toEither(() => 'missing');

/// Chain computations
final flatMap = option.flatMap((a) => Option.of(a + 10));

/// Return [None] if the function throws an error
final tryCatch = Option.tryCatch(() => int.parse('invalid'));

Used to handle errors (instead of Exceptions).

Either<L, R>: L is the type of the error (for example a String explaining the problem), R is the return type when the computation is successful

/// Create an instance of [Right]
final right = Either<String, int>.of(10);

/// Create an instance of [Left]
final left = Either<String, int>.left('none');

/// Map the right value to a [String]
final mapRight = right.map((a) => '$a');

/// Map the left value to a [int]
final mapLeft = right.mapLeft((a) => a.length);

/// Return [Left] if the function throws an error.
/// Otherwise return [Right].
final tryCatch = Either.tryCatch(
  () => int.parse('invalid'),
  (e, s) => 'Error: $e',
);

/// Extract the value from [Either]
final value = right.getOrElse((l) => -1);

/// Chain computations
final flatMap = right.flatMap((a) => Either.of(a + 10));

/// Pattern matching
final match = right.match(
  (l) => print('Left($l)'),
  (r) => print('Right($r)'),
);

/// or use Dart's pattern matching as well ๐Ÿค
final dartMatch = switch (right) {
  Left(value: final l) => 'Left($l)',
  Right(value: final r) => 'Right($r)',
};

/// Convert to [Option]
final option = right.toOption();

Wrapper around an sync function. Allows to compose synchronous functions that never fail.

/// Create instance of [IO] from a value
final IO<int> io = IO.of(10);

/// Create instance of [IO] from a sync function
final ioRun = IO(() => 10);

/// Map [int] to [String]
final IO<String> map = io.map((a) => '$a');

/// Extract the value inside [IO] by running its function
final int value = io.run();

/// Chain another [IO] based on the value of the current [IO]
final flatMap = io.flatMap((a) => IO.of(a + 10));

Wrapper around an async function (Future). Allows to compose asynchronous functions that never fail.

If you look closely, it's the same as IO but for async functions ๐Ÿ’ก

/// Create instance of [Task] from a value
final Task<int> task = Task.of(10);

/// Create instance of [Task] from an async function
final taskRun1 = Task(() async => 10);
final taskRun2 = Task(() => Future.value(10));

/// Map [int] to [String]
final Task<String> map = task.map((a) => '$a');

/// Extract the value inside [Task] by running its async function
final int value = await task.run();

/// Chain another [Task] based on the value of the current [Task]
final flatMap = task.flatMap((a) => Task.of(a + 10));

Utility types

These types compose together the 4 above (Option, Either, IO, Task) to join together their functionalities:

  • IOOption: sync function (IO) that may may miss the return value (Option)
  • IOEither: sync function (IO) that may fail (Either)
  • TaskOption: async function (Task) that may miss the return value (Option)
  • TaskEither: async function (Task) that may fail (Either)

Read values from a context without explicitly passing the dependency between multiple nested function calls. View the example folder for an explained usecase example.

Combine the Reader type (dependecy) with Task (asynchronous).

Combine the Reader type (dependecy) with Task (asynchronous) and Either (error handling).

Used to store, update, and extract state in a functional way. View the example folder for an explained usecase example.

๐Ÿ”— Do notation

Version v0.6.0 introduced the Do notation in fpdart. Using the Do notation makes chaining functions easier.

For example, a typical chain of methods in fpdart looks as follows:

/// Without the Do notation
String goShopping() => goToShoppingCenter()
    .alt(goToLocalMarket)
    .flatMap(
      (market) => market.buyBanana().flatMap(
            (banana) => market.buyApple().flatMap(
                  (apple) => market.buyPear().flatMap(
                        (pear) => Option.of('Shopping: $banana, $apple, $pear'),
                      ),
                ),
          ),
    )
    .getOrElse(
      () => 'I did not find ๐ŸŒ or ๐ŸŽ or ๐Ÿ, so I did not buy anything ๐Ÿคทโ€โ™‚๏ธ',
    );

Notice how you need to call flatMap multiple times to collect multiple variables and use them together (market, banana, apple, pear).

Everything looks more linear and simple by using the Do notation:

/// Using the Do notation
String goShoppingDo() => Option.Do(
      ($) {
        final market = $(goToShoppingCenter().alt(goToLocalMarket));
        final amount = $(market.buyAmount());

        final banana = $(market.buyBanana());
        final apple = $(market.buyApple());
        final pear = $(market.buyPear());

        return 'Shopping: $banana, $apple, $pear';
      },
    ).getOrElse(
      () => 'I did not find ๐ŸŒ or ๐ŸŽ or ๐Ÿ, so I did not buy anything ๐Ÿคทโ€โ™‚๏ธ',
    );

You initialize the Do notation using the Do() constructor.

You have access to a $ function, that you can use to extract and use the value inside each Option, without using flatMap.

Note: We recommend using the Do notation whenever possible to improve the legibility of your code ๐Ÿค

โš ๏ธ Warning: Pay attention to avoid the following mistakes when using the Do notation:

  • Do not throw inside the Do() constructor
  • Do not await without executing the $ function
  • Do not use a nested Do() constructor inside another one
  • Do not call the $ function inside another callback in the Do() constructor

Using the Do notation in these cases may fail at runtime or may produce unexpected behavior. For more information take a look at the Do Notation discussion or at this issue. Examples for each of the pitfalls can be viewed here: do-constructor-pitfalls

๐Ÿ“ฆ Immutable Collections

If you are going to use fpdart in your project, make sure to use immutable collections as well โ˜๏ธ

Immutability is at the core of functional programming (alongside pure functions ๐Ÿค).

fpdart does not provide immutable collections. Instead, we strongly suggest to use the fast_immutable_collections package.

fast_immutable_collections provides all the necessary immutable collections (List, Set, Map and more) with an extensive API and top-class performance.

fpdart instead provides some extension methods on Iterable, List, and Map. These are designed to extend the native Dart API with immutable methods, as well as providing many handy additional functions:

/// Dart: `1`
[1, 2, 3, 4].first;

/// fpdart: `Some(1)`
[1, 2, 3, 4].head;

/// Dart: Throws a [StateError] โš ๏ธ
[].first;

/// fpdart: `None()`
[].head;

final map = {'a': 1, 'b': 2};

/// Dart: mutable โš ๏ธ
map.updateAll((key, value) => value + 10);

/// fpdart: immutable equivalent ๐Ÿค
final newMap = map.mapValue((value) => value + 10);

You can use these extension methods on any native Dart collection and also in combination with fast_immutable_collections immutable collections.

More

Many more examples are coming soon. Check out my website and my Twitter for daily updates.


๐ŸŽฏ Types

  • Option
  • Either
  • Unit
  • IO
  • IORef
  • IOOption
  • IOEither
  • Task
  • TaskOption
  • TaskEither
  • Reader
  • ReaderTask
  • ReaderTaskEither
  • State
  • StateAsync
  • Iterable (List) extension
  • Map extension
  • Predicate
  • ReaderTaskOption
  • ReaderIO
  • ReaderIOEither
  • ReaderIOOption
  • ReaderEither
  • ReaderOption
  • StateReaderTaskEither
  • Lens
  • Writer

๐Ÿ’ก Motivation

๐Ÿ“š Read the full story of the origin and motivation for fpdart

Functional programming is becoming more and more popular, and for good reasons.

Many non-functional languages are slowly adopting patterns from functional languages, dart included. Dart already supports higher-order functions, generic types, type inference. Since Dart 3, the language supports also pattern matching, destructuring, multiple return values (Read more about these new features here).

Other functional programming features are coming to the language, like higher-order types.

Many packages are bringing functional patterns to dart, like the amazing freezed for unions/pattern matching.

fpdart aims to provide all the main types found in functional languages to dart. Types like Option (handle missing values without null), Either (handle errors and error messages), Task (composable async computations), and more.

Goal

Differently from many other functional programming packages, fpdart aims to introduce functional programming to every developer. For this reason, every type and method is commented and documented directly in the code.

You do not need to have any previous experience with functional programming to start using fpdart.

fpdart also provides real-world examples of why a type is useful and how it can be used in your application. Check out my website for blog posts and articles.

Comparison with dartz

One of the major pain points of dartz has always been is lack of documentation. This is a huge issue for people new to functional programming to attempt using the package.

dartz was released in 2016, initially targeting Dart 1.

dartz is also missing some features and types (Reader, TaskEither, and others).

fpdart is a rewrite based on fp-ts and cats. The main differences are:

  • fpdart is fully documented.
  • fpdart implements higher-kinded types using defunctionalization.
  • fpdart is based on Dart 3.
  • fpdart is completely null-safe from the beginning.
  • fpdart has a richer API.
  • fpdart implements some missing types in dartz.
  • fpdart does not provide implementation for immutable collections (ISet, IMap, IHashMap, AVLTree).

๐Ÿค” Roadmap

Being documentation and stability important goals of the package, every type will go through an implementation-documentation-testing cycle before being considered as 'stable'.

The current work on fpdart aims to:

  • Expand the API to include more methods to work and convert between types easier (send an issue on the repository if you have a suggestion)
  • Simplify the current API to make it easier to use and learn
  • Expand the documentation with more articles and documentation comments

Note: There is also an experimental research in progress to implement ZIO in fpdart, stay tuned ๐Ÿ”œ


The long-term goal is to provide all the main types and typeclasses available in other functional programming languages and packages. All the types should be completely documented and fully tested.

A well explained documentation is the key for the long-term success of the project. Any article, blog post, or contribution is welcome.

In general, any contribution or feedback is welcome (and encouraged!).

๐Ÿ“ƒ Versioning

  • v1.1.0 - 13 August 2023
  • v1.0.0 - 26 July 2023

  • v0.6.0 - 6 May 2023
  • v0.5.0 - 4 March 2023
  • v0.4.1 - 25 February 2023
  • v0.4.0 - 16 December 2022
  • v0.3.0 - 11 October 2022
  • v0.2.0 - 16 July 2022
  • v0.1.0 - 17 June 2022
  • v0.0.14 - 31 January 2022
  • v0.0.13 - 26 January 2022
  • v0.0.12 - 24 October 2021
  • v0.0.11 - 22 September 2021
  • v0.0.10 - 13 August 2021
  • v0.0.9 - 3 August 2021
  • v0.0.8 - 13 July 2021
  • v0.0.7 - 6 July 2021
  • v0.0.6 - 29 June 2021
  • v0.0.5 - 20 June 2021
  • v0.0.4 - 15 June 2021
  • v0.0.3 - 13 June 2021
  • v0.0.2 - 13 June 2021
  • v0.0.1 - 28 May 2021

๐Ÿ˜€ Support

If you are interested in my work you can subscribe to my newsletter.

I share tutorials, guides, and code snippets about fpdart and functional programming: Subscribe to the Newsletter here ๐Ÿ“ง

For more frequent updates you can also follow me on my Twitter.

๐Ÿ‘€ License

MIT License, see the LICENSE.md file for details.

fpdart's People

Contributors

acromondx avatar devnico avatar f-person avatar hbock-42 avatar jacobaraujo7 avatar jcdang avatar lrhn avatar mateusfccp avatar michaelzingg avatar purplenoodlesoop avatar randalschwartz avatar sandromaglione avatar sectore avatar tim-smart avatar timwhiting avatar utamori avatar zellidev0 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fpdart's Issues

Map.upsertAt() does not obey `eq`

upsertAt should either use eq or not have the param.

final d1 = DateTime(2001,1,1);
final d2 = DateTime(2001,1,2);
final m = {}
 .upsertAt(dateEqYear)(d1, 1)
 .upsertAt(dateEqYear)(d2, 2);

print(m);

Expected: {2001-01-01 00:00:00.000: 2} or {2001-01-02 00:00:00.000: 2}

Found :{2001-01-01 00:00:00.000: 1, 2001-01-02 00:00:00.000: 2}

Map<K, V> Function(K key, V value) upsertAt(Eq<K> eq) => (K key, V value) {

Suggestion: Don't use factories for constructors

One thing that was great about dartz was the ability to pass Option constructors directly to methods.

For example with dartz you could do this:

futureThatReturnsNullable.then(optionOf);

But you wouldn't be able to pass Option.fromNullable in this case.

Question about tryCatch

Hi, I have a question, as I am quite new to Functional programming and even imperative programming too.

Let's say I have something like this

class FirstException implements Exception {}

class SecondException implements Exception {}

class Student {
  final String name;
  Student(this.name);
}

class StudentRepo {
  static Future<List<Student>> getAllStudents() async => [
        Student('Juan'),
        Student('Maria'),
      ];
}

The question is how to handle only particular exception with tryCatch.
In this case, I want to throw exception other than FirstException and SecondException because I heard catching all exceptions and errors are not good practice. But I am not sure how to handle this with functional way.

Imperative way:

Future<Either<String, List<Student>>> imperative() async {
  try {
    final students = await StudentRepo.getAllStudents();
    return right(students);
  } on FirstException {
    return left('FirstException');
  } on SecondException {
    return left('SecondException');
  }
  // I want to throw exception other than FirstException and SecondException
  // I want to avoid catch all exceptions without on clause
  // catch (e) {
  //   print(e);
  // }
}

Functional way:

TaskEither<String, List<Student>> getStudents = TaskEither.tryCatch(
  () => StudentRepo.getAllStudents(),
  (e, s) => // how to handle exception here?
);

Sorry, I may have misunderstood the concept of functional programming and this may be a dumb question.
Anyway, thank you for creating this wonderful package and very detailed documentation.

Is every constructor that can be const, already const?

Now that my IDE is adding const to many of my widgets, I've been looking at other packages. I see the _Order(this.comp); constructor doesn't have const in front of it, so I add it, and my IDE doesn't complain. Is const missing everywhere? Is that intentional?

Misleading documentation

There is a comparison to dartz package that states that the fpdart is richer.

However, fpdart does not provide the core typeclasses like monoid, functor, applicative or monoid ๐Ÿค”

On the other hand, I'm not sure if all this typeclasses can be effectively used in OO language like dart.
Also, I was not able to use these typeclasses from dartz either.
I wanted to use monoid on my data class, however it was already extending equatable so it was not possible to be used since the dartz package made the monoid a class that needs to extended.

I think that these typeclasses could be implemented in dart using double dispatch to overcome such problem.

Alt runs the Task/IO twice

Due to how Alt is implemented, in TaskEither especially, it runs the Task twice.

TaskEither(() async => (await run()).match((_) => orElse().run(), (_) => run()))

It runs the code, matches and then returns the unrun code to the right (which will then be run once again by the user). Especially for stateful function chains this can be both difficult to diagnose (since there is no documentation saying that this is an intended effect) and undesirable in general (I've taken to side-stepping Alt completely).

Here's a code snippet:

TaskEither(() async {
  print('running');
  return right(1);
}).alt(() {
  print('running alt');
  return TaskEither.right(2);
}).map((r) {
  print('done');
  return r;
}).run();

Here's what I get:

I/flutter ( 8087): running
I/flutter ( 8087): running
I/flutter ( 8087): done

Am I missing something? Is this is way it's meant to function?

Small nitpick with the divide examples

double divideI(int x, int y) {
if (y == 0) {
throw Exception('Cannot divide by 0!');
}
return x / y;
}

This is kind of a red herring but I still wanted to address it. Floating point division by zero is not really breaking.

void main() {
  final allPassed = [
      double.nan != double.nan,
      !double.infinity.isNaN,
      !double.negativeInfinity.isNaN,
      (0 / 0).isNaN,
      (1 / 0) == double.infinity,
      (-1 / 0) == double.negativeInfinity,
  ].every((x) => x);
  print('All True: $allPassed'); // Output: All True: true
}

A better example that definitely breaks:

int divide(int a, int b) => a ~/ b;
divide(10, 0); // Unhandled exception:  IntegerDivisionByZeroException

uppercase => lowercase for a couple of test files.

test/src/Eq_test.dart and test/src/Hash_test.dart trigger the analyzer because the filenames have uppercase. Would you mind renaming them to all lowercase?

edit: and as long as you're in there, could you make .packages be part of .gitignore.... really no point in tracking that, since it's generated automatically.

Do Notation

Hello!

Thank you for your awesome package, really helped me implement most of the concepts I learned from fp-ts in Flutter.

But I do think it's missing something, and that is the Do Notation. I currently have a use case where I need to preserve values across multiple TaskEithers, and I think the Do Notation approach can solve this.

Is there going to be a Do Notation implementation soon in fpdart? Or do you have an alternative approach to my case?

Thank you!

[Question] Why isn't there a flatMap2, flatMap3...

Is there from functional programming perspective a reason why flatMap[2..x] is missing?

In my sample below, function f returns a TaskEither by using the combined parameter b ,c ,d.
I didn't found another way to solve this with the existing api, so i wrote myself an extension function.
Did i miss something?

TaskEither<L, E> flatMap3<C, D, E>(
    TaskEither<L, C> m1,
    TaskEither<L, D> m2,
    TaskEither<L, E> Function(R b, C c, D d) f,
  ) =>
      flatMap((b) => m1.flatMap((c) => m2.flatMap((d) => f(b, c, d))));

Cut-Paste error in Reader.flatten

Reader.flatten has a cut-paste error in the documentation.

It says /// Flat a [Option] contained inside another [Option] to be a single [Option].

It should say /// Flatten a [Reader] contained inside another [Reader] to be a single [Reader].

Error when generating mock class

I encountered the following errors while generating a mock repository which returns an Either object. That being said, it seems like that my test still runs fine.

'Either.map3' ('Either<L, E> Function<C, D, E>(Either<L, C>, Either<L, D>, E Function(R, C, D))') isn't a valid concrete implementation of 'Either.map3' ('Either<L, E> Function<C, D, E>(Either<L, C>, Either<L, D>, E Function(R, C, D))').

'Either.map2' ('Either<L, D> Function<C, D>(Either<L, C>, D Function(R, C))') isn't a valid concrete implementation of 'Either.map2' ('Either<L, D> Function<C, D>(Either<L, C>, D Function(R, C))').

This was the class I was mocking

abstract class AuthRepository {
  Future<Either<Failure, User>> login({
    required String email,
    required String password,
  });
  Future<Either<Failure, Unit>> logout();
  Future<Either<Failure, Unit>> resetPassword(String email);
}

and this is the code generated by Mockito that giving me the compile time error.

import 'package:fpdart/fpdart.dart' as _i2;
import 'package:mockito/mockito.dart' as _i1;

class _FakeEither_0<L, R> extends _i1.Fake implements _i2.Either<L, R> {}

For completeness this is the rest of the Mockito generated class.

/// A class which mocks [AuthRepository].
///
/// See the documentation for Mockito's code generation for more information.
class MockAuthRepository extends _i1.Mock implements _i3.AuthRepository {
  MockAuthRepository() {
    _i1.throwOnMissingStub(this);
  }

  @override
  _i4.Future<_i2.Either<_i5.Failure, _i6.User>> login(
          {String? email, String? password}) =>
      (super.noSuchMethod(
          Invocation.method(#login, [], {#email: email, #password: password}),
          returnValue: Future<_i2.Either<_i5.Failure, _i6.User>>.value(
              _FakeEither_0<_i5.Failure, _i6.User>())) as _i4
          .Future<_i2.Either<_i5.Failure, _i6.User>>);
  @override
  _i4.Future<_i2.Either<_i5.Failure, _i2.Unit>> logout() =>
      (super.noSuchMethod(Invocation.method(#logout, []),
              returnValue: Future<_i2.Either<_i5.Failure, _i2.Unit>>.value(
                  _FakeEither_0<_i5.Failure, _i2.Unit>()))
          as _i4.Future<_i2.Either<_i5.Failure, _i2.Unit>>);
  @override
  _i4.Future<_i2.Either<_i5.Failure, _i2.Unit>> resetPassword(String? email) =>
      (super.noSuchMethod(Invocation.method(#resetPassword, [email]),
              returnValue: Future<_i2.Either<_i5.Failure, _i2.Unit>>.value(
                  _FakeEither_0<_i5.Failure, _i2.Unit>()))
          as _i4.Future<_i2.Either<_i5.Failure, _i2.Unit>>);
  @override
  String toString() => super.toString();
}

mockito: 5.0.15
fpdart: 0.0.10

Unwrap variants for Option and Either

Hello!
It would be great if there was a way to extract/unwrap the value contained in the Option and Either types.
The unwrap terminology is used in Rust, and given that Dart is not a pure functional language sometimes it's useful to extract the value once you've checked it with isSome or isRight to do early returns.
For instance say you return Left and you want to launch an error dialog (Flutter). You would want to break the flow of the program to show this dialog, else continue with the regular flow unwrapping the value of Right.

Here is the API in Rust for reference
For Rust Option -> https://doc.rust-lang.org/std/option/#extracting-the-contained-value
For Rust Result (equivalent to Either) -> https://doc.rust-lang.org/std/result/#extracting-contained-values

The return type 'Null' isn't a 'Failure', as required by the closure's context

I have an Either returning a Failure or Unit.
If the either is left I want to extract the left value (the Failure) and pass it to a method to show that failure to the user.

How can I extract the left value from Either directly?
.getLeft() returns an Option, and .getOrElse(() => null) says The return type 'Null' isn't a 'Failure', as required by the closure's context

This is my code:

if (either.isLeft()) {
   final failure = either.getLeft().getOrElse(() => null);
   showSnackbarFromFailure(failure);
}

What is the right way to archive that?
Thank you for reading.

PD. If this is not the place to public this I will to delete this issue asap.

catch only specified exceptions vs all

I'd have to go check the semantics of the other libraries but it would be nice to have distinctions of:
try-catch for all vs for specific types

Cats has helpers for nonFatal but I'm not sure how it will work given dart lets you throw and catch everything under the sun.

One option is to follow Dart's Future with an optional test function.

// from async/future.dart
Future<T> catchError(Function onError, {bool test(Object error)?});

Another option is to specify it via type

class Test {
  static void catchOnly<T>(void Function() fn, void Function(T) onError) {
    try {
      fn();
    } on T catch(e) {
      onError(e);
    }
  }
}

main() {
  fnHandlerInt(int e) => print('Handling int: $e');
  fnHandlerError(Error e) => print('Handling Error: $e');

  print("1");
  Test.catchOnly(() => throw 1, fnHandlerInt);
  print('2');
  Test.catchOnly(() => 1 ~/ 0, fnHandlerError);
  print('3');
  Test.catchOnly(() => throw 1, fnHandlerError); // Uncaught Error
  print('4'); // unreachable
  // Output:
  // 1
  // Handling int: 1
  // 2
  // Handling Error: Unsupported operation: Result of truncating division is Infinity: 1 ~/ 0
  // 3
  // Uncaught Error: 1
}

It rarely a good idea to catch everything but the dart language doesn't offer classifications from a overflow/OOM versus a parsing error. A possible workout is to explicitly enumerate all the fatal errors and re-throw them immediately.
Thoughts?

Future<T?> to TaskOption<T>

Hi, i'm stuck in one part of my code, the thing is, my database provider brings me a Future<Map<String, Object>?>, so, i was trying to transform to TaskOption<Map<String, Object>> with not sucess, the only thing i've come to, is Task<Option<Map...>> .

The closest approach is this code but it's not the best for me.

late Future<Map<String,Object>?> example;
final taskOp = TaskOption.flatten((TaskOption.fromTask(Task(example).map((ex) => Option.fromNullable(ex).toTaskOption()))));

Is there any other alternative to get a TaskOption<T> object easily from Future?

Thanks a lot, your work in this library is excellent ๐Ÿ’ฏ and sorry foy my bad english ๐Ÿ˜ข

[QUESTION] How to combine multiple IO/TaskEithers results within a chain

Hi,

first at all - thank you very much for your amazing job here! I'm new to FP and need some help chaining multiple IO/TasksEithers.

Imagine i have three functions where all return an IOEither<L,R>

IOEither<L, R> func1() => IOEither.tryCatch(() => "A", (error, stackTrace) => ...)
IOEither<L, R> func2() => IOEither.tryCatch(() => 1, (error, stackTrace) => ...)
IOEither<L, R> func3() => IOEither.tryCatch(() => Combined result of func1 and func 2, (error, stackTrace) => ...)

How do i chain func1 and func2 so i get both results to func3, where func3 returns a combined result of both and probably more?

My real world use case is a nested mapper where each mapper returns an IOEither<L, R>

chainFirst only works for successful cases

When a chained TaskEither with chainFirst fails it propagates back which contradicts the documentation of chainFirst "ignore the result, and return the same value as the current [TaskEither]."

FP Loops and Dart

Given #29, how do I implement loops in a functional way?

I've been stumped on this for a while and since the functional programming sect of Dart seems to be small, I've yet to find any answers that have been relevant to my Flutter app. From my understanding, the lack of tail recursion optimisation means that the using recursive functions in general (especially for large datasets or small stacks on constrained devices) has a chance of making my app misbehave or perform poorly.

Here's a small example of a loop I'd like to get rid of (it's riddle with side effects).

    while (cacheSize > maxSize) {
      final fileSize = await _deleteOldestFile();
      cacheSize = cacheSize - fileSize;
    }

P.S. Sorry if this is the wrong place for this.

Bind and asyncBind (Thank you!)

We need to add an Either.bind and Either.asyncBind that takes another Either (or Future) processing the result of R, without neglecting the result of L.

Thanks for documenting the concepts of functional programming, which dartz unfortunately still fails to do. I hope I can help with a PR solving this issue soon. Hugs!

Add an extension on`Function` which convert a function that returns `Either` to async one

Hi! I have a small proposal regarding TaskEither.

I konw that i can chain functions that return TaskEither using flatMap. And it is also possible to chain a function that returns Either using chainEither. But I think it would be better to have an extension on Function type which convert Either<L, R> Function(T) to TaskEither<L, R> Function(T) something like:

extension EitherToTaskEither<L, R, T> on Either<L, R> Function(T) {
  TaskEither<L, R> Function(T) get async =>
      (T t) => TaskEither(() async => this(t));
}

Then, we can use it as the following:

final newTaskEither = taskEither.flatMap(returnsEither.async);

This extension duplicates the functionality of chainEither, but I think this one is more readable because we can use the same api (flatMpap) in context.

BTW, a very nice library. I can't wait for the day when pattern matching is implemented in Dart and fpdart gets more better ๐Ÿ˜„

Error on Flutter Web

I got this error trying to compile to chrome:

../../../../../.pub-cache/hosted/pub.dartlang.org/fpdart-0.0.6/lib/src/typeclass/group.dart:3:28: Error: The integer literal 9223372036854775807 can't be represented exactly in JavaScript. Try changing the literal to something that can be represented in Javascript. In Javascript 9223372036854775808 is the nearest value that can be represented exactly. const int _int64MaxValue = 9223372036854775807; ^^^^^^^^^^^^^^^^^^^ Failed to compile application.

Searching, i found it from https://api.dart.dev/stable/2.12.4/dart-core/int-class.html:

Note: When compiling to JavaScript, integers are restricted to values that can be represented exactly by double-precision floating point values. The available integer values include all integers between -2^53 and 2^53, and some integers with larger magnitude. That includes some integers larger than 2^63.

It successfully compiled when i changed _int64MaxValue to 2^53. Is it a viable solution?

IORef

Hello!

Firstly, thank you for your awesome work. The Dart community really needed an alternative to now legacy dartz, and fpdart successfully fulfilled that need.

As a fellow Haskell programmer, I found myself implementing a version of IORef myself in a few projects where fpdart is used, hence I propose to add it to the package. Sometimes a mutable reference that can be organically threaded into the fluent flow of IO/Task monad is needed, and IORef is the most obvious choice here.

I already have an implementation, and will be more than happy to submit a PR :)

Show example in docs of mixing functional packages and renaming classes

I wrote this recently for a customer:

/// This file is used to export all the functional programming libraries used in
/// the project. It also exports the [FpState] type alias, which is used to
/// avoid conflicts with the [State] class from the Flutter SDK.
///
import 'package:fpdart/fpdart.dart' as fpdart show State;

/// The `fpdart` library is used to create functional programming constructs.
export 'package:fpdart/fpdart.dart' hide State;

/// The `tuple` library is used to create tuples, which are immutable lists of
/// fixed length.
export 'package:tuple/tuple.dart' show Tuple3, Tuple4, Tuple5, Tuple6, Tuple7;

/// A type alias for the [State] class from the `fpdart` library.
typedef FpState<S, A> = fpdart.State<S, A>;

which we then put in lib/core/functional.dart and include that instead of fpdart or tuple. It's a handy way to show how to rename the classes that conflict with other SDK or third-party packages. Maybe this could go somewhere in the docs, or in one of the examples? It'd also be a great place to add extensions to fpdart's types, so it'd need only one include for the project.

[Question] is there any better way to combine two Reader<Deps1, Result1> and Reader<Deps2, Result2>?

So, I have a function that return Reader<Deps1, Task<bool>> called waitForAuthenticated() and a second function that return Reader<Deps2, Task<R>> called getSomething(), is there a better way to compose both reader?

I have tried to use

var task1 = waitForAuthenticated().run(_deps1);
var task2 = getSomething().run(_deps2);

// ...
// At the outside part of my code / public facing API
task1(task2).run();

But then, I kind of "dislike" calling .run() method on reader as it make my reader not lazy now, is there a better way to do this without calling .run() method on my reader?

[QUESTION] How to await a map function

Hello,

I just started using fpdart so I have what I believe is a noob question.

I have a funcion that returns Future and I am using async/await.

Inside that function, I have an Either. In non FP y would bifurcate my paths based on the either with an if statement.
To be more "FPish" I tried to use map on the Either, but what I do on both paths is async, and should be awaited before the function continues.

I tried doing
either.map((left) async => await foo(), (right) async => await bar())

which compiled, but it didn't work, as I can't await on either.map

What should be the suggested way to use option/either .map in conjunction with await?

change match/fold extension for bools to named parameters

Hi!

I just found out, that match (and fold) for booleans exist. Which is nice and I prefer it over the ternary operator. What I have troubles with, is to distinct between true and false. Sure, you get used to it, if used more often, but at first, I didn't know which is which, since there are actually parameterless functions to provide as paramters.

So my suggestion would be, use named parameters.

Like so:

true.match(onFalse: () => 'it is false', onTrue: () => 'it is true');

I use the match function, if I want to continue with the return value for more functional stuff.

Like so:

result.match(() => something(), () => somethingdifferent()).morestuff()

Feels better then this:

(result ? something() : somethingdifferent()).morestuff()

BTW, a very nice library, coming from F# to flutter/dart, it feels just like home ๐Ÿ˜„

cheers, Thomas

Streams in FPDart

I'm just in the middle of a major refactor which involves moving a lot of my Dartz code into FPDart. While going through your articles I found how amazing the TaskEither.tryCatch() is at cleaning up my code (๐Ÿ˜™๐Ÿ‘Œ) .

But now I've come to my Streams and i'm drawing a bit of a blank on how to tidy them up and wonder if this is something that FPDart has a solution for? I'm currently returning Stream<Either<AlertsFailure, List<Alert>>>> in something like this:

Stream<Either<AlertsFailure, List<Alert>>> watchAlerts() async* {
    final userOption = await _auth.getSignedInUser();
    final user = userOption.getOrElse(() => throw NotAuthenticatedError());

    final alertsRef = _firestore.allUserAlerts(user.uid.getOrCrash());
    yield* alertsRef.snapshots().map((query) {
      if (query.docs.isEmpty) {
        return const Left<AlertsFailure, List<Alert>>(AlertsFailure.noAlerts());
      }
      return Right<AlertsFailure, List<Alert>>(query.docs
          .map(
            (alert) => AlertModel.fromFirestore(alert).toDomain(),
          )
          .toList()
        ..sort((a, b) => b.dateCreated.compareTo(a.dateCreated)));
    }).onErrorReturnWith((e, stack) {
      if (e is FirebaseException && e.code.contains('permission-denied')) {
        return const Left<AlertsFailure, List<Alert>>(
            AlertsFailure.insufficientPermission());
      } else {
        return const Left<AlertsFailure, List<Alert>>(
            AlertsFailure.serverError());
      }
    });
  }

i'm trying to imagine something like TaskEither to wrap the snapshot() but don't know how to handle errors from the stream. The other idea was that there might be some kind of StreamEither but no searching has brought up any examples.

This could be my FP noobish-ness or maybe this is actually a feature request... or maybe this isn't something FP even deals with and then my Stream is the right solution?

Testing Either List

I had the problems with darts already and thought you might have a solution for that.
How do I test an Either List type?
Let's say I have the following simple test:


import 'package:flutter_test/flutter_test.dart';
import 'package:fpdart/fpdart.dart';

void main() {
  String expectedLeft = "lefti";
  List<int> expectedRight = [1, 2, 3, 4, 5];

  group("test either", () {
    test("test left", () {
      final res = getExpected(true);
      expect(res, Left(expectedLeft));
    });

    test("test right", () {
      final res = getExpected(false);
      expect(res, Either.right(expectedRight));
    });
  });
}

Either<String, List<int>> getExpected(bool isLeft) {
  if (isLeft) {
    return Left("lefti");
  } else {
    return Right([1, 2, 3, 4, 5]);
  }
}


The second test failed, because of the deep list inequality (or no deep list equality test).

How do you test this scenario in a simple manner?

TaskEither: Operator that accepts Either (Sync)

Hi! We are promoting this package to the four winds here in Brazil.
We have a question regarding TaskEither.
We know that we can add an Either to a TaskEither with flatMap by converting the Either to a TaskEither using the toTaskEither method. I believe it would be prudent for an operator to accept an Either (sync) on a TaskEither.

SUGGESTION:


final newTaskEither = taskEither.bind(either)

Execute function on right without returning output of function

Hi. Thanks a lot for this package and your articles, they've completely changed the way I build repositories.

The following usecase occurs a lot in my codebase:

  • Make a request with the repository
  • If the request is successful make some followup request
  • The return type of the followup request and its status(success/failure) are unimportant
  • Return the response of the initial request

Take the following snippet from my account_repository for example

@override
  TaskEither<String, MessageResponse> resendVerificationEmail() {
    return TaskEither.tryCatch(
      _accountClient.resendEmail,
      (e, s) => AppHttpClientException.parseException(e),
    );
  }

Say I want to make some other request if resendVerificationEmail was successful but still return the response from resendVerificationEmail, how would I chain both functions?

Tuple2 `map` and `mapSecond`

Hi, I'm curious about why map and mapSecond behave exactly the same (according to docs). Shouldn't map allow users to change the type of both values in Tuple2?

I'm currently using version 0.0.12

Question about TaskEither.tryCatch

Hello, I have a question, as I am quite new to Functional programming.

In this case I am using TaskEither.tryCatch

Example:

TaskEither.tryCatch(() async {
      final Response getPrice = await post(
        Uri.parse(
            "URL"),
        headers: {
          'Content-Type': 'application/json; charset=UTF-8',
        },
      );
      final Map<String, dynamic> json =
          jsonDecode(getPrice.body) as Map<String, dynamic>;

      if (!json.containsKey("pricing")) {
        throw Exception("I don't have price");
      }
      return json["pricing"].toString();
    }, (error, stackTrace) {
      error.log();
      return error.toString();
    });

In this case if I do a throw, it still returns the function as correct, I don't know if there is no way using the TryCatch or exactly how it works?

Thanks!!

Can I use `Option.map` for side effect?

Hello!
First of all, Thank you for your amazing work!
I'm using fpdart with flutter. I have a quick question. Can I use Option.map for side effect?
I think It will be working. But I'm not sure that.

final optionValue = Option.of(true);
optionValue.map((value) {
  print(value);
})

As the examples for Option. It's better that use match for side effect. But I don't need a None case.

final optionValue = Option.of(true);
optionValue.match(
  () => null,
  (value) => print(value),
);

Which one is better?

Difference between `IOEither` and `Either`

Can you give an example to showcase difference between two? I can use both IOEither and Either in synchronous operations. So I can say they are same? may I see a use case?

Missing fold method for Option

I'm trying to migrate from dartz to fpdart and I noticed that Option has foldLeft and foldRight but doesn't have a fold method. Either has all three, though. They both have match which is the same as fold, any reason we have match?

Thanks for your work on this. I appreciate the great documentation.

Predicate [feature request]

Hi!

Any plans to support Predicate and composing predicates?

Predicate:

typedef Predicate<E> = bool Function(E element);

composition:

var Predicate<File> isEmpty;
var Predicate<File> modifiedToday;
files.where(isEmpty && modifiedToday);

[QUESTION] What is the difference between Either and IOEither?

I understand that TaskEither is for using for the result of the async function.

But what is the difference between Either and IOEither? It seems like both of them is for sync result. When I should use Either and IOEither? Can I use only one of them?

list extension mis-typed(?) return values

I was looking at the "List" extension, and I see this:

  /// Insert element `t` at the beginning of the [Iterable].
  Iterable<T> prepend(T t) => [t, ...this];

The return value is not an Iterable. It's a List. You could turn this function into an iterable with something like

    Iterable<T> prepend(T t) sync * { yield t; yield this* };

I just think it'd be more honest, and better for intellisense if things that are by necessity returning a List say so. After all, you can call sort() on this output, but not with an Iterable, only if it's tagged as a List.

Put another way, if we could keep Iterable to mean "might be infinite", it'd be possible to construct some very cool function applications!

Edit: Idea... you could have both an iterable extension and a list extension, properly typing the function inputs!

Edit 2: On discord, we've just demonstrated that Dart picks the most restrictive type in the case of identical signatures:

extension<T> on Iterable<T> {
  void hello() => print('Hello from Iterable extension');
}

extension<T> on List<T> {
  void hello() => print('Hello from List extension');
}

void main() {
  List<String> list = [];
  list.hello(); // Hello from List extension

  Iterable<String> iterable = list;
  iterable.hello(); // Hello from Iterable extension
}

Consider switching Option.match parameter order?

This is only a matter of personal preference, but here's my reasoning:
You routinely handle Eithers first with onLeft, then onRight. In other words, you handle the error first, which is fine.
For Options however, you handle the Some value before the None value. In other words, you handle the "error" last.

I understand this would mean a lot of refactoring for a lot of people, but I think this is more intuitive on the long-term.

Shortcut for `Option.none()` don't work for me

Sample Code:

import 'package:fpdart/fpdart.dart';

class Fp {
  Option<double> divide(double a, double b) {
    if (b == 0) {
      // return Option.none();
      return none();
    }
    return some(a / b);
  }
}

void main() {
  final fp = Fp();
  final value = fp.divide(8, 0);

  print(value.getOrElse(() => 2));
}

Error:
Unhandled exception:
type '() => double' is not a subtype of type '() => Never' of 'orElse'

Solution:
Replace none() with Option.none() in my code

or

In fpdart-0.0.8/lib/src/option.dart, line 22:
Replace:
Option<T> none<T>() => const None();
With:
Option<T> none<T>() => None<T>();

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.