Skip to content

Instantly share code, notes, and snippets.

@raulraja
Created April 17, 2020 20:06
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save raulraja/e36fc68e8cf7b044b82f1eae2a23a4c6 to your computer and use it in GitHub Desktop.
Save raulraja/e36fc68e8cf7b044b82f1eae2a23a4c6 to your computer and use it in GitHub Desktop.
Kinds without uncheched casts
/** kind, all nested F's have to be also kinds `*` **/
interface `*`<out F>
interface `*-*`<out F : `*`<F>, out A> : `*`<F>
interface `*-*-*`<out F : `*`<F>, out A, out B> : `*`<F>
/** Bifunctor IO */
class IO<out E, out A> :
`*`<IO<*, *>>,
`*-*`<IO<*, *>, A>,
`*-*-*`<IO<*, *>, E, A> {
fun <B> fmap(f: (A) -> B): IO<Nothing, B> = TODO()
fun <B> flatMap(f: (A) -> `*-*`<IO<*, *>, B>): IO<Nothing, B> = TODO()
fun <C, D> bimap(fl: (E) -> C, fr: (A) -> D): IO<C, D> = TODO()
companion object {
fun <A> just(a: A): IO<Nothing, A> = TODO()
}
}
// having these as member is not possible because would enforce E and A to be declared invariant
fun <E, A, E2 : E, AA : A> IO<E, A>.flatMapLeft2(f: (E) -> `*-*-*`<IO<*, *>, E2, AA>): IO<E2, AA> = TODO()
//all safe casts no need to suppress warnings
fun <A> `*-*`<IO<*, *>, A>.fix(): IO<*, A> = this as IO<*, A>
fun <E, A, EE : E, AA : A> `*-*-*`<IO<*, *>, EE, AA>.fix(): IO<EE, AA> = this as IO<EE, AA>
class IOMonad<L> : Monad2<IO<*, *>, L> {
override fun <A> just(a: A): IO<Nothing, A> =
IO.just(a)
override fun <A, B> `*-*`<IO<*, *>, A>.fmap(f: (A) -> B): IO<Nothing, B> =
fix().fmap(f)
override fun <A, B> `*-*`<IO<*, *>, A>.flatMap(f: (A) -> `*-*`<IO<*, *>, B>): IO<Nothing, B> =
fix().flatMap(f)
override fun <B, C, D> `*-*-*`<IO<*, *>, L, B>.bimap(fl: (L) -> C, fr: (B) -> D): IO<C, D> =
fix().bimap(fl, fr)
override fun <B, AA : L, BB : B> `*-*-*`<IO<*, *>, L, B>.flatMapLeft(f: (L) -> `*-*-*`<IO<*, *>, AA, BB>): IO<AA, BB> =
fix().flatMapLeft2(f)
}
/** type class only accepts kinded types not just any marker **/
interface Monad<F : `*`<F>> {
fun <A> just(a: A): `*-*`<F, A>
fun <A, B> `*-*`<F, A>.fmap(f: (A) -> B): `*-*`<F, B>
fun <A, B> `*-*`<F, A>.flatMap(f: (A) -> `*-*`<F, B>): `*-*`<F, B>
}
interface Monad2<F : `*`<F>, A> {
fun <B, C, D> `*-*-*`<F, A, B>.bimap(fl: (A) -> C, fr: (B) -> D): `*-*-*`<F, C, D>
/* lower bounds on A and B*/
fun <B, AA : A, BB : B> `*-*-*`<F, A, B>.flatMapLeft(f: (A) -> `*-*-*`<F, AA, BB>): `*-*-*`<F, AA, BB>
}
// this cast is now safe and not unchecked, the compiler gets it
// and it's an improvement over what we have
fun <A> `*-*`<Option<*>, A>.fix(): Option<A> = this as Option<A>
class Option<A> :
`*`<Option<*>>,
`*-*`<Option<*>, A> {
fun <B> fmap(f: (A) -> B): Option<B> = TODO()
fun <B> flatMap(f: (A) -> `*-*`<Option<*>, B>): Option<B> = TODO()
companion object {
fun <A> just(a: A): Option<A> = TODO()
}
}
object OptionMonad : Monad<Option<*>> {
override fun <A> just(a: A): Option<A> =
Option.just(a)
override fun <A, B> `*-*`<Option<*>, A>.fmap(f: (A) -> B): Option<B> =
fix().fmap(f)
override fun <A, B> `*-*`<Option<*>, A>.flatMap(f: (A) -> `*-*`<Option<*>, B>): Option<B> =
fix().flatMap(f)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment