
Robert Krook's September 15 post shows how MicroHs runtime graph serialisation lets Haskell spawn remote processes in direct style, shipping code and captured values together and moving some compile-time guarantees to runtime.
I x = x
K x y = x
S f g x = f x (g x)_1 and its definition as :1 encode the recursion:C S C == @ #0 @@ S * @ B _1 @ C - @ #1 @@@@@#1 @ :1ProcessM monad, and spawn starts another one, optionally on a different node. In the example below a parent picks a remote node, spawns a child there, and the child waits for a Ping before answering Pong:data PP = Ping | Pong
program :: ProcessM ()
program = do
s <- self
n <- node
ns <- nodes
let remote = head $ filter (/= n) ns -- assume length ns >= 2
pid <- spawn remote $ do
Ping <- expect
send s Pong
send pid Ping
Pong <- expect
liftIO $ putStrLn "received pong!"s from the scope around it. PP crosses the wire as an ordinary user type, with no serialisation instance written for it. And the process body sits inline where it is spawned, with no closure conversion. 1connect blocks until two nodes have joined. The network is a mesh: when a new node joins one member, that member propagates the connection to the others. Processes register themselves in a per-node registry, after which other processes can address them by name. Failure handling follows Erlang's "let it crash" slogan — a parent monitors a child with TrapExit, receives a ProcessDied message when the child stops, and restarts it. 1gen_server. A server is described by one record:data ServerSpec st callReq castReq reply = ServerSpec
{ setup :: ProcessM st
, handleCall :: st -> callReq -> ProcessM (st, reply)
, handleCast :: st -> castReq -> ProcessM st
, tearDown :: st -> ProcessM ()
}call is the synchronous request that returns a reply, cast is the one that returns nothing, and the generic server owns the receive loop, the message matching and the state transitions. Clients hold a typed Server handle, so the request types a server accepts are checked before anything is sent. The worked example is a counter with Get, Inc and Reset requests, started with startServer on a chosen node. 1ChildSpec carries a name, the process to start, an optional node to pin it to, and a restart policy of Permanent, Transient or Temporary. A SupervisorSpec adds a strategy — OneForOne restarts only the child that stopped, OneForAll restarts the whole set — plus restart limits: at most intensity restarts within period milliseconds, after which the supervisor terminates its children and reports the failure. Two details from the post are worth keeping. A restarted child receives a new process identifier, so a client should ask the supervisor for the current one rather than holding an old handle, and registered names resolve only on the node where the process registered them. 1
fac as a graph: circles are @ application nodes, boxes are combinator leaves, and the red arc is the back-reference that stands for recursion. MicroHs serialises this graph, which is how a function's closure travels with the values it captured. Diagram from Robert Krook's post. 1hSerialize and hDeserialize are the plain pair, and the post says plainly that the deserialising side is unsafe: the caller chooses the result type a, and the serialised graph carries no evidence that the choice is correct. The second pair, hSerializeDescr and hDeserializeDescr, stores a type descriptor next to the graph and compares it with the expected type on the way back in, returning Maybe. Three kinds of value can never cross a node boundary: MVars, threads, and pointers into C code. 1network-light, a small networking package Krook wrote because MicroHs cannot yet compile the standard network package. Both the library and the case study are on GitHub — CloudMicroHaskell and MicroSync — and the paper is in the ACM Digital Library. 1"CloudMicroHaskell reimplements Cloud Haskell on top of MicroHs. Its defining feature is MicroHs's two compiler primitives for serializing and deserializing almost any value."— Robert Krook, CloudMicroHaskell: Direct-Style Distributed Haskell with MicroHs
"This convenience comes with tradeoffs: many errors that Cloud Haskell catches at compile time surface only at runtime in CloudMicroHaskell. Values are also not forced before serialization, so they may be evaluated somewhere other than intended."— Robert Krook, CloudMicroHaskell: Direct-Style Distributed Haskell with MicroHs
"Runtime graph serialisation changes how it feels to write distributed programs in Haskell. A remote process can be expressed as an ordinaryProcessMaction, capture variables from its surrounding scope, and exchange user-defined values without handwritten serialisation or static-closure machinery."— Robert Krook, CloudMicroHaskell: Direct-Style Distributed Haskell with MicroHs
dl.acm.org
github.com
dl.acm.org
This story was produced automatically by a channel. One sentence is all it takes for Neodrop to keep producing for you.