Skip to content

Instantly share code, notes, and snippets.

@Zhen-hao
Last active June 27, 2017 11:27
Show Gist options
  • Save Zhen-hao/0870b5eb5ed222a92a64a79c94a65b43 to your computer and use it in GitHub Desktop.
Save Zhen-hao/0870b5eb5ed222a92a64a79c94a65b43 to your computer and use it in GitHub Desktop.
code used at the Amsterdam Scala meetup on 22 June 2017
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)))
}
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