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!'
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.'
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


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:
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.
jjinux said…

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,

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

Drawing Sierpinski's Triangle in Minecraft Using Python

In his keynote at PyCon, Eben Upton, the Executive Director of the Rasberry Pi Foundation, mentioned that not only has Minecraft been ported to the Rasberry Pi, but you can even control it with Python . Since four of my kids are avid Minecraft fans, I figured this might be a good time to teach them to program using Python. So I started yesterday with the goal of programming something cool for Minecraft and then showing it off at the San Francisco Python Meetup in the evening. The first problem that I faced was that I didn't have a Rasberry Pi. You can't hack Minecraft by just installing the Minecraft client. Speaking of which, I didn't have the Minecraft client installed either ;) My kids always play it on their Nexus 7s. I found an open source Minecraft server called Bukkit that "provides the means to extend the popular Minecraft multiplayer server." Then I found a plugin called RaspberryJuice that implements a subset of the Minecraft Pi modding API for B

Creating Windows 10 Boot Media for a Lenovo Thinkpad T410 Using Only a Mac and a Linux Machine

TL;DR: Giovanni and I struggled trying to get Windows 10 installed on the Lenovo Thinkpad T410. We struggled a lot trying to create the installation media because we only had a Mac and a Linux machine to work with. Everytime we tried to boot the USB thumb drive, it just showed us a blinking cursor. At the end, we finally realized that Windows 10 wasn't supported on this laptop :-/ I've heard that it took Thomas Edison 100 tries to figure out the right material to use as a lightbulb filament. Well, I'm no Thomas Edison, but I thought it might be noteworthy to document our attempts at getting it to boot off a USB thumb drive: Download the ISO. Attempt 1: Use Etcher. Etcher says it doesn't work for Windows. Attempt 2: Use Boot Camp Assistant. It doesn't have that feature anymore. Attempt 3: Use Disk Utility on a Mac. Erase a USB thumb drive: Format: ExFAT Scheme: GUID Partition Map Mount the ISO. Copy everything from