Tool that allows to write Erlang NIF libraries in Haskell
Haskell Erlang
Latest commit 16ec9c8 Jun 14, 2013 @urbanserj 0.1.1
Permalink
Failed to load latest commit information.
example Initial commit Jan 20, 2013
hs_src/Foreign/Erlang Add import for enif_consume_timeslice (R16) Jun 14, 2013
src
.gitignore
LICENSE
README.md
rebar.config Initial commit Jan 20, 2013

README.md

Hsnif allows to write Erlang NIF libraries in Haskell.

Intro

Hsnif consists of two parts:

  • Rebar plugin that compilates Haskell code to shared library
  • Haskell library which is an interface to functions and types of Erlang NIF library

Rebar plugin

Rebar plugin implements compile and clean commands.

To add rebar plugin to a new project, add following lines to rebar.config:

{deps, [
  {hsnif, ".*", {git, "https://github.com/urbanserj/hsnif.git", "master"}}
]}.
{plugin_dir, "deps/hsnif/src"}.
{plugins, [hsnif]}.

For specifying Target, Source and Compilation options (the last is optional) these lines need to be added:

{hsnif_spec, [
  {"priv/target.so", "hs_src/Source.hs", [
    {cflags, ["-O"]},
    {ldflags, []}
  ]}
]}.

Haskell code

All exported from Source file functions will be NIF functions, and each of them should satisfy the following criteria:

  • Each function's argument and return value should be an instance of the class ErlTerm (see below)
  • First argument is optional, it should be ErlNifEnv

Example:

id :: ErlNifTerm -> ErlNifTerm
sum :: Int -> Int -> Int
reverse :: ErlNifEnv -> ErlNifTerm -> IO ErlNifTerm
tratata :: ErlNifEnv -> IO ErlNifTerm

Foreign.Erlang.Nif

This haskell library is a part of hsnif and it is an interface to functions and types of Erlang NIF library.

To convert between Erlang and Haskell types class ErlTerm is used. Instance of the class ErlTerm must implement two functions: toErlNifTerm (haskell to erlang term convertation) and fromErlNifTerm (vice versa).

class ErlTerm a where
  toErlNifTerm :: ErlNifEnv -> a -> IO ErlNifTerm
  fromErlNifTerm :: ErlNifEnv -> ErlNifTerm -> IO a

Following instances already exist in the Foreign.Erlang.Nif library:

ErlTerm Char
ErlTerm Double
ErlTerm Int32
ErlTerm Int64
ErlTerm Word32
ErlTerm Word64
ErlTerm ()
Integral a => ErlTerm a
ErlTerm CStringLen
ErlTerm CString
ErlTerm ErlAtom
ErlTerm ErlNifBinary
ErlTerm ErlNifTerm
ErlTerm a => ErlTerm [a]
ErlTerm a => ErlTerm (IO a)
ErlTerm a => ErlTerm (ErlTuple a)
ErlTerm (ErlBinary String)
ErlTerm (ErlBinary CStringLen)
(ErlTerm a, ErlTerm b) => ErlTerm (a, b)
(ErlTerm a, ErlTerm b, ErlTerm c) => ErlTerm (a, b, c)
(ErlTerm a, ErlTerm b, ErlTerm c, ErlTerm d) => ErlTerm (a, b, c, d)

To create a new instance of the class ErlTerm for arbitrary type add an instance for this type to source file.

Example:

import Data.ByteString
import Foreign.C.String

instance ErlTerm (ByteString) where
  toErlNifTerm env x =
    useAsCStringLen x $ \cstr ->
    toErlNifTerm env (ErlBinary cstr)
  fromErlNifTerm env x = do
    ErlBinary cstr <- fromErlNifTerm env x :: IO (ErlBinary CStringLen)
    packCStringLen cstr

onLoad and onUnload

You can specify two optional functions onLoad and onUnload in the source file. These functions will be called on loading and on unloading the module respectively and should be one of the following types:

onLoad :: ErlNifEnv -> Ptr (Ptr ()) -> IO ErlNifTerm
onLoad :: ErlNifEnv -> IO ErlNifTerm
onLoad :: Ptr (Ptr ()) -> IO ErlNifTerm
onLoad :: IO ErlNifTerm

onUnload :: ErlNifEnv -> Ptr () -> IO ()
onUnload :: ErlNifEnv -> IO ()
onUnload :: Ptr () -> IO ()
onUnload :: IO ()

Look for semantics of these functions in Erlang NIF documentation.