The future belongs to Effect systems.
Effect systems are used to provide compile-time check of the possible effects of the program
This week I worked on a prototype of an effect system in dart, aka fpdart v2 ποΈ
This is how it all looks like π
Tweet not found
The embedded tweet could not be foundβ¦
Effect systems over functional programming
Functional programming is hard for 2 reasons:
- Different paradigm from "usual" OOP
- Jargon π₯΄
Combine something different with mystic terms (e.g. Monad π») makes people justified to run away
What we want instead:
- Use both OOP and functional programming at their best
- No Jargon, but solutions to real-world problems
Welcome to Effect systems ππΌββοΈ
Problems with fpdart (v1)
- API surface: too many different classes, hard to understand what to use (
Task
,TaskEither
,IO
,Reader
) - Interoperability: too many conversion functions (
toTask
,toTaskEither
,toReaderTaskEither
) - Jargon (from functional programming): things like
Reader
,Task
,State
- Not OOP friendly: classes like
Eq
are more useful in pure fp languages, less in dart
New Effect
class
Solution: a single Effect
class to handle all effects (dependencies, errors, success).
π οΈ fpdart <> @EffectTS_ update π οΈ Remove confusion with TaskEither, TaskOption, Task, IO, and all π₯΄ Instead use a single Effect class and a single Do notation π‘ Work in progress ποΈ
- Remove all other classes (
Task
,IO
,Reader
and all) - No need of conversion function (everything is
Effect
) - No more jargon: removed methods like
pure
in favor of clear terms likesucceed
- Embrace dart OOP features
Furthermore, this allows to have a single Do notation function that handles all, Option
and Either
included.
Do notation to the next level π₯ Effect in fpdart allows a single Do function for everything Even for Option and Either π Write readable step-by-step functional code π
There is more π€©
Timeless coding principles, practices, and tools that make a difference, regardless of your language or framework, delivered in your inbox every week.
How this works
A single interface and a single Effect
class:
abstract interface class IEffect<E, L, R> {
final UnsafeRun<E, L, R> _unsafeRun;
const IEffect._(this._unsafeRun);
Future<Exit<L, R>> _runEffect(E env) async => _unsafeRun(env);
/// ...
}
final class Effect<E, L, R> extends IEffect<E, L, R> {
/// ...
}
This substitutes all other classes:
ReaderTaskEither
:Effect<E, L, R>
TaskEither
:Effect<dynamic, L, R>
Task
(same asIO
):Effect<dynamic, dynamic, R>
There is no distinction between sync and async code in the API.
This is achieved by using
FutureOr
:typedef UnsafeRun<E, L, R> = FutureOr<Exit<L, R>> Function(E env);
In practice the usage is the same:
/// Create an [Effect] (same as [TaskEither])
final effect = Effect.tryCatch(
() => Future.value(10),
(error, stackTrace) => "Error",
);
/// Use the Do notation for all [Effect], [Option], [Either]
final doing = doEffect<int, String, int>(
(_) async {
final env = await _(Effect.ask());
/// Use `withEnv` to provide a valid dependency value
final mapped = await _(effect.map((r) => r + 10).withEnv(identity));
/// Same for [Either] and [Option]
final eitherValue = await _(Right<String, int>(10).withEnv<int>());
final optionValue = await _(Some<int>(10).withEnv(() => "Some"));
return mapped + eitherValue + optionValue;
},
);
So, when?
This is still in the experimentation phase (not even alpha ππΌββοΈ). You can view and follow the progress on Github:
This would be a radical change for fpdart: the most breaking change ever π
If this works I am going to open a discussion with the community to discuss how and if to move in this direction for fpdart v2
Stay tuned π
If you want a peek in how powerful effect system are take a look at Effect (typescript).
My goal is to provide something similar also in dart, but all adapted to the dart language and its features π₯
Working on it ποΈ
See you next π