Created
December 9, 2014 15:18
-
-
Save fsarradin/5806bc6324f44161304e to your computer and use it in GitHub Desktop.
Reader functor in Scala (the typeclass way)
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
object FunctorModule { | |
trait Functor[F[_]] { | |
def map[A, B](f: A => B): F[A] => F[B] | |
} | |
implicit class ReaderFunctor[E](r: E => _) extends Functor[({ type l[a] = E => a })#l] { | |
override def map[A, B](f: A => B): (E => A) => (E => B) = { r => r andThen f } | |
} | |
implicit def functorOps[F[_]: Functor, A](fa: F[A]) = new { | |
val functor = implicitly[Functor[F]] | |
final def map[B](f: A => B): F[B] = functor.map(f)(fa) | |
} | |
def main(args: Array[String]) { | |
val f: (Int) => Int = (_: Int) * 5 | |
val g: (Int) => Int = (_: Int) + 3 | |
// what a type!? | |
val h: ((Int) => Int) => (Int) => Int = f map g | |
// compilation error here because 8 of type Int doesn't match (Int) => Int | |
println(h(8)) | |
} | |
} |
Second version using point free style 😄
import scala.language.higherKinds;
import scala.language.implicitConversions;
import scala.language.reflectiveCalls;
object FunctorModule {
trait Functor[F[_]] {
def fmap[A, B](f: A => B)(fa: F[A]): F[B]
}
case class Reader[-E, +A](runReader: E => A) {
def apply(e: E) = runReader(e)
def map[B](f: A => B): Reader[E, B] = Reader(runReader andThen f)
}
implicit def FunctorReader[E] = new Functor[({type T[A] = Reader[E, A]})#T] {
override def fmap[A, B](f: A => B)(fa: Reader[E, A]): Reader[E, B] = fa map f
}
implicit def arrawToF[E, A](fa: E => A) = new {
val F = implicitly[Functor[({type T[A] = Reader[E, A]})#T]]
def fmap[B](f: A => B): Reader[E, B] = F.fmap (f) (Reader(fa))
}
def main(args: Array[String]) {
val f = (_: Int) * 5
val g = (_: Int) + 3
val h: Reader[Int, Int] = f fmap g
println(h(8))
}
}
arrawToF works on
E => A
type
One more exercice for you 😄
Make the Reader class given before as instance of Monad given bellow and prove 3 Laws :
- Left identity :
return a >>= f = f a
- Right Identity :
ma >>= return = ma
- Associativity :
(f >>= g) >>= h == f >>= ((\a -> g a) >>= h)
trait Monad[M[_]] {
def point[A](a: A): M[A]
def bind[A, B](ma: M[A], f: A => M[B]): M[B]
}
"Et voilà !"
case class Reader[E, A](runReader: E => A) {
def apply(e: E) = runReader(e)
def map[B](f: A => B): Reader[E, B] = Reader(runReader andThen f)
def bind[B](f: A => Reader[E, B]): Reader[E, B] =
Reader { e: E =>
val a: A = runReader(e)
f(a).runReader(e)
}
}
implicit def ReaderMonad[E] = new Monad[({type T[A] = Reader[E, A]})#T] {
override def point[A](a: A): Reader[E, A] = Reader(_ => a)
override def bind[A, B](ma: Reader[E, A], f: (A) => Reader[E, B]): Reader[E, B] = ma bind f
}
you can do
Reader { e: E =>f(runReader(e))(e) }
The whole solution 😄
import scala.language.higherKinds;
import scala.language.implicitConversions;
import scala.language.reflectiveCalls;
import scala.Predef;
object FunctorModule {
trait Monad[M[_]] {
def point[A](a: A): M[A]
def bind[A, B](ma: M[A], f: A => M[B]): M[B]
}
trait Functor[F[_]] {
def fmap[A, B](f: A => B)(fa: F[A]): F[B]
}
case class Reader[-E, +A](runReader: E => A) {
def apply(e: E) = runReader(e)
def map[B](f: A => B): Reader[E, B] = Reader(runReader andThen f)
}
implicit def FunctorReader[E] = new Functor[({ type T[A] = Reader[E, A] })#T] {
override def fmap[A, B](f: A => B)(fa: Reader[E, A]): Reader[E, B] = fa map f
}
implicit def MonadReader[E] = new Monad[({ type T[A] = Reader[E, A] })#T] {
def point[A](a: A): Reader[E, A] = Reader(_ => a)
def bind[A, B](ma: Reader[E, A], f: A => Reader[E, B]): Reader[E, B] = Reader(e => f(ma(e))(e))
}
implicit def arrawToF[E, A](fa: E => A) = new {
val F = implicitly[Functor[({ type T[A] = Reader[E, A] })#T]]
def fmap[B](f: A => B): Reader[E, B] = F.fmap(f)(Reader(fa))
}
implicit def arrawToM[E, A, B](continuation: A => B) = new {
val M = implicitly[Monad[({ type T[A] = Reader[E, A] })#T]]
def bind(ma:Reader[E, A]):Reader[E, B] = M.bind(ma, (a : A) => M.point(continuation(a)))
}
def main(args: Array[String]) {
val f = (_: Int) * 5
val g = (_: Int) + 3
val h: Reader[Int, Int] = f fmap g
println(h(8))
println(f.bind(h)(8))
//Monad laws validation
//1) Left identity : return >>= fa = fa
val result : Int = h.runReader.bind(Reader((a : Int) => a))(3)
assert(result == h(3))
//2) Right Identity : fa >>= return = fa
val result2 : Int =((a : Int) => a).bind(h)(3)
assert(result2 == h(3))
//3) Associativity (f >>= g) >>= h = f >>= (\a -> g a >>= h)
val ma = h
val mc = (_: Int) + 9
val mb = ((a: Int) => a * 10)
//(ma >>= mb)>>= mc
val assocResult = mc.bind(mb.bind(ma))(3)
//(ma >>= (\a => mb(a) >>= mc)
val assocResult2 = (mc.bind(Reader((a: Int) => mb(a))).runReader).bind(ma)(3)
assert(assocResult == assocResult2)
}
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Knowing that if I add the code below, it doesn't seem to work anymore...
Meaning that, in this case,
functorOps
doesn't fulfill its initial role to add themap
method to the existing functor.