wasmedge-hs
Haskell FFI bindings for the WasmEdge 0.18 WebAssembly runtime
wasmedge-hs
Type-safe, idiomatic, high-performance Haskell FFI bindings for the WasmEdge WebAssembly runtime.
Overview
wasmedge-hs provides a ground-up rework of the Haskell bindings for WasmEdge (supporting 0.17.x and 0.18.x). It is designed following modern Haskell FFI best practices:
- Two-Layer Architecture:
WasmEdge.Internal.FFI.*: Raw low-level C FFI with shim layer handling struct-by-value C ABI.- Safe, idiomatic Haskell interface with standard types (
Text,ByteString, sum types).
- Strong Phantom Ownership:
OwnedCtx tagensures resources are tracked and finalized via GC finalizers or explicit bracket calls.- Container-owned elements (such as
FunctionInstanceadded to aModuleInstance) are automatically disarmed to eliminate double-free bugs.
- Full Feature Coverage:
- WebAssembly execution (one-shot and step-by-step pipeline)
- Bidirectional host functions (calling Haskell functions from WASM)
- Ahead-of-Time (AOT) compilation to native shared objects (
.so/.dylib) - WASI support with arguments, environment variables, and preopened directories
- Non-blocking asynchronous execution via
WasmEdge.Async - Plug-in discovery and loading (e.g.
wasi_nn,wasi_logging) - WasmEdge 0.18+ exception handling and Tag instances
Installation & Prerequisites
1. Install the WasmEdge C runtime
macOS (Homebrew)
brew install wasmedge
Linux
curl -sSf https://raw.githubusercontent.com/WasmEdge/WasmEdge/master/utils/install.sh \
| sudo bash -s -- -v 0.18.1 -p /usr/local
2. Add wasmedge-hs to your project
Cabal (cabal.project / .cabal):
-- your-package.cabal
build-depends: wasmedge-hs >= 0.18.1 && < 0.19
cabal build
cabal test
Stack (stack.yaml):
extra-deps:
- wasmedge-hs-0.18.1.0
# Default build (LTS 22 / GHC 9.6.6)
stack build
stack test
# Or choose a specific GHC / LTS version:
stack --stack-yaml stack-lts-22.yaml test # GHC 9.6.6
stack --stack-yaml stack-lts-23.yaml test # GHC 9.8.4
stack --stack-yaml stack-lts-24.yaml test # GHC 9.10.1
stack --stack-yaml stack-nightly.yaml test # GHC 9.12.x
Quick Start
1. Execute a WebAssembly Function
{-# LANGUAGE OverloadedStrings #-}
import qualified Data.ByteString as BS
import WasmEdge.VM
import WasmEdge.Types
-- Minimal WebAssembly module implementing: add(a: i32, b: i32) -> i32
addWasm :: BS.ByteString
addWasm = BS.pack
[ 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00 -- magic + version
, 0x01, 0x07, 0x01, 0x60, 0x02, 0x7F, 0x7F, 0x01, 0x7F -- type: (i32, i32) -> i32
, 0x03, 0x02, 0x01, 0x00 -- function section
, 0x07, 0x07, 0x01, 0x03, 0x61, 0x64, 0x64, 0x00, 0x00 -- export "add"
, 0x0A, 0x09, 0x01, 0x07, 0x00, 0x20, 0x00, 0x20, 0x01, 0x6A, 0x0B -- body
]
main :: IO ()
main = do
result <- withVM Nothing Nothing $ \vm ->
runWasmBytes vm addWasm "add" [WasmI32 40, WasmI32 2]
case result of
Right [WasmI32 ans] -> putStrLn $ "Result: 40 + 2 = " ++ show ans -- 42
Left err -> putStrLn $ "Error: " ++ show err
2. Export Haskell Functions to WebAssembly
{-# LANGUAGE OverloadedStrings #-}
import WasmEdge.VM
import WasmEdge.Types
import WasmEdge.Function
import WasmEdge.Module
haskellAddOne :: CallingFrame -> [WasmVal] -> IO (WasmResult [WasmVal])
haskellAddOne _ [WasmI32 n] = pure $ Right [WasmI32 (n + 1)]
haskellAddOne _ args = pure $ Right args
main :: IO ()
main = do
withFunctionType [I32] [I32] $ \fnType ->
withFunction fnType haskellAddOne $ \hostFn -> do
hostMod <- buildHostModule "host_module" $ \b ->
builderAddFunction b "add_one" hostFn
withVM Nothing Nothing $ \vm -> do
Right () <- registerModuleFromImport vm hostMod
res <- runWasmBytes vm guestWasm "call_host" [WasmI32 99]
print res -- Right [WasmI32 100]
For typed host functions (no manual WasmVal unpacking), use builderAddHostFunc:
buildHostModule "host_module" $ \b -> do
builderAddHostFunc b "add" $ \(x :: Int32) (y :: Int32) -> pure (x + y) :: IO Int32
See cabal run wasmedge-hs-example-hostfunc for the complete, runnable example.
3. Ahead-Of-Time (AOT) Compilation
import WasmEdge.Compiler
import WasmEdge.Configure
import WasmEdge.Types (OptimizationLevel (..))
compileToNative :: IO ()
compileToNative = do
withConfigure $ \cfg -> do
setCompilerOptions cfg (defaultCompilerOptions { compOptLevel = OptO3 })
withCompiler (Just cfg) $ \compiler -> do
res <- compileFile compiler "input.wasm" "output.so"
case res of
Right () -> putStrLn "Compiled successfully to output.so"
Left err -> putStrLn $ "Compilation failed: " ++ show err
Examples
Run any of the included examples:
# Cabal
cabal run wasmedge-hs-example-hello
cabal run wasmedge-hs-example-hostfunc
cabal run wasmedge-hs-example-plugin
cabal run wasmedge-hs-example-aot
# Stack
stack run wasmedge-hs-example-hello
hello— Minimal WASM execution (add two i32 values)hostfunc— Calling Haskell callbacks from WebAssemblyplugin— Dynamic plugin discovery and module introspectionaot— AOT compilation of bytecode to optimized native code
Benchmarks
Microbenchmarks with Criterion:
cabal run wasmedge-hs-bench --enable-benchmarks
| Benchmark Scenario | Mean Latency |
|---|---|
| Pre-loaded Function Execution | ~380 ns |
One-shot WASM Execution (runWasmBytes) | ~26 μs |
Step-by-step Pipeline (load/validate/instantiate/exec) | ~26 μs |
VM Lifecycle (createVM/deleteVM) | ~23 μs |
Supported GHC Versions
| GHC | Stackage LTS | Status |
|---|---|---|
| 9.6.6 | LTS 22 | ✅ Tested |
| 9.8.4 | LTS 23 | ✅ Tested |
| 9.10.1 | LTS 24 | ✅ Tested |
| 9.12.x | Nightly | 🧪 Experimental |
Documentation
- Haddock API Docs (GitHub Pages)
- Hackage Package Page
- Architecture Design
- Backwards Compatibility Guidelines
- Maintenance & Upgrades
- Contributing Guide
- Changelog
Branching Strategy
| Branch | Purpose |
|---|---|
main | Stable, release-tagged. Only release commits land here. |
develop | Integration branch. All PRs target develop first. |
feat/* | Feature branches — merge into develop via PR. |
fix/* | Bug-fix branches — merge into develop via PR. |
chore/* | Infrastructure, docs, CI changes. |
Releases are created by merging develop → main, then pushing an annotated tag v<version>.
Contributing
See docs/CONTRIBUTING.md for the full guide. Quick start:
git clone https://github.com/tusharad/wasmedge-hs
cd wasmedge-hs
brew install wasmedge # macOS
stack build
stack test
License
MIT License. Copyright (c) 2026 Tushar Adhatrao. See LICENSE for details.
- base >= 4.18 && < 5
- bytestring >= 0.11 && < 0.13
- text >= 2.0 && < 2.2
- wide-word >= 0.1.5 && < 0.2
- vector >= 0.13 && < 0.14
- containers >= 0.6 && < 0.8
- unliftio-core >= 0.2 && < 0.3
- resourcet >= 1.3 && < 1.4
- deepseq >= 1.4 && < 1.6
- aeson >= 2.1 && < 2.3
- 0.18.1.0
- 0.17.1.0