wasmedge-hs: Haskell FFI bindings for the WasmEdge 0.18 WebAssembly runtime

[ ffi, language, library, mit, program, webassembly ] [ Propose Tags ] [ Report a vulnerability ]

Comprehensive, type-safe Haskell FFI bindings for the WasmEdge C API.

Features:

  • Full coverage of the WasmEdge 0.18.x C API

  • Two-layer design: raw FFI layer + safe idiomatic public API

  • Phantom-typed ownership model — no double-frees or use-after-free

  • bracket-style resource management compatible with MonadUnliftIO

  • Support for WASI, host functions, async execution, AOT compilation, plugins, Tag instances (exception handling), and lazy JIT mode

  • Property-tested with Hedgehog; unit-tested with HSpec


[Skip to Readme]

Flags

Manual Flags

NameDescriptionDefault
static

Link against libwasmedge statically (requires a static build of WasmEdge).

Disabled

Use -f <flag> to enable a flag, or -f -<flag> to disable that flag. More info

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Candidates

  • No Candidates
Versions [RSS] 0.17.1.0, 0.18.1.0
Change log CHANGELOG.md
Dependencies aeson (>=2.1 && <2.3), async (>=2.2 && <2.3), base (>=4.18 && <5), bytestring (>=0.11 && <0.13), clock (>=0.8 && <0.9), containers (>=0.6 && <0.8), cryptohash-sha256 (>=0.11 && <0.12), deepseq (>=1.4 && <1.6), directory (>=1.3 && <1.4), filepath (>=1.4 && <1.6), resourcet (>=1.3 && <1.4), stm (>=2.5 && <2.6), temporary (>=1.3 && <1.4), text (>=2.0 && <2.2), unliftio-core (>=0.2 && <0.3), vector (>=0.13 && <0.14), wasmedge-hs, wide-word (>=0.1.5 && <0.2) [details]
Tested with ghc ==9.6.6, ghc ==9.8.4, ghc ==9.10.1
License MIT
Copyright 2026 Tushar Adhatrao
Author Tushar Adhatrao
Maintainer tusharadhatrao@gmail.com
Uploaded by tusharad at 2026-11-07T19:12:34Z
Category WebAssembly, FFI, Language
Home page https://github.com/tusharad/wasmedge-hs
Bug tracker https://github.com/tusharad/wasmedge-hs/issues
Source repo head: git clone https://github.com/tusharad/wasmedge-hs.git
Distributions
Executables wasmedge-hs-example-api-suite, wasmedge-hs-example-plugin, wasmedge-hs-example-aot, wasmedge-hs-example-hostfunc, wasmedge-hs-example-hello
Downloads 5 total (5 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs uploaded by user
Build status unknown [no reports yet]

Readme for wasmedge-hs-0.18.1.0

[back to package description]

wasmedge-hs

Type-safe, idiomatic, high-performance Haskell FFI bindings for the WasmEdge WebAssembly runtime.

Build & Test Release Hackage Hackage Deps License: MIT Haddock


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 tag ensures resources are tracked and finalized via GC finalizers or explicit bracket calls.
    • Container-owned elements (such as FunctionInstance added to a ModuleInstance) 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 WebAssembly
  • plugin — Dynamic plugin discovery and module introspection
  • aot — 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


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.