Concurrency in Io
I was pretty interested in this area but the coverage in the book was fairly shallow (concurrency will likely be the focus of the erlang chapter). Io's concurrency tools are coroutines, futures and actors. Io's vm also employs cooperative multitasking (as opposed to pre-emptive multitasking), which does make some kinds of reasoning about what going to happen in your program easier, you don't have to constantly deal with the fact that your threads of execution can be interrupted at any time. Instead Io coroutines explicitly yield control to other coroutines in order to get work done. Lets look at some code to make this clearer.
The above code is identical to the code from day 2, except for the last two lines. We have added @@ to the delayMsgs method call, and have added a wait(10) at the end. By prepending @@ to a message, it means execute this message asynchronously and returns nil immediately. Behind the scenes the Me object becomes an actor and starts processing the delayMsgs message in its coroutine. In fact all objects in Io are actors and can process any messages sent to them in an independent coroutine (one per object). Pretty neat!
What if we want to get a return value from an asynchronous method call? That is where futures come in, a future is an object that stands in for the return value of an async call. When the call actually returns a value, the future becomes that value, it is completely transparent. If the future is accessed before the async call has finished, the currently executing coroutine will block and yield to the next coroutine in the pool. To get a future just use @ instead of @@ in the method call as in the example below.
response := URL with("http://example.com/") @fetch #returns immediately and response is a future #you can do whatever you want here. response println #if the http request is not complete, this coroutine will yield until is completes.
I wrote a more substantial program in the form of a solution to the Sleeping Barber problem. This code also lives on github.
The solution is modelled using three objects, one for the barber, one for the waiting room and one that is cloned to represent each customer. The code should hopefully be straightforward to follow. The important thing to note is that each customer object runs in their own coroutine, and the calls to cutHair are asynchronous.
Because actors keep a queue for all the async messages sent to them, once a customer has gotten a seat in the waiting room, they can request a haircut and then sit back and relax. The customer coroutine blocks on the future returned and simply waits until the Barber has had a chance to deal with them, yielding to other coroutines to try to schedule their haircuts (as well as for the Barber to actually cut some hair).
This example is one where I feel futures make for a very natural programming flow in comparison to something like callbacks. Overall the code seems much simpler than what would be needed if we were using mutexes and semaphores. It seems practically identical to what one would write if there was no need for concurrency. In fact I did a little experiment and took all the @ symbols out (just 4 by the way) and the program worked just fine, albeit sequentially!
There are a few other aspects of coroutines and actors that I am haven't quite cracked, for example I tried to have the loop where the Barber checks whether to go back to sleep, live in the Barber object and tried to use the resume method in order to yield to another method in the same object (else the loop was constantly yielding to other coroutines and the barber never did any actual work!). However I couldn't get that to work.
Also I am not completely certain what the interactions are between synchronous and asynchronous method calls to the same object. It could be that synchronous calls execute in the coroutine of the message sender, but I'm not entirely sure (and haven't been motivated enough to check). Nonetheless I felt I was able to get some useful mileage in my exploration of Io's concurrency mechanisms. I really like how easy Io makes it to make do work asynchronously and coordinating between coroutines is fairly painless.
I really really enjoyed programming in Io (and spent longer than I expected in this chapter), I like the clean syntax and the small language feel, which I think makes it easy to learn. There aren't too many different concepts in Io but they come together well and the language feels extremely flexible. Admittedly I haven't written anything particularly large so I don't know if the 'lack' of syntax is disadvantageous as the amount of code one has to read increases, but at the moment I find it to be pretty clear. Its been a blast and I look forward to playing around with Prolog.