Fair enough. What CUE seems to offer compared to a static data language like JSON:
- Modularity. Thanks to the merge operation, you can write separate, modular blocks that are then combined into a configuration.
- Validation. CUE has a particularly well-behaved type system that allows to conveniently specify, combine and verify data schemas. In that category, CUE’s approach really stands out.
However, CUE is very restricted computationally. It is on purpose: first, to follow the philosophy that configuration is not general purpose programming and that it ought to stay close to just filling out options with values. The second reason is that it allows the type and merge system of CUE to be particularly well-behaved in return: types and values live in the same world, they can be freely combined in an associative and commutative way. CUE can generate inhabitants (i.e. examples of well-typed values) of a type, can eliminate redundant conditions, and more.
The consequence of this restricted computational power is that it doesn’t seem adapted to generating data from other data, and at reducing boiler-plate in configurations in general. Concretely, you can’t define nor use functions in the general sense (though you can encode simple ones to some extent). Something as simple as mapping over a list to produce a new value is not possible, for example. This is specific to CUE: Dhall (although not Turing-complete), Jsonnet, Nix and Skylark all allow some form of computations to take place. CUE has something called the scripting layer, that can do computations and perform side-effects (like creating files, querying the network, or whatever), but it is a different language and run in a strictly separated phase.
The approach of Nickel is to try to offer sane and simple defaults so that you have to only leverage complexity when you need it. Writing a simple configuration is like writing JSON. Doing a bit of validation using pre-defined contracts (think data validators) can almost feel like CUE (although not as well-behaved). Most recursion should be achieved with standard combinators like map
, fold
, etc. without resorting to recursive functions explicitly, like you can do in Dhall for example. But when you need it, you have the full power of a programming language at your disposal. For Nix, build systems in general, and probably other applications, you may have to perform parsing, to generate build graphs dynamically, etc.: in these cases, you may reach the limits of something like CUE, leading to dirty hacks at best, or to changing the whole technology stack at worst.
In conclusion, Nickel and CUE make different trade-offs with respect to the computational expressiveness: CUE is validation-oriented and very restricted computationally, while Nickel (and Nix, and others) offers more power to generate and transform data, in exchange of more complexity.