How to validate a configuration file
This guide illustrates how you can valid a configuration file’s value (as
opposed to only validating the type) through the use of the assert
keyword.
We’ll begin from the following example configuration type:
{ cpus : Natural
, minReplicas : Natural
, maxReplicas : Natural
}
We would like to not only validate that a configuration file has that type, but we’d also like to validate that the configuration file has the correct value. For example, we might want to verify that:
The
cpus
field can only be a value in between 1 and 4The
minReplicas
field should be less than or equal to themaxReplicas
field
The following complete example shows how to do so:
let Prelude =
https://prelude.dhall-lang.org/v20.2.0/package.dhall
sha256:a6036bc38d883450598d1de7c98ead113196fe2db02e9733855668b18096f07b
let Config =
{ cpus : Natural
, minReplicas : Natural
, maxReplicas : Natural
}
let example
: Config
= { cpus = 2
, minReplicas = 1
, maxReplicas = 3
}
let validate =
\(config : Config) ->
let expected =
{ validCPUs = True
, validReplicas = True
}
let actual =
{ validCPUs =
Prelude.Natural.lessThanEqual 1 config.cpus
&& Prelude.Natural.lessThanEqual config.cpus 4
, validReplicas =
Prelude.Natural.lessThanEqual
config.minReplicas
config.maxReplicas
}
in expected === actual
let _ = assert : validate example
in example
In other words:
Create a validation function that compares expected values against actual values
e.g. the
validate
function in the above exampleThis validation function uses the equivalence operator (i.e.
===
/≡
) to compare the expected and actual values, but omits theassert
keyword, which is deferred until later.If you need to validate multiple values, then compare two records with one field each per validation.
Verify the validation function against the sample configuration using
assert
e.g.
assert : validate example
This validation still occurs at type-checking time because that is when the
assert
keyword is checked, so these sorts of value-level validations can still
be used for ahead-of-time error detection.
If one or more validations fail, the error message will display only the
validations that failed. For example, if we change the cpus
field to 8 then
we would get this error message:
Use "dhall --explain" for detailed errors
Error: Assertion failed
{ validCPUs = - True
+ False
, …
}
37│ assert : validate example
38│
… indicating that the CPU validation failed.
If you are creating a package for others to consume then you can distribute the validation function as part of the package. However, the main limitation of this approach is that you will need to instruct the users of your package to use the validation since the language does not currently support automatically associating a validation with the corresponding type.