Literate programming in Scala with codedown

April 23, 2016

Codedown is a little utility inspired by literate Haskell to extract code blocks of any given language from Markdown files.

Install codedown with npm install:

$ npm install -g codedown

Essentially, all codedown does is look for code blocks marked as scala like this:

val x = 6
val y = 7
val z = x * y
println(z)

It filters out the rest, and spits out the remaining code:

$ curl -sL earldouglas.com/posts/literate-scala.md |
  codedown scala
val x = 6
val y = 7
val z = x * y
println(z)

This output can be piped through scala to evaluate it:

$ curl -sL earldouglas.com/posts/literate-scala.md |
  codedown scala | scala
Welcome to Scala version 2.11.7 (OpenJDK Server VM, Java 1.8.0_76).
Type in expressions to have them evaluated.
Type :help for more information.

scala> val x = 6
x: Int = 6

scala> val y = 7
y: Int = 7

scala> val z = x * y
z: Int = 42

scala> println(z)
42

We can clean this up by passing it to Scala as a string to be evaluated:

$ curl -sL earldouglas.com/posts/literate-scala.md |
  codedown scala | xargs -0 scala -e
42

This string of commands is getting rather unweildy, so let's make a reusable function out of it:

$ scodedown() {
  curl -sL "$1" | codedown scala | xargs -0 scala "${@:2}" -e;
}

Now we can use scodedown to download, interpret, and run a literate Scala file:

$ scodedown https://earldouglas.com/posts/literate-scala.md
42

A literate Scala file can't be run on its own if the code depends on external libraries.

For example, consider the code in the JDBC database access in Scala article. The code uses an H2 database, so it can't work without access to the com.h2database:h2 library.

We need to grab the dependency and tell scala about it using -cp:

$ wget https://jcenter.bintray.com/com/h2database/h2/1.4.178/h2-1.4.178.jar
$ scodedown https://earldouglas.com/posts/scala-jdbc.md -cp h2-1.4.178.jar
Books: List(Book(Surely You're Joking, Mr. Feynman!))

Manual dependency resolution isn't always practical. We can let Maven do it for us.

$ mvn org.apache.maven.plugins:maven-dependency-plugin:2.1:get \
  -DrepoUrl=url -Dartifact=com.h2database:h2:1.4.178
$ scodedown https://earldouglas.com/posts/scala-jdbc.md \
  -cp ~/.m2/repository/com/h2database/h2/1.4.178/h2-1.4.178.jar
Books: List(Book(Surely You're Joking, Mr. Feynman!))

Using Maven and dealing with its local artifact repository isn't always practical either. We can instead let sbt handle dependency resolution for us.

In a normal sbt project, we would simply import this library in build.sbt:

libraryDependencies += "com.h2database" % "h2" % "1.4.178"

To get this working with codedown, we use sbt's script runner:

$ curl -sL earldouglas.com/posts/scala-jdbc.md | codedown scala > scala-jdbc.scala
$ sbt -Dsbt.main.class=sbt.ScriptMain scala-jdbc.scala
Books: List(Book(Surely You're Joking, Mr. Feynman!))