Question: Record to Map convertion


#1

Context

In dhall-kubernetes you often have types such as List { mapKey : Text, mapValue : Text }. For instance this is the type of the hard field in ResourceQuotaSpec

Objectif

Let’s say you want to provide a more safe interface using such a record:

{ Type =
    { `requests.cpu` : Text
    , `requests.memory` : Text
    , `limits.cpu` : Optional Text
    , `limits.memory` : Text
    }
, default =
  { `requests.cpu` = "1"
  , `requests.memory` = "1Gi"
  , `limits.cpu` = None Text
  , `limits.memory` = "2Gi"
  }
}

How would you go to convert this record to a map ?

Possible solution

Something like:

let toQuotaMap
    : Quota → List { mapKey : Text, mapValue : Text }
    = λ(config : Quota) →
        let cpuLimit =
              merge
                { None = [] : List { mapKey : Text, mapValue : Text }
                , Some = λ(limit : Text) → toMap { `limits.cpu` = limit }
                }
                config.`limits.cpu`

        in    cpuLimit
            # toMap
                { `requests.cpu` = config.`requests.cpu`
                , `requests.memory` = config.`requests.memory`
                , `limits.memory` = config.`limits.memory`
                }
in 
λ(config : Quota) →
{
hard = Some (toQuotaMap config)
}

Question

Is there a more idiomatic way to do this ?

Thanks :wink:


#2

This is a short incomplete answer, but you might like to know that

can be written more succintly using the projection operator:

config.{`requests.cpu`,`requests.memory`,`limits.memory`}

#3

I’ve had a bit more time to write a fuller answer. I think I’d extract an ifPresent function like this:

let ifPresent
    : ∀(fieldName : Text) →
      Optional Text →
        List { mapKey : Text, mapValue : Text }
    = λ(fieldName : Text) →
      λ(field : Optional Text) →
        merge
          { None = [] : List { mapKey : Text, mapValue : Text }
          , Some =
              λ(value : Text) → [ { mapKey = fieldName, mapValue = value } ]
          }
          field

let toQuotaMap
    : Quota.Type → List { mapKey : Text, mapValue : Text }
    = λ(config : Quota.Type) →
          ifPresent "limits.cpu" config.`limits.cpu`
        # toMap config.{ `requests.cpu`, `requests.memory`, `limits.memory` }

let myQuota = Quota::{ `limits.cpu` = Some "5" }

in  toQuotaMap myQuota

This ifPresent utility is flexible enough to be used with arbitrary fields, although I don’t love that you can introduce subtle bugs by giving the wrong field name.


#4

@philandstuff thanks for your help !