When I find something interesting and new, I post it here - that's mostly programming, of course, not everything.

Sunday, June 10, 2012

Cake Pattern Practices

Intro

Cake Pattern is a Scala-specific solution to Dependency Injection problem. In Java, from where Scala has inherited a lot, DI problem is solved by either passing environment as a parameter (Env monad), or using specific ad-hoc tools, like Guice. Scala is a much more powerful language, and Cake Pattern is available here. In my opinion, Cake Pattern is the best. It is similar to passing parameters, but differently. The first publication was by Jonas Bonér in 2008, Real-World Scala: Dependency Injection (DI) I personally found the article kind of hard to read; hope this one is easier. The pattern itself is easy.

Real World Example

We have an application which depends on configuration and credential files, and talks to a server via some proprietary API. We want to work on this application. By working I mean freely refactoring it from its initial prototype state to a well-written piece of code, enriched with logging, statistics, status reporting, etc., and keep it alive, that is, modifiable. One cannot seriously do it using real world connections; tests take minutes to run, connections are brittle etc. So we want to be able to run logic without the need to connect to anything. And this includes also System.out output and System.exit() calls. How do we do it? Let's start with the "real world" part.

Abstracting The File System

The full source code can be found on github, here is a small piece.
import io.{Source => ioS}
import java.io.{PrintWriter, File}

trait FS {

  implicit def file(path: String)                       = new File(path)
  implicit def asFile(file: TextFile)                   = file.file
  implicit def textFile(file: File):       TextFile     = TextFile(file)
  implicit def textFile(path: String):     TextFile     = TextFile(file(path))
  def tempFile(prefix: String)                          = TextFile(File.createTempFile(prefix, "tmp"))

  def exists(file: File): Boolean = file.exists

  protected class Entry(val file: File) {
    lazy val canonicalFile = file.getCanonicalFile.getAbsoluteFile
    def parent = Folder(canonicalFile.getParentFile)
    def delete = file.delete
    override def toString = canonicalFile.toString
  }

  case class Folder(path: File) extends Entry(path) {
    def file(path: String): TextFile = TextFile((canonicalFile /: (path split "/")) (new File(_, _)))

  class TextFile(file: File) extends Entry(file) {
    def text = ioS.fromFile(file).mkString

    def text_=(s: String) {
      val out = new PrintWriter(file, "UTF-8")
      try{ out.print(s) } finally{ out.close }
    }
  }
}

object FS extends FS
To use it in your application, instead of "manually" finding/opening/closing files, one just has to write
import FS._
somewhere in the client code.

Mocking the File System

To avoid creating multiple files just to make sure the code works in various circumstances, we better mock the file system. Here's an example:
val mockFS = new FS {
    class MockFile(path: String, content: String) extends TextFile(new File(path)) {
      override def text = content
    }
    val files = new scala.collection.mutable.HashMap[String, MockFile]
    def add(file: MockFile) { files.put(file.getPath, file)}
    def file(pair: (String, String)) { add(new MockFile(pair._1, pair._2)) }

    file("badcredo.txt"           -> "once upon a midnight dreary while I pondered weak and weary")
    file("credoNoUser.txt"        -> """
    password = "Guess me if you can!"
    realm = fb
    """)
    file("credoBadUser.txt"        -> """
    password = "Guess me if you can!"
    realm = fb
    username=
    """)
    file("credoBadPassword.txt"        -> """
    password =
    realm = fb
    username=badpwd
    """)

    override def exists(file: File) = true // egg or chicken problem
    override def textFile(path: String) = files.getOrElse(path, throw new FileNotFoundException("what is this " + path + "?"))
  }
We can throw in tons of such cases, covering all the situations, which is less probable if you have to deal with dozens of actual files. Now good, but how can we replace a file system with this mock file system? This is how.

Eating The Cake

trait MyApp {
  val FileSystem
  /*...*/
  def login(cred: String) {
    val (userid, password, realm) = parse(FileSystem.textFile(cred).text)
    /*...*/
}

object MyApp {
  val FileSystem = FS
}
Here
MyApp
is a trait, with an accompanying object. In production circumstances we use the accompanying object, which instantiates FileSystem as FS, the object representing a regular file system. In the trait, FileSystem is represented as an abstract value; so if we extend MyApp in our test suite, we can pass our test file system:
val SUT extends MyApp {
  val FileSystem = mockFS
}
Now we can manipulate the app to our pleasure.

Intercepting Everything

In addition, in our test suite we can define this:
  trait Testable {
    val console = new StringWriter
    val out = new PrintWriter(console)
    def printed = console.toString
    class Exit(code: Int) extends Exception(code)
    def print(s: String) = out.print(s)
    def println(s: String) = out.println(s)
    def exit(code: Int) = throw new Exit(code);
And then write
val SUT extends MyApp with Testable {
  val FileSystem = mockFS
}
Provided that we did define the default implementations of println, exit etc, we are now in control of the application.

Rinse, Repeat

But what if MyApp uses another piece of code that talks to an external service? We have to do some instrumentation in that other piece of code, using the same cake pattern; the default implementation would be coming from the accompanying object, but in the case of test suite we can instantiate with something completely different, and pass it along to MyApp.

Questions?

Sunday, March 18, 2012

Miles Sabin's type negation in practice

There was a discussion recently on scala-lang regarding how to ensure that your function returns a non-null value. Here's a solution from Miles Sabin, with my comments.

the source


// Encoding for "A is not a subtype of B"
trait <:!<[A, B]

// Uses ambiguity to rule out the cases we're trying to exclude
implicit def nsub[A, B] : A <:!< B = null
implicit def nsubAmbig1[A, B >: A] : A <:!< B = null
implicit def nsubAmbig2[A, B >: A] : A <:!< B = null

// Type alias for context bound
type |¬|[T] = {
type λ[U] = U <:!< T
}


def foo[T, R : |¬|[Unit]#λ](t : T)(f : T => R) = f(t)

foo(23)(_ + 1) // OK
foo(23)(println) // Doesn't compile


So, how it works.

First we define the trait <:!< which looks like a negation of <:< - in Scala this notation means that one type can be, via implicits or whatever, converted into another.
Note that when you declare a type, and the type depends on two parameters, you can write the type in the infix form, e.g. String Map Int; in our case, instead of val x: <:!<[A, B] we can write val x: A <:!< B

E.g.

scala> def f(m: String Map Int) = Map("one" -> 1)
f: (m: Map[java.lang.String,Int])scala.collection.immutable.Map[java.lang.String,Int]


Why do we need a trait that does not specify any functionality? See below.

We define three implicits. The first one, implicit def nsub[A, B] : A <:!< B = null, is applicable in a case of any two types A and B; if we had just this one, the compiler would be never confused. Now we add two more to confuse the cat the compiler:

implicit def nsubAmbig1[A, B >: A] : A <:!< B = null
implicit def nsubAmbig2[A, B >: A] : A <:!< B = null


Their role is that whenever we encounter a context where <:!<[A, B] is required, and B is a supertype of A, the cat the compiler gets confused. It does not get confused if there is no such relationship between the two types. Namely, we get this error:

:27: error: ambiguous implicit values:
both method nsubAmbig1 in object $iw of type [A, B >: A]=> <:!<[A,B]
and method nsubAmbig2 in object $iw of type [A, B >: A]=> <:!<[A,B]
match expected type <:!<[Unit,Unit]


So, what do we do to use this feature? We declare a type function (seems like a new term) that is negation type for type T: whenever we apply this function to type U, it only allows to compile if U is not a subtype of T.

type |¬|[T] = {
type λ[U] = U <:!< T
}


This is a kind of a type trap; let's see how we use it.

Declare a function foo that takes first parameter of type T, and second parameter a function from T to R:


def foo[T, R](t : T)(f : T => R) = f(t)


We can call it as val n24 = foo(23)(_ + 1) or as foo(23)(println). Now how do we make sure that it does not take a function that returns Unit? We have to delimit the second type parameter, R, so that it's never a Unit or a subclass of Unit. This is how we do it:

def foo[T, R : |¬|[Unit]#λ](t : T)(f : T => R) = f(t)


Now the second example, foo(23)(println), won't compile. Ta-da!

Questions?

Followers

Subscribe To My Podcast

whos.amung.us