Allows composition of dependent effectful functions.
A Monad instance is defined by two operations:
pure from Applicative, which lifts an A value
in the F<A> context
flatMap, which allows us to have a value in a context (F<A>)
and then feed that into a function that takes a normal value and
returns a value in a context (A => F<B>)
Note that having an Applicative instance implies
Functor, Apply, Applicative and FlatMap
implementations are also available, as Monad is a subtype
of these type classes.
Implementation notes
Even though in TypeScript the Funfix library is using abstract class to
express type classes, when implementing this type class it is recommended
that you implement it as a mixin using "implements", instead of extending
it directly with "extends". See
TypeScript: Mixins
for details and note that we already have applyMixins defined.
Implementation example:
import {
HK, Monad, Either,
registerTypeClassInstance,
applyMixins
} from"../src/funfix"// Type alias defined for readability.// HK is our encoding for higher-kinded types.type BoxK<T> = HK<Box<any>, T>
class Box<T> implements HK<Box<any>, T> {
constructor(public value: T) {}
// Implements HK<Box<any>, A>, not really needed, but useful in order// to avoid type casts. Note they can and should be undefined:
readonly _funKindF: Box<any>
readonly _funKindA: T
}
class BoxMonad implements Monad<Box<any>> {
pure<A>(a: A): Box<A> { returnnew Box(a) }
flatMap<A, B>(fa: BoxK<A>, f: (a: A) => BoxK<B>): Box<B> {
returnf((fa as Box<A>).value) asBox<B>
}
tailRecM<A, B>(a: A, f: (a: A) => BoxK<Either<A, B>>): Box<B> {
letcursor = awhile (true) {
constbox = f(cursor) asBox<Either<A, B>>
constv = box.valueif (v.isRight()) returnnewBox(v.get())
cursor = v.swap().get()
}
}
// Mixed-in, asthesehavedefaultimplementationsmap: <A, B>(fa: BoxK<A>, f: (a: A) => B) => Box<B>
map2: <A, B, Z>(fa: BoxK<A>, fb: BoxK<B>, f: (a: A, b: B) => Z) => Box<Z>
ap: <A, B>(fa: BoxK<A>, ff: BoxK<(a: A) => B>) => Box<B>
product: <A, B> (fa: BoxK<A>, fb: BoxK<B>) => Box<[A, B]>
unit: () => Box<void>
followedBy: <A, B>(fa: BoxK<A>, fb: BoxK<B>) => Box<B>
followedByL: <A, B>(fa: BoxK<A>, fb: () => BoxK<B>) => Box<B>
forEffect: <A, B>(fa: BoxK<A>, fb: BoxK<B>) => Box<A>
forEffectL: <A, B>(fa: BoxK<A>, fb: () => BoxK<B>) => Box<A>
}
// Call needed in order to implement `map`, `map2`, `product`, etc.// using the default implementations defined by `Monad`, because// we are using `implements` instead of `extends` above and// because in this sample we want the default implementations,// but note that you can always provide your own
applyMixins(BoxMonad, [Monad])
// Registering global Monad instance for Box, needed in order// for the `functorOf(Box)`, `applyOf(Box)`, `applicativeOf(Box)`,// `flatMapOf(Box)` and `monadOf(Box)` calls to work
registerTypeClassInstance(Monad)(Box, new BoxFunctor())
We are using implements in order to support multiple inheritance and to
avoid inheriting any static members. In the Flow definitions (e.g.
.js.flow files) for Funfix these type classes are defined with
"interface", as they are meant to be interfaces that sometimes have
default implementations and not classes.
Credits
This type class is inspired by the equivalent in Haskell's
standard library and the implementation is inspired by the
Typelevel Cats project.
The
Monadtype class.Allows composition of dependent effectful functions.
A
Monadinstance is defined by two operations:purefrom Applicative, which lifts anAvalue in theF<A>contextflatMap, which allows us to have a value in a context (F<A>) and then feed that into a function that takes a normal value and returns a value in a context (A => F<B>)See Monads for functional programming, by Philip Wadler.
Must obey the laws defined in MonadLaws.
Note that having an
Applicativeinstance implies Functor, Apply, Applicative and FlatMap implementations are also available, asMonadis a subtype of these type classes.Implementation notes
Even though in TypeScript the Funfix library is using
abstract classto express type classes, when implementing this type class it is recommended that you implement it as a mixin using "implements", instead of extending it directly with "extends". See TypeScript: Mixins for details and note that we already haveapplyMixinsdefined.Implementation example:
import { HK, Monad, Either, registerTypeClassInstance, applyMixins } from "../src/funfix" // Type alias defined for readability. // HK is our encoding for higher-kinded types. type BoxK<T> = HK<Box<any>, T> class Box<T> implements HK<Box<any>, T> { constructor(public value: T) {} // Implements HK<Box<any>, A>, not really needed, but useful in order // to avoid type casts. Note they can and should be undefined: readonly _funKindF: Box<any> readonly _funKindA: T } class BoxMonad implements Monad<Box<any>> { pure<A>(a: A): Box<A> { return new Box(a) } flatMap<A, B>(fa: BoxK<A>, f: (a: A) => BoxK<B>): Box<B> { return f((fa as Box<A>).value) as Box<B> } tailRecM<A, B>(a: A, f: (a: A) => BoxK<Either<A, B>>): Box<B> { let cursor = a while (true) { const box = f(cursor) as Box<Either<A, B>> const v = box.value if (v.isRight()) return new Box(v.get()) cursor = v.swap().get() } } // Mixed-in, as these have default implementations map: <A, B>(fa: BoxK<A>, f: (a: A) => B) => Box<B> map2: <A, B, Z>(fa: BoxK<A>, fb: BoxK<B>, f: (a: A, b: B) => Z) => Box<Z> ap: <A, B>(fa: BoxK<A>, ff: BoxK<(a: A) => B>) => Box<B> product: <A, B> (fa: BoxK<A>, fb: BoxK<B>) => Box<[A, B]> unit: () => Box<void> followedBy: <A, B>(fa: BoxK<A>, fb: BoxK<B>) => Box<B> followedByL: <A, B>(fa: BoxK<A>, fb: () => BoxK<B>) => Box<B> forEffect: <A, B>(fa: BoxK<A>, fb: BoxK<B>) => Box<A> forEffectL: <A, B>(fa: BoxK<A>, fb: () => BoxK<B>) => Box<A> } // Call needed in order to implement `map`, `map2`, `product`, etc. // using the default implementations defined by `Monad`, because // we are using `implements` instead of `extends` above and // because in this sample we want the default implementations, // but note that you can always provide your own applyMixins(BoxMonad, [Monad]) // Registering global Monad instance for Box, needed in order // for the `functorOf(Box)`, `applyOf(Box)`, `applicativeOf(Box)`, // `flatMapOf(Box)` and `monadOf(Box)` calls to work registerTypeClassInstance(Monad)(Box, new BoxFunctor())We are using
implementsin order to support multiple inheritance and to avoid inheriting anystaticmembers. In the Flow definitions (e.g..js.flowfiles) for Funfix these type classes are defined with "interface", as they are meant to be interfaces that sometimes have default implementations and not classes.Credits
This type class is inspired by the equivalent in Haskell's standard library and the implementation is inspired by the Typelevel Cats project.