9fans archive / 2004 / 03 / 177    prev next

search terms are split using tokenize from:regexp searches From: lines subject:regexp searches Subject: lines before:yyyy[/mm[/dd]] and after:yyyy[/mm[/dd]] specify date range powered by grep(1)
From: rog@vit... Subject: advantages of limbo Date: Tue, 2 Mar 2004 15:04:25 0000 > Yet [Java] is more powerful in expressing algorithms. Things like closures > or message polymorphism are natural and easy to express in Java, while > either not possible or inconvenient in limbo. re: closures: cheap and easy threads provide a more than adequate substitute in many cases. a channel can represent a connection to a remote procedure with its attached data. for instance, a trivial example: getop(s: string): chan of (string, chan of string) { c := chan of (string, chan of string); spawn append(s, c); return c; } append(s: string, c: chan of (string, chan of string)) { for(;;){ (t, reply) := <-c; reply <-= s + t; } } in this example, the channel returned by getop(s) is functionally equivalent to λt.(s + t). the generic operator would look like: op(s: string, c: chan of (string, chan of string)): string { reply := chan of string; c <-= (s, reply); return <-reply; } of course in this case the amount of code compared with its actual function is considerable, but for less trivial applications this is often viable (moreover, it's considerably more powerful, as the channel represents an ongoing continuation rather than just a closure). re: message polymorphism. strings are often used for this kind of thing in limbo, as they're easy to use, by-value (yet locally mutable) and it's trivial to transfer them externally. generally, Limbo programs tend to avoid polymorphism (although the latest version does have parametric polymorphism) in favour of a simple, direct style of coding. channels i mentioned earlier in this respect. dynamically loaded modules mean that you can have several dynamically loaded pieces of code each complying to the same interface. i've used this to pleasing effect in some limbo programs. for example, a piece of "grid" software i did recently allows one to farm out pieces of work to multiple clients and reliably collect them. the module that actually splits up the work and marshals the results is separately implemented. its interface is entirely specified by the following self-contained module definition: Taskgenerator: module { init: fn(jobid: string, state: string, args: list of string): string; taskcount: fn(): int; state: fn(): string; start: fn(id: string, tries: int, spec: ref Clientspec, read: chan of (int, chan of array of byte, chan of int), write: chan of (array of byte, chan of string, chan of int), finish: chan of (int, big, chan of string)): (int, string); reconnect: fn(id: string, read: chan of (int, chan of array of byte, chan of int), write: chan of (array of byte, chan of string, chan of int), finish: chan of (int, big, chan of string)): int; complete: fn(); quit: fn(); Clientspec: adt { addr: string; attrs: list of (string, string); nodeattrs: list of (string, string); }; Started, Error, Nomore: con iota; }; this uses the above mentioned forms of polymorphism in several ways: multiple different implementations of Taskgenerator modules coexist concurrently, each with its own unrelated state. calling start() asks the task generator to start a new task; it returns some channels that can then be used by the core software to communicate with an instance of that task (usually a thread will be started). the interface is straightforward, and both sides of the software can be written in a straightforward manner. strings are used to hold arbitrary data (attribute-value pairs, arbitrary client-created identifiers). implementation is almost completely divorced from interface, making the whole thing highly modular. i can replace task generator modules at run time with no hassle (and often do). of course, it's no magic bullet, but the features of Limbo do seem to me to work in synergy to produce a language that spurs creativity rather than inviting perplexity. cheers, rog.