# 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: ```sh make haskell-build ``` Run the Haskell executable that calls Rust: ```sh make haskell-run ``` Run the Rust executable that calls the Haskell foreign library: ```sh make rust-calls-haskell ``` You can also run the commands manually: ```sh 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.