Homoiconicity [in Rails]
For the past ~ year, I’ve been working full time on helping students save money on college tuition with Quottly. Quottly is a rails application, and far from my first - I think my first Rails app was Rails 3.0, build working off of a book written for 2.1, and now we’ll shortly be upgrading to Rails 5.
One thing has previously never bothered me about ruby and rails, but now is: the lack of, depending on your perspective, homoiconicity or idempotent database operations.
Here’s the situation. Traditional applications have a sharp divide between data and code. Things that go in databases are data. Things that the programmer writes, and that are stored in your version control system, are code. This works pretty well for most applications. I have a users table in my database, and if I need different code to work with some subset of users, I just give them an appropriate class in my class hierarchy. When the user’s detail changes, I just update the appropriate database row.
Quottly, on the other hand, is sort of an odd beast. There are lots of objects that, in some senses, straddle the line between data and code.
Let’s take a university as an example. Quottly matches college students with the best classes for them across all schools, so we have to store some information about each university that we work with in our database.
What is a university? Parts of it - for example, let’s say the current price of a credit hour, are clearly data. But this “data” has some interesting properties:
One, there can only ever be one instance of a university. There is only one University of Florida, and it is uniquely named; if I ever have two instances of the University of Florida in my database, something is very wrong. And the existence of the University of Florida has nothing to do with its existence in my database, it is, in some ways, a property of the world.
Second, there are bits of things that look very much like code associated with each university. For example, the University of Florida has a rule that you must complete the last 30 credits of your degree at UF. Is that data, or code? I can convert it into “pure data” by making some sort of LastCreditsRule
class, putting a row in my database with 30
as the value for “number of credits”, and associating it with the database row that’s associated with UF. But, I could equally (and in many ways, more easily) define that rule with a small bit of code - in effect, that LastCreditsRule
is a Verb in what should properly be a Kingdom of Nouns.
I can combine this with a whole lot of wrappers - or some nonstandard active record modifications - to ensure that the create
and update
operations for the University model in Rails both correspond to what I would describe colloquially as ‘create-or-update-as-appropriate’.
If I wanted to, I could equally implement the University of Florida functionality by making a University of Florida
class, and defining a bit of code in it that would implement the last-30-credits rule. In this case, it’d be pure code - the University of Florida would be a singleton class, information about which is only stored in our version control system, and which would require a redeploy to fix.
Neither of these solutions is very appealing. The first, the option in which we treat the university as pure data, involves creating (potentially) a whole lot of Verb classes (things ending in Rule
or Policy
), which are a code smell. There’s going to be a lot of dumb classes floating around, and that whole class hierarchy will be hard to maintain.
The second option is just not very scalable - if we expand to all 3,000 universities in the US, I’ll end up with 3,000 files in my /models/universities
folder? But, that solution does have the nice option of letting me easily grab the object I want by its globally unique name whenever I want, which can be convenient.
There is a third, halfway option, in which ruby code is shove into a database - making it into data - but then is eval()
’d out of the database to implement the -Rule
classes. This has the advantage of making the Rule
class reasonable, constrained, and somewhat maintainable, as the class won’t need to change, but has other disadvantages (security issues being one of the major ones, in addition to inelegance, and a lack of reliability and ability to test easily).
I believe that (rare) situations like this are where homoiconic languages show their value. Homoiconic languages “allow all code in the language to be accessed and transformed as data, using the same representation” - that is, to the programmer, there’s no difference between data and code. The languages in the lisp family are the only example I know of.
In Lisp, I think I could implement this (at first) with a simple file that defines some structs that represent universities, eventually replacing that with some sort of macro that can fetch the university from a database or hash table lazily - I am, sadly, not well versed enough in lisp to understand how that would work, exactly, but I believe it could be done.
This would seem to me to give the best of both worlds. Since there is no difference in representation between code and data, no decision need be made about what is code and what is data; the different code-like and data-like aspects of the objects may be put into their proper storing places appropriately and easily (and that decision can be changed later without much fanfare).
I am interested in which other situations homoiconic languages would have obvious value. I believe that anything that involves diverse business rules would be a prime candidate - perhaps medical billing systems? - as ‘rules’ naturally fall on the line between code and data. Or, if you have an idea on how to address this situation in ruby/rails, I’d love to hear it! If you have an example, ping me on twitter - @jamescgibson.