Skip to content

Instantly share code, notes, and snippets.

@calvinlfer
Last active September 2, 2016 04:33
Show Gist options
  • Select an option

  • Save calvinlfer/145af70d702ccfe0585f9ebbd32b44a3 to your computer and use it in GitHub Desktop.

Select an option

Save calvinlfer/145af70d702ccfe0585f9ebbd32b44a3 to your computer and use it in GitHub Desktop.
Implementing the Monad and Functor typeclass in Scala and also looking into Point Free, you can run this in a Scala worksheet
import scala.language.higherKinds
// Functor typeclass - it abstracts over higher kinded types (functions at the type level)
trait Functor[HigherKindedType[_]] {
def map[A, B](functorA: HigherKindedType[A])(mapper: A => B): HigherKindedType[B]
def unit[A](simpleA: => A): HigherKindedType[A]
}
object Functor {
// A Functor typeclass implementation for the List Higher Kinded Type (function at the type level)
// Note that it's marked implicit so it will be present in scope if you use Functor (refer to it as a type)
implicit val listFunctorImplementation = new Functor[List] {
override def map[A, B](functorA: List[A])(mapper: (A) => B): List[B] = {
val result: List[B] = for {eachElement <- functorA} yield mapper(eachElement)
result
}
override def unit[A](simpleA: => A): List[A] = List(simpleA)
}
}
// type class method, notice the implicit parameter of type Functor[HigherKindedType]
// so this typeclass method works for any Higher Kinded Type that has a Functor implementation
def fmap[A, B, HigherKindedType[_]](mapper: A => B)
(higherKindedTypeOfA: HigherKindedType[A])
(implicit higherKindedFunctorImpl: Functor[HigherKindedType]): HigherKindedType[B] =
higherKindedFunctorImpl.map(higherKindedTypeOfA)(mapper)
val simpleUsage = fmap[Int, Int, List](x => x + 1)(List(1, 2, 3)) == List(2, 3, 4)
def stringSplit(s: String): List[String] = s.split("").toList
// Point free style (right to left composition)
val composedFn: (String => List[String]) =
(fmap[String, String, List](input => input + " hello") _) compose (stringSplit _)
composedFn("Cal") == List("C hello", "a hello", "l hello")
// Monad typeclass - it also abstracts over higher kinded types (functions at the type level)
// A Monad is also a Functor
trait Monad[HigherKindedType[_]] extends Functor[HigherKindedType] {
def flatMap[A, B](monadA: HigherKindedType[A])(nestedMapper: A => HigherKindedType[B]): HigherKindedType[B]
// if you implement flatMap, then you get map for free :-)
def map[A, B](monadA: HigherKindedType[A])(mapper: A => B): HigherKindedType[B] =
flatMap(monadA)(a => unit(mapper(a)))
}
object Monad {
// Monad typeclass implementation for List (List is a higher kinded type)
implicit val listMonad = new Monad[List] {
override def flatMap[A, B](monadA: List[A])(nestedMapper: (A) => List[B]): List[B] = monadA.flatMap(nestedMapper)
override def unit[A](simpleA: => A): List[A] = List(simpleA)
}
implicit class FancyListMonadOps[A](listA: List[A]) {
def >>=[B](nestedMapperFn: A => List[B]) = listMonad.flatMap(listA)(nestedMapperFn)
}
}
// Point free symbolic flatMap and curried so you can easily compose (note with respect to Haskell, the parameters are reversed)
def >>=[A, B, HigherKindedType[_]](nestedMapper: A => HigherKindedType[B])
(higherKindTypeOfA: HigherKindedType[A])
(implicit monadTypeClassImplementation: Monad[HigherKindedType]): HigherKindedType[B] = {
monadTypeClassImplementation.flatMap(higherKindTypeOfA)(nestedMapper)
}
>>=[Int, Int, List](x => List(x + 1))(List(1, 2, 3)) == List(2, 3, 4)
// Point free
def unit[A, HigherKindedType[_]](simpleA: A)(implicit monadTypeClassImplementation: Monad[HigherKindedType]) =
monadTypeClassImplementation.unit(simpleA)
// partially apply both >>= and unit using the point free style
// (>>=[Int, Int, List]((x: Int) => List(x + 1)) _) means we feed in the first argument (nestedMapper of type Int => List[Int])
// and leave higherKindedTypeOfA to be filled in later, hence the underscore _ (higherKindedTypeOfA is a List[A] in this case)
// (unit[Int, List] _) means we leave the argument to be filled in later
// reads right to left like mathematical composition
val composedFunction: (Int) => List[Int] =
(>>=[Int, Int, List]((x: Int) => List(x + 1)) _) compose (unit[Int, List] _)
composedFunction(10) == List(11)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment