Enhancing types without breaking compatibility - best practice


Our team is using dhall for some time now and we see some real benefits in using it. At the same time we are still struggling with it.

One of the things we might haven’t done right is something the team members were calling “backward compatibility”. It means that we want to enhance a type by adding a new “parameter/field” without breaking existing usages of such type.

So something that you could do in programming languages by providing sensible defaults, e.g. changing your api from

func(a: String)
func(a:String, extraOption: String = “bar”)
does not break existing code.

In dhall we define our types like this (simplified version of what we actually do):

let CsvOptions : Type  =  { header : Bool,  separator : Text }

Those definitions go into some repository and we reference them via http link

So our “users” utilize it like this (again simplified)

let Metadata = https:// ….
let options : Metadata.CsvOptions  =  { header = True, separator = ";" }

So now we want to add a quote attribute/field to CsvOptions and we could also provide a sensible default. Just adding the quote to the type definition would look like this

let CsvOptions : Type  =  { header : Bool,  separator : Text, quote : Text }

As a consequence thereof the user code would break, because he doesn’t have quote defined. That is undesireable.

How can I extend my CscOptions with the quote field (and some default value) in such a way that the user code doesn’t break?

We experimented with some ways of handling this, but I am not sure what the best way would be to handle that.

So finally the question: What is the best practice to cope with this issue without putting any burden to the user utilizing the type?

Thanks a lot


1 Like

I believe what you want is Dhall’s support for defining “schemas” (record types with default values) and then instantiating schemas using the record completion operator (::).

For example, your starting example would look like this:

let CsvOptions =
      { Type = { header : Bool, separator : Text }
      , default = {=}

in  CsvOptions::{ header = True, separator = ";" }

… and then you could later add a quote field to that type without breaking existing record completions, like this:

let CsvOptions =
      { Type = { header : Bool, separator : Text, quote : Text }
      , default = { quote = "\"" }

in  CsvOptions::{ header = True, separator = ";" }

To learn more, see:

1 Like