From b3226eacd913efb26b9d5175b6781422a9131af7 Mon Sep 17 00:00:00 2001 From: Hassan Abedi Date: Tue, 24 Mar 2026 13:40:02 +0100 Subject: [PATCH] WIP --- haskell/notes/001-garnet.md | 167 ++++++++---------------------------- 1 file changed, 36 insertions(+), 131 deletions(-) diff --git a/haskell/notes/001-garnet.md b/haskell/notes/001-garnet.md index 59da5f0..c281312 100644 --- a/haskell/notes/001-garnet.md +++ b/haskell/notes/001-garnet.md @@ -1,153 +1,58 @@ -# Garnet findings +# Garnet Findings Date: 2026-03-24 Path inspected: `./tmp/garnet` -## Plain-English summary +## Summary -`./tmp/garnet` is a small experiment for calling Rust from Haskell. +`./tmp/garnet` is a proof-of-concept for Rust/Haskell interop. -The flow is: +The repo is testing a specific pipeline: -1. Define Rust functions and data types. -2. Export them as a C ABI. -3. Generate a C header with `cbindgen`. -4. Generate Haskell-side low-level bindings with `hs-bindgen`. -5. Add hand-written Haskell wrappers on top. -6. Run a demo executable to show the setup works. +Rust exports a C ABI -> `cbindgen` emits a header -> `hs-bindgen` produces Haskell bindings -> hand-written Haskell wrappers clean up the API. -This looks like a prototype or reference repo, not a finished product. +It succeeds as a demo. It does not read as production-ready infrastructure. -## What it is +## What It Contains -This is a mixed Haskell/Rust FFI demo. +- `./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. -It contains: +## Review -- a Cabal package named `garnet` -- a Rust static library in `./tmp/garnet/rust` -- Haskell modules for raw bindings and higher-level wrappers -- a small executable that exercises the API -- a shell build script -- a Nix flake for the development environment +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. -## What it is trying to prove +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. -The repo appears to be testing whether this toolchain is practical: +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. -Rust -> `cbindgen` -> C header -> `hs-bindgen` -> Haskell wrappers +## Pros -It also tests whether the approach can handle more than trivial examples, including: +- 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. -- C strings -- plain structs -- enums -- simple arithmetic -- recursive tree-shaped data +## Cons -## Key files +- 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. -- `./tmp/garnet/rust/lib.rs` - - Rust definitions for exported FFI functions and example types -- `./tmp/garnet/rust/build.rs` - - generates `garnet_rs.h` and patches it for compatibility with `hs-bindgen` -- `./tmp/garnet/lib/GarnetRs/Raw.hs` - - drives low-level binding generation from the generated header -- `./tmp/garnet/lib/GarnetRs/Wrapped.hs` - - adds Haskell-friendly wrapper types and conversion code -- `./tmp/garnet/exe/Main.hs` - - demo executable using the wrapper API -- `./tmp/garnet/build` - - custom build script for Rust + Cabal coordination +## Status -## Easy reading of the design +Current status: working prototype. -### Rust side +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. -The Rust library exports a handful of example FFI functions: - -- `hello` -- `hello_struct` -- `hello_shape` -- `add` -- `sum_tree` - -These are not a product API. They are examples chosen to stress different interop cases. - -### Haskell side - -The Haskell code is split into two layers: - -- `GarnetRs.Raw` - - low-level generated bindings -- `GarnetRs.Wrapped` - - nicer Haskell-facing types and functions - -This separation is a good design choice. It keeps generated code concerns away from the public API. - -## Critical assessment - -This repo is technically promising, but clearly unfinished. - -What looks good: - -- It demonstrates the full end-to-end workflow. -- It goes beyond the easiest possible FFI examples. -- It keeps raw and wrapped APIs separate. -- It includes a dev shell, which improves reproducibility. - -What looks weak: - -- The build flow is manual and somewhat brittle. -- The project depends on pinned git sources and alpha-stage tooling. -- The Rust build script contains a workaround for upstream tooling issues. -- The Haskell wrapper layer still requires manual boilerplate. -- There is no obvious sign of tests, CI, or a stability story. - -## Pros of the approach - -- **Clear architecture**: raw bindings and ergonomic wrappers are separated well. -- **Good exploration value**: useful for learning the actual friction points in Rust/Haskell interop. -- **Covers realistic cases**: includes enums and recursive data, not just simple integers. -- **Small and understandable**: the repo is compact enough to inspect quickly. -- **Useful as a reference**: someone exploring `hs-bindgen` could learn from it. - -## Cons of the approach - -- **Custom build glue**: the shell script and symlink step suggest the toolchain is not yet smooth. -- **Dependency fragility**: pinned git dependencies increase maintenance burden. -- **Tooling immaturity**: the repo already needs local workarounds for upstream issues. -- **Manual wrapper overhead**: generated bindings do not remove the need for hand-written API cleanup. -- **Potential safety complexity**: FFI, pointer conversions, and `unsafe` code are easy to get wrong. -- **Weak production story**: this setup does not yet look ready for long-term team use. - -## Status of the work - -My assessment of current status: - -- **Stage**: proof of concept / exploration -- **Scope**: enough code exists to show the full path works -- **Maturity**: decent as an experiment, weak as a reusable foundation -- **Stability**: uncertain because of upstream pins and workarounds -- **Readiness**: not production-ready - -In simple terms: this looks like “we got it working” rather than “we finished the system.” - -## Likely next problems if this grows - -If someone tried to turn this into a serious integration layer, the next hard problems would likely be: - -- making the build process less custom -- reducing wrapper boilerplate -- keeping generated headers and bindings in sync -- handling more complex Rust types safely -- upgrading upstream tooling without breakage -- adding tests that catch FFI regressions early - -## Bottom line - -`./tmp/garnet` is a useful and interesting interop prototype. - -It succeeds as a demonstration that Rust -> C header -> Haskell bindings -> Haskell wrapper can work. But it also shows the current cost of that path: manual build glue, unstable dependencies, wrapper code, and reliance on tool-specific workarounds. - -Overall: good experiment, useful reference, not mature infrastructure yet. +In reviewer terms: promising experiment, useful reference, not ready to trust as infrastructure.