dhall-haskell
dhall-haskell copied to clipboard
Pretty Print Builtin
Note: I added this to dhall-haskell although possibly? it belongs under dhall-lang or dhall-kuberentes ¯\_(ツ)_/¯
The following usecase and example outlines how I'd like to pretty-print fully normalized expressions as Text
literals within Dhall. This is desirable because I'd like to configure Haskell services using Dhall - but these configurations must be passed as Text
to dhall-kubernetes ConfigMap
type. To workaround this currently, multiple invocations of dhall
must be run, which is error prone.
Usecase
Below is an expression which results in a list of dhall-kubernetes
types corresponding to Kubernetes resources, such as a Haskell Deployment
s and the respective Dhall configuration for the Haskell application which is stored in a ConfigMap
:
-- Config.dhall
{ name : Text, role : Text, domain : Text }
-- my-application-config.dhall
let Config = ./Config in { name = "foo", role = "bar", domain = "foo.bar.com" } : Config
-- kuberenetes-config.dhall
let k8s = ./k8s/package.dhall -- from dhall-kubernetes
{-
This could be whatever is considered valid configuration for the deployed application.
Assume that it is a large or nested expression that is tedious to write manual
pretty-printers for everytime we come up with a new configuration type, where
the printer is of type : Config -> Text
Alternatively it could be a partially applied function, so the printer would need
to be of type : (Foo -> Config) -> Text
-}
let Config = ./Config.dhall
let manifests : Config -> List k8s.typeUnion
= \(config : Config) ->
{-
The configMap should contain the fully normalized dhall configuration,
as this file alone is copied to the host independent of any imports, etc.
-}
let configMap =
k8s.ConfigMap::{
, data = toMap { `my-application-config` = "${Expr/print config}"
}
{-
The pod containers in the deployment mount configMap above as a file
and the applications reads the file contents when the container starts.
-}
let deployment =
k8s.Deployment::{
..
}
in [ k8s.typeUnion.ConfigMap configMap
, k8s.typeUnion.Deployment deployment
]
in manifests
Reality
The current workaround to make the above example work would be to replace the use of Config
with Text
in kubernetes-config.dhall
, removing any semblance of typing:
dhall <<< ./my-application-config.dhall > my-application-config.normalized
dhall-to-yaml <<< ./kubernetes-config.dhall (./my-application-config.normalized as Text) | \
kubectl apply -f -
And hope that you didn't stuff it up and accidentally deploy the non-normalized configuration which still has relative imports, etc. otherwise your container will explode when it tries to read the configuration file.
Pretty Printing Builtin
Regarding a hypothetical Expr/print
builtin. At a very cursory glance it seems possible to write a normalizer for this:
let normalizer (App (Var "Expr/print") expr) = TextLit [] (pretty expr)
Whether or not the above is a good idea or not is moot because we cannot write a type-checker, as we've no idea what the expected type might be.
In case it wasn't clear this is more of a 'how do I do this?' question, rather than a 'I think this is good/necessary' suggestion.
As one counterargument - Expr/print
gives rise to ad-hoc overloading of other */show
builtins by way of Expr -> Text
. print
or pretty
could be considered sufficiently different in intent, maybe.
@brendanhay: Probably the most direct route would be to build a custom Dhall interpreter to supply your own built-ins. For more details on how to do that, see:
https://docs.dhall-lang.org/howtos/How-to-add-a-new-built-in-function.html
You also might be interested in https://github.com/dhall-lang/dhall-lang/issues/800 which is a related proposal to add a standard way to declare custom built-ins (so that standard tools like the dhall
or dhall-lsp-server
can still work with code that depends on custom built-ins).
Another possible route would be to upstream related functionality directly into the language standard. The main issue I can think of with that is that if there were a new built-in for rendering Dhall expressions, it would most likely need to render them as binary (i.e. the standard CBOR encoding) rather than Text
. The reason why is that there is no standard way to render/display Dhall expressions as Text
(even unformatted expressions). If there were a built-in to encode to binary that would entail an additional Binary
type and ways to convert that Binary
type to Text
(such as base64 encoding).
@Gabriel439 Thanks. I've previously added builtins for other purposes but it just ends up being untenable due to the reimplementation of not just the entire dhall
binary, but dhall-to-yaml/json
as well - which have a bunch of their internals un-exported, etc.
Regarding the standard + rendering as binary all makes sense, although I'm hesitant to push for this unless someone else steps forward and has a similar need for using Dhall to render Dhall sub-configurations.
As an example of groundwork needed to realistically implement such a builtin I've thrown together #1613