Functional Javascript for OO developers, Part 2

In part 1 I breifly introduced functional language concepts from the point of view of a traditional OO developer. In this post we'll apply some of those concepts to a real world problem, which we'll approach in both OO and functional ways.

The Problem

Suppose you need to create an API that produces XML containing data about a book store. You've got a store with books, stock levels, that sort of thing.

The OO Solution

This is a great problem for OO modelling: it natually decomposes into modellable objects. So we'd have something like this:

public class Store
{
    public Store(string address, string phone, List<Book> books)
    {
        Address = address;
        PhoneNumber = phone;
        Books = books;
    }

    public string Address {get; set;}
    public string PhoneNumber {get; set;}
    public List<Book> Books {get; private set}

    public string ToXML()
    {
        var doc = new XmlDocument();
        var store = doc.begin("store");

        store.Add("Address", Address).Up()
            .Add("PhoneNumber", PhoneNumber);

        var books = store.Add("books");

        foreach(var book in Books)
            books.Children.Add(book.ToXMLFragment());

        return doc.ToString();
    }
}

public class Book
{
    pubilc Book(string title, List<string> authors, double price, int stockLevel)
    {
        Title = title;
        Authors = authors;
        Price = price;
        StockLevel = stockLevel;
    }

    public string Title {get; set;}
    public List<string> Authors {get; set;}
    public double Price {get; set;}
    public int StockLevel {get; set;}

    public string ToXMLFragment()
    {
        var book = new XmlElement("book")
                    .Add("title", Title).Up()
                    .Add("price", Price)Up()
                    .Add("stock", StockLevel);

        foreach(var a in Authors)
            book.Add("author", a);

        return book;
    }
}

We could flesh out the model further, but you get the idea. Each class defines the structure of the data needed, and instances of the classes maintain the state. Each object is responsible for generating its own part of the XML hierarchy. Simple and encapsulated.

(Sidenote: if that XML construction looks easier than what you're used to in C#, it's because I'm using XMLGuy).

The Functional Solution

In a statically typed functional language, you might go ahead and define data types that resemble the classes above, then functions to work on thosse types. Javascript, however, is dynamic and classless, so there's not really a sensible way to define a data type.

Our convert to XML function still needs a certain structure to its input data. The cleanest way to do this is with some helper functions:

function store(addr, phone, books){
    return {address: addr,
            phoneNumber: phone,
            books: books || []};

function book(title, authors, price, stock){
    return {title: title,
            authors: authors || [],
            price: prince,
            stock: stock};
}

Each function creates an anonymous object that contains the properties needed by the XML generator. Keep in mind of course that any object with the right properties could be passed in, no matter how it was constructed.

And now the generate function:

function ToXML(store){
    function addBook(book){
        books.ele("book")
                .ele("title", book.title).Up()
                .ele("price", book.price).Up()
                .ele("stock", book.stock);

        each(book.author, function(a){ book.ele("author", a)};
    }

    var doc = xmlBuilder.create();
    var root = doc.begin("store");

    root.ele("address", store.address).Up()
        .ele("phoneNumber", store.phoneNumber();

    var books = root.ele("books");

    each(site.books, addBook);

    return doc.toString();
}

The thing to notice here is that the logic to produce XML for a particuar data structure is not encapsulated in the same construct that defines the data structure: instead it will be a function that lives elsewere. A function transforms data; it doesn't own it.

This sort of separation is of course possible in OO, athough it's not how you would generally approach it. However, if you had a pre-existing domain model for stores and books you might create a separate class that produces XML from a Store object. You might even extract IStore and IBook interfaces to stop the generator being tied directly to your domain.

Where do the functions live?

Wherever makes sense :) Javascript itself doesn't (yet) have inbuit module support, but node.js and some client-side libraries do provide it. I tend to group functions that work on the same data structures into a module, so in this case I'd put the object creation functions and the main generation function in a single module.

One of the advantages of OO is that it gives you a structure and a direction when it comes to the 'right' way to group things, but that can also be a limitation that prevents you from structuring your code in the most sensible way.

What's the big deal?

You may be thinking that, while the solutions are somewhat different, they are essentialy the same. Taking a functional approach didn't really make the solution any easier or clearer. There are some benefits though. The data structure code is solely responsible for creating the data structure, which makes it easier to re-use without modification. That is, you can create other users of the structure that are essentially peers of the XML generator.

Additionally, the code that works on the data structure to create the XML is all together, making it (to me) easier to follow. The code is grouped by function (generate XML) rather than by model (a Book).

Best of all though, you're not fighting the language. The OO C# code is pretty readable, but if we were to use Javascript the Store 'class' would look something like this:

function Store(addr, phone, books){
    // protect against people forgetting the 'new' keyword
    if(!(this instanceof Store))
        return new Store(addr, phone, books);

    // properties with getters and setters
    var addr = addr;
    this.getAddress = function(){ return addr;};
    this.setAddress = function(val){ addr = val;};

    var phone = phone;
    this.getPhoneNumber = function(){ return phone;};
    this.setPhoneNumber = function(val){ phone = val;};

    var books = books || [];
    this.getBooks = function(){ return books;};
    this.addBook = function(book){ books.push(book);};
}

Store.prototype.toXML = function(){
    var doc = xmlBuilder.create();
    var root = doc.begin("store");

    root.ele("address", this.getAddress()).up()
        .ele("phoneNumber", this.getPhoneNumber());

    var booksXML = root.ele("books");

    each(this.getBooks(), function(book){
        booksXML.children.push(book.toXMLFragment());
    });

    return doc.toString();
}

It's still fairly clear what's going on, but the hoops you need to jump through to get proper encapsulation are pretty horrible. And if you want to make inheritance work somewhat classically, it gets messier. Essentially you're building OO plumbing on top of the language.

Conclusion

More than just letting you write cleaner Javascript, thinking functionally will encourage you to approach probems in a different way. You may start to find that re-use via composition (bringing many functions together to do something) rather than inheritance feels much cleaner and much less fragile. And if you're like me you'll find the functional approach a nice change from years of OO :)

comments powered by Disqus