Skip to content

Instantly share code, notes, and snippets.

@noelwelsh
Forked from Fristi/FreeInvariantMonoidal.scala
Last active January 12, 2016 17:04
Show Gist options
  • Save noelwelsh/514f38b704cb088d6d0e to your computer and use it in GitHub Desktop.
Save noelwelsh/514f38b704cb088d6d0e to your computer and use it in GitHub Desktop.
Free version of Invariant functor type class + Monoidal type class
package nl.mdj
import cats.arrow.NaturalTransformation
import cats.functor.Invariant
import cats._
import cats.syntax.all._
import scala.language.higherKinds
sealed abstract class FreeInvariant[F[_], A] extends Product with Serializable {
import FreeInvariant.{FA, Suspend, Zip, Imap}
def imap[B](f: A => B, g: B => A): FA[F, B] = Imap(this, f, g)
def product[B](b: FreeInvariant[F, B]): FA[F, (A, B)] = Zip(this, b)
def foldMap[G[_]](n: F ~> G)(implicit I: Invariant[G], M: Monoidal[G]): G[A]
}
object FreeInvariant {
type FA[F[_], A] = FreeInvariant[F, A]
private final case class Zip[F[_], A, B](fa: FA[F, A], fb: FA[F, B]) extends FA[F, (A,B)] {
def foldMap[G[_]](n: F ~> G)
(implicit I: Invariant[G], M: Monoidal[G]): G[(A,B)] =
M.product(fa.foldMap(n), fb.foldMap(n))
}
private final case class Suspend[F[_], A](value: F[A]) extends FA[F, A] {
def foldMap[G[_]](n: F ~> G)
(implicit I: Invariant[G], M: Monoidal[G]): G[A] =
n.apply(value)
}
private final case class Imap[F[_], X, Y](value: FA[F, X], g: X => Y, f: Y => X) extends FA[F, Y] {
def foldMap[G[_]](n: F ~> G)
(implicit I: Invariant[G], M: Monoidal[G]): G[Y] =
I.imap(value.foldMap(n))(g)(f)
}
final def lift[F[_], A](a: F[A]): FA[F, A] = Suspend(a)
implicit def imap[F[_]]: Invariant[FA[F, ?]] = new Invariant[FA[F, ?]] {
override def imap[A, B](fa: FA[F, A])(f: (A) => B)(g: (B) => A): FA[F, B] = fa.imap(f, g)
}
implicit def monoidal[F[_]]: Monoidal[FA[F, ?]] = new Monoidal[FA[F, ?]] {
override def product[A, B](fa: FA[F, A], fb: FA[F, B]): FA[F, (A, B)] = fa.product(fb)
}
}
object Main extends App {
object algebra {
sealed trait RoutePartF[A]
object RoutePartF {
final case class IntPart[A](description: Option[String], f: Int => A, g: A => Int) extends RoutePartF[A]
final case class Slash[A](f: Unit => A, g: A => Unit) extends RoutePartF[A]
final case class Segment[A](text: String, f: Unit => A, g: A => Unit) extends RoutePartF[A]
}
}
object dsl {
import algebra._
type Dsl[A] = FreeInvariant[RoutePartF, A]
private def lift[A](value: RoutePartF[A]): Dsl[A] = FreeInvariant.lift[RoutePartF, A](value)
def int: Dsl[Int] = lift(RoutePartF.IntPart(None, identity, identity))
def slash: Dsl[Unit] = lift(RoutePartF.Slash(identity, identity))
def segment(text: String): Dsl[Unit] = lift(RoutePartF.Segment(text, identity, identity))
}
import dsl._
import algebra._
val route = (segment("test") |@| slash |@| int).tupled
val codec = route.foldMap(new NaturalTransformation[RoutePartF, Codec] {
override def apply[A](fa: RoutePartF[A]): Codec[A] = fa match {
case RoutePartF.IntPart(_, f, g) => Codec.int.imap(f, g)
case RoutePartF.Segment(text, f, g) => Codec.const(text).imap(f, g)
case RoutePartF.Slash(f, g) => Codec.const("/").imap(f, g)
}
})
println(codec.encode(((), (), 5)))
trait Codec[A] { self =>
def encode(a: A): String
def decode(v: String): Option[A]
def imap[B](f: A => B, g: B => A): Codec[B] = new Codec[B] {
override def encode(a: B): String = self.encode(g(a))
override def decode(v: String): Option[B] = self.decode(v).map(f)
}
}
object Codec {
def const(text: String) = new Codec[Unit] {
override def encode(a: Unit): String = text
override def decode(v: String): Option[Unit] = ???
}
val int = new Codec[Int] {
override def encode(a: Int): String = a.toString
override def decode(v: String): Option[Int] = ???
}
implicit val invariant: Invariant[Codec] = new Invariant[Codec] {
override def imap[A, B](fa: Codec[A])(f: (A) => B)(g: (B) => A): Codec[B] = fa.imap(f, g)
}
implicit val monoidal: Monoidal[Codec] = new Monoidal[Codec] {
override def product[A, B](fa: Codec[A], fb: Codec[B]): Codec[(A, B)] = new Codec[(A, B)] {
override def encode(a: (A, B)): String = fa.encode(a._1) ++ fb.encode(a._2)
override def decode(v: String): Option[(A, B)] = for {
a <- fa.decode(v)
b <- fb.decode(v)
} yield a -> b
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment