Search code examples
dhall

Is it possible to create YAML from _within_ a dhall expression?


I would like to generate a ConfigMap for a service using dhall-kubernetes. The service is configured using a YAML file. I can use dhall to create the config in two passes:

$ dhall-to-yaml < server.dhall > server.yaml
$ dhall-to-yaml < configmap.dhall 
apiVersion: v1
data:
  server.yaml: |
    server:
      port: 8080
kind: ConfigMap
metadata:
  name: my-configmap

server.dhall:

let _config = { server.port = 8080 } in _config

configmap.dhall:

let kubernetes =
      ./package.dhall sha256:d9eac5668d5ed9cb3364c0a39721d4694e4247dad16d8a82827e4619ee1d6188

let _configMap1 =
      kubernetes.ConfigMap::{
      , metadata = kubernetes.ObjectMeta::{ name = "my-configmap" }
      , data = Some
        [ { mapKey = "server.yaml", mapValue = ./server.yaml as Text } ]
      }

in  _configMap1

package.dhall

https://raw.githubusercontent.com/dhall-lang/dhall-kubernetes/master/package.dhall

Is it possible to use only one invocation of dhall-to-yaml by generating the YAML from within dhall? Something like this:

let kubernetes =
      ./package.dhall sha256:d9eac5668d5ed9cb3364c0a39721d4694e4247dad16d8a82827e4619ee1d6188

let _config = { server.port = 8080 }

let _configMap3 =
      kubernetes.ConfigMap::{
      , metadata = kubernetes.ObjectMeta::{ name = "my-configmap" }
      , data = Some
        [ { mapKey = "server.yaml"
          , mapValue = makeYAML _config -- make this work 
          }   
        ]   
      }   

in  _configMap3

I found the JSON.renderYAML function from the Prelude but this does not seem to be exactly what is needed.


Solution

  • To generate YAML using Prelude.JSON.renderYAML, you first need to convert your config to the input Prelude.JSON.Type type. See the makeJSON function included in my version of makeYAML below:

    let Prelude = https://prelude.dhall-lang.org/v15.0.0/package.dhall
    
    let J = Prelude.JSON
    
    let Config = { server : { port : Natural } }
    
    let makeJSON
        : Config → J.Type
        =   λ(c : Config)
          → J.object
              ( toMap
                  { server = J.object (toMap { port = J.natural c.server.port }) }
              )
    
    let makeYAML
        : Config → Text
        = λ(c : Config) → J.renderYAML (makeJSON c)
    
    let example =
          let _config
              : Config
              = { server.port = 8080 }
    
          in    assert
              :   makeYAML _config
                ≡ ''
                  "server":
                    "port": 8080
                  ''
    
    in  makeYAML