Search code examples
dhall

How to create a dhall schema with arbitrary key but typed values?


In dhall, how can I create a schema for the following yaml?

environment:
    local:
        path: "/path/to/env"
        envvars:
            - var1
            - var2
    stage:
        path: "/path/to/env"
        envvars:
            - var1
            - var2
    prod:
        path: "/path/to/env"
        envvars:
            - var1
            - var2

As you can see, it has arbitrary key ("local", "stage", etc) but same type for the values. I believe this is a fairly common use case, but I didn't see it discussed in the tutorial.


Solution

  • You can use Map, defined in the Prelude and discussed in the Language Tour.

    let Prelude = http://prelude.dhall-lang.org/v16.0.0/package.dhall
    
    let Environment = { path : Text, envvars : List Text }
    
    let EnvironmentSet = { environment : Prelude.Map.Type Text Environment }
    
    in    { environment = toMap
              { local = { path = "/path/to/env", envvars = [ "var1", "var2" ] }
              , stage = { path = "/path/to/env", envvars = [ "var1", "var2" ] }
              , prod = { path = "/path/to/env", envvars = [ "var1", "var2" ] }
              }
          }
        : EnvironmentSet
    

    dhall-to-yaml produces the original YAML (modulo key order):

    % dhall-to-yaml < tmp.dhall
    environment:
      local:
        envvars:
          - var1
          - var2
        path: /path/to/env
      prod:
        envvars:
          - var1
          - var2
        path: /path/to/env
      stage:
        envvars:
          - var1
          - var2
        path: /path/to/env
    

    You can also get rid of much of the boilerplate by defining some functions to create the records and maps.

    let Map = http://prelude.dhall-lang.org/v16.0.0/Map/package.dhall
    
    let Environment = { path : Text, envvars : List Text }
    
    let makeEnv =
            λ(name : Text)
          → λ(path : Text)
          → λ(vars : List Text)
          → Map.keyValue Environment name { path = path, envvars = vars }
    
    let makeEnvironmentSet =
          λ(envs : List (Map.Entry Text Environment)) → { environment = envs }
    
    in  makeEnvironmentSet
          [ makeEnv "local" "/path/to/local" [ "localvar1", "localvar2" ]
          , makeEnv "stage" "/path/to/stage" [ "stagevar1", "stagevar2" ]
          , makeEnv "prod" "/path/to/prod" [ "prodvar1", "prodvar2" ]
          ]
    

    dhall-to-yaml converts a record of type {mapKey: k, mapValue: v} to a YAML object.