Difference between revisions of "Applications and libraries/Interfacing other languages/Erlang"
(simple tutorial on using the Erlang FFI)
m (removed invalid link)
|Line 1:||Line 1:|
== Overview ==
== Overview ==
The [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/erlang Haskell/Erlang-FFI]
The [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/erlang 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 ==
== Theory of Operation ==
Latest revision as of 18:35, 16 November 2011
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)]
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 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.
Erlang's fundamental abstraction is an asynchronous message send. In Haskell that's:
mboxSend mbox node pid msg
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
Left pid or
Right name (
mboxSelf returns the Pid for the given
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.
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
rpcCast mbox node module function arguments
The library also provides a set of wrappers for making calls to Mnesia.
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.
To complete the items listed in the previous section. :)