Skip to main content

Computer Science: Prototypal Match Templates

In object-oriented programming languages, you can subclass an existing class and override a few of its methods. This allows you to take an existing piece of code and tweak it for your own use. However, it's only as granular as the methods that you are overriding. If you want to change one line in a 30 line method, you either have to refactor that 30 line method into several methods (which is the right thing to do if you're in control of the code) or you have to copy the 30 lines and modify that one line (which may be the only thing you can do if you're not in control of the code). Sometimes I actually do both. If I'm using a third-party library that has a 30 line function that I want to change one line of, I copy the whole function into my class, and then refactor it there as if I were refactoring the superclass.

Genshi has a cool mechanism called match templates. I assume XSLT has this too, but since I don't know XSLT, I can't say for certain. Genshi's mechanism let's you write an HTML template and say things like "Every time you see HTML that matches this XPath expression, replace it with this HTML". It turns out that this is a really flexible way of doing templating. It makes it really easy to setup a global look-and-feel and then customize it however you want on a per-template basis. You just write match templates that "tweak" the global look-and-feel. Unlike the template design pattern, the person writing the global look-and-feel doesn't need to do anything to set you up. He doesn't need to create "hook" divs for you to override or anything like that. You can tweak anything you like.

I wonder if the same thing might be useful as a replacement for object-oriented inheritance. Instead of subclassing a class and then overriding some of its methods, you subclass a class and then write match templates that "tweak" the code in the superclass. I think "prototypal match templates" are a good name for this, because you're taking a prototype piece of code and then tweaking it to your needs as if you were doing text substitutions. You would need something like XPath that would make sense for the programming language you're using, but that's not too hard to imagine.

Ok, let me show you what I have in mind. Let's start with how I would do things today:
class Greeter:

"""Let's pretend this is in a third-party module."""

def greet(self, sex):
print 'Howdy,',
if sex == 'female':
print 'good looking!'
else:
print 'stranger.'
Here's my subclass:
class PoliticallyCorrectGreeter(Greeter):

def greet(self, sex):
"""I either have to refactor or I have to duplicate code here."""
print 'Howdy,',
if sex == 'female':
print 'person of the opposite sex.'
else:
print 'stranger.'
If I had prototypal match templates (including some sort of XPath-like syntax for Python syntax), I could write something like:
class PoliticallyCorrectGreeter(Greeter):

match def[name='greet']/if/print[args[0]]:
'person of the opposite sex.'
Ok, I can imagine that many people are going to hate this idea. That's why I'm turning off comments...just joking ;)

One valid complaint is that this breaks encapsulation. I'm overriding a method in a way that requires knowledge of the implementation. That's a fair point. However, I'd like to punt on this issue. When I'm subclassing something, I often need to understand the implementation of the superclass anyway to do what I need to do. I think that if you subclass a class, you're "closer" to that class than if you were just using it. If the superclass's implementation changes, it'll break my code. That's okay. I can look at how it's changed and fix it. That's just a normal part of my life as a modern programmer. Furthermore, I think there are smart ways to use this feature and not-so-smart ways to use this feature. It's a hammer--don't hit yourself over the head with it ;)

Next, I'm sure there's a Lisp programmer out there somewhere saying, "Yeah, been there done that. Haven't you heard of macros?" That's a good point too. Lisp is nice because the syntax tree is just Lisp data. That's one of the nicest things about Lisp syntax. However, an XPath-like syntax for navigating an AST for, say, Python would let Python programmers use some of the same tricks that Lisp programmers use. McCarthy said, "Any sufficiently complicated C or Fortran program contains an ad-hoc, informally-specified bug-ridden slow implementation of half of Common Lisp," but I sure do like the way that Python makes Lisp-based ideas more readable and accessible to the masses. Back when Lisp was created, doing things like what I'm talking about just didn't make sense. Making the AST normal Lisp data was a brilliant move. These days with strong reflection in scripting languages, it does make sense to play with the AST in your code. You can have your syntax and tweak it too :-D

Comments

Anonymous said…
You definitely came up with one of the best descriptions of the benefits of match templates in Genshi I've seen so far… I personally always have trouble expressing this aspect :P
S.B. said…
Generic Functions from pyProtocols come to mind.
jjinux said…
> Generic Functions from pyProtocols come to mind.

I can see why. They have a match mechanism. That's not so different than the match mechanism in languages like Haskell. However, I think doing matches on the syntax tree of the code itself rather than on the incoming data is interesting.
Ryan Braley said…
It is surprising that you haven't heard of one of the greatest obscure languages:
Scala www.scala-lang.org/
It is exactly what you want. It is a purely OOP language. It is a purely functional language. Scala fused them into the same elegant thing. All functions are values, all values are objects. This fusion enabled Scala to do Actor Model concurrency (like erlang or stackless) in a library, without having to fork the compiler or some such nonsense. It supports pattern matching and functional programming like haskell but with all the productivity and reflection of ruby or python, So you can match on the types and blocks of code if you like, for free. The coolest thing is that although it writes like python, it is statically compiled and type-checked and is fully compatible with all Java and .Net libraries and runtime environments. So you have RAD code with more elegant expressive power than python or haskell, but it is enterprise ready. Spread the word and the love.
~Ryan
jjinux said…
Ryan,

Thanks for the Scala plug. I've used the words "actor" and "Scala" twice today while talking about concurrency, but I haven't gotten around to learning Scala. Thanks for the tip :)

To be fair, my prototypal match templates still sound very different than Scala.

Best Regards,
-jj

Popular posts from this blog

Ubuntu 20.04 on a 2015 15" MacBook Pro

I decided to give Ubuntu 20.04 a try on my 2015 15" MacBook Pro. I didn't actually install it; I just live booted from a USB thumb drive which was enough to try out everything I wanted. In summary, it's not perfect, and issues with my camera would prevent me from switching, but given the right hardware, I think it's a really viable option. The first thing I wanted to try was what would happen if I plugged in a non-HiDPI screen given that my laptop has a HiDPI screen. Without sub-pixel scaling, whatever scale rate I picked for one screen would apply to the other. However, once I turned on sub-pixel scaling, I was able to pick different scale rates for the internal and external displays. That looked ok. I tried plugging in and unplugging multiple times, and it didn't crash. I doubt it'd work with my Thunderbolt display at work, but it worked fine for my HDMI displays at home. I even plugged it into my TV, and it stuck to the 100% scaling I picked for the othe

ERNOS: Erlang Networked Operating System

I've been reading Dreaming in Code lately, and I really like it. If you're not a dreamer, you may safely skip the rest of this post ;) In Chapter 10, "Engineers and Artists", Alan Kay, John Backus, and Jaron Lanier really got me thinking. I've also been thinking a lot about Minix 3 , Erlang , and the original Lisp machine . The ideas are beginning to synthesize into something cohesive--more than just the sum of their parts. Now, I'm sure that many of these ideas have already been envisioned within Tunes.org , LLVM , Microsoft's Singularity project, or in some other place that I haven't managed to discover or fully read, but I'm going to blog them anyway. Rather than wax philosophical, let me just dump out some ideas: Start with Minix 3. It's a new microkernel, and it's meant for real use, unlike the original Minix. "This new OS is extremely small, with the part that runs in kernel mode under 4000 lines of executable code.&quo

Haskell or Erlang?

I've coded in both Erlang and Haskell. Erlang is practical, efficient, and useful. It's got a wonderful niche in the distributed world, and it has some real success stories such as CouchDB and jabber.org. Haskell is elegant and beautiful. It's been successful in various programming language competitions. I have some experience in both, but I'm thinking it's time to really commit to learning one of them on a professional level. They both have good books out now, and it's probably time I read one of those books cover to cover. My question is which? Back in 2000, Perl had established a real niche for systems administration, CGI, and text processing. The syntax wasn't exactly beautiful (unless you're into that sort of thing), but it was popular and mature. Python hadn't really become popular, nor did it really have a strong niche (at least as far as I could see). I went with Python because of its elegance, but since then, I've coded both p