Friday, April 20, 2007

An introduction to Termite

NOTE: This post was written by Marc Feeley, the author of Gambit-C, following yesterday's MSLUG meeting where I gave a short introduction to Erlang. We were about twenty people, many of them were there for the first time. (It seems that Erlang is getting more and more attention these days.) In short, we had a great evening.


(Reproduced with permission)

At yesterday's MSLUG meeting Dominique showed us how easy distribution can be done using Erlang. There was a question asking how Erlang and Termite compare. To give a quick idea, here is an example of how distribution can be done with Gambit-C/Termite.

Assuming you have built the Gambit runtime to include Termite, you need to start a Termite instance on a machine (say "dynamo") and initialize it:


dynamo% gsi -e '(node-init (make-node "dynamo" 7777))' -
>


This instance of termite is a "node" which is identified by the IP address "dynamo" and the IP port 7777. Note that a REPL is started, but we will not be using it.

Now let's start a Termite instance on machine "neo" to get a REPL:


neo% gsi -e '(node-init (make-node "neo" 8888))' -
>


This second node is identified by the IP address "neo" and the IP port 8888. We could have started two nodes on the same machine, as long as the IP ports are different. All the remaining REPL interaction is on neo. First, let's create a reference to the first node on "dynamo":


> (define dynamo (make-node "dynamo" 7777))


Synchronous remote procedure call is done with the "on" function. It executes a thunk remotely and returns the result:


> (on dynamo host-name)
"dynamo.iro.umontreal.ca"


Let's develop an asynchronous remote procedure call in stages. First a simple example of local process creation and communication:


> (define s (self))
> (define p (spawn (lambda () (! s (expt 2 (?))))))
> (! p 10)
> (?)
1024


The process p uses the "?" function to wait for and retrieve the next message in p's mailbox. The function "!" sends a message back to the primordial process (whose PID was saved in global variable s). This code can easily be extended to get a simple "power of 2" server:


> (define p (spawn (lambda () (let loop () (! s (expt 2 (?))) (loop)))))
> (! p 16)
> (! p 32)
> (?)
65536
> (?)
4294967296


Note that the results are retrieved asynchronously (i.e. the mailbox queues the messages received). Moreover the results are always sent back to the primordial process. To generalize, the request can be a pair containing, in the car, the PID of the process which the result should be sent to, and in the cdr, a function that is executed by the server to generate the result:


> (define p (spawn (lambda ()
(let loop ()
(let ((x (?)))
(! (car x) ((cdr x)))
(loop))))))
> (! p (cons (self) host-name))
> (?)
"neo.iro.umontreal.ca"


In this example the server is running locally. By changing the "spawn" to a "remote-spawn" the server can be started on "dynamo":


> (define p (remote-spawn dynamo
(lambda ()
(let loop ()
(let ((x (?)))
(! (car x) ((cdr x)))
(loop))))))
> (! p (cons (self) host-name))
> (?)
"dynamo.iro.umontreal.ca"


Any serializable Scheme object can be passed in a message. This includes closures. Here is an example:


> (! p (cons (self)
(let ((a (host-name)))
(lambda ()
(let ((b (host-name)))
(lambda ()
(list a b (host-name))))))))
> (define f (?))
> (f)
("neo.iro.umontreal.ca" "dynamo.iro.umontreal.ca"
"neo.iro.umontreal.ca")


Note that the cdr of the pair sent to p is a closure which remembers the binding for a, which is "neo.iro.umontreal.ca" because that's where the first (host-name) was evaluated. When the server calls this closure, the second (host-name) is evaluated, giving "dynamo.iro.umontreal.ca". The binding of b is remembered in the closure created by the second lambda. This closure is sent back to the primordial process, which binds f to it. When this closure is called the third (host-name) is evaluated, resulting in "neo.iro.umontreal.ca".

This example shows that the meaning of the global environment is node dependent. The global environment is not automatically kept consistent on all nodes, it is the programmer's responsibility to ensure this where needed. So for example:


> (on dynamo (lambda ()
(with-exception-catcher
(lambda (e) e)
(lambda () (fact 10)))))
#<unbound-global-exception>
> (on dynamo (lambda () (load "test.scm")))
"/Users/feeley/test.scm"
> (on dynamo (lambda ()
(with-exception-catcher
(lambda (e) e)
(lambda () (fact 10)))))
3628800
> (fact 10)
*** ERROR IN (console)@24.2 -- Unbound variable: fact
1>


Erlang's autoloading behavior is not built in to Termite. However it could be implemented on top of Gambit's exception handling system. The idea would be to extract from a fully qualified name (such as test#fact) the filename of the file to load, like this:


(define (char-pos str c)
(let loop ((i 0))
(if (< i (string-length str))
(if (char=? c (string-ref str i))
i
(loop (+ i 1)))
#f)))

(define base-eh (current-exception-handler))

(current-exception-handler
(lambda (e)
(if (unbound-global-exception? e)
(let* ((sym (unbound-global-exception-variable e))
(str (symbol->string sym))
(i (char-pos str #\#)))
(if (not i)
(base-eh e)
(let ((mod (substring str 0 i)))
(load (string-append mod))
(let ((val (##global-var-ref (##make-global-var sym))))
(if (##unbound? val)
(base-eh e)
val)))))
(base-eh e))))

(namespace ("test#" fact))

(pp (fact 10))



The call (fact 10), due to the namespace declaration, is rewritten into (test#fact 10). When the interpreter evaluates the reference to test#fact the exception that is raised is caught by the exception handler which extracts the module name "test" and loads that file to access the definition of "fact". The file "test.scm" should contain:


(namespace ("test#" fact))

(define (fact n)
(if (= n 0)
1
(* n (fact (- n 1)))))


Marc

Thursday, April 19, 2007

Another commercial Erlang adopter

Slideaware, a company that develops a presentation management platform, has decided to port all its code base Erlang. They started blogging about their experience here. They seem very pleased by the end result. I particularly like the reason why they investigated Erlang:

We liked the idea of replacing our multiple language stack (Lighttpd + RoR + SQLlite + XMLRpc + Jython + Lucene) with an Erlang-only solution (Yaws + Mnesia + Erlang).

Friday, April 13, 2007

The problem with VoiceXML development tools

Note: this post does not contain any Scheme/Lisp-related content.

I have been working in the speech recognition industry for 10 years now. 10 years ago, you had to develop your speech applications in C, you had to interface with the telephony cards using (usually buggy) proprietary drivers (think Dialogic), etc. Now, VoiceXML lets you abstract from that and forget about managing all those low-level resources - telephony cards, speech servers, text-to-speech servers, etc. (For the benefits of my readers who don't know VoiceXML, it's a markup language somewhat like HTML, but mainly for speech-enabled telephony-based applications. A VoiceXML browser is accessed by dialing a phone number. VoiceXML is a W3C standard getting a larger market adoption every day. All large call center are migrating their applications to VoiceXML. Even Microsoft, who tried to compete with VoiceXML by introducing SALT, now fully supports VoiceXML.)

So what do I have against VoiceXML? Nothing per se. VoiceXML has its limitations, of course, with which I have to work with every day (but that'll be the subject for other posts ;-). I mainly complain that development environments for VoiceXML-based applications are really limited and only try to mimic what you would have found twenty years ago in most proprietary IVR development tools.

You could argue that these tools help transition from those old IVR platforms to the more modern VoiceXML-based platforms. And you may be right.

But from a developer's perspective, it's like having to program in Cobol! And I only slightly exaggerate.

Now, what would you expect from a modern VoiceXML development environment, apart from a nice Eclipse-based GUI?

  • Abstraction support. By this, I mean the possibility of defining patterns of dialog that you can reuse across you application. Leading edge development tools, like Cisco Studio (formerly Audium) and the Microsoft Speech Server, don't offer good support in this respect. What they offer is much more like structure copy/paste, nothing more.

    In an object-oriented language, you can define abstract classes that can serve this purpose. Why can't we have this concept when developing speech applications?

  • Unit test support. This one is really important. When you develop a speech application, you don't want to re-run hundreds of manual tests using the phone whenever you refactor a piece of code in your application, just to make sure you didn't break it. This would take too much time. Unfortunately, that's how most developers do!

    (Don't forget that when you develop speech applications for large call centers, you usually must respect very restrictive service level agreements (SLAs). Your application cannot crash too often, otherwise you'll have to pay some compensations to your client.)

    This issue arises very often on the Yahoo VoiceXML group. And nobody has a good solution to this problem!

    A good development environment would let you code dialog unit tests that you could run without having to deploy your application on an application server (Tomcat, WebSphere, whatever). Ideally, they would be integrated with the JUnit framework. Using a single keystroke, you could then run hundreds of unit tests, covering the whole call-flow, in a matter of seconds.

In summary, could it be that we are still stuck with concepts almost thirty years old, with programming habits almost as old? Where are the truly innovative solutions in the market? Why is academia not more concerned with these problems?

Wednesday, April 11, 2007

The Termite way

Last week, I compared Erlang and Gambit-C/Termite for the development of commercial-grade distributed applications. One of my points was the lack of good RDBMS support in Gambit-C. I argued that using the FFI (foreign function interface) was not acceptable since a call to a C function from Scheme would block the whole system.

That's still true. But that's not a problem at all. Why? I was not attacking the problem the Termite way!

Since a call to a C function blocks the whole system, simply start a new Gambit-C runtime to execute the DB query. That's the theory, of course. In practice, you won't start a new process for each query.

You could simply start a predetermined number of Termite runtimes beforehand, each capable of sending/executing a query to the database. Those processes could even connect to the database at startup time. When you want to execute a query, you simply send a message to one of those processes, which will send the result of the query back to your application.

This is the idea of a connection pool that we find in many large-scale web applications, limiting the number of concurrent accesses to the database. To achieve better decoupling, a special node could serve as the front-end and dispatch your query to one of the available nodes, waiting for one if they are all busy. So your application would only need to interact with this dispatching node.

The nice thing is that those processes can be run on different nodes of a cluster, thus achieving fault-tolerance almost for free (the details are left as an exercise for the reader ;-).

Next week's MSLUG meeting

I have not yet announced it officially, but I will give a talk at the Montreal Scheme/Lisp User Group next week. Following January's meeting, I will give a short, dynamic introduction to Erlang and present the implementation of MSLUG web site.

Dynamic Languages Symposium in Montreal

The next Dynamic Languages Symposium will be held in Montreal, in October 2007. I'll definitely try to attend the conference. I can't use the high travel expenses as an excuse... I'll try to organize a meeting of the MSLUG somewhere around this event. This could be a great opportunity to meet the people behind Termite, Gambit-C, and SchemeScript. Even Patrick Logan, who writes regularly about Termite, will come to the symposium!

Also, I will give a talk at the SpeechTEK conference in the New approaches to dialog design track, in New York, on August 21st.

Thursday, April 05, 2007

Erlang or Gambit-C/Termite? A practitioner's perspective

Regularly, I need to prototype a new idea that could lead to the development of a new project or product at work. Each time, I have to ask myself: do I prototype it in Scheme or in Erlang (just to make sure it's a throw away ;-)

A few years ago, I would have used Scheme without asking. Now, I would definitely choose Erlang. Why?
    1. Syntax. Even if I prefer the Scheme/Lisp syntax for its simplicity and regularity, the Erlang syntax is not that bad and can be easier to grasp for non-Schemers. If your prototype becomes the basis for a new product or internal project (this should not be, but you know how things work in the industry ;-), Erlang may be easier to sell to other developers because of this.
    2. Proven platform. Erlang/OTP has been used in a number of large-scale commercial applications and systems. Can you say the same for Gambit-C? Where are the success stories? When selling your project to management, this argument will certainly hit you.
    3. Commercial support. Erlang/OTP is supported by a team of developers at Ericsson. Gambit-C, although a great piece of software, is the work of a single person. And Marc Feeley is a pretty busy guy. Also, several companies have developed commercial products in Erlang (Nortel, Process One, LShift, to name a few), or provide consulting services in Erlang (like Erlang Consulting).
    4. SMP support. Gambit-C does not support SMP. This may not be a show-stopper (simply start as many Gambit-C processes as there are cores on your machine). But intra-process communication is way more efficient.
    5. Mnesia. When you need to add a database to your application, this is a no-brainer in Erlang. Just use Mnesia (unless you have some very specific constraints that Mnesia cannot match). With Gambit-C, you'll need an external RDBMS (like MySQL or PostsgreSQL), and a good library to interface with the database. Using the FFI to integrate the C API of your favorite RDBMS is a no-no! If your code is multi-threaded, all your threads will be blocked during a call to the C API. You don't want this. So you'll need a library that implements the native protocol. That's a lot of work, because you'll want it to be robust and efficient.
    6. SSH. Erlang comes with a complete SSH server and client. This means you can access your remote Erlang process with SSH and continue to benefit from the REPL.
    7. OTP. Although I'm still not an OTP expert (I just barely scratched its surface), much of the power of the Erlang platform for the development of reliable, robust distributed applications comes from OTP. Much of it can certainly be implemented in Gambit-C, given enough time and effort, but not everything. Some of its features (like hot code reloading) are built into the runtime system and cannot be easily emulated.
    8. Yaws. Building a new web app or web service with Yaws is really simply. It is REALLY robust and efficient. With Gambit-C, you have two choices: implement both an HTTP server and an application server yourself, or interface Gambit-C with Apache/mod_lisp. The first option represents a lot of work and requires a lot of expertise to get it right. The second option is a lot easier to implement, but at the expense of administrating another piece in you puzzle.
For these reasons, I would choose Erlang over Gambit-C+Termite for the development of a new scalable and robust distributed application. That being said, Gambit-C is a nice piece of software (in fact my preferred Scheme system), it has a great debugger, and the Scheme Now! project will certainly lead to a number of good portable libraries for Scheme. But from the perspective of an industrial developer, Erlang wins easily.

Your mileage may vary, of course. Feel free to disagree!