Skip to content

Instantly share code, notes, and snippets.

@olexale
Created January 3, 2022 19:10
Show Gist options
  • Save olexale/568ae37ac956ffb53dac1eda96edf5e6 to your computer and use it in GitHub Desktop.
Save olexale/568ae37ac956ffb53dac1eda96edf5e6 to your computer and use it in GitHub Desktop.
Sample for the "Dart Functors, Applicatives, And Monads In Pictures" article
void main() {
// Functor
num plus3(num x) => x + 3;
print(Just(2).fmap(plus3)); // Just 5
print(Nothing<num>().fmap(plus3));
print([1,2,3].map((x) => x + 2)); // (3, 4, 5)
final foo = fmap((x) => x + 3, (x) => x + 2);
print(foo(10)); // 15
// Aplicatives
print(Just(2).apply(Just((x) => x + 3))); // Just 5
print([1, 2, 3].apply<int, int>([
(x) => x * 2,
(x) => x + 3,
])); // [2, 4, 6, 4, 5, 6]
final curriedAddition = (num x) => (num y) => x + y;
// num Function(num) curriedAddition(num x) => (num y) => x + y;
final a = Just(3).fmap(curriedAddition); // Just<(int) => int>
// Just(5).fmap(a); // COMPILATION ERROR
print(Just(5).apply(a)); // Just 8
final curriedTimes = (int x) => (int y) => x * y;
// num Function(num) curriedTimes(num x) => (num y) => x * y;
print(Just(5).apply(Just(3).fmap(curriedTimes))); // Just 15
// Monads
Maybe<num> half(num a) => a % 2 == 0 ? Just(a / 2) : Nothing();
print(Just(3).bind(half)); // Nothing
print(Just(4).bind(half)); // Just 2
print(Nothing<num>().bind(half)); // Nothing
print(Just(20).bind(half).bind(half).bind(half)); // Nothing
}
// Helpers
typedef IntFunction = int Function(int);
IntFunction fmap(IntFunction f, IntFunction g) => (x) => f(g(x));
extension ApplicativeList on List {
Iterable<U> apply<T, U>(List<U Function(T)> list) sync* {
for (final item in list) {
for (var i = 0; i < length; i++) {
yield item(this[i]);
}
}
}
}
// Maybe
abstract class Functor<T> {
Functor<U> fmap<U>(U Function(T) f);
}
abstract class Applicative<T> {
Applicative<U> apply<U>(Applicative<U Function(T)> f);
}
abstract class Monad<T> {
Monad<U> bind<U>(Monad<U> Function(T) f);
}
/// The Maybe type encapsulates an optional value.
/// Using Maybe is a good way to deal with errors or
/// exceptional cases without resorting to
/// drastic measures such as error.
abstract class Maybe<T> implements Functor<T>, Applicative<T>, Monad<T> {}
/// Represents a value of type Maybe that contains a value
/// (represented as Just a).
class Just<T> extends Maybe<T> {
Just(this.value);
final T value;
@override
Maybe<U> fmap<U>(U Function(T) f) => Just(f(value));
@override
Maybe<U> apply<U>(covariant Maybe<U Function(T)> f) =>
f.fmap((ff) => ff(value)) as Maybe<U>;
@override
Maybe<U> bind<U>(covariant Maybe<U> Function(T) f) => f(value);
@override
String toString() => 'Just $value';
}
/// Represents an empty Maybe that holds nothing
/// (in which case it has the value of Nothing)
class Nothing<T> extends Maybe<T> {
@override
Maybe<U> fmap<U>(U Function(T) f) => Nothing();
@override
Maybe<U> apply<U>(covariant Maybe<U Function(T)> f) => Nothing();
@override
Maybe<U> bind<U>(covariant Maybe<U> Function(T) f) => Nothing();
@override
String toString() => 'Nothing';
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment