Skip to content

Instantly share code, notes, and snippets.

@julien-truffaut
Created February 25, 2016 20:04
Show Gist options
  • Save julien-truffaut/b462de25aaf54e4bae81 to your computer and use it in GitHub Desktop.
Save julien-truffaut/b462de25aaf54e4bae81 to your computer and use it in GitHub Desktop.
Free example scala coding dojo
package freee
import scalaz.\/
import scalaz.concurrent.Task
object Example extends App {
import Interact._
val program: API[Unit] = for {
_ <- tell("Hey")
name <- ask("What's your name")
_ <- tell(s"Your name is $name")
} yield ()
val interactToTask: NaturalTransformation[Interact, Task] = new NaturalTransformation[Interact, Task] {
import scala.io.StdIn
def apply[A](fa: Interact[A]): Task[A] = fa match {
case Ask(prompt, f) => Task.delay(f(StdIn.readLine(prompt)))
case Tell(msg, a) => Task.delay{
println(msg)
a
}
}
}
val taskProgram = program.transform(interactToTask): Free[Task, Unit]
def runTask(f: Free[Task, Unit]): Unit = f match {
case Return(a) => a
case Bind(fi, k) => fi.map(i => runTask(k(i))).run
}
runTask(taskProgram)
type Alegrabe[A] = Interact[A] \/ Http[A]
type APIV2[A] = Free[Alegrabe, A]
}
sealed trait Http[A]
// case class get ...
package freee
sealed trait Free[F[_], A] { // F ~ List Option Future
def flatMap[B](f: A => Free[F, B])(implicit ev: Functor[F]): Free[F, B] = this match {
case Return(a) => f(a)
case Bind(fi, k) => Bind(fi, (i: Any) => k(i).flatMap(f))
}
def map[B](f: A => B)(implicit ev: Functor[F]): Free[F, B] = flatMap(a => Return(f(a)))
def transform[G[_]](nat: NaturalTransformation[F, G]): Free[G, A] = this match {
case Return(a) => Return(a)
case Bind(fi, k) => Bind(nat.apply(fi), (i: Any) => k(i).transform(nat))
}
}
case class Return[F[_], A](a: A) extends Free[F, A]
case class Bind[F[_], I, A](fi: F[I], k: I => Free[F, A]) extends Free[F, A]
trait Functor[F[_]]{
def map[A, B](fa: F[A])(f: A => B): F[B]
}
trait Monad[F[_]] extends Functor[F] {
def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B]
def pure[A](a: A): F[A]
def map[A, B](fa: F[A])(f: A => B): F[B] = flatMap(fa)(a => pure(f(a)))
def flatten[A](ffa: F[F[A]]): F[A] = flatMap(ffa)(identity)
}
// F[_] and Functor[F] => Monad[Free[F, A]]
object Free {
def lift[F[_],A](fa: F[A]): Free[F,A] =
Bind(fa, (a: A) => Return(a))
implicit def monadForFree[F[_]](implicit ev: Functor[F]): Monad[({type λ[α] = Free[F, α]})#λ] = // Free[F, ?]
new Monad[({type λ[α] = Free[F, α]})#λ] {
def flatMap[A, B](fa: Free[F, A])(f: A => Free[F, B]): Free[F, B] =
fa.flatMap(f)
def pure[A](a: A): Free[F, A] = Return(a)
}
// Reasonably priced http://functionaltalks.org/2014/11/23/runar-oli-bjarnason-free-monad/
// Freer http://okmij.org/ftp/Haskell/extensible/more.pdf
// modern FP http://degoes.net/articles/modern-fp/
// cats Free
// https://github.com/typelevel/cats/blob/master/core/src/main/scala/cats/free/Free.scala
// http://typelevel.org/cats/tut/freemonad.html
// haskell Free https://hackage.haskell.org/package/free-4.12.4/docs/Control-Monad-Free.html
}
sealed trait Interact[A]
case class Ask[A](prompt: String, f: String => A) extends Interact[A]
case class Tell[A](msg: String, a: A) extends Interact[A]
object Interact {
implicit def functor: Functor[Interact] = new Functor[Interact] {
def map[A, B](fa: Interact[A])(f: A => B): Interact[B] = fa match {
case Ask(p, g) => Ask(p, f compose g)
case Tell(m, a) => Tell(m, f(a))
}
}
type API[A] = Free[Interact, A]
def tell(msg: String): API[Unit] =
Free.lift(Tell(msg, ()))
def ask(prompt: String): API[String] =
Free.lift(Ask(prompt, identity))
}
trait NaturalTransformation[F[_], G[_]]{
def apply[A](fa: F[A]): G[A]
}
object NaturalTransformation {
val listToOption = new NaturalTransformation[List, Option] {
override def apply[A](fa: List[A]): Option[A] = fa.headOption
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment