Caolan

15
Oct
2009

Erlang map/reduce in CouchDB

Enabling the native Erlang view server in CouchDB 0.10.0

After some initial confusion on my part, it appears that a native Erlang view server has made the 0.10.0 release of CouchDB. Unfortunately, at the time of writing, there is no documentation on enabling or using this feature. The only example currently available is in the test suite, so for anyone else wanting to play around with Erlang map/reduce functions, these are the steps I carried out on Ubuntu Jaunty...

First, you'll need to edit your local.ini to include a native_query_servers section:

[native_query_servers]
erlang = {couch_native_process, start_link, []}

Your local.ini will most likely be at /usr/local/etc/couchdb/local.ini or /etc/couchdb/local.ini. To see these changes you will also need to restart the server:

sudo /etc/init.d/couchdb restart

To test out using Erlang views, visit the Futon admin interface, create a new database and open a temporary view. You should now be able to select erlang from the language drop-down. Let's try an example of map/reduce functions which count the total documents at each number of revisions (there are x many documents at version "1", and y documents at "2"... etc). Add a few documents to the database, then enter the following functions as a temporary view:

%% Map Function
fun({Doc}) ->
  <<K,_/binary>> = proplists:get_value(<<"_rev">>, Doc, null),
  V = proplists:get_value(<<"_id">>, Doc, null),
  Emit(<<K>>, V)
end.

%% Reduce Function
fun(Keys, Values, ReReduce) -> length(Values) end.

If all has gone well, you should see a list of the total number of documents at each revision number. Have fun!

Edit: I've now added this to the CouchDB wiki.

01
Oct
2009

Notifications Update

Extended browser compatability for jquery.notify.js

Just announcing a few bug fixes to the Ubuntu notifications plugin for jQuery. The plugin should now work on Firefox 2+, IE 6+, Safari and Opera. This mostly relied on removing the reliance on getBoundingClientRect() and introducing SVG-based rounded corners as a fall-back for Opera.

I'd appreciate more testing though, so if you get chance please play around with the examples, and let me know if you have any problems.

28
Sep
2009

Ubuntu Notifications in Javascript

"ephemeral overlays, which can be clicked through so they don't block your work"

There are a whole bunch of growl-like plugins for jQuery, but I'm a real fan of the new Ubuntu notifications introduced in Jaunty (see: http://www.markshuttleworth.com/archives/253). So, I decided to create a plugin which imitates these instead.

The hardest part was getting the click-through to work nicely, requiring me to create custom mouseover and mouseout events. There are still some issues to address, such as rounded corners for Opera, but there is enough in place to test the idea out. I wouldn't recommend using this in production yet, as only FF3, IE7 and IE6 have been tested as working.

Get the code or see the plugin in action!

23
Aug
2009

Erlmpd - An Erlang library for MPD

A simple client library for communicating with Music Player Daemon

Just a short notice to announce the uploading of erlmpd to Github. This module implements the MPD protocol in Erlang and was developed for a web-based MPD client I'm currently working on. Since I felt this module could potentially stand on its own two feet, I've released the source code here. I've also uploaded the docs for reference.

MPD is a "flexible, powerful, server-side application for playing music" - essentially allowing you to control it over a network. Pretty handy for collaboratively creating playlists or controling playback wirelessly.

17
Jul
2009

Flatten for Python

A Python implementation of flatten list

After making frequent use of lists:flatten/1 in Erlang, I found myself wanting to use this function in Python too. Somewhat surprisingly, Python doesn't have a builtin for flattening lists. If you're not familiar with flatten, its most easily explained through some quick examples:

>>> flattened([1,2,3,4])
[1,2,3,4]
>>> flattened([[1,2],[3,4]])
[1,2,3,4]
>>> flattened([[[[1],2],3],4]])
[1,2,3,4]

Hopefully you get the idea, it takes nested lists and places all the items they contain in order into a single list. My initial hack to get this working was the following one-liner:

def flattened(l):
    return reduce(lambda x,y: x+[y] if type(y) != list else x+flatten(y), l,[])

This was fine in the scenario I'd written it for, but I knew full-well it was going to explode with recursion errors if it ever encountered a long, complicated list. Unlike Erlang, Python doesn't do nice tail recursion, and will fall over when its call stack fills up, reporting a RuntimeError.

Recently, I came across this snippet of code again and decided to solve the problem properly. After some Googling around I found this post by Danny Yoo on a Continuation Passing Style version of flatten. This got the geek in me all interested, and I decided to see if I could write something in the form of a Python generator instead, while still avoiding the maximum recursion depth issue. Here is the resulting code (seriously nasty code coming up!):

def flattened(l):
    result = _flatten(l, lambda x: x)
    while type(result) == list and len(result) and callable(result[0]):
        if result[1] != []:
            yield result[1]
        result = result[0]([])
    yield result

def _flatten(l, fn, val=[]):
    if type(l) != list:
        return fn(l)
    if len(l) == 0:
        return fn(val)
    return [lambda x: _flatten(l[0], lambda y: _flatten(l[1:],fn,y), x), val]

Apart from making my brain hurt, what really amazed me about this approach was the speed increases. On a list nested to a depth of 100, this turned out to be around 2 times faster than the the CPS version by Danny Yoo. However, this is not a linear relationship, at a depth of 20,000 it was completing around 12 times faster (and yes, thats generating the whole list, not the time taken to hit the first yield). I didn't expect there to be such a difference, but I'm now interested in seeing where else generators and this kind of pattern can be used!

Note: I decided to rename the function 'flattened', because of its similarity to the Python builtin 'reversed' which returns a new generator instead of editing the list in place. This function also only flattens lists, not tuples (intentionally), if you wanted it to flatten tuples it should only require some slight tweaking (which I'll leave to you).