Overriding dhall shemas (dhall-kubernetes example)


#1

My question is really about the general use case of extending/overriding a schema in Dhall.

Here is an illustration with a small example.

Given this schema called ‘Secret.dhall’:

let  k8s = https://raw.githubusercontent.com/dhall-lang/dhall-kubernetes/master/1.17/package.dhall

in  { Type = k8s.Secret.Type
    , default = k8s.Secret.default ⫽ { type = Some "Opaque" }
    }

User can then use Secret.dhall as such:

secrets = Some
        [ Secret::{
          , metadata = oc.core.ObjectMeta::{ name = Some "test.crt" }
          , data = Some (toMap { test = "test"})
          }
        ]

Let’s say I want the consummer of Secret.dhall to go on using all the fields defined by the k8s.Secret.Type.

Let’s say I know user will only care about defining a name in the ObjectMeta field for Secret. What should I do if I wish to provide a more simple abstraction such as:

secrets = Some
        [ Secret::{
          , name = "test.crt"
          , data = Some (toMap { test = "test"})
          }
        ]

I can easily add a ‘name’ Type using Type = k8s.Secret.Type //\\ { name : Text}. The challenge seems to change the metadata field in default. Is this documentation relevant ?

I relate this question to a more general one. Do I need to use a recursive record machinery (as described in the doc) to emulate something like rec { version = "1.1", name = "foo-" + version } from nix ?


#2

So if the goal is for the user to write something similar to this:

let secrets : Optional (List k8s.Secret.Type) =
      Some
        [ Secret::{
          , name = "test.crt"
          , data = Some (toMap { test = "test"})
          }
        ]

… then you will need to wrap either your custom Secret type with a function, like this:

let convertSecret : Secret.Type → k8s.Secret.Type = …

let secrets : Optional (List k8s.Secret.Type) =
      Some
        [ convertSecret Secret::{
          , name = "test.crt"
          , data = Some (toMap { test = "test"})
          }
        ]

#3

Thanks for the input.

How does the (custom) Secret schema look like ?

Do I need to go and see what’s in k8s.Secret, make a copy of it (omitting the metadata field and adding the name field) or is there a way to extend k8s.Secret ?

If I try an extension such as this one:

{ Type = k8s.Secret.Type ⩓ { name : Text }
, default = k8s.Secret.default ⫽ { type = Some "Opaque" }
convertSecret Secret::{
          , name = "test.crt"
          , data = Some (toMap { test = "test"})
          }

won’t compile because metadata is an expected field for Secret.


#4

The following works

{ Type = k8s.Secret.Type ⩓ { name : Text }
, default = k8s.Secret.default ⫽ { type = Some "Opaque", metadata = k8s.ObjectMeta::{=}  }

Not the most ergonomic but I guess there is no way around it.

convertSecret is also awkward:

 let convertSecret
    : Secret.Type → k8s.Secret.Type
    = λ(cfg : Secret.Type) →
        k8s.Secret::{
        , type = cfg.type
        , data = cfg.data
        , stringdata = cfg.stringdata
        , metadata = k8s.ObjectMeta::{ name = Some cfg.name }
        }

I would love a link to some higher level dhall-kubernetes constructs to understand better how to build such abstractions.


#5

@PierreR: Currently the only higher-level constructs are those available in dhall-packages, which you can think of as the Dhall analog of Helm’s charts repository:

… so if you think there is a higher-level construct that would make it more ergonomic to work with dhall-kubernetes that would be the appropriate repository to contribute to


#6

@Gabriel439 Thanks for the pointer. Am I doing something obviously stupid in the example above ? Is there another way to extend schemas (is “copy/pasting from one interface to the other” the only possible approach ?)


#7

@PierreR: The only improvement I can see is that you might be able to simplify the convertSecret function like this:

 let convertSecret
    : Secret.Type → k8s.Secret.Type
    = λ(cfg : Secret.Type) →
        k8s.Secret::(cfg.{type, data, stringdata} // {
            metadata = k8s.ObjectMeta::{ name = Some cfg.name }
          }
        )

#8

@Gabriel439 Thanks !


#9

@PierreR: You’re welcome! :slightly_smiling_face: