Scala vs Clojure - Round 2: Concurrency!

2009-09-17 12:51:47

The time for ignoring concurrency has almost passed. If you read my last post you'll know that I'm looking at Scala Vs Clojure and a big part of both of these languages is their support for concurrency. Scala uses an Erlang inspired actor model, which is a distributed approach to concurrency. Clojure on the other hand leans on it's STM, a non-distributed approach - Lets put them in the ring together!

The goal

My last post sparked heavy debate in the #scala camp, wherein many frustrations and concerns were expressed by the Scala community. Many responded by publicly flaming me, accusing me of lying etc etc. Before I start this post let me point out to the Scala community that you should welcome reviews not dread them, open up for a discussion rather than trying to silence it. I'll try to be fair and shed light on both sides in return I expect you to leave a comment if you feel there's something that I left out - Let the goal be a healthy debate on concurrency not a flamewar which nobody benefits from.

The problem

An old concurrency problem/exercise is 'The sleeping barber' and you can either read the full definition here or read my shorter version here:

We simulate a barber-shop with 1 barber, 3 waiting chairs and 1 chair for haircutting.

The barber sleeps between customers and is thus awoken when one enters the shop. If customers come in while he's working they take a seat, if no seats are available they leave.

This is a perfect problem for comparing these two languages for the following reasons

So we can finally put Clojure at a disadvantage and see if we can leverage its power to find a good solution anyway. If you read the Wikipedia link describing the challenge, you'll notice that they point to manual locks as a solution, but anyone who's ever worked with locks wants run away from those! Lets examine 2 escape routes:

Actors

Scala's Actor model is heavily inspired by Erlang. It's a distributed method which means that you have various agents working on their individual tasks/data/responsibilities and all they share is basically interfaces - You don't need to know what's under the hood. These Actors talk by way of messages:

scala> Redford ! "Go act!"

Now Redford will receive the message "Go act!" - That message lands in Redford's queue which he walks through one by one, possibly doing some pattern-matching allowing him to act different to this String than he would to a float for instance.

Software Transactional Memory

Clojure uses an STM which is analogous to a database. To access shared memory the Clojure programmers can use mutable references to immutable data - an indirection. Mutable state being the root of all evil bugs must be contained in a thread-safe fashion: Enter the STM. To manipulate the references we wrap them in transactions. These guarantee that our threads get a consistent view of the world by setting values atomically. Closure's STM implementation uses snapshot isolation to achieve this - in effect all reader-threads get their own snapshot of the 'database' when they work, that also means, no reader-locks. In the following code look for the dosync macro, which contains code which runs in a transaction.

Pitfalls of the Barber problem

If you think that the Barber problem looks simple, odds are you haven't tried to implement it. There are quite a few things which can go wrong when we have several agents working on shared data, primarily

A deadlock happen when 2 threads are waiting for each other to finish - the result being something like

When two trains approach each other at a crossing, both shall come to a full stop and neither shall start up again until the other has gone.

Race conditions are a result of programs using the 4.th dimension: Time. In the barber shop example we have 3 chairs for waiting customers. So in order to seat a customer, we could do something like this:

if (seats-taken <_ strong="strong" number-of-seats="number-of-seats">then seat_customer else reject_customer

Seems simple enough, but imagine that there is 1 free seat and 2 threads ask at the exact same time if they can seat their customer. Both will get permission. How many seats are then taken? 4 out of 3. If we are to do successful concurrency this must never be allowed to happen.

Let's do an experiment!

Like I mentioned in the preface, O'reilly has already forged an idiomatic solution to this problem, which you can read about: here. I've also built an implementation in Clojure which gets the same result, so lets present them. On the left hand side you have Clojure, weighing in at 31 lines, on the right Scala weighing in at 102 lines

(update: If you're interested in Functional Programming odds are that you've heard of CGrand. He's contributed a 28 line version which is more correct and I'd say more idiomatic, please see it: here)

(def queue  (ref (with-meta
                   clojure.lang.PersistentQueue/EMPTY
                   {:tally 0})))
(def seats  3)

(defn debug
  [_ msg n]
  (println msg (apply str (repeat (- 35 (count msg)) \space)) n)
  (flush))

(defn the-shop
  [a]
  (debug "(c) entering shop" a)
  (dosync
   (if (< (count @queue) seats)
     (alter queue conj a)
     (debug "(s) turning away customer" a))))

(defn the-barber
  [st q]
  (Thread/sleep (+ 100 (rand-int 600)))
  (dosync
   (when (peek @q)
     (debug "(b) cutting hair of customer" (peek @q))
     (ref-set queue (with-meta (pop @q)
                      {:tally (inc (:tally (meta @q)))})))))

(add-watcher queue :send (agent 'barber) the-barber)

(doseq [customer (range 1 20)]
; (Thread/sleep (+ 100 (rand-int 200)))
  (send-off (agent customer) the-shop))

(Thread/sleep 2000)
(println "(!) " (:tally (meta @queue)) "customers got haircuts today")
import scala.actors.Actor
import scala.actors.Actor._
import scala.util.Random
import scala.collection.{immutable, mutable}

case object Haircut

class Customer(val id: Int) extends Actor {
  var shorn = false

  def act() = {
    loop {
      react {
        case Haircut => {
          shorn = true
          println("(c) customer " + id + " got a haircut")
        }
      }
    }
  }
}

class Barber extends Actor {
  private val random = new Random()

  def helpCustomer(customer: Customer) {
    if (self.mailboxSize >= 3) {
      println("(b) not enough seats, turning customer " + customer.id + " away")
    } else {
      println("(b) cutting hair of customer " + customer.id)
      Thread.sleep(100 + random.nextInt(400))
      customer ! Haircut
    }
  }

  def act() {
    loop {
      react {
        case customer: Customer => helpCustomer(customer)
      }
    }
  }
}

class Shop extends Actor {
  val barber = new Barber()
  barber.start

  def act() {
    println("(s) the shop is open")

    loop {
      react {
        case customer: Customer => barber ! customer
      }
    }
  }
}

object BarbershopSimulator {
  private val random = new Random()
  private val customers = new mutable.ArrayBuffer[Customer]()
  private val shop = new Shop()

  def generateCustomers {
    for (i <_- style="color: #afeeee; font-weight: bold;" span="span" _="_" _20="_20" to="to" _1="_1">val customer = new Customer(i)
      customer.start()
      customers += customer
    }

    println("[!] generated " + customers.size + " customers")
  }

  // customers arrive at random intervals
  def trickleCustomers {
    for (customer <_- style="color: #afeeee; font-weight: bold;" span="span" thread.sleeprandom.nextint450="thread.sleeprandom.nextint450" customer="customer" shop="shop" _="_" customers="customers">def tallyCuts {
    // wait for any remaining concurrent actions to complete
    Thread.sleep(2000)

    val shornCount = customers.filter(c => c.shorn).size
    println("[!] " + shornCount + " customers got haircuts today")
  }

  def main(args: Array[String]) {
    println("[!] starting barbershop simulation")
    shop.start()

    generateCustomers
    trickleCustomers
    tallyCuts

    System.exit(0)
  }
}

When either of these is run, you'll get an output somewhat similar to this:

[c] entering shop                    1
[c] entering shop                    2
[c] entering shop                    3
[b] cutting hair of customer         1
[c] entering shop                    4
[c] entering shop                    5
[s] turning away customer            5
[b] cutting hair of customer         2
[c] entering shop                    6
[b] cutting hair of customer         3
[c] entering shop                    7
[c] entering shop                    8
[s] turning away customer            8
[c] entering shop                    9
[s] turning away customer            9
[b] cutting hair of customer         4
[c] entering shop                    10
[c] entering shop                    11
[s] turning away customer            11
[c] entering shop                    12
[s] turning away customer            12
[b] cutting hair of customer         6
[c] entering shop                    13
[c] entering shop                    14
[s] turning away customer            14
[b] cutting hair of customer         7
[c] entering shop                    15
[b] cutting hair of customer         10
[c] entering shop                    16
[c] entering shop                    17
[s] turning away customer            17
[b] cutting hair of customer         13
[c] entering shop                    18
[c] entering shop                    19
[s] turning away customer            19
[b] cutting hair of customer         15
[b] cutting hair of customer         16
[b] cutting hair of customer         18
[!]  11 customers got haircuts today

General comparison

Normally the first argument you make for the STM is the correctness that comes with it, but where do we stand after exhanging blows? Well O'Reilly says

Were we not trying to simulate a real-world scenario, we would of course remove the call to Thread.sleep() and allow our barber to run full tilt.

Well, since our programs need to work in the real-world I pulled out the stops allowing the customers to hit the shop at 50 ms intervals and this blew O'Reilleys model to bits. Since he let his queue situation rest with the mailBox this happens:

Does that seem fair? Not at all, in fact in a real-word scenario this would ruin a business quite quickly. So the implementation of the actor-model is blown and the concurrency abilities of Scala which we set out to demonstrate has been found buggy in O'Reilly's code. I'll proceed to explain this model conceptually, even though this is actually disastrous.

The Scala architecture is like this

Once this is implemented it's a simple matter of traversing a list of customers, sending each to the Shop to get the show rolling.

The Clojure architecture is like this

Common for both architectures is that the division of labor is easy to reason about, ie. barbers are separate from customers, customers are separate from shops. If you had a problem in the Clojure model with the barbers behaviour you'd instinctly know smell the rat in "the-barber" and similar for Scala you'd go to class Barber.

Let's have a closer look

I'll point of a few keys in both approaches, starting with Clojure.

Clojure:

(def queue  (ref (with-meta
                   clojure.lang.PersistentQueue/EMPTY
                   {:tally 0})))
(def seats  3)

First we have our definitions. queue is of type PersistentQueue, I figured it was easier to relate to given the task at hand but really I could have used anything from lists to vectors to sets. There are 2 interesting things about my queue though. Firstly it's a reference and secondly it has meta-data. A reference is an indirection, meaning I can modify it from within a transaction to point somewhere other than where it is - that's a safe way to mutate values.

Meta-data is a very clever thing, which is not found in Scala. It's a way of describing your data without affecting the data. In business you'll usually want to know where your data comes from, if it's validated, how old it is etc. All of this can be safely tucked away as meta-data - In this case, I'll keep a tally of how many people got their hair cut.

(code-style freebie: I explicitly define seats as 3 instead of having a 3 directly in my code-logic. Generally you refer to in-code numbers as 'magic numbers', because 6 months from now, nobody knows what they're there for)

(defn the-shop
  [a]
  (debug "[k] entering shop" a)
  (dosync
   (if (< (count @queue) seats)
     (alter queue conj a)
     (debug "[s] turning away customer" a))))

Our definition of a shop is very simple: Check if there's a seat available, if yes conj(oin) a to the queue, if no reject. Remember my concern about race-conditions in our problem definition? For Clojurians concerns about race-conditions are usually boiled down to remembering to initiate a transaction by wrapping your code in (dosync ...). What happens if you forget? Clojure tells you!

(defn the-barber
  [st q]
  (Thread/sleep (+ 100 (rand-int 600)))
  (dosync
   (when (peek @q)
     (debug "[b] cutting hair of customer" (peek @q))
     (ref-set queue (with-meta (pop @q)
                      {:tally (inc (:tally (meta @q)))})))))

This is the main logic and still very simple. I use 2 functions to work on my shared resource: Peek and pop. Peek gives me the head and pop the tail. If there is a head I change my ref to point to the tail instead (having cut the hair of the head). While doing this I pass the old meta-data to inc (upping it by 1) and store it back in :tally. Simple?

Scala:

Since each Actor in the Scala model has his own mailbox with pending messages O'reilly has chosen to use the barbers mailbox as a representation of the chairs. This means that once a message gets sent to the barber, he checks the size of the mailbox and depending on the result either cuts or rejects.

class Barber extends Actor {
  private val random = new Random()

  def helpCustomer(customer: Customer) {
    if (self.mailboxSize >= 3) {
      println("[b] not enough seats, turning customer " + customer.id + " away")
    } else {
      println("[b] cutting hair of customer " + customer.id)
      Thread.sleep(100 + random.nextInt(400))
      customer ! Haircut
    }
  }

  def act() {
    loop {
      react {
        case customer: Customer => helpCustomer(customer)
      }
    }
  }
}

In the act() function we have a loop which activates in response to an incoming message, it reacts. If the message being sent is of class Customer, it passes that customer as an argument to its helper function helpCustomer. Like I mentioned above, the function then checks its mailbox to see if we can queue more people. If there's room then the person gets a haircut after an artistic wait of 100 + random(400). As you would expect the OOP approach gives you a division of labor which is great for a white board presentation, but even for a small project such as this we're generating a lot of code and also  our first bug!

 
def trickleCustomers {
    for (customer <_- style="color: #afeeee; font-weight: bold;" span="span" thread.sleeprandom.nextint450="thread.sleeprandom.nextint450" customer="customer" shop="shop" _="_" customers="customers">def tallyCuts {
    // wait for any remaining concurrent actions to complete
    Thread.sleep(2000)

    val shornCount = customers.filter(c => c.shorn).size
    println("[!] " + shornCount + " customers got haircuts today")
  }

Both Customer and Shop are implemented without any special tricks or points of interest, so I'll close by showing you how nicely you initiate this whole procedure.

Observations

Well, first off there one thing I really hate about certain languages. It's a phenomenon which on one hand can take a long time to master and on the other can cause you to write poor code by not leveraging the languages full power. It's called syntax.

Scala has quite a bit of Syntax which you need to study. For instance less-than-minus (<_- p="p" so="so" in.="in." means="means">

for (item collection)

will traverse a collection letting me work with one item at a time. Another is the exclamation mark, which we now know means to send message. A third could be slash-colon (/:) which means foldLeft and backslash-colon (\:) which means foldRight, ex:

scala> (0 /: (1 to 3)) (_ + _)
res5: Int = 6

I've scratched the surface here, fortunately Scala is very well documented and if you want to dig deeper you should read the 180 page language spec: here. If you want to learn the entire syntax for Clojure, I can teach you right now:

() = list

[] = vector

{} = map

#{} = set

All expressions take the form (func arg1 arg2 ... arg-n)

There's also a syntax for macros, it's also nicely documented here.

Caveats

So this seems almost too good to be true - has all concerns with concurrency really been nullified with the arrival of the STM and Actors? Not really, but we now have an easier way to reason about these problems, which is central to managing them.

Clojure

Looking at Clojure's snapshot isolatation, we still have to pay attention to write skew. Imagine an interface for managing security patrols on a military base, Area 51 for instance. You have 3 barracks, each containing 45 soldiers and you never want less than 100 soldiers in the barracks at any time. So you have 2 administrators who log on simultaneously, get their individual snapshots of the database and check if they can withdraw 20 solders.

Admin 1: if ((G1 - 20) + G2 + G3) > 100 then dispatchPatrol

Admin 2: if (G1 + (G2 - 20) + G3) > 100 then dispatchPatrol

Since neither of them sees the other transaction because they're looking at isolated snapshots they both commit since (45 - 20) + 45 + 45 = 115, pulling out 40 solders, leaving the total at 95 - below the threshold. Thats write-skew. Clojure provides (ensure r) to prevent this, but you need to pay attention.

Scala

Scala raises some concerns too. Firstly the OOP model is showing some teeth in the way you are always extending and implementing act() - To me as a new-comer it seems ceremonial and the lines of code agree. But if you're coding Scala you've accepted that this is life.

Secondly, the pattern-matching in the react/receive methods have a big pitfall - If you send messages of a type which is not implemented, what happens? You've guessed it, the Actor hordes these messages filling up its queue. In a massive OO project I can imagine this causing quite a few grievances.

Thirdly, with Clojure's agents/refs you can always read their values at no cost. Scala is a distributed system which means no shared-memory space, so by implication the Actors are always polling. Distributing resources always comes with a cost in terms of resource waste.

Fourth, for Scala programmers they are already dealing with OOP which is making their programs more complex, harder to reason about and understand. Adding to that is the thin/invisible line between functional- and imperative code. On top of those two you add the Actor model, which in it self adds complexity being distributed. If you take the sum of this final concern and try to calculate a development cost, maintenance cost, bug fixing cost - I think you'll want to scroll up and re-read the Clojure code. All this taken in account, it's not a huge surprise that the official Sleeping Barber solution from Scala was found buggy.

Conclusion

I'll make the argument that Scala isn't doing much for you, if you want to simplify your code and shoot for elegance. In my last post I was focused on smaller examples where you could leverage Scalas functional methods to good effect, but taking on a larger (but still small) project it's apparent that the end result is looking dangerously similar to 'what we used to do'. On top of this is the fact that the official solution from the Scala camp was buggy, which gives little grounds for confidence in the Actor model.

I don't claim that Clojure is problem free, but I let Rich worry about it. So far, I think it's ready to go the distance even in concurrent waters.

/Lau

mkneissl
2009-09-18 06:15:05
I think your comparison suffers from having to little Scala knowledge compared to clojure knowledge.

For example if you want a more concise style with actors you can write them like this.

import scala.actors.Actor._
val world = actor {
  receive {
    case x =&gt; println(x)
  }
}
world.start()
world ! "hello"

And if you want a no compromise functional language, just use Haskell and don't bother with the OO baggage of the JVM ;-) .
Brett Morgan
2009-09-18 08:35:39
It's worth noting that Clojure also has Actors, which possibly make more sense for this problem that STM.
Stephan Schmidt
2009-09-18 09:01:39
You're not comparing Clojure and Scala, but STM and message passing concurrency - so the tilte looks misleading.

Did you take a look at Akka?

The main difference I see is, Scala does actors in libraries (and does STM in libraries, see Akka) while Clojure does STM in the language. Doesn't take anything away from the great job Rich did.

Otherwise very good post,
Cheers
Stephan

http://www.codemonkeyism.com
Stephan Schmidt
2009-09-18 09:59:35
(second short remark).

Essentially this compares a drill and saw - which one is better to drive nails in a wall.

Using a hammer (Queue, Master/Worker) would be best for this problem, not a drill (STM) or a saw (message passing).

Cheers
Stephan
bestinclass
2009-09-18 10:16:07
@mkneissl: I think a common response to all articles regarding a language is a flurry of suggestions for more concise/idiomatic examples. I opted to go with code that was official and in print, not wanting to claim to be a Scala expert.

We always come back to this one central thing: Whatever the language defaults to, is what the lowest common denominator will be. If the default is mutability, people will mutate values.
bestinclass
2009-09-18 10:17:19
@Brett: Good observation and you're completely right! :) The reason I flaunted the STM (which is overkill) was because I wanted to show the key differences between non-distributed systems and distributed systems.
Mark
2009-09-18 11:03:08
In the Clojure code, you have a lot of debug/print statements inside transactions.  I believe this is a problem, because if the transaction retries, the message could print more than once.

One solution is to set up a debug-message-printing agent that just prints messages to the screen.  By using the agent to print your messages, the printing is delayed until the transaction completes.

But there's a whole other approach that is worth considering.  If you look at the O'Reilly Scala example, there are also print statements for when a customer finishes his haircut.  But this reveals problems due to the asynchronous nature of agents, e.g.,
[b] cutting hair of customer 1
[b] cutting hair of customer 2
[c][/c] customer 1 got a haircut
[c][/c] customer 2 got a haircut

Notice that customer 2 is reported as getting a haircut before customer 1 is reported as finished.  This violates the spirit of the problem that the barber can only cut the hair of one customer at a time.  I suspect that the Clojure version would be similarly flawed.

So right off the bat, you can see that actors/agents aren't really a great solution for this problem, if one of the objectives is to make sure that at every instant the world is in a consistent state.

In Clojure you can take another approach, eschewing agents altogether.  You could represent each customer as a ref containing a state (planning to go to the shop, entering the shop, sitting in a seat, getting haircut, leaving the shop).  You could represent the barber as a ref containing a state (sleeping in chair, setting up for customer #n, cutting hair of customer #n).  And you could represent the queue of seats as a ref (as you are currently doing).  Then you could attach watchers to all these refs to print out any state changes, and handle the appropriate logic (for example, when a customer changes from planning to go to the shop to entering the shop, the logic is triggered that determines whether he sits in a seat or is forced to leave the shop).  Start the simulation by starting a number of different threads responsible for waiting the appropriate time before changing the customer's state to entering the barbershop.  The simulation then flows from these state changes triggering watchers, which in turn may trigger other state changes (possibly using threads that sleep for an amount of time to delay certain state transitions).

I believe that with this ref-based approach you end up with an even more robust simulation in which consistency is guaranteed to a degree that you simply can't achieve with agents.  It's also a great demonstration of how you can mutate a lot of different pieces of state from different threads, and keep them all carefully in sync via STM.

As an example of this approach, you can look at my ref-based implementation of the similar, but more complex, "Santa Claus problem".

http://paste.lisp.org/display/79744
Mark
2009-09-18 11:07:56
Whoops, the "customer" lines above got interpreted as a code block.  Well, you get the idea.
bestinclass
2009-09-18 11:10:52
@Mark: Awesome comment.

You are right on the money in terms of how to write a solution which is well suited this problem. Also a good catch on the debug statements. Yes on solution would be to send them to an agent, but truthfully I/O should be entirely avoided in dosyncs. You solutions looks good and the documentation is almost that of a tutorial, so it's a recommended read!

You might also want to checkout Christophe Grand's (legend of the legends in functional programming) revised version of my solution http://gist.github.com/189002

Thanks for stopping by!
Mark
2009-09-18 11:13:15
One more point here about the ref-based approach I propose is that you could easily imagine hooking a GUI up, via watchers, to the various ref-based states.  For example, one windows shows you who is sitting in what seats in the seat queue, and what the barber is doing.  Another window shows you the state of all the customers.  And it would all be in sync.  There would be no instant where, for example, the customer thinks he is "sitting in a seat", but the seat doesn't yet show itself as being occupied by the customer.  That's because both these state changes would be part of the same transaction.
Christophe Grand
2009-09-18 12:20:08
Mark,

I think it's simpler  to have the GUI to periodically snapshot the refs rather than to have watchers trigger GUI updates. And it's likely to have a better behaviour under a high update rate.
Julian Morrison
2009-09-18 15:10:30
Mailboxes aren't queues, they're blobs of pending messages which do not make delivery order guarantees. If you want guarantees they have to be made inside an actor.

I would have made the seats themselves an actor, which gets messaged a customer and makes a non-blocking accept/reject decision immediately, and the barber an actor which loops polling the seats for the next customer (using an explicit request, response protocol so it simulates blocking).

Result:

- Barber asks seats for customer. Seats set barber sleeping flag.
- 1 appears, seats forward 1 to barber and clear sleeping flag
- Barber starts cutting 1
- Customer 2, 3, 4, 5, 6 show up
- Seats accept 2, 3, 4 and turn away 5 and 6
- Barber finishes cutting 1
- Barber asks seats for customer. Seats respond immediately with 2 and will accept the next customer who arrives.
Volkan YAZICI
2009-09-18 16:04:43
Here is an innocent Erlang try from an Erlang newbie. (I hope I get the problem described in the post right.)

shop(AvailSeats, BarberPid) -&gt;
    receive
        {BarberPid, done} -&gt;
            io:format("One seat more!~n"),
            shop((AvailSeats + 1), BarberPid);
        {CustomerId, seat_req} when AvailSeats &gt; 0 -&gt;
            BarberPid ! {self(), CustomerId},
            shop((AvailSeats - 1), BarberPid);
        {CustomerId, seat_req} -&gt;
            io:format("(s) turning away customer ~p~n", [CustomerId]),
            shop(AvailSeats, BarberPid)
    end.


barber() -&gt;
    barber(0).

barber(HairCuts) -&gt;
    receive
        {ShopPid, CustomerId} -&gt;
            timer:sleep(100 + random:uniform(200)),
            io:format("(b) cutting hair of customer ~p~n", [CustomerId]),
            ShopPid ! {self(), done},
            barber(HairCuts + 1);
        hair_cut_count -&gt;
            io:format("~p hair cuts completed so far.~n", [HairCuts]),
            barber(HairCuts)
    end.


customer(0, _ShopPid) -&gt;
    ok;

customer(Customers, ShopPid) -&gt;
    timer:sleep(100 + random:uniform(200)),
    io:format("(c) entering shop ~p~n", [Customers]),
    ShopPid ! {Customers, seat_req},
    customer((Customers - 1), ShopPid).


main(AvailSeats, Customers, MilliSeconds) -&gt;
    BarberPid = spawn(barber, barber, []),
    ShopPid = spawn(barber, shop, [AvailSeats, BarberPid]),
    customer(Customers, ShopPid),
    BarberPid ! hair_cut_count,
    timer:sleep(MilliSeconds),
    exit(ShopPid, ok),
    exit(BarberPid, ok),
    ok.
datura
2009-09-18 20:34:53
bug: in contrast to "&lt;-&quot;, which is syntax, &quot;!&quot; and &quot;:/&quot; are just methods with weird names. and please don&#039;t forget it takes some time to learn _not_ to read all the () in lisp code, too.
grokcode
2009-09-18 23:56:26
Excellent series of articles. I fooled around some with Scala a few months back, but had some criticisms about the quality of documentation, and as you mentioned above, the syntax is quirky. If you are interested, you can <a href="http://grok-code.com/75/learning-scala-with-project-euler/" rel="nofollow">view my impressions of scala here.</a>

Anyway thanks to your posts, I'm going to have a look at Clojure. I've been pretty immersed in the Java and php world for too long and am really missing the clean style of lisp.

It's also interesting to see a consultancy that only works on Clojure projects. I would be interested in seeing some case studies of different Clojure projects you have done (if your clients don't object).
mikhailfranco
2009-09-23 21:47:27
Volkan - I think there are bugs in your code. For example, you forget the initial (maximum) AvailSeats, so the value can grow with the number of completed customers (as if they each leave an extra chair in the waiting room when they leave :)

Mik
mikhailfranco
2009-09-28 09:53:31
I have put an Erlang version here:

  http://snipt.org/ngni

Mik
Antony Stubbs
2009-09-29 23:37:47
Just want to point out that the /: :\ method names are optional. If you don’t like using the symbols – use the name foldLeft or foldRight instead.
http://www.scala-lang.org/docu/files/api/scala/Iterable.html#foldLeft%28B%29
vs
http://www.scala-lang.org/docu/files/api/scala/Iterable.html#%2F%3A%28B%29
mikhailfranco
2009-10-07 10:35:28
Apologies to Volkan, after formatting your code I can see how it works. Very nice. 
I like the trickling customer loop, and the implicit counts and ids.
Not sure I like putting the customer queue in the barber mailbox,
it's a good implicit hack for the simple phrasing of this problem, 
but doesn't generalize to other queueing policies (e.g. priority).

Mik
Daniel Sobral
2009-12-13 23:24:06
The only problem with this article is that the Scala code's goal is to show actors, not to solve the problem. So the only valid comparison would be to show actors in Clojure.
Lau
2009-12-13 23:29:19
@Daniel: Oh, so they set out to show how not to solve the problem using actors... I guess they should have mentioned that somewhere in the book :P
Toby
2011-01-18 03:43:22
Didn't realize this was an older post, but somehow stumbled on it today. Anyways, here's a nicer scala version, shrunk down to 58 lines of code. This should be closer to the clojure implementation, and also cuts down on some of the boilerplate from the O'Reilly solution.

https://gist.github.com/785100
Lau
2011-01-19 05:28:42
Hey Toby,

Great work, very readable.

newobj
2011-04-04 06:59:05
I wrote a version in Java. It's quite short and I think very non-esoteric. I'm really curious what value the complexity of the Clojure/Scala solutions bring.

https://gist.github.com/900954
Vincent
2011-04-03 11:10:44
This is a great question you pose and a really great article interleaved with some obvious Scala hate.   

Actors vs. STM is really orthogonal to Clojure vs. Scala, as I believe both languages have libraries for both.

Some of the Scala comments that I thought were a bit over the top:  You're really advocating a dynamic language and then knocking Actors for not being type safe?  You complain that the Scala syntax is awkward, but what if people haven't done a Lisp before and find the Clojure syntax mysterious?  

Anyway, back to the Actor/STM debate, I think you do make a good point in this instance that Actors don't do as much for you.  I've found them useful for doing a certain type of fire and forget task and for keeping certain types of events/messages run in a particular order.

I'm not sold that STM is a concurrency silver bullet, as you alluded to the fact that live locks can be as much of a problem for STM algorithms as deadlocks can be for a traditional locking algorithm. 

I think the biggest thing Clojure did brilliantly was the immutability by default, unless specified.  

Going back to the Scala vs. Clojure 'debate' though, I guess I can just keep hoping that Rich joins the Scala team and really shakes things up! ;-)