Last active
September 2, 2016 04:33
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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