Whereas Monads allow for the composition of effectful functions,
Comonads allow for composition of functions that extract the
value from their context.
Example:
const F = comonadOf(Eval)
F.extract(Eval.of(() =>2)) // 2
Note that having an Comonad instance implies Functor and
CoflatMap implementations are also available, which is why
Comonad is a subtype of Functor and CoflatMap.
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, Comonad,
registerTypeClassInstance,
applyMixins
} from"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 BoxComonad implements Comonad<Box<any>> {
map<A, B>(fa: BoxK<A>, f: (a: A) => B): BoxK<B> {
consta = (fa as Box<A>).valuereturnnewBox(f(a))
}
coflatMap<A, B>(fa: BoxK<A>, ff: (a: BoxK<A>) => B): BoxK<B> {
returnnewBox(ff(fa))
}
coflatten<A>(fa: BoxK<A>): BoxK<BoxK<A>> {
returnnewBox(fa)
}
extract<A>(fa: BoxK<A>): A {
return (fa as Box<A>).value
}
}
// Atthemomentofwriting, thiscallisnotneeded, butitis
// recommendedanywaytofuture-proofthecode ;-)
applyMixins(BoxComonad, [Comonad])
// RegisteringglobalComonadinstanceforBox, neededinorder
// forthe `functorOf(Box)`, `coflatMapOf(Box)` and `comonadOf(Box)`
// callstoworkregisterTypeClassInstance(Comonad)(Box, new BoxComonad())
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.
Comonadis the dual of Monad.Whereas Monads allow for the composition of effectful functions, Comonads allow for composition of functions that extract the value from their context.
Example:
const F = comonadOf(Eval) F.extract(Eval.of(() => 2)) // 2Note that having an
Comonadinstance implies Functor and CoflatMap implementations are also available, which is whyComonadis a subtype ofFunctorandCoflatMap.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, Comonad, registerTypeClassInstance, applyMixins } from "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 BoxComonad implements Comonad<Box<any>> { map<A, B>(fa: BoxK<A>, f: (a: A) => B): BoxK<B> { const a = (fa as Box<A>).value return new Box(f(a)) } coflatMap<A, B>(fa: BoxK<A>, ff: (a: BoxK<A>) => B): BoxK<B> { return new Box(ff(fa)) } coflatten<A>(fa: BoxK<A>): BoxK<BoxK<A>> { return new Box(fa) } extract<A>(fa: BoxK<A>): A { return (fa as Box<A>).value } } // At the moment of writing, this call is not needed, but it is // recommended anyway to future-proof the code ;-) applyMixins(BoxComonad, [Comonad]) // Registering global Comonad instance for Box, needed in order // for the `functorOf(Box)`, `coflatMapOf(Box)` and `comonadOf(Box)` // calls to work registerTypeClassInstance(Comonad)(Box, new BoxComonad())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.