Originally posted on Wed, 07/03/2013
Lately I’ve been looking at Node.js, a high-performance web server built in JavaScript, and at Kotlin, a language designed to make programming large applications easy. I haven’t actually used Node.js, but that doesn’t keep me from having opionions. It seems to me that combining the two could make it easy to write big, complicated web applications– if it’s done right.
Node.js is designed to serve tens of thousands of users at a time– something that cheap computer hardware is theoretically capable of, but which is limited by the way that operating systems handle threads and processes. Node.js (and others, like ngnx) does this by strictly using non-blocking I/O. That is, a single Node.js thread handles many simultaneous requests, and that thread never waits for I/O, such as disk or database access. Consider how one would tell it to serve a file:
- Node.js is configured so that the URL
https://www.my-node.example.com/index.html
invokes the JavaScript functionwriteMyFile("index.html", response)
where response is how you send data back to the web browser that’s making the request. -
writeMyFile(filename, response)
tells the filesystem object to read the file and gives it a function to call once the file has been read. It might be written as:
fs.readFile(filename, function (error, data) {
if (error) throw error;
response.write(data);
response.end();
});
- The filesystem object requests the data in the file from the computer’s file system, and rather than waiting for the file to get read off a disk, the next request is handled.
- At some point in the future, Node.JS notices that the file has been read, and invokes the callback function listed above. The file data is sent to the web browser, and the request is done.
The important point here is that when I/O is required, all work on that request shifts from the current function to a call-back function. For simple tasks, such as reading a single file, this is not too difficult. But consider a typical web page generated by Ruby on Rails, PHP, or other web frameworks. Such as this blog page. Lots of disparate data are loaded from at least one database (user account information, the text of a blog entry, listings of other blog entries, comments, etc.) And to make programming easy, the database calls can be done at any time, in any order.
A PHP script (such as what generates this blog page) looks like plain HTML, with PHP code interspersed where plain HTML won’t do. As the PHP script runs, it prints out the HTML until it gets to a PHP segment, and then it does the PHP computation– such as reading a value from a database– and then goes back to writing HTML. Most web frameworks work this way: you have an HTML template with code mixed in, and the program goes back and forth between writing the HTML and running the embedded code. If it takes 10 milliseconds to do a database query, the thread just stops and waits. But stopping and waiting isn’t allowed in Node.JS. (Actually it is, but you would instantly go from having one of the fastest web servers to the slowest.)
Simply put, a Node.js program with one I/O request can be read from top to bottom. A Node.js program with multiple I/O requests is a tangle of call-back functions.
Which brings us to Kotlin. Kotlin has a number of language features which could (in theory) be used to get the best of both worlds. That is, the code would be structured like HTML, but the web framework would extract all the parts that require I/O, and call them with a call-back function that feeds back into a function that is called once all the I/O is done. That final function would stitch together the template and the data from the I/O into the final HTML result.
Central to this is the ability to take a program that is structured like HTML, extract the I/O portions, and also extract HTML generating code. Central to this is Kotlin’s Groovy-style builders. This is a way to construct objects with a syntax that is structured like HTML. (It doesn’t look like HTML, but it’s at least as compact, and similarly easy to read.) The end result is an HTML object that can generate HTML, but isn’t just a representation of HTML, it’s a full-fledged Kotlin object. It has methods that can be run, it can be subclassed, it can be analyzed and traversed. So in theory you could traverse an HTML builder and extract I/O-dependent objects before generating HTML text.
Here’s the rub: builders typically just contain text and other builders. I’m particularly familiar with Scala, where you can embed XML-within-code-within-XML-within-code all day long. The problem is, the embedded code is run when the XML object is constructed, so you don’t have a chance to pull out embedded, non-XML objects. (In Scala, this is considered an advantage, as the XML is guaranteed not to change every time you read it.)
I suspect Kotlin works much the same way, but there is a silver lining. Kotlin also supports delegation, whereby one object can be obtained via another– such as where the actual thing you want is in a database, and you want to avoid calling the database until absolutely necessary. Delegation is done in a controlled manner, and you can’t simply give something a database proxy when it requires a string. Again, this is usually an advantage, since you know that you’re getting an unchanging string when you expect a string.
So what you need is a special-purpose Builder (perhaps a builder builder) which understands both HTML and I/O-dependent HTML generators. The latter would be designed so they could be extracted and given a call-back function which generates the HTML when all the I/O is done. Note that each I/O object would be responsible for only one I/O call, although they could be chained together for something more complex (e.g. look up data from a database, and then write something back to the database.) At worst, this would bring us back to the Node.js tangle of call-backs, but slightly less tangled. At best, this enables all sorts of places for automatic optimization: identical I/O objects could be merged, the I/O requests could be done in any order, and similar database queries could be combined into a single query.
This is the sort of thing that would be fun to work on, if I had the time. Perhaps someone else should do it. Or perhaps someone else already has, but I haven’t discovered it yet. The core idea here is lazy builders where you can extract the I/O, so that code that is written with I/O interspersed within it can be called in a non-blocking manner