2026-03-24 13:40:02 +01:00
|
|
|
# Garnet Findings
|
2026-03-24 09:50:06 +01:00
|
|
|
|
|
|
|
|
Date: 2026-03-24
|
|
|
|
|
Path inspected: `./tmp/garnet`
|
|
|
|
|
|
2026-03-24 13:40:02 +01:00
|
|
|
## Summary
|
2026-03-24 09:50:06 +01:00
|
|
|
|
2026-03-24 13:40:02 +01:00
|
|
|
`./tmp/garnet` is a proof-of-concept for Rust/Haskell interop.
|
2026-03-24 09:50:06 +01:00
|
|
|
|
2026-03-24 13:40:02 +01:00
|
|
|
The repo is testing a specific pipeline:
|
2026-03-24 09:50:06 +01:00
|
|
|
|
2026-03-24 13:40:02 +01:00
|
|
|
Rust exports a C ABI -> `cbindgen` emits a header -> `hs-bindgen` produces Haskell bindings -> hand-written Haskell wrappers clean up the API.
|
2026-03-24 09:50:06 +01:00
|
|
|
|
2026-03-24 13:40:02 +01:00
|
|
|
It succeeds as a demo. It does not read as production-ready infrastructure.
|
2026-03-24 09:50:06 +01:00
|
|
|
|
2026-03-24 13:40:02 +01:00
|
|
|
## What It Contains
|
2026-03-24 09:50:06 +01:00
|
|
|
|
2026-03-24 13:40:02 +01:00
|
|
|
- `./tmp/garnet/rust/lib.rs` - Rust FFI examples: strings, structs, enums, arithmetic, recursive trees.
|
|
|
|
|
- `./tmp/garnet/rust/build.rs` - header generation plus a local patch for `hs-bindgen` compatibility.
|
|
|
|
|
- `./tmp/garnet/lib/GarnetRs/Raw.hs` - low-level binding generation from the generated header.
|
|
|
|
|
- `./tmp/garnet/lib/GarnetRs/Wrapped.hs` - Haskell-facing wrapper types and conversions.
|
|
|
|
|
- `./tmp/garnet/exe/Main.hs` - demo executable that exercises the bridge.
|
|
|
|
|
- `./tmp/garnet/build` - custom build glue for Cargo and Cabal.
|
2026-03-24 09:50:06 +01:00
|
|
|
|
2026-03-24 13:40:02 +01:00
|
|
|
## Review
|
2026-03-24 09:50:06 +01:00
|
|
|
|
2026-03-24 13:40:02 +01:00
|
|
|
The project makes the intended architecture clear. Separating raw bindings from the wrapper layer is the right call, and the examples are broad enough
|
|
|
|
|
to be useful. This is more than a toy "add two numbers" demo.
|
2026-03-24 09:50:06 +01:00
|
|
|
|
2026-03-24 13:40:02 +01:00
|
|
|
That said, the repo still looks early. The build is custom, the dependency stack is pinned to git revisions, and the Rust build script contains a
|
|
|
|
|
workaround for upstream tooling issues. That combination is acceptable in an experiment, but it is a weak base for a maintained integration layer.
|
2026-03-24 09:50:06 +01:00
|
|
|
|
2026-03-24 13:40:02 +01:00
|
|
|
The biggest limitation is that code generation does not remove the hard part. The Haskell side still needs manual wrapper code to turn raw bindings
|
|
|
|
|
into something ergonomic. The repo proves that the pipeline works, but it also shows that the pipeline is not yet cheap.
|
2026-03-24 09:50:06 +01:00
|
|
|
|
2026-03-24 13:40:02 +01:00
|
|
|
## Pros
|
2026-03-24 09:50:06 +01:00
|
|
|
|
2026-03-24 13:40:02 +01:00
|
|
|
- The architecture is clean enough to follow.
|
|
|
|
|
- The examples cover realistic FFI shapes, not just trivial functions.
|
|
|
|
|
- The split between generated and hand-written code is sensible.
|
|
|
|
|
- The repo is small enough to serve as a reference implementation.
|
2026-03-24 09:50:06 +01:00
|
|
|
|
2026-03-24 13:40:02 +01:00
|
|
|
## Cons
|
2026-03-24 09:50:06 +01:00
|
|
|
|
2026-03-24 13:40:02 +01:00
|
|
|
- The build flow is manual and brittle.
|
|
|
|
|
- The setup depends on alpha-stage or pinned upstream tooling.
|
|
|
|
|
- The local header patch is a red flag for maintainability.
|
|
|
|
|
- The wrapper layer still carries noticeable manual boilerplate.
|
|
|
|
|
- There is no obvious sign of tests, CI, or a long-term stability plan.
|
2026-03-24 09:50:06 +01:00
|
|
|
|
2026-03-24 13:40:02 +01:00
|
|
|
## Status
|
2026-03-24 09:50:06 +01:00
|
|
|
|
2026-03-24 13:40:02 +01:00
|
|
|
Current status: working prototype.
|
2026-03-24 09:50:06 +01:00
|
|
|
|
2026-03-24 13:40:02 +01:00
|
|
|
It appears far enough along to prove the interop path and demonstrate the tooling choices. It does not appear far enough along to support repeated
|
|
|
|
|
reuse without more work on build integration, test coverage, and dependency stability.
|
2026-03-24 09:50:06 +01:00
|
|
|
|
2026-03-24 13:40:02 +01:00
|
|
|
In reviewer terms: promising experiment, useful reference, not ready to trust as infrastructure.
|
2026-03-24 14:04:37 +01:00
|
|
|
|
|
|
|
|
## Ecosystem Maturity
|
|
|
|
|
|
|
|
|
|
Rust/Haskell interop is uneven.
|
|
|
|
|
|
|
|
|
|
The low-level foundation is mature enough. Haskell has had a solid FFI story for years, and Rust is comfortable exporting or importing a C ABI.
|
|
|
|
|
If the boundary is narrow and C-like, the approach is viable.
|
|
|
|
|
|
|
|
|
|
The weak part is the tooling layer. Header generation on the Rust side is serviceable. Binding generation on the Haskell side is improving, but it
|
|
|
|
|
still looks early. The Garnet repo reflects that gap exactly: the ABI path works, but the workflow still needs custom glue and hand-written cleanup.
|
|
|
|
|
|
|
|
|
|
Reviewer verdict: mature enough for disciplined teams, not mature enough to feel easy.
|
|
|
|
|
|
|
|
|
|
## Calling Direction
|
|
|
|
|
|
|
|
|
|
### Calling Rust From Haskell
|
|
|
|
|
|
|
|
|
|
This is the easier direction.
|
|
|
|
|
|
|
|
|
|
Rust can expose a conventional C ABI, and Haskell can consume it through its normal FFI mechanisms. That keeps the ownership model and runtime story
|
|
|
|
|
relatively simple, at least compared with the reverse direction.
|
|
|
|
|
|
|
|
|
|
This is the path Garnet takes, and it is the right first choice.
|
|
|
|
|
|
|
|
|
|
### Calling Haskell From Rust
|
|
|
|
|
|
|
|
|
|
This is the harder direction.
|
|
|
|
|
|
|
|
|
|
Once Rust starts calling into Haskell, the GHC runtime becomes part of the design. That raises the complexity around runtime initialization,
|
|
|
|
|
threading, callbacks, shutdown, and operational correctness. It is possible, but it is a sharper tool and a worse default.
|
|
|
|
|
|
|
|
|
|
Reviewer verdict: call Rust from Haskell when possible; only call Haskell from Rust when there is a strong reason.
|
|
|
|
|
|
|
|
|
|
## Toolchain Recommendation
|
|
|
|
|
|
|
|
|
|
Current best default:
|
|
|
|
|
|
|
|
|
|
- keep the ABI small and C-shaped
|
|
|
|
|
- use Rust `extern "C"` plus `repr(C)` at the boundary
|
|
|
|
|
- use `cbindgen` to emit headers
|
|
|
|
|
- keep Haskell bindings thin
|
|
|
|
|
- write the final Haskell API by hand
|
|
|
|
|
|
|
|
|
|
That is not glamorous, but it is the most credible path today.
|
|
|
|
|
|
|
|
|
|
For teams that want the lowest risk, hand-written Haskell FFI bindings are still easier to trust than a large generated surface. For teams exploring
|
|
|
|
|
automation, `hs-bindgen` is interesting but still reads as early-stage technology.
|
|
|
|
|
|
|
|
|
|
## Practical Read On Garnet
|
|
|
|
|
|
|
|
|
|
Garnet is valuable because it shows both sides of the story.
|
|
|
|
|
|
|
|
|
|
It shows that the basic interop path works. It also shows that the surrounding tooling is not yet smooth enough to disappear into the background.
|
|
|
|
|
That makes it a good experiment and a fair warning.
|