MyNixOS website logo
Description

Extism bindings.

Bindings to Extism, the universal plugin system

Extism Haskell Host SDK

This repo contains the Haskell package for integrating with the Extism runtime.

Note: If you're unsure what Extism is or what an SDK is see our homepage: https://extism.org.

Documentation

Documentation is available at https://hackage.haskell.org/package/extism

Installation

Install the Extism Runtime Dependency

For this library, you first need to install the Extism Runtime. You can download the shared object directly from a release or use the Extism CLI to install it.

Add the library to dune

Then add extism to your cabal file:

library
  build-depends: extism

Getting Started

This guide should walk you through some of the concepts in Extism and the Haskell bindings.

Creating A Plug-in

The primary concept in Extism is the plug-in. You can think of a plug-in as a code module stored in a .wasm file.

Since you may not have an Extism plug-in on hand to test, let's load a demo plug-in from the web:

module Main where
import Extism

main = do
  plugin <- unwrap <$> newPlugin (manifest [wasm]) [] True
  res <- unwrap <$> call plugin "count_vowels" "Hello, world!"
  putStrLn res
  where
    wasm = wasmURL "GET" "https://github.com/extism/plugins/releases/latest/download/count_vowels.wasm"

-- Prints: {"count":3,"total":3,"vowels":"aeiouAEIOU"}"

Note: See the Manifest docs as it has a rich schema and a lot of options.

This plug-in was written in Rust and it does one thing, it counts vowels in a string. As such, it exposes one "export" function: count_vowels. We can call exports using Extism.call:

All exports have a simple interface of bytes-in and bytes-out. This plug-in happens to take a string and return a JSON encoded string with a report of results.

This library also allowes for conversion of input/outputs types using FromBytes and ToBytes

Plug-in State

Plug-ins may be stateful or stateless. Plug-ins can maintain state b/w calls by the use of variables. Our count vowels plug-in remembers the total number of vowels it's ever counted in the "total" key in the result. You can see this by making subsequent calls to the export:

ghci> unwrap <$> call plugin "count_vowels" "Hello, world!"
{"count":3,"total":9,"vowels":"aeiouAEIOU"}
ghci> unwrap <$> call plugin "count_vowels" "Hello, world!"
{"count":3,"total":12,"vowels":"aeiouAEIOU"}

These variables will persist until this plug-in is freed or you initialize a new one.

Configuration

Plug-ins may optionally take a configuration object. This is a static way to configure the plug-in. Our count-vowels plugin takes an optional configuration to change out which characters are considered vowels. Example:

ghci> let manifest = manifest [wasm]
ghci> plugin <- unwrap <$> newPlugin manifest [] True
ghci> res <- (unwrap <$> call plugin "count_vowels" "Yellow, world!" :: String)
ghci> res
{"count":3,"total":3,"vowels":"aeiouAEIOU"}

ghci> plugin <- withConfig (manifest [wasm]) [("vowels","aeiouyAEIOUY")] ;;
ghci> res <- (unwrap <$> call plugin "count_vowels" "Yellow, world!" :: String)
ghci> res
{"count":4,"total":4,"vowels":"aeiouAEIOUY"}

Host Functions

Let's extend our count-vowels example a little bit: we can intercept the results and adjust them before returning from the plugin using a hello_worldhost function with wasm/code-functions.wasm

Host functions allow us to grant new capabilities to our plug-ins from our application. They are simply some OCaml functions you write which can be passed down and invoked from any language inside the plug-in.

Using Extism.HostFunction.hostFunction we can define a host function that can be called from the guest plug-in.

In this example, we want to expose a single function to our plugin (in Haskell types): hello_world :: String -> String which will intercept the original result and replace it with a new one.

Let's load the manifest like usual but load up wasm/code-functions.wasm plug-in:

{-# LANGUAGE DeriveDataTypeable #-}

module Main where

import Extism
import Extism.HostFunction
import Extism.JSON
import Extism.Manifest (manifest, wasmFile)

newtype Count = Count {count :: Int} deriving (Data, Typeable, Show)

hello currPlugin msg = do
  putStrLn . unwrap <$> input currPlugin 0
  putStrLn "Hello from Haskell!"
  putStrLn msg
  output currPlugin 0 (JSON $ Count 999)

main = do
  setLogFile "stdout" LogError
  f <- newFunction "hello_world" [ptr] [ptr] "Hello, again" hello
  plugin <- unwrap <$> newPlugin m [f] True
  id <- pluginID plugin
  print id
  JSON res <- (unwrap <$> call plugin "count_vowels" "this is a test" :: IO (JSON Count))
  print res
  where
    m = manifest [wasmFile "wasm/code-functions.wasm"]
-- Prints: Count {count = 999}

Note: In order to write host functions you should get familiar with the methods on the Extism.HostFunction module.

Metadata

Version

1.2.1.0

Platforms (75)

    Darwin
    FreeBSD
    Genode
    GHCJS
    Linux
    MMIXware
    NetBSD
    none
    OpenBSD
    Redox
    Solaris
    WASI
    Windows
Show all
  • aarch64-darwin
  • aarch64-genode
  • aarch64-linux
  • aarch64-netbsd
  • aarch64-none
  • aarch64_be-none
  • arm-none
  • armv5tel-linux
  • armv6l-linux
  • armv6l-netbsd
  • armv6l-none
  • armv7a-darwin
  • armv7a-linux
  • armv7a-netbsd
  • armv7l-linux
  • armv7l-netbsd
  • avr-none
  • i686-cygwin
  • i686-darwin
  • i686-freebsd
  • i686-genode
  • i686-linux
  • i686-netbsd
  • i686-none
  • i686-openbsd
  • i686-windows
  • javascript-ghcjs
  • loongarch64-linux
  • m68k-linux
  • m68k-netbsd
  • m68k-none
  • microblaze-linux
  • microblaze-none
  • microblazeel-linux
  • microblazeel-none
  • mips-linux
  • mips-none
  • mips64-linux
  • mips64-none
  • mips64el-linux
  • mipsel-linux
  • mipsel-netbsd
  • mmix-mmixware
  • msp430-none
  • or1k-none
  • powerpc-netbsd
  • powerpc-none
  • powerpc64-linux
  • powerpc64le-linux
  • powerpcle-none
  • riscv32-linux
  • riscv32-netbsd
  • riscv32-none
  • riscv64-linux
  • riscv64-netbsd
  • riscv64-none
  • rx-none
  • s390-linux
  • s390-none
  • s390x-linux
  • s390x-none
  • vc4-none
  • wasm32-wasi
  • wasm64-wasi
  • x86_64-cygwin
  • x86_64-darwin
  • x86_64-freebsd
  • x86_64-genode
  • x86_64-linux
  • x86_64-netbsd
  • x86_64-none
  • x86_64-openbsd
  • x86_64-redox
  • x86_64-solaris
  • x86_64-windows