Is Dhall A configuration language guaranteed to terminate dhall is a total programming language specialized to configuration files • from Gabriel Gonzalez (Pipes, Turtle, …) • language: github.com/dhall-lang/dhall-lang • haskell: github.com/dhall-lang/dhall-haskell • This presentation: dhall-1.15.0 Markus Hauck @markus1189 Dhall: An Introduction 4
• Haskell integration — Dhall expressions can be marshalled into Haskell • Total — Programs always terminate and will never hang • Safe — Programs never crash or throw exceptions • Distributed — Expressions can reference other expressions by URL or path • Strong normalization — Every expression can be reduced to a normal form • Statically typed — Configuration files can be validated ahead-of-time • Strongly typed — No coercions, casts or subtyping • Built-in data types — Includes lists, anonymous records and anonymous unions Markus Hauck @markus1189 Dhall: An Introduction 5
Example 1 let twentyOne = 21 in 2 let answer = twentyOne + twentyOne in 3 { x = 1 4 , y = 2 : Natural -- with type 5 , foo = "foo" -- text 6 , bar = "bar" 7 , foobar = "foo" ++ "bar" -- append 8 } Markus Hauck @markus1189 Dhall: An Introduction 6
Dhall Tool 1 Usage: dhall ([version] | [resolve] | [type] | [normalize] | [repl] | [diff] | 2 [hash] | [lint] | [format] | [freeze]) [--explain] [--plain] 3 Interpreter for the Dhall language 4 5 Available options: 6 -h,--help Show this help text 7 --explain Explain error messages in more detail 8 --plain Disable syntax highlighting 9 10 Available commands: 11 version Display version 12 resolve Resolve an expression's imports 13 type Infer an expression's type 14 normalize Normalize an expression 15 repl Interpret expressions in a REPL 16 diff Render the difference between the normal form of two 17 expressions 18 hash Compute semantic hashes for Dhall expressions 19 lint Improve Dhall code 20 format Formatter for the Dhall language 21 freeze Add hashes to all import statements of an expression Markus Hauck @markus1189 Dhall: An Introduction 8
Is Not A General Purpose Language • before we go in deeper, important disclaimer • dhall is not a general purpose language • you might get frustrated if you want to program in it • that’s not the purpose Markus Hauck @markus1189 Dhall: An Introduction 11
Bindings 1 let x = 100 in 2 let y = (200 : Natural) in 3 let z : Natural = 300 in 4 [ x, y, z] 5 -- [100, 200, 300] Markus Hauck @markus1189 Dhall: An Introduction 18
1 { 1 = 2 } -- invalid 2 { x = 1, y = 2.0, z = +3 } 3 { foo = "foo", y = "bar"} 4 {=} : {} 5 { x = 1, y = 2, z = 3}.x 6 { x = 1, y = 2, z = 3}.y 7 { x = 1, y = 2, z = 3}.{x, y} Markus Hauck @markus1189 Dhall: An Introduction 21
Records 1 • not recursive • changes type if necessary, right side has priority • useful to specify defaults 1 { x = 1 } // { y = 2 } 2 { x = 1 } // { x = 2 } 3 { x = 1 } // { x = +2 } 4 { x = 1 } // { x = "foo" } 5 { x = 1 } // {=} 6 { = } // { x = 1 } 7 { x = { y = 1 }} // { x = "nope" } Markus Hauck @markus1189 Dhall: An Introduction 22
Records 2 • recursive • error on field collision (not a record) 1 { x = 1 } /\ { x = 2 } -- nope 2 { x = { y = 1 } } /\ { x = { z = 2 } } Markus Hauck @markus1189 Dhall: An Introduction 23
1 < Left = 1 | Right : Text > 2 < Left : Natural | Right = "text" > 3 \(a : Type) -> < None = {=} | Some : a > 4 \(a : Type) -> \(x : a) -> < None : {} | Some = x > 5 < Dog : Text | Cat : Text | Bird : Text | Fish = "nemo" > Markus Hauck @markus1189 Dhall: An Introduction 24
• thankfully there is constructors 1 let Animal = constructors < Dog : Text | 2 Cat : Text | 3 Bird : Text | 4 Fish : Text > in 5 [ Animal.Dog "barney" 6 , Animal.Cat "garfield" 7 , Animal.Bird "tweety" 8 ] Markus Hauck @markus1189 Dhall: An Introduction 25
Variables 1 let Schema = { home : Text 2 , user : Text 3 , id : Optional Natural 4 } in 5 let Some = https://raw.githubusercontent.com/dhall-lang/Prelud 6 let None = https://raw.githubusercontent.com/dhall-lang/Prelud 7 { home = env:HOME as Text 8 , user = env:USER as Text 9 , id = Some Natural env:IDENTITY ? None Natural 10 } : Schema Markus Hauck @markus1189 Dhall: An Introduction 26
1 -- people 2 [ { first = "Markus", last = "Hauck" } 3 , { first = "Lotte", last = "Rie" } 4 , { first = "Rainer", last = "Zufall" } 5 ] 1 -- Person 2 { first : Text 3 , last : Text 4 } 1 ./people : List ./Person Markus Hauck @markus1189 Dhall: An Introduction 28
• import via url 1 let Person = https://bit.ly/dhall-person-type in 2 let people = https://bit.ly/dhall-people in 3 people : List Person Markus Hauck @markus1189 Dhall: An Introduction 29
• but is that safe? • you can add checksums to imports • dhall freeze to the rescue 1 let Person = https://bit.ly/dhall-person-type 2 sha256:19b699c1ec8b2dc12b75c835db0d2b2faed1a911226146c545c98bb0f1890fca in 3 let people = https://bit.ly/dhall-people 4 sha256:d754b3d6074b056d7ff244c4625e3d55cfcaf0a493eceeed915474946c2ec9b3 in 5 people : List Person Markus Hauck @markus1189 Dhall: An Introduction 30
Alternatives • scenario: what if an import fails • we would like to have a fallback / mirror 1 let Person = https://bit.ly/dhall-person-type ? 2 https://cutt.ly/dhall-person-type in 3 let people = https://bit.ly/dhall-people in 4 people : List Person Markus Hauck @markus1189 Dhall: An Introduction 31
Prelude • Dhall also has a Prelude with lots of functions • import a-la-carte or get everything • https://github.com/dhall-lang/Prelude Markus Hauck @markus1189 Dhall: An Introduction 32
Types As A Schema • need a schema? • use types! 1 { remote : Text 2 , port : Natural 3 , timeout : Optional Natural 4 , consumerKey : Text 5 , accessToken : Text 6 } 1 ./myConfig : ./Schema Markus Hauck @markus1189 Dhall: An Introduction 34
Is Total • total: every program terminates (eventually) • seems like a heavy restriction • some pretty cool optimizations • reduce/optimize programs with partial input possible Markus Hauck @markus1189 Dhall: An Introduction 35
Is Total 1 let Prelude = 2 https://raw.githubusercontent.com/dhall-lang/Prelude/master/package.dhall in 3 4 \(a : Type) -> 5 \(xs : List a) -> 6 \(f : a -> a) -> 7 Prelude.`List`.map a a f xs 1 ./partial.dhall Text ["a", "b", "c"] 2 -- \ (f : Text -> Text) -> [ f "a", f "b", f "c" ] Markus Hauck @markus1189 Dhall: An Introduction 36
To Haskell • have: type checked configuration • need: make values accessible in Haskell • dhall-haskell is the Haskell implementation of Dhall • map Dhall types to Haskell types • the best: also unions and functions! Markus Hauck @markus1189 Dhall: An Introduction 38
To Haskell Dhall Haskell Text Text Natural Natural Integer Int Double Double List a Vector a Optional a Maybe a records ADT unions ADT functions functions Markus Hauck @markus1189 Dhall: An Introduction 39
Persons 1 -- people 2 [ { first = "Markus", last = "Hauck" } 3 , { first = "Lotte", last = "Rie" } 4 , { first = "Rainer", last = "Zufall" } 5 ] Markus Hauck @markus1189 Dhall: An Introduction 40
Persons 1 main :: IO () 2 main = do 3 p <- personInput "../dhall/persons/people" 4 print p 5 -- Results in the following output 6 {- 7 [ Person {personFirst = "Markus", personLast = "Hauck"} 8 , Person {personFirst = "Lotte", personLast = "Rie"} 9 , Person {personFirst = "Rainer", personLast = "Zufall"} 10 ] 11 -} Markus Hauck @markus1189 Dhall: An Introduction 42
Functions • think a moment how cool that is 1 interpolate :: Text -> IO (Text -> Text) 2 interpolate = input auto 1 main2 = do 2 f <- interpolate "\\(t : Text) -> t ++ \"!!!\"" 3 print (f "Hello, World") 4 -- Hello, World!!! Markus Hauck @markus1189 Dhall: An Introduction 45
Formats • unfortunately not everything is in Haskell • lucky: Dhall can be converted into other formats • JSON, YAML, Bash, Cabal, … Markus Hauck @markus1189 Dhall: An Introduction 47
Why Not X??? • JSON • no comments • repetition • annoying to write • no types! • YAML • it has comments! • but: even more awful • think you know YAML? Let’s see Markus Hauck @markus1189 Dhall: An Introduction 48
• very hard to edit, easy to mess up • indentation error does still parse, but differently • 9 different ways to write multiline strings http://bit.ly/2lT0NEd • non-intuitive quoting rules • … Markus Hauck @markus1189 Dhall: An Introduction 52
Gotchas • hard if the target JSON/YAML is unprincipled • most of the translation support is very new • Dhall takes some time getting used to • some documentation missing (you can help!) Markus Hauck @markus1189 Dhall: An Introduction 56
Advantages • get rid of YAML madness • type of your configuration as documentation • know if your config is correct • keep the config DRY and build abstraction • best case: map directly to your Haskell types • use functions, unions and advanced constructs Markus Hauck @markus1189 Dhall: An Introduction 57