From: David Masover Date: 2008-07-27T05:53:16+09:00 Subject: DSL/thread design question I am writing something which I am calling "suits" for now -- the idea being "something you make with threads". It's a lightweight actor model. Works something like this: s = Suit.new([]) s.push(1,2,3,4,5) # asynchronous, so ignoring the return value s[2].now # returns 3 s.length.then{|l| puts l} # prints 5, at some point s.join # reap the thread The idea is simple: Map Ruby's object model onto the actor model. (Actually, I didn't know I was doing actors until I found Revactor -- I just knew it was vaguely Erlang-inspired.) Therefore, an actor simply looks and feels like an object, and message-passing is done through Ruby method calls (which are sort of message passing anyway). My motivation is to at least make multithreaded Ruby an attractive option, so that there is a motivation to remove the Python-esque Global Interpreter Lock. I don't believe separate Unix processes will scale well enough. There are a few design issues, and I think I'm out of my league here. Three problems: First, while I like the syntax of "now" vs "then", it does require some discipline if you were expecting this to behave like the object it's referring to. It also means I can't drop a Suit-wrapped object into something expecting the original. But making things asynchronous by default would force developers to be aware of how synchronous (or not) they're actually being. So, should this be synchronous by default? (Right now, it's not.) Maybe I should return a "promise" instead, so that things are asynchronous until you actually need information out of the returned value. Second, for asynchronous calls, since I'm using a Queue, things arrive in-order. Would there be an advantage to not requiring that behavior? Intuitively, I think there would be times when random delivery would be an asset, but I can't justify it. About the only reason for attempting random delivery would be to force the programmer to think about multiple threads sending messages simultaneously. And finally, what about exception handling? Right now, exceptions will be delivered on #join, but that requires explicitly checking for them. The thing that I love about exceptions in a single-threaded app is that unless you do something special, the exception kills your program. I would much prefer the behavior to be similar to that, rather than trying to return an exception. I kind of like how exception handling is done in Erlang, in which one "process" (think: actor) is responsible for handling the errors of another. But I'm not sure what the simplest way of doing that in Ruby would be.