Monday, March 5, 2007

Objects add traction to a slippery slope

(Warning: long programming-related post.)

Almost all of my spare coding time in the past few months has been in Lua, a scripting language. There are a few things I love about it. I love how clean and expressive certain things about weak typing can make code. I love how functions can have multiple return values. I love how it combines VB's lack of semicolons yet still not care whether or not you have line breaks like C. I like how hashtables are first-class language constructs. But there's a lot that I hate about it. I hate that it isn't object-oriented. I hate that it isn't strongly-typed. I hate that it's a scripting language and still is case-sensitive. Et cetera.

After having developed in Lua for quite some time now, on probably the biggest scripting project I've ever undertaken, I've come to the same realization that just about everyone comes to: using a scripting language is a slippery slope. You find yourself more and more accepting of weird constructs that you normally wouldn't allow. My scripting project needs to manipulate command line arguments. Normally I'd write (or plug in) a class to parse the command line arguments and keep the code clean. But Lua doesn't have classes. You can kind of fake it like you can in JavaScript (you sort of make your own vtable), but I really don't like the way the code looks. So my command line-parsing code in my project is just a string of if statements.

if Command == "ids on" then
elseif Command == "ids off" then
elseif Command == "debug on" then

It's gross. But trying to fake OO in Lua would result in a bunch of differently ugly code. And really, those if statements aren't hurting anything.

So now it's okay to just have a big string of if statements as your command line parser. If that's okay, what else is okay? I have to be ever-vigilant to make sure that the code stays acceptable. Suddenly, the quick and easy scripting language starts requiring more effort than a "big boy" language. The fact that the language doesn't easily support writing code in the better way practically forces you to write crappy code. The Lua code seems to just gravitate toward mediocrity.

And, it's harder to improve the Lua code over time than in non-scripting languages. Refactoring is horrible... if you can't do it with Ctrl+H you probably shouldn't. Renaming variables is risky because if you miss just one instance of MyVariable and it stays MyOldVariable, you end up with two, both implicitly defined because you can't declare variables in Lua (except to scope them). The same problem exists with functions. It's perfectly valid Lua code to call MyFunctionX() from anywhere you like, without ever defining MyFunctionX(), because, you know, maybe someday you'll define one. You won't even find out until you try to execute it. Argh. I hate scripting languages.

Sure, it's at least somewhat a comfort thing. Maybe with a couple decades of Lua experience (shudder), and a big bag of best practices, I wouldn't complain anymore. I would probably learn to accept the creepy hacks that give you sort-of-OO support in Lua. But for now, the things that are supposed to make Lua "easy" and "friendly" are making it difficult and hostile. It's something that you're supposed to just "know" as a principle of programming languages, but I'd never really experienced it until now. My past exploits in scripting languages were limited to a couple pages of code at best, so these sort of things never really cropped up as big problems. (And things that normal people would write script to do I would write "real" code for out of my hatred of script.) I knew the pain of debugging script, but hadn't experienced the slippery slope and how it becomes harder and harder to write good code. It's probably a good lesson to learn first-hand. But it's a painful one.

Actually, I think this lesson can be generalized to something else I've been thinking about recently. Developers (or at least me) tend to avoid programming paradigms that make it seem like the language is fighting you back. When languages feel like they're working with you instead of fighting you, you write better code. To me, a language feels like it's working with me instead of against me when the code I'm writing is clear and concise.

It's often advised to create all of the objects in your programs through class factories, or at least static factory methods. But people don't do this, because using class factories for simple operations makes your code uglier and less readable than just using the new operator. If languages made class factories first-class citizens, and the resulting code wasn't so needlessly verbose and weird, people would write better code.

I think this post has rambled enough for now.


James said...

I came across a recent blog entry arguing 'all OOP all the time can be POO':

Doesn't touch so much on script vs. program like you do, but still a good, slightly opposing viewpoint.

Travis said...

Thanks for the link; it's a good article. I actually think that the opinions in my post versus those in the one you linked to are pretty compatible for the most part. You can, of course, overdo anything. But, there's a difference between setting up a hierarchy of abstract base classes and file after file of code that isn't really needed yet but might be someday, and "light" OO work like abstracting out groups of common functionality into a class that can be reused later. You have to consider whether or not using OOP ACTUALLY makes the code easier to read, understand, and maintain.