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

Last update: Jun 24, 2022

Fpdart

GitHub: SandroMaglione Twitter: SandroMaglione

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

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.

Note: The package is still in early development. The API may change frequently and there will be many breaking changes. The documentation and testing is currently under development, but it is coming soon and fast. Follow my Twitter for daily updates.

πŸ“– Learn functional programming and fpdart

Would you like to know more about functional programming, fpdart, and how to use the package? Check out this series of articles about functional programming with fpdart:

  1. Fpdart, Functional Programming in Dart and Flutter
  2. How to use fpdart Functional Programming in your Dart and Flutter app
  3. Pure Functional app in Flutter – Pokemon app using fpdart and Functional Programming

🎯 Types

  • Option
  • Either
  • Unit
  • Task
  • TaskEither
  • State
  • Reader
  • Tuple
  • IO
  • Iterable (List) extension
  • IOEither
  • TaskOption
  • ReaderEither
  • ReaderTask
  • ReaderTaskEither
  • StateReaderTaskEither
  • Lens
  • Writer

πŸ’» Installation

# pubspec.yaml
dependencies:
  fpdart: ^0.0.8 # Check out the latest version

✨ Examples

Option

/// 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(
  (a) => print('Some($a)'),
  () => print('None'),
);

/// 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'));

Either

/// 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)'),
);

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

Reader

View the example folder for an explained usecase example.

State

View the example folder for an explained usecase example.

More

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


πŸ’‘ Motivation

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. Other functional programming features are coming to the language, like pattern matching, destructuring, multiple return values, 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 2.
  • Fpdart is completely null-safe from the beginning.
  • Fpdart has a richer API.
  • Fpdart implements some missing types in dartz.
  • Fpdart (currently) 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 roadmap for types development is highlighted below (breaking changes to 'stable' types are to be expected in this early stages):

  1. Option
    • Implementation
    • Documentation
    • Testing
  2. Either
    • Implementation
    • Documentation
    • Testing
  3. Unit
    • Implementation
    • Documentation
    • Testing
  4. Task
    • Implementation
    • Documentation
    • Testing
  5. TaskEither
    • Implementation
    • Documentation
    • Testing
  6. Tuple
    • Implementation
    • Documentation
    • Testing
  7. State
    • Implementation
    • Documentation
    • Testing
  8. Reader
    • Implementation
    • Documentation
    • Testing
  9. IO
    • Implementation
    • Documentation
    • Testing
  10. IOEither
    • Implementation
    • Documentation
    • Testing
  11. TaskOption
    • Implementation
    • Documentation
    • Testing
  12. ReaderEither
    • Implementation
    • Documentation
    • Testing
  13. ReaderTask
    • Implementation
    • Documentation
    • Testing
  14. ReaderTaskEither
    • Implementation
    • Documentation
    • Testing
  15. StateReaderTaskEither
    • Implementation
    • Documentation
    • Testing
  16. Writer
    • Implementation
    • Documentation
    • Testing
  17. Lens
    • Implementation
    • Documentation
    • Testing

Note: Integrations for immutable collections (IList, ISet, IMap, etc.) are still being discussed with the community. fpdart does not want to be another immutable collection solution in the ecosystem. That is why we are working to integrate fpdart with other more mature packages that already implements immutable collections. 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

  • 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

Currently the best way to support me would be to follow me on my Twitter.

Another option (or Option) would be to buy me a coffee.

πŸ‘€ License

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

GitHub

https://github.com/SandroMaglione/fpdart
Comments
  • 1. 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);
    
    Reviewed by pihentagy at 2021-07-28 09:52
  • 2. 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?

    Reviewed by Babwenbiber at 2021-06-29 18:31
  • 3. [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?

    Reviewed by mbabuglia at 2022-01-21 13:12
  • 4. Added new functions and wrapper

    Added Functions:

    bind -> Bind other Either. asyncBind -> same as Bind, but return Future fold -> same as match (Help people who used dartz to migrate)

    Wrappers:

    id -> Function wrapper for return the parameter; right -> Return Right(r) left -> Return Left(l) (These wrappers also help people who used dartz to migrate).

    Reviewed by jacobaraujo7 at 2021-06-19 19:04
  • 5. 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?

    Reviewed by Prn-Ice at 2022-06-20 15:36
  • 6. [Question] is there any better way to combine two Reader and Reader?

    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?

    Reviewed by apiep at 2021-11-19 09:36
  • 7. [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?

    Reviewed by PTLam25 at 2021-11-15 09:57
  • 8. 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 :)

    Reviewed by purplenoodlesoop at 2021-10-08 01:19
  • 9. Tuple3,4,5,6

    What about we have Tuple3-6 or 8 as in this package https://pub.dev/packages/tuple. I can still do something hacky like Tuple2<First, Tuple2<Second, Third>> but it will be not very friendly.

    Reviewed by erabti at 2021-09-26 15:56
  • 10. 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

    Reviewed by Alias3e at 2021-09-02 05:31
  • 11. 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?

    Reviewed by ghost at 2021-08-07 13:32
  • 12. 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?

    Reviewed by lloydrichards at 2021-10-29 10:27
  • 13. 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!

    Reviewed by prazedotid at 2021-10-14 19:51

Related

Megaflixz is a movie app for the movie lover in you. Includes all new and upcoming movies and TV series. Also has an option to save movies to watch later list
Megaflixz is a movie app for the movie lover in you. Includes all new and upcoming movies and TV series. Also has an option to save movies to watch later list

MegaflixZ MegaflixZ is a movie app for the movie lover in you. It is an app that displays new and upcoming movies and tv series. Team members Deepak M

Aug 23, 2021
MobX for the Dart language. Hassle-free, reactive state-management for your Dart and Flutter apps.
MobX for the Dart language. Hassle-free, reactive state-management for your Dart and Flutter apps.

Language: English | PortuguΓͺs | Chinese mobx.dart MobX for the Dart language. Supercharge the state-management in your Dart apps with Transparent Func

Jun 22, 2022
A simple way to access state while robust and testable.
A simple way to access state while robust and testable.

A state-management library that: catches programming errors at compile time rather than at runtime removes nesting for listening/combining objects ens

Jun 29, 2022
Open screens/snackbars/dialogs/bottomSheets without context, manage states and inject dependencies easily with Get.
Open screens/snackbars/dialogs/bottomSheets without context, manage states and inject dependencies easily with Get.

Languages: English (this file), Indonesian, Urdu, Chinese, Brazilian Portuguese, Spanish, Russian, Polish, Korean, French. About Get Installing Counte

Jul 2, 2022
App to Watch Animes And Read Manga.
App to Watch Animes And Read Manga.

App To Watch Anime And Read Manga NOTE: Manga Section Contains 18+ content. Sometimes App may take some time to load. Constants Folder is removed from

Sep 12, 2021
A flutter boilerplate project with GetX state management.
A flutter boilerplate project with GetX state management.

flutter_getx_boilerplate Languages: English (this file), Chinese. Introduction During my study of Flutter, I have been using the flutter_bloc state ma

Jun 30, 2022
Flutter MVU architecture/state management package

mvu_flutter No mutability. No builders. No connectors. No reducers. No StreamControllers and subscription management. A truly declarative state manage

Jul 29, 2021
Simple global state management for Flutter

Slices Slices is a minimalist state manegement, focused specifically for applications that needs a global state where different "pieces" of the applic

Jun 15, 2021
The modular state management solution for flutter.
The modular state management solution for flutter.

The modular state management solution for flutter. Easy debugging : each event is predictable and goes into a single pipeline Centralized state : soli

Jan 5, 2022
Flutter State Management with provider :rocket:
Flutter State Management with provider :rocket:

Flutter - Gerenciamento de Estados com Provider Objetivos ao completar os estudos Aprenda a gerenciar o estado da sua aplicação com Single Source of T

Dec 6, 2021
Flutter Navigation - all types of navigation in flutter run main.tabBar.dart to see tabBar, and run main.dart to see the otheres

pop_up_bar A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you started if th

Jan 1, 2022
Examples of all types of widgets used for animation in Flutter.

Anny Flutter application for learning animations concept. Checkout the live demo here. License Copyright 2020 Ashutosh Singh Licensed under the A

Jun 25, 2022
A full, sound, modern and documented JS interop for Dart.

This is (or should be) a full JavaScript interop package using package:js bindings. The bindings are generated by machine-reading WebIDL files for typ

May 14, 2022
Experimenting with 6 examples of different types of simple and complex JSON structures in Flutter

Parsing complex JSON in Flutter Gives a detailed explanation of working with simple and complex JSON structures using dart:convert library in Flutter

Jun 17, 2022
Simple access log parsing for zabbix (tested with nginx)
Simple access log parsing for zabbix (tested with nginx)

zabbix-nginx-stats Simple script to import basic nginx statistics into zabbix. It parses the log file (currently only supports one basic format) and p

Mar 18, 2022
Reactive Programming - BLoC - Practical Use Cases and Patterns

Reactive Programming - BLoC - Practical Use Cases and Patterns Source code of the article available on didierboelens.com This article introduces some

Mar 3, 2022
Feb 25, 2022
Dart port of FormCoreJS: A minimal pure functional language based on self dependent types.

FormCore.js port to Dart. So far only the parser and typechecker have been ported i.e. the FormCore.js file in the original repo. (Original readme fro

Jan 28, 2022
(Full-stack) Fully functional social media app (Instagram clone) written in flutter and dart with backend node.js and Postgres SQL.
(Full-stack) Fully functional social media app (Instagram clone) written in flutter and dart with backend node.js and Postgres SQL.

Photoarc A Fully functional social media app written in flutter and dart using node.js and Postgres SQL as backend. Backend Repository Demo Download t

Jun 17, 2022