Viewing version 1.2.0. Open the latest package page.
fpdart
1.2.0Functional programming in Dart and Flutter. All the main functional programming types and patterns fully documented, tested, and with examples.
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.
- Introduction
- ๐ Learn
functional programmingandfpdart - ๐ป Installation
- โจ Examples
- ๐ฏ Types
- ๐ก Motivation
- ๐ค Roadmap
- ๐ Versioning
- ๐ Support
- ๐ License
๐ 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
- Option type and Null Safety in dart
- Either - Error Handling in Functional Programming
- Future & Task: asynchronous Functional Programming
- How to use TaskEither in fpdart
- How to make API requests with validation in fpdart
- How to map an Either to a Future in fpdart
- Flutter Supabase Functional Programming with fpdart
- fpdart, Functional Programming in Dart and Flutter
- How to use fpdart Functional Programming in your Dart and Flutter app
- Pure Functional app in Flutter โ Pokemon app using fpdart and Functional Programming
๐งโ๐ซ Getting started with functional programming
- Functional Programming Option type โ Introduction
- Chain functions using Option type โ Functional Programming
- Practical Functional Programming - Find repeated characters | Part 1
- Pure Functions - Practical Functional Programming | Part 2
- Immutability โ Practical Functional Programming | Part 3
- Loops using fold โ Practical Functional Programming | Part 4
- Maybe | Practical Functional Programming
- 5 Lessons from Functional Programming in Scala, By Paul Chiusano and Runar Bjarnason
๐ป 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:
- Open Meteo API - Functional programming with fpdart (Part 1)
- Open Meteo API - Functional programming with fpdart (Part 2)
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
Stringtoint, since not allStringcan be converted toint
/// 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>:Lis the type of the error (for example aStringexplaining the problem),Ris 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
IObut 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
throwinside theDo()constructor- Do not
awaitwithout executing the$function- Do not use a nested
Do()constructor inside another one- Do not call the
$function inside another callback in theDo()constructorUsing the Do notation in these cases may fail at runtime or may produce unexpected behavior. For more information take a look at the
Do Notationdiscussion 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
fpdartin 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 -
Mapextension -
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
ZIOinfpdart, 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,fromTaskFlattentoTaskOptionandTaskEither - 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
IterableandMapextension (thanks to lrhn ๐) -
Added
lookupEqanddropRightonIterableextension
[].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
lookupKeyEqonMapextension
<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
ReaderTaskEithertypeReaderTaskEithermodels a complete program usingReaderfor dependency injection,Taskto perform asynchronous computation, andEitherto handle errors ๐ฏ
- Added new
ReaderTasktype Eitherassealedclass (Dart 3๏ธโฃ)- You can now use exhaustive pattern matching (
LeftorRight)
- You can now use exhaustive pattern matching (
/// 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)',
};
Optionassealedclass (Dart 3๏ธโฃ)- You can now use exhaustive pattern matching (
NoneorSome)
- You can now use exhaustive pattern matching (
/// 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(noextendsnorimplements) (Dart 3๏ธโฃ)UnitReaderStateStateAsyncIOIORefIOOptionIOEitherTaskTaskOptionTaskEitherReaderTaskReaderTaskEither
- 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
- Updated all internal APIs to use records instead of
- Major refactoring of
IterableandListextension methods- Improved performance
- Correct return types (
IterableandList) (#65) - Added the following methods
prependAll(Iterable)intersperse(Iterable)difference(Iterable)filterWithIndex(Iterable)
- Fixed the following methods โ ๏ธ
takeWhileRight: ResultingListnow in reversed order as expecteddropWhileRight: ResultingListnow in reversed order as expected
- Updated the following methods โ ๏ธ
foldRight,foldRightWithIndex(List): Changed parameter order incombinefunctionzipWith(Iterable): Changed parameters definition, no more curried
- Renamed the following methods โ ๏ธ
plusโconcat(Iterable)concatโflatten(onIterable<Iterable<T>>)
- Removed the following methods โ ๏ธ
concatMap(useflatMapinstead)bind(useflatMapinstead)bindWithIndex(useflatMapWithIndexinstead)concatMapWithIndex(useflatMapWithIndexinstead)
- Refactoring of
Mapextension methods- Improved performance
- Added the following methods
lookupEqlookupWithKeyEq
- Removed the following methods โ ๏ธ
member(usecontainsKeyinstead)elem(usecontainsValueinstead)toIterable(usetoSortedListinstead)
- Updated the following methods โ ๏ธ
toIterablerenamed totoSortedList(return aListinstead ofIterable)modifyAtchanged parameter order and no more curriedmodifyAtIfPresentchanged parameter order and no more curriedupdateAtno more curriedupdateAtIfPresentno more currieddeleteAtno more curriedupsertAtno more curriedpopno more curriedfoldLeftno more curriedfoldLeftWithKeyno more curriedfoldLeftWithIndexno more curriedfoldLeftWithKeyAndIndexno more curriedfoldRightno more curriedfoldRightWithKeyno more curriedfoldRightWithIndexno more curriedfoldRightWithKeyAndIndexno more curriedunionno more curriedintersectionno more curriedisSubmapno more curriedcollectno more currieddifferenceno more curried
- Added conversions helpers from
Stringtonum,int,double, andboolusingOptionandEither(both as extension methods onStringand as functions) (#80)toNumOptiontoIntOptiontoDoubleOptiontoBoolOptiontoNumEithertoIntEithertoDoubleEithertoBoolEither
/// 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, andrandomBoolto 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
Predicateclass 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
currytocurryAllfor functions with 3, 4, 5 parameters - Changed definition of
curryto curry only the first parameter - Changed
uncurryandcurryextension to getter function - Removed
curryanduncurryas functions (use extension method instead) - Added
curryLast(curry last parameter)
- Renamed
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
Eqstatic constructors to methodsorand
- Added
xormethod toEq - Moved
DateTimeinstances ofEqasEqstatic members
/// Before
final eq = dateEqYear; // Global
/// Now
final eq = Eq.dateEqYear;
- Added
Eqinstances fornum,int,double,String, andbool
[1, 2, 3].difference(Eq.eqInt, [2, 3, 4]); /// `[1]`
- Added new method to
Eqcontramap
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
reverseinOrderfrom static constructor to getter method
/// Before
final reversed = Order.reverse(instance);
/// Now
final reversed = instance.reverse;
- Moved
DateTimeinstances ofOrderasOrderstatic members - Added
Orderinstances fornum,int,double - Added new methods to
Orderbetweenclampcontramap
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
boolextension (matchandfold), 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
idandidFuture, useidentityandidentityFutureinstead โ ๏ธ - Removed global
idFirstandidSecondfunctions โ ๏ธ - Removed
Composeclass and extension methods โ ๏ธ - Removed
Magmatypedef โ ๏ธ - Removed extension methods on nullable types (
toOption,toEither,toTaskOption,toIOEither,toTaskEither,toTaskEitherAsync) โ ๏ธ - Organized all extensions inside internal
extensionfolder - 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 ๐ฏ)
- All the main types now have a
/// 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
IOOptiontype - Added conversion methods from and to all classes (
IO,IOOption,IOEither,Task,TaskOption,TaskEither)- Removed
toTaskinIOEither(usetoTaskEitherinstead) โ ๏ธ
- Removed
- Improved performance of
fpdart'ssortBylist extension #101 (thanks to @hbock-42 ๐) - Updated
pokeapi_functionalexample to Riverpod v2 #99 (thanks to @utamori ๐) - Updated repository folder structure #105
v0.5.0 - 4 March 2023
- Updates to
Optiontype #92 [โ ๏ธ BREAKING CHANGE]- Added
const factoryconstructor forNone(fixes #95) - Removed
AltandFoldabletype classes, the following methods are not available anymorefoldLeftfoldRightfoldMapfoldRightWithIndexfoldLeftWithIndexlengthanyallconcatenateplusprependappend
- Added
- Updated examples and fixed lint warnings #93 (thanks to tim-smart ๐)
v0.4.1 - 25 February 2023
- New methods for
Optiontype (thanks to tim-smart ๐)flatMapNullableflatMapThrowable
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
fromJsonforOptiontype (thanks [again] to tim-smart ๐)- Allow for decoding of non-primitive types (with custom
fromJsonconstructors)
- Allow for decoding of non-primitive types (with custom
/// `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 ๐)extractextractMap
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.ofandOption.nonefactoriesconst(thanks to f-person ๐)
Note: People who have the prefer_const_constructors lint enabled will notice a warning to use
const๐ค
- New
managing_importsexample (thanks to RandalSchwartz ๐) - Updated README introduction
v0.4.0 - 16 December 2022
- Added extension methods to work with nullable types (
T?)- From
T?tofpdart's typestoOptiontoEithertoTaskOptiontoIOEithertoTaskEithertoTaskEitherAsyncfromNullable(Either,IOEither,TaskOptionTaskEither)fromNullableAsync(TaskEither)
- From
fpdart's types toT?toNullable(Either)
- From
/// [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
toIOEithertoEither - Removed parameter from
EitherfromNullable[โ ๏ธ 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
chainEithertoTaskEither - Added
safeCast(EitherandOption) - Added
safeCastStrict(EitherandOption)
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);
- Added Open API Meteo example (from imperative to functional programming)
- Added new articles
- Option type and Null Safety in dart
- Either - Error Handling in Functional Programming
- Future & Task: asynchronous Functional Programming
- Flutter Supabase Functional Programming with fpdart
- Open Meteo API - Functional programming with fpdart (Part 1)
- Open Meteo API - Functional programming with fpdart (Part 2)
v0.3.0 - 11 October 2022
- Inverted
onSomeandonNonefunctions parameters inmatchmethod ofOption[โ ๏ธ 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
traverseandsequencemethods (#55)traverseListtraverseListWithIndexsequenceListtraverseListSeqtraverseListWithIndexSeqsequenceListSeq
/// "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
bindEithermethod inTaskEither(#58)
/// Chain [Either] to [TaskEither]
TaskEither<String, int> binding =
TaskEither<String, String>.of("String").bindEither(Either.of(20));
- Added
lefts,rights, andpartitionEithersmethods toEither(#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
bimapmethod toEither,IOEither, andTuple2(#57) - Added
mapLeftmethod toIOEither(#57) - Added
foldmethod toOption(same asmatch) (#56) - Fixed
chainFirstforEither,TaskEither, andIOEitherwhen chaining on a failure (Left) (#47) by DevNico ๐ - Added
constto all constructors in which it was missing (#59) - Minimum environment dart sdk to
2.17.0โ ๏ธ
environment:
sdk: ">=2.17.0 <3.0.0"
-
Updated README and documentation
-
Testing improvements (internal)
- Added testing utils
- Added Property-based testing using
glados - Fixed tests for
match()method by addingfailin unexpected matched branch
-
Contribution improvements
- Added testing workflow with Github actions (#54)
v0.2.0 - 16 July 2022
- Refactoring for mixin breaking change (#42) by TimWhiting ๐
- Added
chainFirstmethod for the following classes (#39)TaskEitherEitherIOIOEitherStateStateAsyncReader
v0.1.0 - 17 June 2022
- Added
idFutureandidentityFuturemethods (#38) by f-person ๐ - Added
mapBothmethod toTuple2(#30) - Fixed linting warnings
- Fixed issue with
upsertmethod forMap(#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, andOptionmapLeft(TaskEither)bimap(TaskEither)toTaskEither(Either)toTaskOption(Option)
- New Blog posts and tutorials section in
README- New blog post How to map an Either to a Future in fpdart
v0.0.12 - 24 October 2021
- Completed
IOReftype implementation, documentation, and testing- Merged PR (#25) by purplenoodlesoop ๐
v0.0.11 - 22 September 2021
- Fixed major issue in
StateandStateAsyncimplementation [BREAKING CHANGE]- Methods
flatMap,map,map2,map3,ap,andThen,call, andflattenhad an implementation issue that has been now fixed
- Methods
v0.0.10 - 13 August 2021
- Released introduction to Practical Functional Programming
- Completed
StateAsynctype implementation, documentation, and testing - Fixed problem with
Alttypeclass (#21) - Added
callmethod to more easily chain functions inMonadandMonad2
v0.0.9 - 3 August 2021
- Released two new tutorials on the
Optiontype: - Added
toJsonandfromJsonmethods toOptionto usejson_serializableto convertOptiontype to and from Json (using@JsonSerializable) - Added functional extension methods on
Map - Added composable
Predicatetype (and&, or|, not~, xor^) (#18)
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
flatMapTaskandtoTaskmethods toIOto lift and chainIOwithTask - Added
flatMapTaskandtoTaskmethods toIOEitherto lift and chainIOEitherwithTaskEither - Added pattern matching extension methods to
bool(boolean.dart) - Added functions to get random
int,double, andboolin a functional way (usingIO) (random.dart) - Added functions, extension methods,
Ord, andEqinstances toDateTime(date.dart)
v0.0.7 - 6 July 2021
- Released Part 2 of Fpdart, Functional Programming in Dart and Flutter
- Added
ComposeandCompose2, used to easily compose functions in a chain - Added
curryanduncurryextensions on functions up to 5 parameters - Completed
TaskOptiontype implementation, documentation, and testing - Expanded documentation and examples
- Added
TaskEither.tryCatchKandEither.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
IOEithertype implementation, documentation, and testing - Added
constFfunction - Added
optionandoptionOf(same as dartz) - Added
Either.right(r)factory constructor toEitherclass (same asEither.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
Statetype implementation, documentation, and testing - Completed
Readertype implementation, documentation, and testing - Completed
IOtype implementation, documentation, and testing - Merged PR (#2) by jacobaraujo7 ๐
- Added
rightandleftfunctions to create instance ofEither - Added
idfunction (same asidentity) - Added
foldmethod toEither(same asmatch) - Added
bindmethod toEither(same asflatMap) - Added
bindFuturemethod toEither, which returnsTaskEither
- Added
v0.0.4 - 15 June 2021
- Completed
Unittype documentation - Completed
Tasktype implementation, documentation, and testing - Completed
TaskEithertype implementation, documentation, and testing - Completed implementation, documentation, and testing of
Foldableinstance onOptionandEither[BREAKING CHANGE] - Completed
Tuple2type implementation, documentation, and testing [BREAKING CHANGE] - Renamed
foldmethod ofFoldabletofoldLeft[BREAKING CHANGE] - Updated methods API (
foldRight,foldLeft, etc.) ofFoldableinstances (Option,Either,Tuple) [BREAKING CHANGE] IListnot longer working correctly (waiting for a better solution for immutable collections) [BREAKING CHANGE]
v0.0.3 - 13 June 2021
- Changed name of type
MaybetoOptionto be inline with fp-ts, cats, and dartz [BREAKING CHANGE]
v0.0.2 - 13 June 2021
First major release:
Types
EitherIListMaybeReaderStateTaskTaskEitherTupleUnit
Typeclasses
AltApplicativeBandBoundedSemilatticeCommutativeGroupCommutativeMonoidCommutativeSemigroupEqExtendFilterableFoldableFunctorGroupHashHKTMonadMonoidOrderPartialOrderSemigroupSemilattice
Examples
EithercurryMaybeReaderState
v0.0.1 - 28 May 2021
EqHashPartialOrder
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
| Version | Published | Archive | SHA-256 |
|---|---|---|---|
| 0.0.1 | On demand | Download | e31d870eโฆf8a2e7 |
| 0.0.2 | On demand | Download | be19aa47โฆafbacf |
| 0.0.2+1 | On demand | Download | a30b9ab5โฆ832ff1 |
| 0.0.3 | On demand | Download | b49216aeโฆ0f344a |
| 0.0.4 | On demand | Download | 3342820dโฆ899864 |
| 0.0.5 | On demand | Download | afe38068โฆceb486 |
| 0.0.6 | On demand | Download | 7af084bbโฆ2cac49 |
| 0.0.7 | On demand | Download | 6e58fc74โฆ7f3632 |
| 0.0.8 | On demand | Download | bbaf8b31โฆ7fbd69 |
| 0.0.9 | On demand | Download | b9a2c822โฆ8b44b0 |
| 0.0.10 | On demand | Download | a7ea0671โฆ9dfb36 |
| 0.0.11 | On demand | Download | c877dfbbโฆ2344b4 |
| 0.0.12 | On demand | Download | 5a3a49edโฆaa0c88 |
| 0.0.13 | On demand | Download | 126401cbโฆ6616af |
| 0.0.14 | On demand | Download | cfeeedd0โฆf12655 |
| 0.1.0 | On demand | Download | 9c7b0f50โฆ3867e9 |
| 0.2.0 | On demand | Download | 2c807871โฆ04e444 |
| 0.3.0 | On demand | Download | f198825bโฆ11e98c |
| 0.4.0 | On demand | Download | 19db038cโฆdae935 |
| 0.4.1 | On demand | Download | 21053522โฆd6a57b |
| 0.5.0 | On demand | Download | 40dfb6f8โฆ989926 |
| 0.6.0-dev.1 | On demand | Download | 87345c61โฆ8f5e89 |
| 0.6.0 | On demand | Download | 4a0d047cโฆ47106e |
| 1.0.0-beta.1 | On demand | Download | df0cf507โฆ1f2f1c |
| 1.0.0 | On demand | Download | b254aef8โฆe8bf0b |
| 1.1.0 | On demand | Download | 7413acc5โฆ6e8ee9 |
| 1.1.1 | On demand | Download | 1b84ce64โฆ37af77 |
| 1.2.0 | Cached ยท Upstream SHA-256 matched | Download | f8e9d098โฆ7a73d4 |
| 2.0.0-dev.1 | On demand | Download | 981b9ddaโฆ016042 |
| 2.0.0-dev.2 | On demand | Download | be005b39โฆ69d348 |
| 2.0.0-dev.3 | On demand | Download | 9c82219bโฆ22d055 |
Dependencies
No direct dependencies declared.