Playing the Stooge with Humus
Happy New Year and welcome to 2011. With the coming of the new year, I’m happy to announce the availability of a simulator/debugger environment for Humus, (Note: the 2011 version has been archived).
Please note that this is a simulator of the Humus language written in JavaScript, so it runs very slowly. It is intended to allow experimentation with the language. Let’s try it out with a few examples!
Label behavior
We may as well begin with the traditional “Hello, World” program. Copy the following program into the edit panel on the simulator, then click the “Execute” button.
SEND (#Hello, #World) TO println
This should produce the following output (above the “Execute” button):
#Hello, #World
Each time you click “Execute”, the text in the edit panel is executed, so clicking repeatedly will give you multiple lines of output.
Now let’s establish some re-usable components. We will define an actor behavior which adds a label to a message and sends it to a pre-defined customer. Using that definition, we create a couple of label actors and send a message to each one.
LET label_beh(cust, label) = \msg.[ SEND (label, msg) TO cust ] CREATE R WITH label_beh(println, #Right) CREATE L WITH label_beh(println, #Left) SEND #Hello TO R SEND #World TO L
This should produce the following output:
#Right, #Hello #Left, #World
Definitions and actor creations are persistent (until the browser is refreshed). Once a behavior has been defined, we can clear the edit panel and write new code that uses previous definitions. We can also send messages to any previously-created actors. For example, the println actor is pre-created by the system to provide a simple means of producing output.
Another pre-created actor is random. We can send a request to random consisting of a customer and a range value. The random actor will send the customer a number from 0 to range-1. Clear the edit panel and copy the follow code:
SEND (R, 100) TO random SEND (L, 100) TO random
Click “Execute” a few times and you should see that a new pair of random numbers are generated each time.
Hot Potato
We can use the random service to help create an actor that forwards each messages it receives to one of two other actors chosen at random.
LET hot_potato_beh(left, right) = \msg.[ SEND (choice, 2) TO random CREATE choice WITH \n.[ CASE n OF 0 : [ SEND msg TO left ] 1 : [ SEND msg TO right ] END ] SEND (SELF, msg) TO println ] CREATE hot_potato WITH hot_potato_beh(L, R) SEND 1 TO hot_potato SEND 2 TO hot_potato SEND 3 TO hot_potato
Let’s take a closer look at what’s happening here. We define the behavior function hot_potato_beh, which takes a left and right target. When a message arrives, a request is sent to random to generate either a 0 or 1 value. The customer for this request choice is a new actor that receives the random value n, and sends the message msg to either left or right depending on the value of n. Since choice is created in the context of an actor behavior, a new choice actor is created for each message. In parallel, the actor’s identity and the original message msg are sent to println, so we can watch the action.
Next we create a hot_potato actor, using this behavior, that will forward messages to either L or R, our previously defined label actors. Then we send three messages to hot_potato and observe which way the messages are forwarded. Note that the output may not occur in the order you expect. Since the message sends are processed concurrently, the messages may not arrive in the same order they are sent. Keep in mind that Humus is concurrent by default. Sequencing must be arranged explicitly, where it is required.
Three Stooges
Let’s get a few more actors into the game. We will create the three stooges, Larry, Curly and Moe, each with hot_potato_beh and references to the other two stooges. Then we’ll kick off the fun by sending a message to one of the stooges (we picked Moe).
Caution: Before you execute this code, you will want to take note of the “Halt” button in upper-right corner of the simulator window. This button stops the actor run-time engine. You will need this to interrupt the unbounded flow of messages created by this program.
CREATE Stooges WITH \msg.[ CREATE Larry WITH hot_potato_beh(Curly, Moe) CREATE Curly WITH hot_potato_beh(Moe, Larry) CREATE Moe WITH hot_potato_beh(Larry, Curly) SEND msg TO Moe ] SEND #Potato TO Stooges
The behavior of Stooges is critically dependent on the concurrent execution of each statement in the block, and the automatic resolution of data dependencies, allowing each actor to refer to the others at creation time.
Now consider what would happen if we sent additional messages to Stooges. Each message would create its own instances of Larry, Curly and Moe; and each message would circulate among a distinct set of actors. We could arrange, instead, for the actors to be created once, and additional messages to be injected into the same set of stooges.
LET hot_potato_beh(left, right) = \msg.[ SEND (choice, 2) TO random CREATE choice WITH \n.[ CASE n OF 0 : [ SEND msg TO left ] 1 : [ SEND msg TO right ] END ] SEND (SELF, msg) TO println ] CREATE Stooges WITH \msg.[ CREATE Larry WITH hot_potato_beh(Curly, Moe) CREATE Curly WITH hot_potato_beh(Moe, Larry) CREATE Moe WITH hot_potato_beh(Larry, Curly) BECOME \msg.[ SEND msg TO Moe ] SEND msg TO SELF ] SEND #Potato TO Stooges SEND #Tomato TO Stooges
If you’ve “Halt”ed the actor run-time, you’ll have to reload the simulator page to reset the environment. After the reset, there will be no actors or definitions other than those provided by default, like println and random. You will need to provide all of the relevant definitions together, as shown in the code sample above.
The significant change we’ve made to the behavior of Stooges is that it BECOME
s a new behavior after creating Larry, Curly and Moe. The new behavior simply forwards any message it receives directly to Moe. Note that the initial creation behavior was triggered by receiving a message, so that message is re-sent to SELF
, where it will be received by the same actor after the BECOME
has taken effect. This is a form of lazy initialization. We don’t create the three stooges until there is actually a message for them to handle.
Incremental Enhancements
It’s not really fair that we always send new messages to Moe first. We should arrange to select our target at random. We can use the same technique that we used to choose between left and right in hot_potato_beh.
LET hot_potato_beh(left, right) = \msg.[ SEND (choice, 2) TO random CREATE choice WITH \n.[ CASE n OF 0 : [ SEND msg TO left ] 1 : [ SEND msg TO right ] END ] SEND (SELF, msg) TO println ] CREATE Stooges WITH \msg.[ CREATE Larry WITH hot_potato_beh(Curly, Moe) CREATE Curly WITH hot_potato_beh(Moe, Larry) CREATE Moe WITH hot_potato_beh(Larry, Curly) BECOME \msg.[ SEND (choice, 3) TO random CREATE choice WITH \n.[ CASE n OF 0 : [ SEND msg TO Larry ] 1 : [ SEND msg TO Curly ] 2 : [ SEND msg TO Moe ] END ] ] SEND msg TO SELF ] SEND #Potato TO Stooges SEND #Tomato TO Stooges
If you’ve been executing these samples along the way, you may have noticed that it can be difficult to keep track of who is throwing what. Let’s enhance the output to be more informative. For each hand-off, we want to print which actor is throwing, what is being thrown, and which actor is catching.
LET hot_potato_beh(left, right) = \msg.[ SEND (choice, 2) TO random CREATE choice WITH hand_off_beh(SELF, msg, left, right) ] LET hand_off_beh(source, msg, left, right) = \n.[ LET target = $( CASE n OF 0 : left 1 : right END ) SEND (source, msg, target) TO println SEND msg TO target ] CREATE Stooges WITH \msg.[ CREATE Larry WITH hot_potato_beh(Curly, Moe) CREATE Curly WITH hot_potato_beh(Moe, Larry) CREATE Moe WITH hot_potato_beh(Larry, Curly) BECOME \msg.[ SEND (choice, 3) TO random CREATE choice WITH \n.[ CASE n OF 0 : [ SEND msg TO Larry ] 1 : [ SEND msg TO Curly ] 2 : [ SEND msg TO Moe ] END ] ] SEND msg TO SELF ] SEND #Potato TO Stooges SEND #Tomato TO Stooges SEND #Banana TO Stooges
We’ve extracted the behavior of the hand-off so we can collect all of the information needed in one place. We define target to be the value of either left or right depending on the value of n. Then we can use target to both create our enhanced output and pass along the original message msg. Just for kicks, we’ve also thrown a #Banana
into the party.
Conclusion
With all this produce flying around at random, we’re clearly working with non-deterministic concurrent activities. We could imagine events like these driving an amusing animation showing the three stooges passing these objects back and forth among themselves. As we built up this example, piece by piece, we’ve illustrated some of the interesting characteristics of the Humus language. For full effect, I hope you’ve run these samples in the Humus Simulator/Debugger and observed their behavior directly.
Tags: actor, concurrency, debugging, evaluation, Humus, language, simulation, tutorial
[…] Maybe this will help when your goal is to learn a thing or two about the Actor Model, like me. The original code in Humus, by Dale Schumacher Label behaviour in PHP This is a simple OO implementation with classes for […]