Cocoa and Objective-C: Up and Running (by me) is now available from O'Reilly.

Sanity Checking for Scripting Languages

My little goofball mistake with Rails reminded me of one of the biggest timesinks in working with scripting languages: there's no compiler doing sanity checking for you.

I'd say approximately 90% of the issues I've debugged over the course of nine years of using PHP, for example, are due to a mistyped variable which I think is the same as another variable. I just did the same with with Ruby. In a compiled language, you hit errors (or at least warnings) like this upfront.

What I would personally like is something that will run through my Ruby or PHP code and tell me things like "Ummm, Mr. Stevenson, you seem to have a variable here doing absolutely nothing. Is that really what you want?"

Anybody else think there's value in this? Does anything like this already exist for Ruby? Am I just passing the buck?
Design Element
Sanity Checking for Scripting Languages
Posted Dec 24, 2006 — 20 comments below




 

Mike Abdullah — Dec 24, 06 2847

I think there's definite value in this. When I moved from AppleScript to Cocoa, I found I really appreciated the compiler errors and warnings.

Sometimes with AppleScript things would just fail and I had absolutely no idea - usually it was simply down to a mistyped function name or suchlike.

Cameron Hayne — Dec 24, 06 2848

With Perl, this sort of issue is dealt with by using the 'strict' and 'warnings' pragmas:

use strict;
use warnings;

Maybe there is something similar for Ruby or PHP?

Matt Tavares — Dec 24, 06 2849

error_reporting(E_ALL)

perhaps?

Matt Tavares — Dec 24, 06 2850

Edit, i meant to link that to the php error reporting page found here:

http://php.net/error_reporting

Chuck — Dec 24, 06 2854

There is definitely something to be said for early checks, but there's also definitely a use for the extra rope a dynamic language gives you. Take a look at all of _why's ridiculously cool metaprogramming tricks (and, actually, a lot of the code for Rails) for examples of things that would drive a sanity checker crazy. Dealing with singleton methods, metaclasses, methods that don't even exist (via method_missing) and passed-around code blocks is probably doable, but it would certainly take a lot of doing.

Ruby does have some error checking, but due to the lack of a compile time, they're not automatically run for you in the process of making the program. In your particular case, the error Ruby gave was about as good as any you'd get in a C compile-time check: "No such method or variable: 'photo'".

Scott Stevenson — Dec 24, 06 2855 Scotty the Leopard

methods that don't even exist (via method_missing)

Objective-C does most of the stuff you mention, even though it's a compiled language. You can have it both ways (or pretty close). I'm not looking for it to find every single problem, just the obvious typos and whatnot.

Ruby gave was about as good as any you'd get in a C compile-time check: "No such method or variable: 'photo'"

Well, sort of. The error mentions "ActiveRecord::ConnectionAdapters::MysqlAdapter," which doesn't tell me a lot about where to look. If it mentioned the file in question (maybe it can't), that would be helpful. Maybe a trace would have helped.

Scott Guelich — Dec 24, 06 2856

As Cameron mentioned, Perl provides exactly what you want in the form of use strict and the even more pedantic use warnings.

I've become more and more interested in Ruby, but one of the hardest things to give up from the Perl world is this kind of basic sanity checking, which really isn't hard for a script interpreter to provide.

Another Ruby custom I've had a hard time getting used to is omitting parentheses when calling methods without arguments. In most languages parens are required for argument-less methods/functions, and with Perl you have the leading punctuation to differentiate variables from functions, but in Ruby you can't tell by looking whether "photos" is a variable or a method. In your case the interpreter guessed wrong -- which only made the error more cryptic.

Chuck — Dec 24, 06 2857

Well, sort of. The error mentions "ActiveRecord::ConnectionAdapters::MysqlAdapter," which doesn't tell me a lot about where to look. If it mentioned the file in question (maybe it can't), that would be helpful.

Ah, you're right. I think that's actually the fault of Rails catching the error and creating its own message. The standard error Ruby gives in that case is along the lines of [name of file]:[line number]: undefined local variable or method `[whatever]' for [the object it was called on][/i]. I'm not sure why ActiveRecord overrides that.

dc — Dec 24, 06 2858

Noone's posted the correct answer yet: unit testing! Every method you write should have at least one test to go along with it. (Ideally, you write the tests before you code the functionality: when your tests pass, the object does what you need it to do, and you're done.)

Maybe that's not what you're looking for, though. You can't write a unit test to make sure you're using the correct block-level variables, as in the specific problem you had. But having reasonably comprehensive unit tests at least makes sure that all your code gets executed. At that point, the interpreter error messages ought to be as good as compiler errors, once you get used to the bugs you're apt to introduce in whatever language you're writing in (undefined method warnings being the Ruby indication that you probably mistyped something).

Patrick Thomson — Dec 24, 06 2859

Python has PyChecker.
Additionally, Python 2.4's new decorator syntax allows for some really neat decorators that automatically check the types of arguments passed into any given function. Decorators took me a little while to grok, but I think I understand them now - and boy, are they awesome.

Ruby, on the other hand, is a tad bit more difficult to enforce type safety - I'm not sure if Kwala does it, and many people say that there's no substitute for unit tests to ensure type safety. This problem has me quite inspired now - I may take a look at it.

David — Dec 24, 06 2860

Yes, the lack of full IDE support in scripting languages can be a major hurdle, to the point where otherwise useful features of a language go unused in larger projects because there is no way to visualize those features or structures within the IDE.

For example: creating classes for OOP in Lua. Dangerous to do on larger scales because your text editor of choice won't show you "member functions" within your "classes". Which means you spend half of your days doing text searches for your functions, meaning that OOP actually becomes a productivity killer.

(BTW, Lua developers, check out SubEthaEdit for its highly useful Lua module.)

If you're creating more than a handful of functions in a scripting language, I think it's essential to start building a library of debugging routines, assertions and the like, including logging out the state of globals, printing out the stack calling chain, etc.

Also required is an extremely strict adherence to a naming system, regardless of the length of your variable/function/class names. Your scripting system generally won't care that your new variable "fooo" doesn't exist (it's a nil) and you probably won't pick it up until one of your Asserts find it.

Scripters may want to read Code Complete, especially the chapters on naming and project organization. It's even more relevant to scripts than it is for traditional, lower level languages.

Another trick is to use wrappers when loading scripts. Instead of calling the language's native load function, instead call your own wrapper which can remember details about the file (path, caller, etc.) and then attempt to load the file with error checking. If anything goes wrong, the trace can be logged to file.

When your application exits, files can also be checked off through this system to see if they've been cleaned-up properly or not. (Resource management is notoriously tricky in some scripting languages.)

Which brings me to the next trick: separate your code in to far more files than you would normally do in a compiled language. Why? Because it only takes one tiny mistake to screw up an entire script so by parcelling out your code into smaller sub-sections, you automatically help pin-point which file the problem belongs to. Then put all the sub-files into one folder for organizational purposes, where the folder is treated as a module or a unit.

Scripting languages are clearly the way of the future and are being used to extend applications in all manner of ways, but we need the tools to catch up. But that's supply/demand, so...

Finally, how to spend a day debugging in Lua:

local table = {};

That's it. No "keyword" warning error, no indication whatsoever that anything has gone wrong or that you've actually killed off a major package in the language. Heh.

labrat — Dec 24, 06 2861

I think this is a big reason why "writing tests" is such a hot topic in the rails community. You've basically got to become your own compiler.

This and the other so-called "goofball" post was a sigh of relief for me. Getting the hang of rails sometimes makes me feel like a total and complete idiot. I'm sure there are long-term productivity gains hidden somewhere but sometimes you waste large chunks of time going down undocumented rabbit holes so much that yu lose motivation.

Thanks for sharing this.

Chuck,
Anytime I see "ActiveRecord::ConnectionAdapters::MysqlAdapter", I usually assume that my MySQL server is stopped or something fudged my mysql gem.

Other than that I get debugging help from unit tests and taking a good look at the error messages that come up in the terminal (these can be quite informative when you get used to it).

ssp — Dec 24, 06 2863

Those compile time checks are a definite plus. I think this is one of the main reasons why I'm not a big fan of scripting languages. It just makes things seem disorganised and chaotic.

(In a way this may be a milder version of the problems we are seeing with HTML: Browsers are expected to render incorrect code - thus making real-world browser programming a nightmare. Just compare this to the world of TeX where documents are properly compiled, error messages are given and documents render consistently over decades.)

Chris — Dec 25, 06 2867

You mix up 'scripting languages' (perhaps rather bytecode interpreted languages?) with dynamic typing. Common mistake for C-/ASM-programmers...

Let me cite Don Roberts:
Static types give me the same feeling of safety as the announcement that my seat cushion can be used as a floatation device

Scott Stevenson — Dec 25, 06 2868 Scotty the Leopard

You mix up 'scripting languages' (perhaps rather bytecode interpreted languages?)

I do?

Jan — Dec 26, 06 2880

Scott: [...] over the course of nine years of using PHP[...]

Did you ever had a look at PHP on Trax? I wanted to get what Rails was all about but I just didn't have the time to learn Ruby first. With a huge PHP background, Trax gives you a lot of Rails goodies without taking you too far away from what you know on a pretty intimate level.

PHP on Trax is not as complete and actively developed as Rails, but all the neat things are there and it is very easy to add features. There are more Rails-like implementations in PHP which I didn't look into yet.

I managed to bring up a project in around half the time it used to take with Trax. I even bought the Rails Book and I really appreciate the effort that is put into it but as for now, I can't afford to leave PHP for Ruby for my commercial work.

Currently, I try to wrap my head around ObjC and Cocoa and Carbon on the side, which is why I don't want to mess with Ruby now — maybe later.

Scott Stevenson — Dec 26, 06 2881 Scotty the Leopard

gives you a lot of Rails goodies without taking you too far away from what you know on a pretty intimate level
I actually like Ruby, and I'm happy to have an excuse to learn it. It's a spiritual brother of Objective-C in many ways. I do appreciate the heads up about PHP on Trax, though. I think a friend of mine will find this useful.

Currently, I try to wrap my head around ObjC and Cocoa and Carbon on the side, which is why I don't want to mess with Ruby now
Other than some syntax things, Rails makes a tremendous amount of sense to a Cocoa programmer (in my opinion). A lot of the dynamic language tricks Rails uses to make things work show up in similar ways in Cocoa.

Jan — Dec 26, 06 2885

I too hoped that Rails would be a good excuse to actually learn Ruby, but I only got that far with my time. I really hope I'll get to it eventually ;-)

--
no em-dashes? :-/

David — Dec 26, 06 2888

Those compile time checks are a definite plus. I think this is one of the main reasons why I'm not a big fan of scripting languages. It just makes things seem disorganised and chaotic.

It can do, yes, which is why organization is of paramount importance. But you're missing out on truly enormous productivity gains by dismissing scripting languages.

Structured and written properly (with all the aforementioned naming and organizational tactics), you can typically write solutions within a scripting language within a fraction of the time that it takes for the same ObjC/C++ write-compile-test sequence (which of course, is done all day long in major projects). One reason for this is that scripts allow you to solve problems in fundamentally different ways than most compiled languages allow for.

Of huge importance: you can write and edit your scripts as your host application is running! So you can implement functionality without a compile, and see the results in real-time, without having to quit and relaunch the application. (Thus, state is retained and current logging remains intact.) For big projects, this can save hours in every single day. This can bring about certain emergent sorts of behavior and interesting features to your designs that would be next-to-impossible to see with traditional languages. Also, you can implement new functionality in a more human-like, buzzword-compliant language that doesn't care if you cast to void* or not.

Scott Stevenson — Dec 26, 06 2890 Scotty the Leopard

you can typically write solutions within a scripting language within a fraction of the time that it takes for the same

I think there's something to this, but there's a weird paradox here. You can write code at a higher level in a scripting language and let the interpreter work things out. In theory, this saves a lot of time. But the flexibility also means you can spend hours tracking down an errant comma (I have).

Maybe the best of both worlds is something that does a file-by-file syntax check for you without requiring you to deal with low-level data structures. I don't need my web app to actually be compiled, I just want something to check for what a compiler would check for.




 

Comments Temporarily Disabled

I had to temporarily disable comments due to spam. I'll re-enable them soon.




Technorati Profile
Copyright © Scott Stevenson 2004-2008