Skip to content

Instantly share code, notes, and snippets.

@d-plaindoux
Last active December 16, 2020 09:20
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save d-plaindoux/ec3dd2b705a551342713fc6437dab15b to your computer and use it in GitHub Desktop.
Save d-plaindoux/ec3dd2b705a551342713fc6437dab15b to your computer and use it in GitHub Desktop.
This gist shows how typeclass can be "simply" designed "à la Arrow".
// HKT simulation
interface Kind<out F, out A>
// ----------------------------------------------------------------------------
interface Monad<F> {
fun <A, B> map(ma: Kind<F, A>, f: (A) -> B): Kind<F, B>
fun <A> join(ma: Kind<F, Kind<F, A>>): Kind<F, A>
fun <A, B> bind(ma: Kind<F, A>, f: (A) -> Kind<F, B>): Kind<F, B> = join(map(ma, f))
fun <A> returns(a: A) : Kind<F, A>
class Fluent<F>(val monad: Monad<F>) {
fun <A, B> Kind<F, A>.map(f: (A) -> B): Kind<F, B> = monad.map(this, f)
fun <A> Kind<F, Kind<F, A>>.join(): Kind<F, A> = monad.join(this)
fun <A, B> Kind<F, A>.bind(f: (A) -> Kind<F, B>): Kind<F, B> = monad.bind(this, f)
fun <A> returns(a: A) : Kind<F, A> = monad.returns(a)
}
companion object {
fun <F, R> Monad<F>.fluent(block: Fluent<F>.() -> R): R = Fluent(this).block()
}
}
// ----------------------------------------------------------------------------
class OptionK private constructor() {}
fun <A> Kind<OptionK, A>.fix(): Option<A> =
this as Option<A> // <- The dirty part! [unchecked]
// ----------------------------------------------------------------------------
sealed class Option<out A> : Kind<OptionK, A> {
object None : Option<Nothing>()
class Some<A>(val value: A) : Option<A>()
}
// ----------------------------------------------------------------------------
object OptionMonad : Monad<OptionK> {
override fun <A, B> map(ma: Kind<OptionK, A>, f: (A) -> B): Kind<OptionK, B> =
when (val self = ma.fix()) {
is Option.None -> Option.None
is Option.Some -> Option.Some(f(self.value))
}
override fun <A> join(ma: Kind<OptionK, Kind<OptionK, A>>): Kind<OptionK, A> =
when (val self = ma.fix()) {
is Option.None -> Option.None
is Option.Some -> self.value
}
override fun <A> returns(value: A): Option<A> = Option.Some(value)
}
// ----------------------------------------------------------------------------
object Main {
fun mainFunctional(monad: Monad<OptionK>) {
with(monad) {
join(
map(
bind(returns(0))
{ returns(it + 40) }
)
{ returns(it + 2) }
)
}
}
fun mainObjectified(monad: Monad<OptionK>) {
monad.fluent {
returns(0)
.bind { returns(it + 40) }
.map { returns(it + 2) }
.join()
}
}
}
@d-plaindoux
Copy link
Author

d-plaindoux commented Apr 29, 2020

For more explainations, @xvw has written a blog post about Kotlin extension in French

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment