[ToC]
Professor Carl Hewitt published the famous paper Actor model of computation in 1974. In the thesis, he elaborates that:
An Actor is a computational entity that, in response to a message it receives, can concurrently:
- send a finite number of messages to other Actors;
- create a finite number of new Actors;
- designate the behavior to be used for the next message it receives.
With the rise of multi-core computing and large-scale distributed systems, the Actor Model is becoming increasingly important because of its natural concurrent, parallel, and distributed.
An actor in Hamler/Erlang is defined as a process, which works like an OS process. Each process has its own memory, composed of a mailbox, a heap, a stack and a process control block(PCB) with information about the process.
Processes in Erlang are very lightweight. We can create millions of processes on a running Erlang virtual machine.
A process is identified by a Pid. Other processes can send messages to a process via Pid.
import Prelude
go :: Process ()
go = do
self <- getSelf
pid <- spawn loop
pid ! (self, :ping)
receive
:pong -> println "Pong!"
pid ! :stop
loop :: Process ()
loop =
receive
(from, :ping) -> do
println "Ping!"
from ! :pong
loop
:stop -> return ()
In Hamler, a new process is created via the spawn
functions, which are defined in Control.Process.Spawn
module.
-- | Create a process
spawn :: forall a. IO a -> Process Pid
-- | Create and link a process
spawnLink :: forall a. IO a -> Process Pid
-- | Create and monitor a process
spawnMonitor :: forall a. IO a -> Process (Pid, Ref)
go :: Process ()
go = do
pid <- spawn recv
pid ! :msg
recv :: Process ()
recv = receive x -> printf "recv: %s" (showAny x)
go :: Process ()
go = do
pid <- spawn selectiveRecv
pid ! :bar
pid ! :foo
selectiveRecv :: Process ()
selectiveRecv = do
receive :foo -> println "foo"
receive :bar -> println "bar"
go :: Process ()
go = do
pid <- spawn recvAfter
pid ! :foo
recvAfter :: Process ()
recvAfter =
receive
:bar -> println "recv bar"
after
1000 -> println "timeout"
A process can be registered under a name, which has an Atom type.
import Control.Process (register, whereis, unregister)
go :: Process ()
go = do
pid <- spawn proc
register :name pid
res <- whereis :name
unregister :name
proc :: Process ()
proc = receive _ -> return ()
Two processes can be linked to each other.
import Prelude
import Control.Process (killProc, trapExit)
go :: forall a. Process a
go = do
trapExit true
pid <- spawn (receive _ -> return ())
link pid
killProc pid
receive msg -> return msg
One process Pid1
can monitor another process Pid2
. If Pid2
terminates, Pid1
will receive a 'DOWN' message from Pid2
.
import Prelude
import Control.Process (killProc)
go :: forall a. IO a
go = do
pid <- spawn proc
ref <- monitor pid
killProc pid
receive msg -> return msg
proc :: Process ()
proc = receive _ -> return ()
import Prelude
import Control.Process (isAlive, exitProc)
-- Exit the current process
exit :normal
go :: Process Boolean
go = do
pid <- spawn (receive _ -> return ())
exitProc pid :reason
isAlive pid