Viewing version 1.2.0. Open the latest package page.

fpdart

1.2.0

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

Download archive

Readme

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


fpdart v2.0.0

Version 2 of fpdart is currently in development. v2 is a rewrite based on a new Effect class, that aims to simplify the API and make it easier to use and learn.

You can learn more and follow the progress here.

fpdart v2 is planned to remain in pre-release stage because of some usability issues caused by limitations with the Dart language.

Meanwhile, fpdart is open for PRs if anyone is interested in contributing with improvements and bug fixes.

Read the full discussion here.


๐Ÿ“– Learn functional programming and fpdart

Would you like to know more about functional programming, fpdart, and how to use the package? 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.2.0

โœจ Examples

fpdart + riverpod

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

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

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

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

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

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.

ReaderTask

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

ReaderTaskEither

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

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 ๐Ÿค

โš ๏ธ 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 or BlueSky 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.2.0 - 29 October 2025
  • v1.1.1 - 7 November 2024
  • 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 file for details.

Changelog

v1.2.0 - 29 October 2025

  • Added toTaskOptionFlat, toTaskEitherFlat, fromTaskFlatten to TaskOption and TaskEither
  • Added new iterable extension functions singleOption, elementAtOption, singleWhereOption, lastWhereOption
  • Minor internal improvements
  • Status update for fpdart v2 (full discussion here)

v1.1.1 - 7 November 2024

  • Fixed broken documentation links

v1.1.0 - 13 August 2023

  • Improved performance of some iterable based functions in Iterable and Map extension (thanks to lrhn ๐ŸŽ‰)

  • Added lookupEq and dropRight on Iterable extension

[].lookupEq(Eq.eqInt, 5) // None()
[1, 2, 3, 4].lookupEq(Eq.eqInt, 5) // None()
[1, 2, 3, 4].lookupEq(Eq.eqInt, 3) // Some(3)
[1, 6, 4, 3, 2].lookupEq(Eq.by((n) => n % 3, Eq.eqInt), 0) // Some(6)

[1, 2].dropRight(3) // []
[1, 2, 3, 4].dropRight(1) // [1, 2, 3]
  • Added lookupKeyEq on Map extension
<String, int>{'a': 1, 'b': 2, 'c': 3, 'd': 4}.lookupKeyEq(Eq.eqString, 'b'); // Some('b')
<String, int>{'a': 1, 'b': 2, 'c': 3, 'd': 4}.lookupKeyEq(Eq.eqString, 'e'); // None()

v1.0.0 - 27 May 2023

  • Minimum environment dart sdk to 3.0.0 โš ๏ธ (Dart 3๏ธโƒฃ)
environment:
  sdk: ">=3.0.0 <4.0.0"
  • Added new ReaderTaskEither type
    • ReaderTaskEither models a complete program using Reader for dependency injection, Task to perform asynchronous computation, and Either to handle errors ๐ŸŽฏ
  • Added new ReaderTask type
  • Either as sealed class (Dart 3๏ธโƒฃ)
    • You can now use exhaustive pattern matching (Left or Right)
/// 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)',
};
  • Option as sealed class (Dart 3๏ธโƒฃ)
    • You can now use exhaustive pattern matching (None or Some)
/// 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)',
};
  • Types marked as final (no extends nor implements) (Dart 3๏ธโƒฃ)
    • Unit
    • Reader
    • State
    • StateAsync
    • IO
    • IORef
    • IOOption
    • IOEither
    • Task
    • TaskOption
    • TaskEither
    • ReaderTask
    • ReaderTaskEither
  • Removed Tuple2, use Dart 3 Records instead (Tuple2(a, b) becomes simply (a, b) ๐ŸŽฏ) โš ๏ธ (Dart 3๏ธโƒฃ)
    • Updated all internal APIs to use records instead of Tuple2
  • Major refactoring of Iterable and List extension methods
    • Improved performance
    • Correct return types (Iterable and List) (#65)
    • Added the following methods
      • prependAll (Iterable)
      • intersperse (Iterable)
      • difference (Iterable)
      • filterWithIndex (Iterable)
    • Fixed the following methods โš ๏ธ
      • takeWhileRight: Resulting List now in reversed order as expected
      • dropWhileRight: Resulting List now in reversed order as expected
    • Updated the following methods โš ๏ธ
      • foldRight, foldRightWithIndex (List): Changed parameter order in combine function
      • zipWith (Iterable): Changed parameters definition, no more curried
    • Renamed the following methods โš ๏ธ
      • plus โ†’ concat (Iterable)
      • concat โ†’ flatten (on Iterable<Iterable<T>>)
    • Removed the following methods โš ๏ธ
      • concatMap (use flatMap instead)
      • bind (use flatMap instead)
      • bindWithIndex (use flatMapWithIndex instead)
      • concatMapWithIndex (use flatMapWithIndex instead)
  • Refactoring of Map extension methods
    • Improved performance
    • Added the following methods
      • lookupEq
      • lookupWithKeyEq
    • Removed the following methods โš ๏ธ
      • member (use containsKey instead)
      • elem (use containsValue instead)
      • toIterable (use toSortedList instead)
    • Updated the following methods โš ๏ธ
      • toIterable renamed to toSortedList (return a List instead of Iterable)
      • modifyAt changed parameter order and no more curried
      • modifyAtIfPresent changed parameter order and no more curried
      • updateAt no more curried
      • updateAtIfPresent no more curried
      • deleteAt no more curried
      • upsertAt no more curried
      • pop no more curried
      • foldLeft no more curried
      • foldLeftWithKey no more curried
      • foldLeftWithIndex no more curried
      • foldLeftWithKeyAndIndex no more curried
      • foldRight no more curried
      • foldRightWithKey no more curried
      • foldRightWithIndex no more curried
      • foldRightWithKeyAndIndex no more curried
      • union no more curried
      • intersection no more curried
      • isSubmap no more curried
      • collect no more curried
      • difference no more curried
  • Added conversions helpers from String to num, int, double, and bool using Option and Either (both as extension methods on String and as functions) (#80)
    • toNumOption
    • toIntOption
    • toDoubleOption
    • toBoolOption
    • toNumEither
    • toIntEither
    • toDoubleEither
    • toBoolEither
/// As extension on [String]
final result = "10".toNumOption; /// `Some(10)`
final result = "10.5".toNumOption; /// `Some(10.5)`
final result = "0xFF".toIntOption; /// `Some(255)`
final result = "10.5".toDoubleOption; /// `Some(10.5)`
final result = "NO".toBoolEither(() => "left"); /// `Left("left")`

/// As functions
final result = toNumOption("10"); /// `Some(10)`
final result = toNumOption("10.5"); /// `Some(10.5)`
final result = toIntOption("0xFF"); /// `Some(255)`
final result = toDoubleOption("10.5"); /// `Some(10.5)`
final result = toBoolEither("NO", () => "left"); /// `Left("left")`
  • Changed dateNow, now, random, and randomBool to getter functions
/// Before
Option<T> getRandomOption<T>(T value) => randomBool()
    .map((isValid) => isValid ? some(value) : none<T>())
    .run();

/// Now
Option<T> getRandomOption<T>(T value) => randomBool
    .map((isValid) => isValid ? some(value) : none<T>())
    .run();
  • Removed Predicate class and added extension methods in its place โš ๏ธ
bool isEven(int n) => n % 2 == 0;
bool isDivisibleBy3(int n) => n % 3 == 0;

final isOdd = isEven.negate;
final isEvenAndDivisibleBy3 = isEven.and(isDivisibleBy3);
final isEvenOrDivisibleBy3 = isEven.or(isDivisibleBy3);
final isStringWithEvenLength = isEven.contramap<String>((n) => n.length);
  • Updated curry / uncarry extensions โš ๏ธ
    • Renamed curry to curryAll for functions with 3, 4, 5 parameters
    • Changed definition of curry to curry only the first parameter
    • Changed uncurry and curry extension to getter function
    • Removed curry and uncurry as functions (use extension method instead)
    • Added curryLast (curry last parameter)
int Function(int) subtractCurried(int n1) => (n2) => n1 - n2;

/// Before
subtractCurried.uncurry()(10, 5);

final addFunction = (int a, int b) => a + b;
final add = curry2(addFunction);

[1, 2, 3].map(add(1));  // returns [2, 3, 4]

/// New
subtractCurried.uncurry(10, 5);

final addFunction = (int a, int b) => a + b;
final add = addFunction.curry;

[1, 2, 3].map(add(1)); // returns [2, 3, 4]
[1, 2, 3].map(addFunction.curry(1)); // returns [2, 3, 4]
  • Changed Eq static constructors to methods
    • or
    • and
  • Added xor method to Eq
  • Moved DateTime instances of Eq as Eq static members
/// Before
final eq = dateEqYear; // Global

/// Now
final eq = Eq.dateEqYear;
  • Added Eq instances for num, int, double, String, and bool
[1, 2, 3].difference(Eq.eqInt, [2, 3, 4]); /// `[1]`
  • Added new method to Eq
    • contramap
class Parent {
  final int value1;
  final double value2;
  const Parent(this.value1, this.value2);
}

/// Equality for values of type [Parent] based on their `value1` ([int]).
final eqParentInt = Eq.eqInt.contramap<Parent>(
  (p) => p.value1,
);

/// Equality for of type [Parent] based on their `value2` ([double]).
final eqParentDouble = Eq.eqDouble.contramap<Parent>(
  (p) => p.value2,
);
  • Changed reverse in Order from static constructor to getter method
/// Before
final reversed = Order.reverse(instance);

/// Now
final reversed = instance.reverse;
  • Moved DateTime instances of Order as Order static members
  • Added Order instances for num, int, double
  • Added new methods to Order
    • between
    • clamp
    • contramap
class Parent {
  final int value1;
  final double value2;
  const Parent(this.value1, this.value2);
}

/// Order values of type [Parent] based on their `value1` ([int]).
final orderParentInt = Order.orderInt.contramap<Parent>(
  (p) => p.value1,
);

/// Order values of type [Parent] based on their `value2` ([double]).
final orderParentDouble = Order.orderDouble.contramap<Parent>(
  (p) => p.value2,
);
  • Removed bool extension (match and fold), use the ternary operator or pattern matching instead โš ๏ธ
final boolValue = Random().nextBool();

/// Before
final result = boolValue.match<int>(() => -1, () => 1);
final result = boolValue.fold<int>(() => -1, () => 1);

/// Now
final result = boolValue ? 1 : -1;
final result = switch (boolValue) { true => 1, false => -1 };
  • Removed global id and idFuture, use identity and identityFuture instead โš ๏ธ
  • Removed global idFirst and idSecond functions โš ๏ธ
  • Removed Compose class and extension methods โš ๏ธ
  • Removed Magma typedef โš ๏ธ
  • Removed extension methods on nullable types (toOption, toEither, toTaskOption, toIOEither, toTaskEither, toTaskEitherAsync) โš ๏ธ
  • Organized all extensions inside internal extension folder
  • Updated README

v0.6.0 - 6 May 2023

  • Do notation #97 (Special thanks to @tim-smart ๐ŸŽ‰)
    • All the main types now have a Do() constructor used to initialize a Do notation chain
    • Updated examples to use Do notation (new recommended API ๐ŸŽฏ)
/// 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 ๐Ÿคทโ€โ™‚๏ธ',
    );
/// 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 ๐Ÿคทโ€โ™‚๏ธ',
    );
  • Added new IOOption type
  • Added conversion methods from and to all classes (IO, IOOption, IOEither, Task, TaskOption, TaskEither)
    • Removed toTask in IOEither (use toTaskEither instead) โš ๏ธ
  • Improved performance of fpdart's sortBy list extension #101 (thanks to @hbock-42 ๐ŸŽ‰)
  • Updated pokeapi_functional example to Riverpod v2 #99 (thanks to @utamori ๐ŸŽ‰)
  • Updated repository folder structure #105

v0.5.0 - 4 March 2023

  • Updates to Option type #92 [โš ๏ธ BREAKING CHANGE]
    • Added const factory constructor for None (fixes #95)
    • Removed Alt and Foldable type classes, the following methods are not available anymore
      • foldLeft
      • foldRight
      • foldMap
      • foldRightWithIndex
      • foldLeftWithIndex
      • length
      • any
      • all
      • concatenate
      • plus
      • prepend
      • append
  • Updated examples and fixed lint warnings #93 (thanks to tim-smart ๐ŸŽ‰)

v0.4.1 - 25 February 2023

  • New methods for Option type (thanks to tim-smart ๐ŸŽ‰)
    • flatMapNullable
    • flatMapThrowable
final option = Option.of(10);

option.flatMapNullable((a) => a + 1); /// ๐Ÿ‘ˆ `Some(11)`
option.flatMapThrowable((a) => a + 1); /// ๐Ÿ‘ˆ `Some(11)`

option.flatMapNullable<int>((a) => null); /// ๐Ÿ‘ˆ `None()`
option.flatMapThrowable<int>((a) => throw "fail"); /// ๐Ÿ‘ˆ `None()`
  • Improved support fromJson for Option type (thanks [again] to tim-smart ๐ŸŽ‰)
    • Allow for decoding of non-primitive types (with custom fromJson constructors)
/// `fromJson` on `DateTime` with `Option` type
final now = DateTime.now();
Option<DateTime>.fromJson(now.toIso8601String(), (a) => DateTime.parse(a as String)); /// ๐Ÿ‘ˆ `Some(now)`

Option<DateTime>.fromJson("fail", (a) => DateTime.parse(a as String)); /// ๐Ÿ‘ˆ `None()`
  • New extension methods for Map (thanks [once again] to tim-smart ๐ŸŽ‰)
    • extract
    • extractMap
final map = <String, dynamic>{'a': 1, 'b': 2, 'c': 3, 'd': 4};
map.extract<int>('b'); /// ๐Ÿ‘ˆ `Some(2)`
map.extract<String>('b'); /// ๐Ÿ‘ˆ `None()`, not of type `String` โš ๏ธ

final map = <String, dynamic>{'a': 1};
map.extractMap('a'); /// ๐Ÿ‘ˆ `None()`, not a `Map`

final map = <String, dynamic>{'a': {'b': 2} };
map.extractMap('a'); /// ๐Ÿ‘ˆ `Some({'b': 2})`
  • Option.of and Option.none factories const (thanks to f-person ๐ŸŽ‰)

Note: People who have the prefer_const_constructors lint enabled will notice a warning to use const ๐Ÿค

v0.4.0 - 16 December 2022

  • Added extension methods to work with nullable types (T?)
    • From T? to fpdart's types
      • toOption
      • toEither
      • toTaskOption
      • toIOEither
      • toTaskEither
      • toTaskEitherAsync
      • fromNullable (Either, IOEither, TaskOption TaskEither)
      • fromNullableAsync (TaskEither)
    • From fpdart's types to T?
      • toNullable (Either)
/// [Option] <-> `int?`
int? value1 = 10.toOption().map((t) => t + 10).toNullable();

bool? value2 = value1?.isEven;

/// `bool?` -> [Either] -> `int?`
int? value3 = value2
    .toEither(() => "Error")
    .flatMap((a) => a ? right<String, int>(10) : left<String, int>("None"))
    .toNullable();

/// `int?` -> [Option]
Option<int> value4 = (value3?.abs().round()).toOption().flatMap(Option.of);
  • Added toIOEither to Either
  • Removed parameter from Either fromNullable [โš ๏ธ BREAKING CHANGE]
final either = Either<String, int>.fromNullable(value, (r) => 'none');

/// ๐Ÿ‘† Removed the value `(r)` (it was always null anyway ๐Ÿ’๐Ÿผโ€โ™‚๏ธ) ๐Ÿ‘‡

final either = Either<String, int>.fromNullable(value, () => 'none');
  • Added chainEither to TaskEither
  • Added safeCast (Either and Option)
  • Added safeCastStrict (Either and Option)
int intValue = 10;

/// Unhandled exception: type 'int' is not a subtype of type 'List<int>' in type cast
final waitWhat = intValue as List<int>;
final first = waitWhat.first;

/// Safe ๐ŸŽฏ
final wellYeah = Either<String, List<int>>.safeCast(
  intValue,
  (dynamic value) => 'Not a List!',
);
final firstEither = wellYeah.map((list) => list.first);

v0.3.0 - 11 October 2022

  • Inverted onSome and onNone functions parameters in match method of Option [โš ๏ธ BREAKING CHANGE] (Read more on why ๐Ÿ‘‰ #56)
/// Everywhere you are using `Option.match` you must change this:
final match = option.match(
  (a) => print('Some($a)'),
  () => print('None'), // <- `None` second ๐Ÿ‘Ž 
);

/// to this (invert parameters order):
final match = option.match(
  () => print('None'), // <- `None` first ๐Ÿ‘
  (a) => print('Some($a)'),
);
  • Added traverse and sequence methods (#55)
    • traverseList
    • traverseListWithIndex
    • sequenceList
    • traverseListSeq
    • traverseListWithIndexSeq
    • sequenceListSeq
/// "a40" is invalid ๐Ÿ’ฅ
final inputValues = ["10", "20", "30", "a40"];

/// Verify that all the values can be converted to [int] ๐Ÿ”
///
/// If **any** of them is invalid, then the result is [None] ๐Ÿ™…โ€โ™‚๏ธ
final traverseOption = inputValues.traverseOption(
  (a) => Option.tryCatch(
    /// If `a` does not contain a valid integer literal a [FormatException] is thrown
    () => int.parse(a),
  ),
);
  • Added bindEither method in TaskEither (#58)
/// Chain [Either] to [TaskEither]
TaskEither<String, int> binding =
    TaskEither<String, String>.of("String").bindEither(Either.of(20));
  • Added lefts, rights, and partitionEithers methods to Either (#57)
final list = [
  right<String, int>(1),
  right<String, int>(2),
  left<String, int>('a'),
  left<String, int>('b'),
  right<String, int>(3),
];
final result = Either.partitionEithers(list);
expect(result.first, ['a', 'b']);
expect(result.second, [1, 2, 3]);
  • Added bimap method to Either, IOEither, and Tuple2 (#57)
  • Added mapLeft method to IOEither (#57)
  • Added fold method to Option (same as match) (#56)
  • Fixed chainFirst for Either, TaskEither, and IOEither when chaining on a failure (Left) (#47) by DevNico ๐ŸŽ‰
  • Added const to all constructors in which it was missing (#59)
  • Minimum environment dart sdk to 2.17.0 โš ๏ธ
environment:
  sdk: ">=2.17.0 <3.0.0"

v0.2.0 - 16 July 2022

v0.1.0 - 17 June 2022

  • Added idFuture and identityFuture methods (#38) by f-person ๐ŸŽ‰
  • Added mapBoth method to Tuple2 (#30)
  • Fixed linting warnings
  • Fixed issue with upsert method for Map (#37) โš ๏ธ
  • Minimum environment dart sdk to 2.16.0 โš ๏ธ
environment:
  sdk: ">=2.16.0 <3.0.0"

v0.0.14 - 31 January 2022

  • Updated package linting to lints

v0.0.13 - 26 January 2022

  • New methods to TaskEither, TaskOption, Either, and Option
    • mapLeft (TaskEither)
    • bimap (TaskEither)
    • toTaskEither (Either)
    • toTaskOption (Option)
  • New Blog posts and tutorials section in README

v0.0.12 - 24 October 2021

  • Completed IORef type implementation, documentation, and testing

v0.0.11 - 22 September 2021

  • Fixed major issue in State and StateAsync implementation [BREAKING CHANGE]
    • Methods flatMap, map, map2, map3, ap, andThen, call, and flatten had an implementation issue that has been now fixed

v0.0.10 - 13 August 2021

  • Released introduction to Practical Functional Programming
  • Completed StateAsync type implementation, documentation, and testing
  • Fixed problem with Alt typeclass (#21)
  • Added call method to more easily chain functions in Monad and Monad2

v0.0.9 - 3 August 2021

v0.0.8 - 13 July 2021

  • Released Part 3 of Fpdart, Functional Programming in Dart and Flutter
  • Added Pure Functional Flutter app example (pokeapi_functional)
  • Added flatMapTask and toTask methods to IO to lift and chain IO with Task
  • Added flatMapTask and toTask methods to IOEither to lift and chain IOEither with TaskEither
  • Added pattern matching extension methods to bool (boolean.dart)
  • Added functions to get random int, double, and bool in a functional way (using IO) (random.dart)
  • Added functions, extension methods, Ord, and Eq instances to DateTime (date.dart)

v0.0.7 - 6 July 2021

  • Released Part 2 of Fpdart, Functional Programming in Dart and Flutter
  • Added Compose and Compose2, used to easily compose functions in a chain
  • Added curry and uncurry extensions on functions up to 5 parameters
  • Completed TaskOption type implementation, documentation, and testing
  • Expanded documentation and examples
  • Added TaskEither.tryCatchK and Either.tryCatchK, by tim-smart (#10, #11) ๐ŸŽ‰

v0.0.6 - 29 June 2021

  • Released Part 1 of Fpdart, Functional Programming in Dart and Flutter
  • Added functional extension methods on Iterable (List)
  • Completed IOEither type implementation, documentation, and testing
  • Added constF function
  • Added option and optionOf (same as dartz)
  • Added Either.right(r) factory constructor to Either class (same as Either.of(r)) (#3)
  • Added example on reading local file using TaskEither (read_write_file)
  • Added more examples
  • Added constant constructors to Eq and variants, by mateusfccp (#4) ๐ŸŽ‰

v0.0.5 - 20 June 2021

  • Completed State type implementation, documentation, and testing
  • Completed Reader type implementation, documentation, and testing
  • Completed IO type implementation, documentation, and testing
  • Merged PR (#2) by jacobaraujo7 ๐ŸŽ‰
    • Added right and left functions to create instance of Either
    • Added id function (same as identity)
    • Added fold method to Either (same as match)
    • Added bind method to Either (same as flatMap)
    • Added bindFuture method to Either, which returns TaskEither

v0.0.4 - 15 June 2021

  • Completed Unit type documentation
  • Completed Task type implementation, documentation, and testing
  • Completed TaskEither type implementation, documentation, and testing
  • Completed implementation, documentation, and testing of Foldable instance on Option and Either [BREAKING CHANGE]
  • Completed Tuple2 type implementation, documentation, and testing [BREAKING CHANGE]
  • Renamed fold method of Foldable to foldLeft [BREAKING CHANGE]
  • Updated methods API (foldRight, foldLeft, etc.) of Foldable instances (Option, Either, Tuple) [BREAKING CHANGE]
  • IList not longer working correctly (waiting for a better solution for immutable collections) [BREAKING CHANGE]

v0.0.3 - 13 June 2021

  • Changed name of type Maybe to Option to be inline with fp-ts, cats, and dartz [BREAKING CHANGE]

v0.0.2 - 13 June 2021

First major release:

Types

  • Either
  • IList
  • Maybe
  • Reader
  • State
  • Task
  • TaskEither
  • Tuple
  • Unit

Typeclasses

  • Alt
  • Applicative
  • Band
  • BoundedSemilattice
  • CommutativeGroup
  • CommutativeMonoid
  • CommutativeSemigroup
  • Eq
  • Extend
  • Filterable
  • Foldable
  • Functor
  • Group
  • Hash
  • HKT
  • Monad
  • Monoid
  • Order
  • PartialOrder
  • Semigroup
  • Semilattice

Examples

  • Either
  • curry
  • Maybe
  • Reader
  • State

v0.0.1 - 28 May 2021

  • Eq
  • Hash
  • PartialOrder

Installing

export PUB_HOSTED_URL=https://pub.mirror.p-hatan.com
flutter pub add 'fpdart:1.2.0'
pubspec.yaml
name: fpdart
description: >
  Functional programming in Dart and Flutter.
  All the main functional programming types and patterns fully documented, tested, and with examples.
version: 1.2.0
homepage: https://www.sandromaglione.com/
repository: https://github.com/SandroMaglione/fpdart
documentation: https://www.sandromaglione.com/articles
issue_tracker: https://github.com/SandroMaglione/fpdart/issues

environment:
  sdk: ">=3.0.0 <4.0.0"

dev_dependencies:
  lints: ^2.0.1
  test: ^1.23.1
  glados: ^1.1.6
  collection: ^1.17.2

screenshots:
  - description: "Basic usage of fpdart Option, Either, TaskEither types."
    path: example/screenshot_fpdart.png

Version history

VersionPublishedArchiveSHA-256
0.0.1On demandDownloade31d870eโ€ฆf8a2e7
0.0.2On demandDownloadbe19aa47โ€ฆafbacf
0.0.2+1On demandDownloada30b9ab5โ€ฆ832ff1
0.0.3On demandDownloadb49216aeโ€ฆ0f344a
0.0.4On demandDownload3342820dโ€ฆ899864
0.0.5On demandDownloadafe38068โ€ฆceb486
0.0.6On demandDownload7af084bbโ€ฆ2cac49
0.0.7On demandDownload6e58fc74โ€ฆ7f3632
0.0.8On demandDownloadbbaf8b31โ€ฆ7fbd69
0.0.9On demandDownloadb9a2c822โ€ฆ8b44b0
0.0.10On demandDownloada7ea0671โ€ฆ9dfb36
0.0.11On demandDownloadc877dfbbโ€ฆ2344b4
0.0.12On demandDownload5a3a49edโ€ฆaa0c88
0.0.13On demandDownload126401cbโ€ฆ6616af
0.0.14On demandDownloadcfeeedd0โ€ฆf12655
0.1.0On demandDownload9c7b0f50โ€ฆ3867e9
0.2.0On demandDownload2c807871โ€ฆ04e444
0.3.0On demandDownloadf198825bโ€ฆ11e98c
0.4.0On demandDownload19db038cโ€ฆdae935
0.4.1On demandDownload21053522โ€ฆd6a57b
0.5.0On demandDownload40dfb6f8โ€ฆ989926
0.6.0-dev.1On demandDownload87345c61โ€ฆ8f5e89
0.6.0On demandDownload4a0d047cโ€ฆ47106e
1.0.0-beta.1On demandDownloaddf0cf507โ€ฆ1f2f1c
1.0.0On demandDownloadb254aef8โ€ฆe8bf0b
1.1.0On demandDownload7413acc5โ€ฆ6e8ee9
1.1.1On demandDownload1b84ce64โ€ฆ37af77
1.2.0Cached ยท Upstream SHA-256 matchedDownloadf8e9d098โ€ฆ7a73d4
2.0.0-dev.1On demandDownload981b9ddaโ€ฆ016042
2.0.0-dev.2On demandDownloadbe005b39โ€ฆ69d348
2.0.0-dev.3On demandDownload9c82219bโ€ฆ22d055

Dependencies

No direct dependencies declared.