Last active
June 27, 2017 11:27
-
-
Save Zhen-hao/0870b5eb5ed222a92a64a79c94a65b43 to your computer and use it in GitHub Desktop.
code used at the Amsterdam Scala meetup on 22 June 2017
This file contains 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 cats.Id | |
// inspired by https://stackoverflow.com/questions/26834230/scala-implicit-for-arbitrarily-deep-functor-composition | |
object CaseStudy { | |
trait Functor[F[_]] { | |
def lift[A, B](f: A => B): F[A] => F[B] | |
def map[A, B](fa: F[A])(f: A => B): F[B] = lift(f)(fa) | |
} | |
object Functor{ | |
implicit def apply[F[_]](implicit functor: Functor[F]): Functor[F] = functor | |
} | |
trait DeepFunctor[X, A] { | |
type Result[_] | |
def map[B](x: X)(f: A => B): Result[B] | |
} | |
trait LowerPriorityDeepFunctor { | |
implicit def identity[A] = | |
new DeepFunctor[A, A] { | |
type Result[x] = Id[x] | |
def map[B](x: A)(f: A => B) = f(x) | |
} | |
} | |
object DeepFunctor extends LowerPriorityDeepFunctor { | |
implicit def deep[F[_], X, A](implicit functorF: Functor[F], inner: DeepFunctor[X, A]) = | |
new DeepFunctor[F[X], A] { | |
type Result[x] = F[inner.Result[x]] | |
def map[B](x: F[X])(f: A => B) = functorF.map(x)(inner.map(_)(f)) | |
} | |
} | |
implicit class Ops[X](self: X) { | |
def fmap[A, B](f: A => B)(implicit F: DeepFunctor[X, A]) = F.map(self)(f) | |
} | |
// Example implementation for List | |
implicit val listFunctor: Functor[List] = new Functor[List]{ | |
def lift[A, B](f: A => B): List[A] => List[B] = { | |
case Nil => Nil | |
case a :: tail => f(a) :: lift(f)(tail) | |
} | |
} | |
// Example implementation for Option | |
implicit val functorForOption: Functor[Option] = new Functor[Option] { | |
def lift[A, B](f: A => B): Option[A] => Option[B] = { | |
case None => None | |
case Some(a) => Some(f(a)) | |
} | |
} | |
val result = implicitly[Functor[Option]].lift((n: Int) => n * 2)(Some(200)) | |
val result1 = Functor[Option].lift((n: Int) => n * 2)(Some(200)) | |
val result2 = Some(200).asInstanceOf[Option[Int]] fmap ((n: Int) => n * 2) | |
val result3 = Some(200).map(identity) fmap ((n: Int) => n * 2) | |
val list0 = Functor[List].lift((n: Int) => n * 2)(List(1,2,3)) | |
val list = List(1,2,3) fmap ((n: Int) => n * 2) | |
val composed = List(None,Some(2),Some(3)) fmap ((n: Int) => n * 2) | |
val doubleCompose = List(None,Some(List(2)),Some(List()), None, Some(List(1,4,5))) fmap ((n: Int) => n * 2) | |
//res1: List[Option[List[Int]]] = List(None, Some(List(4)), Some(List()), None, Some(List(2, 8, 10))) | |
} |
This file contains 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
trait Functor[F[_]] { | |
def lift[A, B](f: A => B): F[A] => F[B] | |
} | |
object Functor{ | |
implicit def apply[F[_]](implicit functor: Functor[F]): Functor[F] = functor | |
implicit def composeFunctor[F[_], G[_]](implicit functorF: Functor[F], functorG: Functor[G]) | |
: Functor[({ type Lambda[N] = G[F[N]]})#Lambda] = | |
new Functor[({ type Lambda[N] = G[F[N]]})#Lambda] { | |
def lift[A, B](f: A => B): G[F[A]] => G[F[B]] = { | |
val F_of_f = functorF.lift(f) | |
functorG.lift(F_of_f) | |
} | |
} | |
} | |
// Example implementation for List | |
implicit val listFunctor: Functor[List] = new Functor[List]{ | |
def lift[A, B](f: A => B): List[A] => List[B] = { | |
case Nil => Nil | |
case a :: tail => f(a) :: lift(f)(tail) | |
} | |
} | |
// Example implementation for Option | |
implicit val functorForOption: Functor[Option] = new Functor[Option] { | |
def lift[A, B](f: A => B): Option[A] => Option[B] = { | |
case None => None | |
case Some(a) => Some(f(a)) | |
} | |
} | |
trait FunctorSyntax[F[_], A]{ | |
def fmap[B](f: A => B): F[B] | |
} | |
object FunctorSyntax{ | |
implicit def apply[F[_], A](fa: F[A])(implicit functor: Functor[F]): FunctorSyntax[F, A] = new FunctorSyntax[F, A]{ | |
def fmap[B](f: A => B): F[B] = functor.lift(f)(fa) | |
} | |
implicit def apply[F[_], G[_], A](fa: G[F[A]])(implicit functorF: Functor[F], functorG: Functor[G]) | |
: FunctorSyntax[({ type Lambda[N] = G[F[N]]})#Lambda, A] | |
= new FunctorSyntax[({ type Lambda[N] = G[F[N]]})#Lambda, A]{ | |
def fmap[B](f: A => B): G[F[B]] = implicitly[Functor[({ type Lambda[N] = G[F[N]]})#Lambda]].lift(f)(fa) | |
} | |
} | |
val result = implicitly[Functor[Option]].lift((n: Int) => n * 2)(Some(200)) | |
val result1 = Functor[Option].lift((n: Int) => n * 2)(Some(200)) | |
import FunctorSyntax._ | |
val result2 = Some(200).asInstanceOf[Option[Int]] fmap ((n: Int) => n * 2) | |
val result3 = Some(200).map(identity) fmap ((n: Int) => n * 2) | |
val list0 = Functor[List].lift((n: Int) => n * 2)(List(1,2,3)) // fmap ((n: Int) => n * 2) | |
val list = List(1,2,3) fmap ((n: Int) => n * 2) | |
val composed = List(None,Some(2),Some(3)) fmap ((n: Int) => n * 2) | |
// this doesn't work; check the Unapply pattern in Cats and Scalaz or use the Aux pattern in the type level recursion | |
val doubleCompose = List(None,Some(List(2)),Some(List()), None, Some(List(1,4,5))) fmap ((n: Int) => n * 2) | |
// this doesn't work; need lambda type | |
implicit val fnctOpsList = implicitly[Functor[Option[List[_]]]] | |
// this doesn't work; need lambda type | |
implicit val fncListOption = implicitly[Functor[List[Option[_]]]] | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment