Skip to content

Instantly share code, notes, and snippets.

@aloiscochard
Created June 25, 2014 10:51
Show Gist options
  • Save aloiscochard/fcd4a6a58cf3f26f5376 to your computer and use it in GitHub Desktop.
Save aloiscochard/fcd4a6a58cf3f26f5376 to your computer and use it in GitHub Desktop.
Transfiguration
package com.timeout
import scala.language.higherKinds
import scalaz._
trait Transfigure[A, B, T] {
def apply(x: A, f: B): T
}
object Transfigure extends TransfigureInstances1 {
def apply[A, B, T](implicit tt: Transfigure[A, B, T]): Transfigure[A, B, T] = tt
def fromFunction[A, B, T](transfigure: (A, B) => T) =
new Transfigure[A, B, T] { def apply(x: A, f: B): T = transfigure(x, f) }
trait Syntax {
implicit class Transfigurable[F[_], A](fa: F[A]) {
def transfigureTo[T]: TransfigureTo[F, A, T] = new TransfigureTo[F, A, T](fa)
}
implicit class Transfigurable2[F[_], G[_], A](fga: F[G[A]]) {
def transfigureTo[T] = new TransfigureTo[({type λ[α] = F[G[α]]})#λ, A, T](fga)
}
class TransfigureTo[F[_], A, T](fa: F[A]) {
def apply[B](f: A => B)(implicit transfigure: Transfigure[F[A], A => B, T]): T = transfigure(fa, f)
}
}
}
trait TransfigureInstances0 {
implicit def map[F[_], A, B]
(implicit F: Functor[F]): Transfigure[F[A], A => B, F[B]] =
Transfigure.fromFunction((fa, f) => F.map(fa)(f))
implicit def map2[F[_], G[_], A, B]
(implicit F: Functor[F], G: Functor[G]): Transfigure[F[G[A]], A => B, F[G[B]]] =
Transfigure.fromFunction((fa, f) => F.map(fa)(ga => G.map(ga)(f)))
implicit def flatMap[F[_], A, B]
(implicit F: Monad[F]): Transfigure[F[A], A => F[B], F[B]] =
Transfigure.fromFunction((fa, f) => F.bind(fa)(f))
implicit def flatMap2[F[_], G[_], A, B]
(implicit F: Monad[F], G: Monad[G]): Transfigure[F[G[A]], A => G[B], F[G[B]]] =
Transfigure.fromFunction((fa, f) => F.map(fa)(ga => G.bind(ga)(f)))
implicit def traverse[F[_], X[_], A, B]
(implicit F: Traverse[F], X: Applicative[X]): Transfigure[F[A], A => X[B], X[F[B]]] =
Transfigure.fromFunction((fa, f) => X.traverse(fa)(f))
}
trait TransfigureInstances1 extends TransfigureInstances0 {
implicit def flatTraverse[A, G[_], B, H[_]]
(implicit HT: Traverse[H], HB: Bind[H], G: Applicative[G]): Transfigure[H[A], A => G[H[B]], G[H[B]]] =
Transfigure.fromFunction((fa, f) => Functor[G].map(G.traverse(fa)(f))(Bind[H].join(_)))
implicit def flatTraverse2[F[_], G[_], A, B]
(implicit F: Monad[F], G: Traverse[G]): Transfigure[F[G[A]], A => F[B], F[G[B]]] =
Transfigure.fromFunction((fa, f) => F.bind(fa)(ga => G.traverse(ga)(f)))
}
package com.timeout
import scala.concurrent.Future
import com.timeout.test.SimpleSpec
class TransfigureTest extends SimpleSpec {
import scala.concurrent.ExecutionContext.Implicits.global
describe("Transfigure should") {
it("map") {
val fa: Option[Int] = Some(42)
val f: Int => String = _.toString
fa.transfigureTo[Option[String]](f) === Some("42")
}
it("map (2 level deep)") {
val fa: Future[Option[Int]] = Future.successful(Some(42))
val f: Int => String = _.toString
fa.transfigureTo[Future[Option[String]]](f).get === Some("42")
}
it("flatMap") {
val fa: Option[Int] = Some(42)
val f: Int => Option[String] = x => Some((x - 10).toString)
fa.transfigureTo[Option[String]](f) === Some("32")
}
it("flatMap (2 level deep)") {
val fa: Future[Option[Int]] = Future.successful(Some(42))
val f: Int => Option[String] = x => Some((x - 10).toString)
fa.transfigureTo[Future[Option[String]]](f).get === Some("32")
}
it("traverse") {
val fa: Option[Int] = Some(42)
val f: Int => Future[String] = x => Future.successful((x - 10).toString)
fa.transfigureTo[Future[Option[String]]](f).get === Some("32")
}
it("traverse (2 level deep)") {
val fa: Future[Option[Int]] = Future.successful(Some(42))
val f: Int => Future[String] = x => Future.successful((x - 10).toString)
fa.transfigureTo[Future[Option[String]]](f).get === Some("32")
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment