How to Write a Wrapper Library
On the last entry I wrote I got a comment asking how to write a wrapper library, and I wanted to respond to it in a somewhat systematic fashion. So, here is my approach to and thoughts about writing wrapper libraries on top of existing libraries.
To write a wrapper library is to create a layer of abstraction on top of an existing body of functionality. Each additional layer of abstraction adds additional room for error, and the typical wrapper library doesn't add any additional functionality. Reflecting on those two points, an astute reader might ask: "What's the freaking point?" Well, Mr. Astute Reader, the point is we are redefining a library's interface. A well conceived redesign of a library's interface can turn an awkward library into something pleasant to use, but poorly executed a wrapper can just as easily restrict the original library's functionality or obscure the cost of library operations.
Why Write Wrappers
Adding additional layers of abstraction to a library is not an inherently good thing. Each layer brings with it additional interactions to butcher, complexity for misunderstand, and lines of code to be maintained. As such, a wrapper layer--like any other layer of abstraction--must be willing to justify its existance.
In general, there are two good reasons for justifying a wrapper library:
To eliminate pain points. Often times a library will be extremely useful, but will be sabotaged by one or two extremely awkward points. For example, your library might be returning its data in a giant XML document, when you really want a list of dictionaries. Or the querying language might be more complex than necessary for standard usage (like the helpful ORM layers over interactions with SQL databases).
To expose functionality in a better suited design. Some libraries will lie to you in their interface. Not a blatant act of malicious deception, but instead just poor design choices. For example, an interface might suggest that the underlying operations are tree-like (cheap lookup) when its actual operations are more similar to an unsorted array (expensive lookup).
That makes it difficult to make efficient usage of the library, because you are making seemingly reasonable decisions on how to use the library, but are unknowingly using a great deal of inefficient operations.
Sometimes, too, the initial interface isn't brutally poor or deceptive, but it doesn't quite mesh with the general language philosophy, and is inflicting a bit of mental entropy on seasoned language practictioners (for example, if a Python library is not 'Pythonic', or if a Scheme library is not written in a functional style).
Picking your Poison
Now you have to pick the pain points to bandage or interface to expose functionality. Finding pain points is usually quite intuitive (did you curse when you first had to figure this out? Its a pain point), but interface design improvements are not always as obvious (if they were, the library's original author would have hopefully already made them).
A helpful general guideline is to look for familiar concepts and data structures that users are already familiar with. In particular it is helpful to choose datastructures that exhibit similar operational costs (extensive adds, but cheap removes, or cheap seeking of position but expensive seeking by value, etc), which makes it easier for users to intuit proper usage of the library.
Implement and Innovate
After you've found the handful of problems you want to address, and decided upon an intuitive interface for accessing the library's functionality, then you go ahead and implement them. Start simple and get something working, and then evolve it from there. As you're evolving your interface, you may notice places where extra functionality will fit nicely. Go ahead and add it! Part of the fun of writing a wrapper is improvisation. You're only recreating the interface instead of the entire depth of functionality, so experimentation is relatively quick and painless.
Case Study: The BossArray Wrapper Library
As an example, lets look at BossArray, which is a wrapper I wrote around Yahoo's BOSS Mashup Framework. The Yahoo BOSS makes it easy to retrieve Yahoo search results, and the Yahoo BOSS Mashup Framework is a verstitle Python library for interacting with Yahoo BOSS. Its really helpful, but I had a few things I wanted to improve upon.
- The simplest use case (searching for a search term) was a bit complex.
- Wasn't easy to deal with more than 50 search results at a time.
- Had created its own API, but I felt that mimicking the Python List api would be more intuitive.
After deciding on mimicking the list api, it turned out that it would be easy to simplify the easiest use case and handle dealing with large quantities of search results by fully embracing the list api. Thus, all the improvements tied into the new interface, and things became quite simple.
While implementing the new interface, it also occured to me that it would be a nice place to add a caching layer, so I included that functionality as well. All in all, this wrapper was about one hundred lines of code (mostly the caching mechanism), and provided a more natural interface to the BOSS service for those with very simple needs.
Wrapping it All Up
A wrapper improves a library by replacing its interface. Sometimes the new interface begs some new functionality, and thus becomes greater than the sum of the previous library interface, but just as often a wrapper can be quite helpful without creating additional value in terms of usability instead of functionality. Writing a wrapper is a fun, easy, and relatively quick way to contribute to a project you are interested in, and can help open the doors to interesting debate and discussion in the project's community. I hope this article was mildly useful, and let me know if you have any questions, or think there are areas I have failed to address properly.