Porting the Rails Inflector to Common Lisp
While building out Vana, a new Common Lisp web framework loosely based on rails, I ran into the usual problem that somehow the Common Lisp ecosystem is a hodgepodge of functionality. Even though there are great packages available, there are so many gaping holes it feels like lisp must have just come out last year. In this case, I ended up needing Railâs inflector, and there wasnât anything quite like it (sidenote: Before you got telling me about FORMAT, yes I realize is has a plural directive, and no itâs not the same damn thing).
Well, nothing gets better unless people pitch in, so I went into the Rails source and ended up porting the pluralization/singularization bits to vana-inflector. Itâs written to be immediately understandable to a programmer of another language with only a few days/weeks training lisp.
If you want to jump in right away, check it out on github. If youâre interested in the porting experience, read along.
Porting
Coming mostly from rails, I wanted the syntax to be intuitive for others making that jump. That means we can take a look at what the railsâ interface offers:
plural(rule, replacement)- adds in a new rule for making singular->plural. Rule can be a regex or a string.singular(rule, replacement)- same for plural->singular.irregular(singular, plural)- adds in a rule for a specific singular-plural set (e.g. no regexes)uncountable(*words)- adds in any number of words as uncountable
And the big ones:
pluralize(word)- e.g."octopus".pluralize=>"octopuses"singularize(word)- e.g."octopuses".singularize=>"octopus"
Also, in the action_view text_helper.rb, thereâs this gem:
pluralize(count, singular, plural = nil)- e.g.pluralize(2, 'person')=>"2 people"
That just about covers the gamut of what I wanted. So all thatâs left is to implement these!
Implementation
First things first, letâs encapsulate all of this in a package, and export the above interface:
(defpackage :vana-inflector
(:use :cl :cl-ppcre)
(:export :pluralize
:singularize
:plural
:singular
:irregular
:uncountable))
A quick look into the inflector source shows that the rules are just arrays of regular expressions. Fair enough, I think we can handle that.
English is a nice language made even more colorful by all its uncountable nouns, and we shouldnât bother trying to pluralize these. Weâll start with a basic list that weâll let the user modify via our exported interface.
And then thereâs every language studentâs nightmare: the irregularities. In this case, general rules wonât help, we have to keep track of them individually.
Usage
Letâs make it so all this is actually useful:
(pluralize "octopus") ;; => "octopuses"
(pluralize "person") ;; => "people"
(pluralize "ox") ;; => "oxen"
(singularize "mice") ;; => "mouse"
Thoughts
It could be cleaned up here and there, but it was a side project I did on a late Friday night while working on other things, and for that itâs not half bad.
However, it does highlight one of the dangers of working in lisp that I mentioned before - there are a ton of things you take for granted in a modern languageâs ecosystem that youâll have to hack at and reinvent on your own when working in Lisp. Itâs not true of everything, and in some cases itâs really a great opportunity to re-invent and improve while better understanding the original, but itâs a really critical issue to be aware of if youâre on a time-sensitive deadline and are wavering between lisp and some other language.
TODO
- It should handle things like CamelCase to camel_case (or camel-case), and vice-versa. Iâll add this when I actually need it (probably will soon for the parenscript bits), but feel free to jump in.