Skip to content

Instantly share code, notes, and snippets.

@joshuakfarrar
Last active October 25, 2018 18:46
Show Gist options
  • Select an option

  • Save joshuakfarrar/716f80c2fa3f0090cf132379039f502d to your computer and use it in GitHub Desktop.

Select an option

Save joshuakfarrar/716f80c2fa3f0090cf132379039f502d to your computer and use it in GitHub Desktop.
from the introduction of functional programming for mortals with scalaz
package LetThereBeEcho
trait Terminal[C[_]] {
def read: C[String]
def write(t: String): C[Unit]
}
trait Execution[C[_]] {
def doAndThen[A, B](c: C[A])(f: A => C[B]): C[B]
def create[B](b: B): C[B]
}
object Execution {
implicit class Ops[A, C[_]](c: C[A]) {
def flatMap[B](f: A => C[B])(implicit e: Execution[C]): C[B] =
e.doAndThen(c)(f)
def map[B](f: A => B)(implicit e: Execution[C]): C[B] =
e.doAndThen(c)(f andThen e.create)
}
implicit val deferred: Execution[IO] = new Execution[IO] {
def doAndThen[A, B](c: IO[A])(f: A => IO[B]): IO[B] = c.flatMap(f)
def create[B](b: B): IO[B] = IO(b)
}
}
// a more safe execution context... IO!
final class IO[A](val interpret: () => A) {
def map[B](f: A => B): IO[B] = IO(f(interpret()))
def flatMap[B](f: A => IO[B]): IO[B] = IO(f(interpret()).interpret())
}
object IO {
def apply[A](a: =>A): IO[A] = new IO(() => a)
}
object TerminalIO extends Terminal[IO] {
def read: IO[String] = IO { io.StdIn.readLine }
def write(t: String): IO[Unit] = IO { println(t) }
}
object Main extends App {
import Execution.Ops
// gross!
//def echo[C[_]](t: Terminal[C], e: Execution[C]): C[String] =
// e.chain(t.read) { in: String =>
// e.chain(t.write(in)) { _: Unit =>
// e.create(in)
// }
// }
// neat!
def echo[C[_]](implicit t: Terminal[C], e: Execution[C]): C[String] =
for {
in <- t.read
_ <- t.write(in)
} yield in
implicit val io: Terminal[IO] = TerminalIO
// because echo[C[_]] expects Terminal[C] and Execution[C] to be
// implicitly available, above you can see that we pull in Terminal[IO]
// with TerminalIO
// can you understand why Execution[IO] exists if not reified here as an implicit?
val program: IO[String] = echo[IO]
program.interpret() // ...and i feel fine...
}
@joshuakfarrar

Copy link
Copy Markdown
Author

In the case where we cannot abstract over execution context, it may be the case that the thread pool which is assigned our work does not have the features available to execute a piece of work; in this case, across environments, parameterizing over execution context buys us the ability to ensure computations being executed are computed in an environment which has available to it the features implemented necessary for the execution of that computation, though the implementation of those methods may be different, despite the similarity of their APIs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment