Not long ago a colleague of mine and I got into a semi-intellectual discussion about the use and horrendous dangers of Meta Programming if abused. He talked about a Python class used in production at a once popular social networking site where he used to work at, this class was written in such a way that it would return two very different values depending on how many times it was initialized. This meant that any small refactor had a chance of breaking critical pieces of software with little to no indication for debugging.
This story inspired me to explore how many conventions and rules we can break within a single article and that’s exactly what we are going to do with a little help from JavaScript, the king of all meta and illogical goodness programming has to offer today but of course meta programming and meta classes are not “bugs” per se and are readily available within many languages to be used for good, but our goal here is to do the exact opposite so don’t count on this article as a great introduction to meta programming.
Let’s start our journey down a very wrong path by messing around with string concatenation. If we assume that the following pseudo statement holds true:
x == "hello"
you would expect that the following would result in hellohello
concat(x + x) == "hellohello"
But this article is about breaking the rules so let’s reduce this certainty by utilizing Object#toString
and assigning it to a function that return a random word at each call using chancejs:
const x = { toString: () => chance.word() };
Now the following statement may at some point in time hold true:
(x + ' is ' + x) === "suwni is onu"
Try running the following a couple of times:
A reasonable expectation when deducting a single constant from itself would be to get zero as the result:
x - x = 0
But we can break this rule, what if:
x - x = 0.2353256
And immediately after it equals to another value? In JavaScript we can abuse Object#valueOf
to return a primitive/number on demand, this in itself was recently popularized in this StackOverflow question. Utilizing this method and Math#random
, we can not only break consistency but also expectations:
x.y = 2
and x.y = 14
, let’s keep track of x.y
with the help of setters. Setters are called whenever a property is being modified:
Can we have an object that holds all properties ever? Not really but we can have one that lazy-loads the value based on a function. ES6 blessed us with Proxies that allow us to go super meta! Beyond just getters and setters Proxies can intercept most possible interactions with a given target/object. In this case we want to intercept any gets on the object, note that if we were to use getters we would need to know the properties to define at creation time, with Proxies, not so much:
Examples above are obviously not real-life use-cases of Meta Programming but abusing certain properties of it. There are many great use-cases and I would definitely encourage the reader to take our fun experiment with a grain of salt and explore the ideas put forward above on your own.
There are many great use-cases to going meta with your favorite programming language (given that it is supported) expressjs uses getters to lazy-parse queries and you can lazy wrap properties post-creation time with features like Proxies but in the wise words of my seasoned colleague whose favorite language is Haskell, don’t use meta classes and possibly do you best to avoid the examples given above unless you are developing something as crazy as your code.