integrations/haskell/README.md

2.0 KiB

Haskell Interop Demo

This directory contains a small Haskell project for the repository's Rust/Haskell integration work.

The project demonstrates both directions:

  • Haskell calling into Rust through a C ABI exposed by the Rust crate.
  • Rust calling back into Haskell through a Cabal foreign-library, with explicit GHC RTS initialization.

Layout

  • src/Interop/Shared.hs - pure shared logic plus the C-compatible struct layout used at the boundary.
  • src/Interop/Exports.hs - Haskell functions exported to C for Rust to call.
  • app/RustClient.hs - Haskell imports for the Rust C ABI.
  • app/Main.hs - Haskell executable that demonstrates the Haskell -> Rust path.
  • test/Spec.hs - small pure tests that avoid crossing the FFI boundary.

The code keeps the FFI surface small on purpose. The boundary uses:

  • integers
  • a fixed C-shaped struct
  • owned C strings with an explicit free function on each side

That is enough to demonstrate the main challenges without pulling in code generation or large bindings.

Build And Run

From the repository root:

make haskell-build

Run the Haskell executable that calls Rust:

make haskell-run

Run the Rust executable that calls the Haskell foreign library:

make rust-calls-haskell

You can also run the commands manually:

cargo build --lib
cabal build --project-file=haskell/cabal.project all
cabal run --project-file=haskell/cabal.project haskell-calls-rust -- Ada 7 5
cargo run -- rust-calls-haskell Ada 7 5

What This Demonstrates

  • The boundary must stay C-shaped. Rich Rust and Haskell types do not cross directly.
  • Strings need explicit ownership rules. Each side exports its own free function.
  • Struct layout must be mirrored carefully on both sides.
  • Rust calling Haskell is the harder direction because it must initialize and shut down the GHC runtime correctly.
  • Build order is part of the design. Haskell links against the Rust static library, and Rust loads the Haskell foreign library after Cabal builds it.