Skip to content

Instantly share code, notes, and snippets.

@javierfs89
Created June 26, 2017 11:13
Show Gist options
  • Save javierfs89/fab1b32943ecdced54a6e887f3241935 to your computer and use it in GitHub Desktop.
Save javierfs89/fab1b32943ecdced54a6e887f3241935 to your computer and use it in GitHub Desktop.
Functional APIs design pattern in action
/* 1. APIs */
trait IO[P[_]] {
def read: P[String]
def write(msg: String): P[Unit]
}
object IO {
object syntax {
def read[P[_]](implicit IO: IO[P]) = IO.read
def write[P[_]](msg: String)(implicit IO: IO[P]) = IO.write(msg)
}
}
trait Monad[P[_]] {
def flatMap[A, B](pa: P[A])(f: A => P[B]): P[B]
def pure[A](a: A): P[A]
}
object Monad {
object syntax {
implicit class MonadOps[P[_], A](pa: P[A])(implicit M: Monad[P]) {
def flatMap[B](f: A => P[B]) = M.flatMap(pa)(f)
def map[B](f: A => B) = M.flatMap(pa)(f andThen M.pure)
// aliases
def >>=[B](f: A => P[B]) = M.flatMap(pa)(f)
def >>[B](next: P[B]) = M.flatMap(pa)(_ => next)
}
}
}
/* 1.1 Possible interpreters */
// Console
type Id[A] = A
type SyncIO = IO[Id]
// Async
import scala.concurrent.Future
type AsyncIO = IO[Future]
// Pure state transformations
case class IOState(reads: List[String], writes: List[String])
type State[A] = IOState => (IOState, A)
type StateIO = IO[State]
/* 2. Programs */
/* 2.1 IO programs */
def hello[P[_]](IO: IO[P]): P[Unit] =
IO.write("Hello, world!")
def sayWhat[P[_]](IO: IO[P]): P[String] =
IO.read
/* 2.2 IO Monadic programs + syntax */
import IO.syntax._
import Monad.syntax._
def helloSayWhat[P[_]: Monad: IO]: P[String] =
write("Hello, say something:") >>
read
def echo[P[_]: Monad: IO]: P[Unit] =
read >>= write[P]
def echo2[P[_]: Monad: IO]: P[String] = for {
msg <- read
_ <- write(msg)
} yield msg
/* 3. Instances */
implicit object ioTerminal extends IO[Id] {
def write(msg: String) = println(msg)
def read() = scala.io.StdIn.readLine
}
implicit object monadId extends Monad[Id] {
def flatMap[A, B](pa: Id[A])(f: A => Id[B]): Id[B] = f(pa)
def pure[A](a: A): Id[A] = a
}
/* 4. Composition */
def helloConsole(): Unit = hello[Id](ioTerminal)
def sayWhatConsole(): String = sayWhat(ioTerminal)
def helloSayWhatConsole() = helloSayWhat(monadId, ioTerminal)
def echoConsole() = echo[Id]
def echo2Console() = echo2[Id]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment