Haskue: exploring CUE evaluation in Haskell

Hi everyone,

I would like to share a project I have been working on for a while: Haskue, an experimental Haskell implementation of part of the CUE configuration language.

CUE is a configuration language based on constraints and unification.

I started Haskue partly to develop my Haskell skills further through a larger and more challenging project. I was also interested in the similarities between CUE’s value lattice and Haskell’s type system, even though they work differently. I wanted to explore how CUE’s evaluation model could be implemented in Haskell and make it easier to understand why evaluation produces a certain result.

For example:

database: {
  host: string
  port: >=1 & <=65535
}
database: {
  host: "db.example.com"
  port: 5432
}

Haskue combines these declarations into one value. It can also explain which constraints produced a result:

haskue eval example.cue -e database.port --explain
database.port = 5432

Conjuncts:
├─ >=1        example.cue:3:9
├─ <=65535    example.cue:3:15
└─ 5432       example.cue:7:9

One important influence on the evaluator was Build Systems à la Carte, written by Andrey Mokhov, Neil Mitchell, and Simon Peyton Jones. The paper says:

Excel is a build system in disguise.

This idea also helped me understand CUE. A value node is similar to a build target, constraints decide its result, and references form a dependency graph. Some reference targets are only known after other values are evaluated, so Haskue builds and updates this graph during evaluation. From this point of view, CUE can also be seen as a build system in disguise.

The evaluator takes ideas from both Excel and Shake. If a dependency is not ready, Haskue can try the evaluation again later. It can also handle some reference cycles and avoid more work when a value has not changed.

Haskue can export results as CUE, JSON, or YAML. It is still experimental rather than a drop-in replacement for the official CUE implementation; the current limitations are documented in the README.

I would be happy to hear your feedback.

Thanks for taking a look!

5 Likes

I have never used CUE and only dumped random examples in your playground and change them a bit, but this looks really cool. Seems like a task that is well-suited for Haskell?

1 Like

Is Haskue a drop-in replacement for CUE (even though only a subset is supported) or could it diverge at some point?

Thank you for trying the playground!

CUE is a really interesting configuration language. At first, its graph unification model seemed strange to me, and I was surprised that it could be used to write practical configurations.

I think Haskell fits some parts of the project well. I wanted to use a high-level language so that the codebase could stay small and understandable. Modeling CUE’s value lattice with Haskell’s algebraic data types felt natural, and pattern matching made the unification code clear. Refactoring feels safe, although large refactors can take a while.

I have not spent much time optimizing the code or algorithms. Even so, in the single-file tests I have tried, Haskue is only slightly slower than the official CUE implementation, which is written in Go.

The hard part is that much of my previous experience with design, implementation, debugging, and tooling did not transfer easily to Haskell, so development has taken longer than I expected.

In general, I think Haskell is a good fit for this project.

1 Like

The goal is for Haskue to follow the CUE specification. I plan to continue implementing the missing features.

Another goal is to make CUE evaluation easier to debug. --explain is the first tool for this. In the future, Haskue could provide more debugging commands, APIs, or an optional Haskue-specific debugging package. They should not change the behavior of standard CUE code.

1 Like