Applications and libraries/Interfacing other languages/Erlang

From HaskellWiki
Jump to navigation Jump to search

Overview

The Haskell/Erlang-FFI enables full bi-directional communication between programs written in Haskell and Erlang. Message sends from Haskell to Erlang just look like function calls (of course), and messages from Erlang to Haskell are delivered to MVars.

Theory of Operation

Because everything interesting that happens in Erlang happens as a result of sending a message, all that is required to fully interoperate with Erlang is to be able to send and receive messages using its native wire protocol. There are similar packages that allow Erlang to interoperate with programs written in C, Java, Clojure, Scheme, Emacs Lisp, Python, and Ruby. This Haskell library is distantly derived from the Emacs Lisp package Distel.

Erlang types are represented in Haskell with the ErlType data type. See the Haddock docs for details. To use this package you will sometimes have to get down to the level of ErlType, but most of the time you can work with native Haskell types that are instances of the Erlang typeclass, and the conversion will be done for you. For example:

ghci> toErlang [("a", 1), ("b", 2)]
ErlList [ErlTuple [ErlString "a",ErlBigInt 1],ErlTuple [ErlString "b",ErlBigInt 2]]
ghci> fromErlang $ ErlList [ErlTuple [ErlString "a",ErlBigInt 1],ErlTuple [ErlString "b",ErlBigInt 2]] :: [(String, Int)]
[("a",1),("b",2)]

Getting Started

Before you can do anything with the Erlang FFI, you minimally need to start up the Erlang Port Mapper Daemon (epmd). The simplest way to do that is to start an Erlang node on the local machine.

Having started Erlang, we can now create a Haskell node. The Self data type represents a Haskell node. You need to instantiate one of these per Haskell process, passing it the network name of the node:

self <- createSelf "haskell@localhost"

The next step is to create an MBox. An MBox has a unique identifier and corresponds to Erlang's notion of a "process". You'll probably want one MBox per thread that needs to communicate with Erlang:

mbox <- createMBox self

You are now ready to talk to Erlang.

Low-Level Communication

Erlang's fundamental abstraction is an asynchronous message send. In Haskell that's:

mboxSend mbox node pid msg

where mbox was created earlier, node is the name of the Erlang node (e.g., "erlang"), and msg is any data item that has an instance of Erlang so that it can be serialized.

Pid is slightly more complicated. In Erlang you can address messages either to "process ids" (pids) that are opaque and not knowable a-priori, or to registered process names. On the Haskell side, we represent this with Either, so Left pid or Right name (mboxSelf returns the Pid for the given MBox):

mboxSend mbox "erlang" (Right "echo") (mboxSelf mbox, "Hello, Erlang!")

Receive messages addressed to your "process" with:

msg <- mboxRecv mbox

You will generally initiate communication to a registered name, at which time you may receive a Pid for later use.

High-Level Communication

In a real Erlang program, low-level message sends are not used for the bulk of the work. Most of the interesting things in Erlang are part of the OTP (Open Telecom Platform) libraries, and these implement higher-level protocols on top of message sends.

The most important of these protocols is gen_server. When talking to a process that implements the gen_server protocol, you can either "call" or "cast" to it (in addition to still being able to do low-level message sends). A call is a two-way faux-synchronous request/response:

reply <- genCall mbox node pid msg

A cast is a one-way notification:

genCast mbox node pid msg

One instance of gen_server in particular is very useful: the RPC server "rex". Send rex a message containing a module name, function name, and a list of arguments, and it will (synchronously or asynchronously) call the named function in the named module, passing it the arguments supplied, and optionally returning the results to you. The RPC server gives you access to nearly all of Erlang:

reply <- rpcCall mbox node module function arguments

or

rpcCast mbox node module function arguments

The library also provides a set of wrappers for making calls to Mnesia.

Not Implemented

The Erlang FFI is a work in progress, and it has some shortcomings. As of this writing, it does not yet register itself with epmd. This means that, even though Erlang can call into Haskell, Haskell must initiate first contact with the Erlang node. (Otherwise Erlang simply doesn't know where the Haskell node is on the network).

A larger issue is that the FFI does not yet implement process linking. Two processes are "linked" in Erlang if one is notified when the other terminates, and linking is the primary mechanism for handling and/or propagating errors in an Erlang system. This is in-progress and should be completed soon. Until it is done, this library is best suited for situations where Haskell is consuming Erlang services. Erlang can't yet reliably consume Haskell services because there is no error notification.

Future Plans

To complete the items listed in the previous section. :)