• Stars
    star
    355
  • Rank 119,764 (Top 3 %)
  • Language
    Dart
  • License
    MIT License
  • Created over 3 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

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

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.

Note: The API is still evolving and it may change. New documentation and testing is always ongoing. 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

Check out also 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
  4. Functional Programming Option type – Introduction
  5. Chain functions using Option type – Functional Programming
  6. Practical Functional Programming - Part 1
  7. Practical Functional Programming - Part 2
  8. Practical Functional Programming - Part 3

πŸ‘¨β€πŸ’» Blog posts and tutorials

πŸ’» Installation

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

✨ Examples

Pokeapi

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

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

Open Meteo API

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:

Read/Write local file

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

Manage imports

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

Option

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

/// 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

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

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

IO

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

Task

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)

Reader

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

State

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 🀝

πŸ“¦ Immutable Collections

Fpdart provides some extension methods on Iterable (List) and Map that extend the methods available by providing some functional programming signatures (safe methods that never mutate the original collection and that never throw exceptions).

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!

More

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


🎯 Types

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

πŸ’‘ 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. 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 (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. ReaderEither
  2. ReaderTask
  3. ReaderTaskEither
  4. StateReaderTaskEither
  5. Writer
  6. Lens

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

  • 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

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

I also have a newsletter, in which I share tutorials, guides, and code snippets about fpdart and functional programming: Subscribe to the Newsletter here πŸ“§

πŸ‘€ License

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

More Repositories

1

step-progress-indicator

Open source Flutter package, bar indicator made of a series of selected and unselected steps
Dart
143
star
2

flutter-supabase-template

Complete configuration of a Flutter app with Supabase: environments, authentication, database, and more.
Dart
43
star
3

learn-dart3

Showcase and try all the new features in Dart 3: Pattern Matching, Records, Sealed classes, and much more 🎯
Dart
28
star
4

eslint-plugin-fp-ts-strict

ESLint plugin for typescript to enforce fp-ts functions to avoid the most common javascript problems
JavaScript
17
star
5

fpdart_riverpod

Complete guide on how to build a safe, maintainable, and testable flutter app using fpdart and riverpod
C++
7
star
6

repo_case.dart

Automatically generate usecase classes from your repository class definition in Dart and Flutter
Dart
5
star
7

strava_plus_flutter

Flutter mobile application to enhance your Strava account with extra informations about your weight, the amount of water you drink every day, your sleeping pattern, and the amount of meals you eat every day.
Dart
4
star
8

nextjs-contentlayer-blog-template

A template for building a blog using contentlayer and nextjs.
TypeScript
3
star
9

SandroMaglione

Coding since I was 12. Master in Computer Science and Engineering. Open-Source contributor and maintainer. Writing technical guides and tutorial articles.
2
star
10

range-analysis-llvm

Integer Range Analysis pass implemented for LLVM (High Performance Processors and Systems | UIC 569, Course Project 2020).
C++
2
star
11

k-means-visualization

Javascript page that displays graphically the various step of a k-means algorithm. The script saves all the data an allows you to download a .json file with the details of every iteration.
JavaScript
2
star
12

parser_entities_models

Parser written with Ohm which converts a definition file to models and entities classes in `.dart` for Flutter projects. Simply define the attributes and their type, and the application will generate the complete nested file structure for you.
JavaScript
2
star
13

dart_cli_with_fpdart

A dart cli application using fpdart that scans a dart project to find unused files・Newsletter sandromaglione.com
Dart
2
star
14

nextjs_search_view_pagination

NextJs application where you can search and view a list of users, implemented with pagination
TypeScript
1
star
15

convertkit-nextjs-newsletter-form

Custom form to add subscriber from email to ConvertKit using NextJs and Effect | Newsletter sandromaglione.com
TypeScript
1
star
16

dil-project-polimi-21

Digitial Innovation Lab implementation deliverable
Dart
1
star
17

supabase-ts

Use supabase with Functional Programming and fp-ts
TypeScript
1
star
18

sandromaglione-portfolio

Showcase of all the projects I developed and all technologies that I used during my career as a web and mobile developer.
TypeScript
1
star