Skip to content

Instantly share code, notes, and snippets.

@drawers
Last active January 10, 2021 23:36
Show Gist options
  • Save drawers/7a193b4a36d43815280cd23336ef9337 to your computer and use it in GitHub Desktop.
Save drawers/7a193b4a36d43815280cd23336ef9337 to your computer and use it in GitHub Desktop.
Higher kinds tl;dr
// I want to parameterize a container F as well as the contents A
Mappable<F<A>>
// but type parameters can't have type parameters in Kotlin :-(
// Instead of a type parameter of a type parameter,
// what if I just use two type parameters?
Mappable<Kind<F,A>>
// now I need to apply this to Option and String
Mappable<Kind</*put Option here somehow*/, String>>
// But Option needs a type parameter.
// I can't do:
Mappable<Kind<Option<*>>, String>
// since type params can't have type params :-(
// I'll define a witness with no type params called "ForOption"
class ForOption private constructor() { companion object }
// I can use type aliases to make OptionOf<A> mean Kind<ForOption, A>
typealias OptionOf<A> = arrow.Kind<ForOption, A>
// and I need a way to get from Kind<ForOption, A> to Option<A>:
@Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE")
inline fun <A> OptionOf<A>.fix(): Option<A> =
this as Option<A>
// Now I can write:
Mappable<Kind<ForOption, String>>
// The witness is tedious to write by hand so I will use code-generation to make it ;-)
// @higherkind tells the annotation processor to emit the witness for my concrete container `Option<out A>`
@higherkind
sealed class Option<out A> : OptionOf<A>
// I want my Mappable container to share extension functions that call Mappable's `map` via code generation
// @extension tells the annotation processor to generate extension functions on Mappable<ForOption> from Mappable<Kind<F,A>>
@extension
interface OptionMappable : Mappable<ForOption> {
override fun <A, B> OptionOf<A>.map(f: (A) -> B): Option<B> =
fix().map(f)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment