Playing with making an Actor-like distributed system in REBOL #2
June 2nd, 2009I had this code sample sitting on my disc for weeks, but didn’t find time to post it. There are few advances from #1, but it’s still focused on just playing, trying out stuff. I am also posting this because REBOL 3 will have concurrency capabilities but it’s not fully determined yet of what kind and on what level. Maybe this goofing around can provide some food for thoughts to the subject.
Fancier act: act-match
I am not the expert but I think Erlang has native pattern matching and message/structure deconstruction build into the language. This comes very handy when actor’s are selecting and deconstructing messages to act on them. I didn’t go anywhere near same depth as Erlang but I built few functions to make simple match/deconstruct more elegant. It was around 10 lines of code to make, REBOL’s “code is just data” is the enabler here. I added act-match callback.
Something like this before in #1 post …
act: [ msg: take mbox
switch first msg [
watch-for-cats [ print "local dog: I am! wof!" ]
go-home [ print "ok, bye!" remove-me ]
]
]
… would with act-match now look like this:
act-match: [
[ 'watch-for-cats ] [ print "local dog: I am! wof!" ]
[ 'go-home ] [ print "ok, bye!" remove-me ]
]
But it doesn’t just compare. It also deconstructs the message and sets the words. Something which can be now done with absolute elegance would be quite messy before:
act-match: [
[ 'log type description ] [ write/append %log reduce [ now type description ] ]
]
What happens here? ‘log is a REBOL’s lit-word (literal) and it acts as a filter (it must match the lit-word in message, similar like I think atoms are used in Erlang). type and description are words and values passed in message get bound to them so you can use those directly in the code block.
~send and ~spawn
Previous function send-msg was so nimble looking. Erlang has the cool looking PID ! Message. To get some visual que I decided to name send-msg into ~send and to add ~spawn.
~send is simple and obvious (it can send a message any actor on this or another computer accessible over the network):
~send address [ 'log "warning" "guest user is about to launch the nukes!" ]
~spawn is also no big deal (it can spawn an actor on this or another computer)
~spawn address (
make actor [ act-match: [ [ 'check-website url ] [ print [ "checking website:" url ] ] ] ]
) bookman "website-checker"
address — is the address of the node (the actor address without the actor id)
( make actor [ ... ] ) — is the same code you would use locally to create an actor
bookman — is address of actor to notify when our actor is created (more about this later)
“website-checker” — is the name under which you want to know the actor
Four basic actor prototypes
Actors are just rebol objects. Rebol objects work like objects work in prototype oriented OO languages. So you could create your actors by extending any of them. These 4 actors are itself just actors that extend the base actor prototype. Basically they are really simple to understand, no magic or anything. This is now the core actor prototype:
raw-actor: make object! [ mode: 'raw mbox: copy [] act: copy [] ]
It has something called a mode, a mbox (mailbox – like any good actor) and act. To create our super fancy default actor with act-match we need only this:
actor: make raw-actor [ mode: 'mbox act: [ match-do take mbox act-match ] act-match: copy [] ]
match-do is a quite simple function that does the “magic”. See it in the files if you are interested.
We have timer-actor which is the same as in post #1 and is a little longer. The new prototype is the once-actor, it does it’s act only once and then removes itself. Code is also minimal and extends the timer-actor.
once-actor: make timer-actor [ act-once: copy [] act-timer: [ do act-once mode: 'remove ] ]
The director
~spawn might look like some special core function but in fact it’s just an simple utilization of the system below. You could make your own ~spawn or director in the same way that you make anything else when you utilize the system.
Let’s first say something more about the director. Director is just an actor like any other actor you create, but we made it because it can be generally useful. This is the simple code of it:
director: make actor [
act-match: [
[ 'spawn actor notify ident ] [
~send notify compose [ 'actor-spawned (ident) (my-actor-addr (add-local-actor actor)) ]
]
[ 'kill id ] [ kill-actor id ]
]
]
Let’s disect it a little. As you can see it acts on two types of messages.
First message is like this [ 'spawn actor notify ident ]. An atom (to use the Erlang term) 'spawn and then 3 values that get bind to the names listed. The expression add-local-actor actor adds received actor to the local system and returns it’s id. my-actor-addr takes that id and creates an actor address. The outer code just sends a 'actor-created message to notify address, with ident spawner gave it (so it can recognize it) and address of the actor so spawner can store it to call it later. The other message director can act on is just to kill the actor.
More about ~spawn
So directoris just an ordinary actor, and what is ~spawn then. Spawn is just an one-liner that sends a message to the director on the target node:
~spawn: func [ addr actor notify name ] [ ~send addr compose [ 'spawn-actor (actor) (notify) (name) ] ]
More in #3
This post is getting long so I will continue in #3. I presented you the basic building blocks I made so far. In #3 I will show a example how to use these and few more functions to make something like a simple master+workers distributed system. I will also post full source code next time.
—
By “playing” and “goofing around” I mean I am playing with ideas, without any fear of being wrong or doing stupid things. I am not trying to build a solid system here, just experiment with this stuff a little.

![Reblog this post [with Zemanta]](http://img.zemanta.com/reblog_e.png?x-id=37607563-ce5e-42a3-ac37-39207c43ef8d)