Skip to content
Paul M Fox edited this page Nov 9, 2016 · 9 revisions

SCL (Sepia Configuration Language; sometimes pronounced 'shizzle') is a configuration language that extends HashiCorp's HCL. The syntax of SCL is concise, intuitive and extremely forgiving. Critically, it also validates much of your configuration by design, so it's harder to configure an application that seems like it should work, but doesn't. It's also something close to a functional programming language, and it's pretty fun to play with.

This wiki concerns the SCL language itself. Documentation on how to use the Go package in your applications (it's very easy) can be found on the main repostory page.

Why?

HCL is a really nice thing. It's well structured, friendly to humans and machines, and it's interoperable with JSON. It's reasonably intuitive, tolerant of inconsistent syntax, and well tested in the wild. There are text editor support packages emerging, and some helpful tools as well.

The problems with using HCL for modern web apps are threefold:

Firstly, it's really verbose. At times it can be annoying, even. The number of lines required to define something relatively simple feels excessive, and all the powerful language features have to be typed longhand. There are good reasons for this (mostly to do with JSON interoperability) but it does make the language less engaging.

Secondly, it's a completely static language which expects to load a single input – usually a file – only. Some HCL-based projects, like Terraform, have extended HCL to use multiple files, simple functions, and basic variable interpolation; but those features are more of a tacked-on afterthought than a well-planned concept. It isn't difficult to pre-parse your HCL and add those sorts of features, but problems quickly arise: line numbers are wrong in error messages, syntax errors in variable statements would produce baffling errors because the underlying HCL system wasn't expecting them, and so forth.

Thirdly, although HCL is tightly structured by its underlying Go types, declarations are freeform in terms of their organisation. Any kind of object declaration can go inside any other kind of object declaration without causing an error. Any improperly nested objects would be ignored by the HCL parser, because that's how HCL handles unexpected-but-valid declarations, and you'd be left wondering why you didn't get any configuration in your application or an error.

Depending on what you're trying to do, none of these things may be an issue. If, however, your application needs finer control, or you aren't tolerant of weirdness in software, then you may find HCL wanting for improvement.

Enter SCL

SCL is a simple, declarative, semi-functional language that extends HCL in the same way that Sass extends CSS. What that means is, any properly formatted HCL is valid SCL. If you really enjoy HCL, you can keep using it exclusively: under the hood, SCL 'compiles' to HCL. The difference is that now you can explicitly include files, use 'mixins' to quickly inject boilerplate code, organise your code, and use properly scoped, natural variables.

In addition to the language itself, there is a useful command-line tool than can compile your .scl files and write the output to the terminal, run gold standard tests against you code, and even fetch libraries of code from public version control systems. The syntax of SCL has been carefully designed to promote use in real world projects, and is clearly described in this wiki.

Finally, SCL libraries can be automatically documented, much like Go can be documented with the godoc tool. If you're building an application with a complex standard library, you're likely to want user documentation. The SCL Go package can generate that for you automatically.

SCL in action

SCL is the product of Sepia, a currently closed-source application for building web applications. This is a bit of valid, tested SCL code that configures a simple application in that system:

include("sepia/*")

$MONGO_URL ?= "localhost/test"
$HOST      ?= "http://localhost"

server("/app"):
    database:
        mongoDB($MONGO_URL)
    validationPaths(["validation"])
    assetsDirectory("public")

client:
    host = $HOST

theme:
    css("app.css", ["scss/app/*.scss"])
    js("app.js", ["js/*.js"])
    dir("images",["assets/images/*.*"])

web:
    asset("robots.txt", "assets/robots.txt")

    module("/register-now")
        view("register_now")

    module("/")
        view("landing")
            css(["landing.css"])

Even if you're not familiar with the SCL language or Sepia concepts, the code should be fairly intuitive: there are bunch of neatly separated sections that looks a bit like function calls. It's indented a bit like Python, and some of the global variables look like the ones found in a Makefile.

Out of interest, let's compare that to the equivalent HCL code:

server config {
  database {
    engine = "mongo"

    config {
      url = "localhost/test"
    }
  }

  validation {
    script_paths = ["validation"]
  }

  assets_directory = "public"
  root_path        = "/app"
}

client config {
  host = "http://localhost"
}

theme "default" {
  css "app.css" {
    paths = ["scss/app/*.scss"]
  }

  js "app.js" {
    paths = ["js/*.js"]
  }

  dir "images" {
    paths = ["assets/images/*.*"]
  }

  template_paths         = ["templates/sepia"]
  master_template        = "master"
  angular_template_paths = ["templates/angular"]
}

asset "robots.txt" {
  source = "assets/robots.txt"
}

module "/register-now" {
  view {
    theme    = "default"
    template = "register_now"
  }

  controller = ""
}

module "/" {
  view {
    theme    = "default"
    template = "landing"
    css      = ["landing.css"]
  }

  controller = ""
}

Those two code blocks contain the same information — in fact, the HCL code is generated from the SCL. As you can see, the SCL code is only about 46% of the length of the HCL code. SCL has dispensed with the curly braces (though they're optional, as you'll see in the syntax section) and variables are declared and used in a vaguely natural way. There's an explicit include statement which pulls in code from a standard library, and there's a whole lot of nesting going on. Individual components of the configuration are sorted into neat sections, and there are fewer lines with almost no content.

Clone this wiki locally