So eval has received a lot of slack over the years. It’s insecure, unhygienic, and generally bad for everyone’s health.
Every language that has this notorious function — PHP, Python, JavaScript, VB, Bash — bemoans its existence. They also warn of the terrible consequences that should arise were the reader foolish and foolhardy enough to invoke it.
While I could speak in favor of eval in those languages, I’d like to tell you a story from my youth instead. There was (and, some say, still is) a little program called MapTool. This was a virtual tabletop application of the olden days, before the Cloud when ports had to be opened and routers configured. The whole thing was generally a considerable fuss.
The community of that program was rich and vibrant. We, for I was one of its members, partook in many games and enjoyed them greatly. As with any virtual tabletop, one of the most prized features of MapTool was its dice roller. It started as something similar to an IRC command:
Code: Select all
/roll 1d20+1d8+1
Soon, however, things such as this became commonplace:Boromir attacks the orc with [1d20 + 16 - 5] and [2d6 + 5 + 16 -2 + 25 + 19 + 127] damage.
However, that stuff became pretty silly if Gornak the Destroyer missed, so after a while, an if expression was born, allowing Gornak to act accordingly:Gornak the Destroyer lifts his Dreadhammer of Darkness +3 in two ham-like fists, the weapon trailed by wisps of shadow in a color deeper than the black of night. With a chilling roar (*Chilling Roar (Ex): Will save [CasterLevel / 2] or be shaken for [1d4] rounds) he brings the weapon down on the hapless [TokenUnderCursor.name], rolling [1d20 + Strength + BAB - PowerAttackBonus + 3] for attack. Gornak’s Dreadhammer bears down upon the [TokenUnderCursor.name], tearing apart flesh and sinew and carving through bone, dealing [2d6 + 2 PowerAttackBonus + 1.5 Strength + 4d6 + 2d8 + 1d6 + 1] damage.
Code: Select all
[
if(AttackRoll < TokenUnderCursor.AC,
"But Gornak underestimated his own incredible might, causing his weapon to smack into a nearby wall instead.",
"Gornak hit the thing, roll damage.")
]
And yet, built upon the foundations of such a simple command, great frameworks would be born. You see, in their pursuit of greater automation, a few canny dungeon masters turned to an ancient power. It was a word that, when wielded properly, allowed them to transcend the bounds the devs inflicted upon them and weave works of greater and greater intricacy.
Code: Select all
[
Eval(
if(AttackRoll < TokenUnderCursor.AC,
"But Gornak underestimated his own incredible might, causing his weapon to smack into a nearby wall instead.",
"Gornak hit the thing, dealing [MyToken.damageRoll] damage."
)
)]
And lo, soon the devs saw the works of the masters, and so the CODE command was brought into the world. Making that use of eval unnecessary, it allowed storing and executing code as a more fundamental concept, including to be used in that if expression.
And so the eval command was put aside, for after enduring the most painful debugging sessions, you can imagine the masters understood its reckless power. It was set aside — but only to be wielded once more for another worthy cause.
And what became of MapTool? Well, roll20 came out, and everyone just stopped using it because of how frustrating the networking was.
TL;DR
I’m not sure why I had to write that. Sometimes I just have to appease the demons that live in my head.
Anyway, the point I was coming to in the narrative is that eval lets users go beyond the limitations of the language. Using this ability, users can implement the functionality they need, informing the development of the language.
It’s true that most languages that have eval hate and fear it, but these are generally established languages used in production. Most of those languages started as scripting languages, and as they grew, eval was used freely to cover the gaps. In some languages like JavaScript, it’s fair to say it’s commonly used in production, though largely by dependencies of whatever you’re writing.
Although the MDN article has countless warnings about its use, JavaScript’s eval is there to stay forever, not just because of some ancient code that has to keep functioning but because many JavaScript libraries use it for all kinds of things to this day.
Usecase in AHK2
Here is a simplified example. I want to write a function called FuncRefArgsToArrayReturn(f). Here is what I want the function to do in pseudo code:
Code: Select all
FuncRefArgsToArrayReturn(f) {
toArrayReturn() {
f(&a1, ..., &aN)
return [a1, ..., aN]
}
return toArrayReturn
}
- A function with N output arguments, and
- A function returning an array of N elements.
It’s possible to solve this issue without adding eval, but eval gives way more functionality than just this. You can also use it to:
- Turn a function taking n arguments into one taking n-1 arguments.
- Do the opposite.
The use cases for eval are limitless. Although it’s said that eval is evil, in the words of the C++ Super-FAQ, it’s normal to use evil constructs
Alternatives
Generally, there are ten better alternatives for anything dynamic code evaluation accomplishes. For example, a carefully designed metaprogramming system, various forms of reflection to create objects at runtime, and things like that.
I'm suggesting it and not something else because there isn't much to be designed -- it's just another way of invoking the language -- and it's usually fairly simple to implement in languages with an interpretation stage.
It can create a host of terrible bugs, but from my experience, people tend not to use these special language features if they are frustrating enough, and anyone who has ever tried to use eval in any language for any purpose knows it's plenty frustrating. Bearing that in mind, perhaps it can also be given a very long-winded name.