Kotlin Typeclasses with Arrow

Software engineering as a discipline has a large number of different subjects to learn on the road to mastery. Typeclasses is one of those which like data structures and algorithms, there are certain expectations which once learnt can help solve problems in different and more effective ways.
Typeclasses are interesting as they are a common set of API across the various functional programming languages and each have at least one law which they must implement for each type. Like the hashcode()
method in JVM languages, there are certain criteria they must adhere to.
Here are a few examples of typeclasses to get you started
Semigroup
For a type A
you can combine it with another of the same type to create another A
:
String.semigroup().run { "1".combine("2") } // 12
Monoid
Similar to Semigroup
but also adds an empty
result. This is very similar to Java 8’s Optional
type.
String.monoid().run { "1".combine("2") } // "12"
String.monoid().run { listOf("1", "@", "3").combineAll() } // "1@3"
String.monoid().run { empty() } // ""
Functor
Provides the map
ability which will either return a result or nothing
Option(1).map { it * 2 } // Some(2)
empty<Int>().map { it * 2 } // None
Monad
Similar to Functor
but instead of map
it provides flatMap
which will return a new Monad
instead of the unwrapped value. This is very useful for chaining together logic to a final result or nothing result.
Option(1).flatMap { Option(it * 2) } // Some(2)
empty<Int>().flatMap { Option(it * 2) } // None
Option(1)
.flatMap { Option(it * 2) } // Some(2)
.map { it + 2 } // 4
MonadError
MonadError
can then be used in conjunction with Monad
to handle failures. This uses the Either
type from Arrow which is a functional way of handling errors besides using try ... catch
blocks. fold
is a way of handling Either
by providing handlers for the ‘left case’ and ‘right case’.
IO {
1 / 0
}.attempt()
.unsafeRunSync()
.fold(
{ "Failed: ${it.message}" },
{ "Success!: $it" }
) // "Failed: / by zero