|
// Of represents a Type Constructor |
|
abstract Of<M,A>(Dynamic) {} // alternative name MA |
|
|
|
|
|
// OfOf represents a Type constructor function that takes two types. |
|
|
|
abstract OfOf<M,A,B>(Dynamic) {} // alternative name MAB |
|
|
|
// alternative OfOf implementation, maybe easier to implement |
|
|
|
typedef OfOf<M,A,B> = Of<Of<M, A>, B>; |
|
|
|
// In is used to represent a specific but not applied type constructor, e.g. Array => Array<In> |
|
abstract In(Dynamic) {} |
|
|
|
interface Monad<M> { |
|
public function fmap<A,B>(x:Of<M,A>, f:A->B):Of<M,B>; |
|
public function flatMap<A,B>(x:Of<M,A>, f:A->Of<M,B>):Of<M,B>; |
|
public function pure<A>(x:A):Of<M,A>; |
|
} |
|
|
|
// Some monads |
|
|
|
class ArrayMonad implements Monad<Array<In>> |
|
{ |
|
public function new () {} |
|
public function fmap<A,B>(x:Array<A>, f:A->B):Array<B> { |
|
return x.map(f); |
|
} |
|
|
|
public function flatMap<A,B>(x:Array<A>, f:A->Array<B>):Array<B> { |
|
var r = []; |
|
for (e in x) { |
|
for (e1 in f(e)) { |
|
r.push(e1); |
|
} |
|
} |
|
return r; |
|
} |
|
|
|
public function pure<A>(x:A):Array<A> { |
|
return [x]; |
|
} |
|
} |
|
|
|
class EitherMonad<L> implements Monad<Either<L, In>> |
|
{ |
|
public function new () {} |
|
public function fmap<A,B>(x:Either<L, A>, f:A->B):Either<L, B> { |
|
return switch (x) { |
|
case Right(r): Right(f(r)); |
|
case Left(l): Left(l); |
|
} |
|
} |
|
|
|
public function flatMap<A,B>(x:Either<L, A>, f:A->Either<L, B>):Either<L, B> { |
|
return switch (x) { |
|
case Right(r): f(r); |
|
case Left(l): Left(l); |
|
} |
|
} |
|
|
|
public function pure<A>(x:A):Either<L, A> { |
|
return Right(x); |
|
} |
|
} |
|
|
|
|
|
class MonadSyntax { |
|
public static function fmap<M,A,B>(x:Of<M,A>, f:A->B, m:Monad<M>):Of<M,B> |
|
{ |
|
return m.fmap(x,f); |
|
} |
|
public static function flatMap<M,A,B>(x:Of<M,A>, f:A->Of<M,B>, m:Monad<M>):Of<M,B> |
|
{ |
|
return m.flatMap(x, f); |
|
} |
|
public static function pure<M, A>(x:A, m:Monad<M>):Of<M,A> { |
|
return m.pure(x); |
|
} |
|
} |
|
|
|
// explicit usage (implicit usage can be achieved by macros) |
|
|
|
using MonadSyntax; |
|
|
|
class Main { |
|
public static function main () { |
|
|
|
// Array usage |
|
var monad = new ArrayMonad(); |
|
// Array<Int> is equivalent to Of<Array<In>, Int>. |
|
// f (Int->Array<Int>) is compatible to Int->Of<Array<In>, Int> |
|
[1].flatMap(function (x) return [x, x+1], monad); // [1,2] |
|
[1].fmap(function (x) return x + 1, monad); // [2] |
|
1.pure(monad); // [1] |
|
|
|
// Either usage |
|
|
|
var monad = new EitherMonad(); |
|
|
|
// If a type has more than one type parameters like Either it unifies with Of<Either<L, In> R> |
|
// (because type parameters are applied from Right to Left). |
|
Right(1).fmap(function (x) return x+1, monad); // Right(2) |
|
|
|
|
|
} |
|
} |
|
|
|
|
|
|
|
|
here is another example use case: https://gist.github.com/frabbit/87f69012620499a747c9