public
Last active

Styles of config propagation: Manual, Implicits, DynamicVariable, Reader

  • Download Gist
config.scala
Scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
package scalaz.example
 
 
object Reader extends App {
 
/**
* Manual propagation of the environment (in the example, `contextRoot`.)
*/
object Config0 {
def fragment1(contextRoot: String) = <a href={contextRoot + "/foo"}>foo</a>
def fragment2(contextRoot: String) = <a href={contextRoot + "/bar"}>bar</a>
def html(contextRoot: String) = <html>
<body>
{fragment1(contextRoot)}{fragment2(contextRoot)}
</body>
</html>
def render = html("/c1")
}
 
/**
* Implicit parameter propagation of the environment (in the example, `contextRoot`.)
* - Advantage: less boilerplate than above
* - Disadvantage: hard to reason that the *same* context root is used everywhere, as some part of the
* computation could pass a different one. (This might also be an advantage, depending on
* your requirements)
*/
object Config0_1 {
case class ContextRoot(s: String) { def toString = s }
def fragment1(implicit contextRoot: ContextRoot) = <a href={contextRoot + "/foo"}>foo</a>
def fragment2(implicit contextRoot: ContextRoot) = <a href={contextRoot + "/bar"}>bar</a>
def html(implicit contextRoot: ContextRoot) = <html>
<body>
{fragment1}{fragment2}
</body>
</html>
def render = html(ContextRoot("/c1"))
}
 
 
 
/**
* Uses a inheritable thread local, provided by `scala.util.DynamicVariable`, to avoid
* explicitly passing the state.
*
* Advantages: no clutter in the code; values can be set separately on on different threads.
* Disadvantages: The functions are no longer pure. `html` is not reusable.
* While the value is available on child threads, it is *not*
* available on threads from an pool that are working on your
* behalf (for example, by perform `xs.par.map(f)`)
*
* @see http://www.youtube.com/watch?v=8oiN-hzBKHE
* @see http://stackoverflow.com/questions/5116352/when-we-should-use-scala-util-dynamicvariable
*/
object Config1 {
import scala.util.DynamicVariable
 
val contextRoot = new DynamicVariable[String]("")
 
def fragment1 = <a href={contextRoot.value + "/foo"}>foo</a>
def fragment2 = <a href={contextRoot.value + "/bar"}>bar</a>
def html = <html>
<body>
{fragment1}{fragment2}
</body>
</html>
 
def render = contextRoot.withValue("/c1") {
html
}
}
 
/**
* Use the Reader monad to thread the environment through the call tree.
*
* Functions that require the environment are of type `ConfigReader`
*
* @see https://groups.google.com/d/msg/scala-user/O8udPVbdg_c/mIMgMnJPomQJ
*/
object Config2 {
 
/**
* Reader Monad. Computation of a value of type `A`, given an environment of type `E`.
*
* Think of the environment as read-only, configuration.
*/
case class Reader[E, A](run: E => A) {
def map[B](f: A => B) = Reader[E, B](s => f(run(s)))
def flatMap[B](f: A => Reader[E, B]) = Reader[E, B](s => f(run(s)).run(s))
}
 
implicit def CR[E, A](run: E => A) = Reader(run)
 
// convenience type alias.
type StringReader[A] = Reader[String, A]
 
import xml._
 
def fragment1: StringReader[Elem] = (contextRoot: String) => <a href={contextRoot + "/foo"}>foo</a>
def fragment2: StringReader[Elem] = (contextRoot: String) => <a href={contextRoot + "/bar"}>bar</a>
def html1(f1: Elem, f2: Elem) = <html><body>{f1}{f2}</body></html>
def html: StringReader[Elem] = for {
f1 <- fragment1
f2 <- fragment2
} yield html1(f1, f2)
 
def render = html.run("/c1")
}
}

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.