-
-
Save tonymorris/cab02ebbc18b9716108f to your computer and use it in GitHub Desktop.
Crash
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package a | |
package b | |
/** | |
* @example {{{ | |
// A use-case to demonstrate the hadoop configuration interpreter. | |
// The use-case deliberately intersperses effects (e.g. println) that are unrelated to hadoop configuration. | |
// This requires the construction of our own interpreter for these effects on top of the existing hadoop interpreter. | |
object HConfigurationInterpreterExample { | |
// A hadoop configuration | |
def setupConfiguration = { | |
val conf = new Configuration() | |
conf set ("a", "A") | |
conf set ("b", "B") | |
conf set ("c", "C") | |
conf | |
} | |
// A typical side-effectful program that uses a hadoop configuration and intersperses arbitrary side-effects. | |
// Two effects are interspersed in this example; println (stdout) and err.println (stderr). | |
// The ultimate goal is mechanically transform the program below into pure functional code using the hadoop interpreter. | |
// This transformation is performed in `object After`. | |
object Before { | |
// The top-level program. | |
def program { | |
val conf = setupConfiguration | |
val a = conf get "a" | |
function1(conf) | |
println("a: " + a) | |
println("a: " + (conf get "a")) | |
println("b: " + (conf get "b")) | |
function2(conf) | |
println("a: " + a) | |
Console.err.println("a: " + (conf get "a")) | |
println("b: " + (conf get "b")) | |
function3(conf) | |
println("a: " + a) | |
Console.err.println("a: " + (conf get "a")) | |
println("b: " + (conf get "b")) | |
} | |
// A sub-program. | |
def function1(conf: Configuration) { | |
println("a: " + (conf get "a")) | |
Console.err.println("b: " + (conf get "b")) | |
conf set ("a", "ax") | |
} | |
// A sub-program. | |
def function2(conf: Configuration) { | |
println("a: " + (conf get "a")) | |
conf set ("b", "bx") | |
conf set ("a", "axx") | |
} | |
// A sub-program. | |
def function3(conf: Configuration) { | |
conf unset "a" | |
println("a: " + (conf get "a")) | |
conf unset "b" | |
conf set ("a", "axxx") | |
} | |
} | |
// The transformation of the `object Before` program using the hadoop interpreter. | |
object After { | |
// Set up a grammar to build on top of the hadoop interpreter. A minimum requirement is that a `Functor` is provided. | |
// To properly model the `Before` program, this grammar accepts one of the following: | |
// * A hadoop configuration interpreter | |
// * A println (out) effect | |
// * A println (err) effect | |
sealed trait HConfigurationEffect[+A] { | |
// The `map` method that is used for the `Functor` which gives the free monad (an arbitrary interpreter). | |
def map[B](f: A => B): HConfigurationEffect[B] = | |
this match { | |
case HConfigurationNoEffect(x) => HConfigurationNoEffect(x map f) | |
case HConfigurationOutPrintlnEffect(s, a) => HConfigurationOutPrintlnEffect(s, f(a)) | |
case HConfigurationErrPrintlnEffect(s, a) => HConfigurationErrPrintlnEffect(s, f(a)) | |
} | |
} | |
case class HConfigurationNoEffect[+A](x: HConfigurationInterpreter[A]) extends HConfigurationEffect[A] | |
case class HConfigurationOutPrintlnEffect[+A](s: String, a: A) extends HConfigurationEffect[A] | |
case class HConfigurationErrPrintlnEffect[+A](s: String, a: A) extends HConfigurationEffect[A] | |
object HConfigurationEffect { | |
// The actual functor instance, which simply calls map | |
implicit val HConfigurationEffectFunctor: Functor[HConfigurationEffect] = | |
new Functor[HConfigurationEffect] { | |
def map[A, B](fa: HConfigurationEffect[A])(f: A => B) = | |
fa map f | |
} | |
} | |
// An interpreter for the hadoop configuration interpreter with effects. A functor must be supplied, which simply uses the underlying `Free`, which is a functor since the grammar is a functor. | |
case class HConfigurationEffectInterpreter[+A](free: Free[HConfigurationEffect, A]) { | |
// The `map` method that is used for the `Functor` which gives the free monad (an arbitrary interpreter). | |
def map[B](f: A => B): HConfigurationEffectInterpreter[B] = | |
HConfigurationEffectInterpreter(free map f) | |
// Interpreters are monads. | |
def flatMap[B](f: A => HConfigurationEffectInterpreter[B]): HConfigurationEffectInterpreter[B] = | |
HConfigurationEffectInterpreter(free flatMap (f(_).free)) | |
// Unsafe operation that runs all effects on the given configuration. | |
// This operation is unsafe in that when it is called, the equational reasoning property are lost. | |
// However, the boundary within which that property is lost is clearly delineated. | |
// This is not a pure function, but it isolates side-effects. | |
@annotation.tailrec | |
final def run(c: Configuration): A = | |
free.resume match { | |
// run the hadoop configuration interpreter then continue | |
case -\/(HConfigurationNoEffect(i)) => | |
HConfigurationEffectInterpreter(i run c) run c | |
// run a println (out) effect then continue. | |
case -\/(HConfigurationOutPrintlnEffect(s, a)) => | |
HConfigurationEffectInterpreter({ | |
println(s) | |
a | |
}) run c | |
// run a println (err) effect then continue. | |
case -\/(HConfigurationErrPrintlnEffect(s, a)) => | |
HConfigurationEffectInterpreter({ | |
Console.err.println(s) | |
a | |
}) run c | |
// halt the recursion and produce a value. | |
case \/-(a) => | |
a | |
} | |
} | |
object HConfigurationEffectInterpreter { | |
// Used internally (see below). | |
private def lift[A](x: HConfigurationInterpreter[A]): HConfigurationEffectInterpreter[A] = | |
HConfigurationEffectInterpreter(x hom (new (HConfiguration ~> HConfigurationEffect) { | |
def apply[X](c: HConfiguration[X]) = | |
HConfigurationNoEffect(HConfigurationInterpreter(Suspend(c map (Return(_))))) | |
})) | |
// Interpreter instruction to get key's value from the hadoop configuration. | |
def get[A](k: String): HConfigurationEffectInterpreter[Option[String]] = | |
lift(HConfigurationInterpreter.get(k)) | |
// Interpreter instruction to unset a key on the hadoop configuration. | |
def unset[A](k: String): HConfigurationEffectInterpreter[Unit] = | |
lift(HConfigurationInterpreter.unset(k)) | |
// Interpreter instruction to print (out) the given string. | |
def outprintln[A](s: String): HConfigurationEffectInterpreter[Unit] = | |
HConfigurationEffectInterpreter(Suspend(HConfigurationOutPrintlnEffect(s, Return(())))) | |
// Interpreter instruction to print (err) the given string. | |
def errprintln[A](s: String): HConfigurationEffectInterpreter[Unit] = | |
HConfigurationEffectInterpreter(Suspend(HConfigurationErrPrintlnEffect(s, Return(())))) | |
} | |
import HConfigurationEffectInterpreter._ | |
// A top-level program. | |
def program = { | |
// A top-level pure-functional program. | |
val p = | |
for { | |
a <- get("a") | |
_ <- function1 | |
_ <- outprintln("a: " + a) | |
aa <- get("a") | |
_ <- outprintln("a: " + aa) | |
b <- get("b") | |
_ <- outprintln("b: " + b) | |
_ <- function2 | |
_ <- outprintln("a: " + a) | |
aaa <- get("a") | |
_ <- outprintln("a: " + aaa) | |
bb <- get("b") | |
_ <- outprintln("b: " + bb) | |
_ <- function3 | |
_ <- outprintln("a: " + a) | |
aaaa <- get("a") | |
_ <- errprintln("a: " + aaaa) | |
bbb <- get("b") | |
_ <- outprintln("b: " + bbb) | |
} yield () | |
val conf = setupConfiguration | |
// Invoke the unsafe operation (and then forget the configuration ever existed). | |
p run conf | |
} | |
// A pure-functional sub-program. | |
def function1 = | |
for { | |
_ <- outprintln("a: " + a) | |
b <- get("b") | |
_ <- errprintln("b: " + b) | |
_ <- set ("a", "ax") | |
} yield () | |
// A pure-functional sub-program. | |
def function2 = | |
for { | |
a <- get("a") | |
_ <- outprintln("a: " + a) | |
_ <- set ("b", "bx") | |
_ <- set ("a", "axx") | |
} yield () | |
// A pure-functional sub-program. | |
def function3 = | |
for { | |
_ <- unset("a") | |
a <- get("a") | |
_ <- outprintln("a: " + a) | |
_ <- unset("b") | |
_ <- set ("a", "axxx") | |
} yield () | |
} | |
// Call the `Before` and `After` programs. | |
def main(args: Array[String]) { | |
println("====") | |
After.program | |
} | |
} | |
}}} | |
*/ | |
class X |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment