Sunday, August 7, 2005

Ghost functions

Here's something I'd like to see possible in code. I presented it to the C# design team (well, actually, my suggestion was a bit more limited in scope), but they weren't particularly interested because it goes against a couple of the language design tenets.

I want a way to call methods on a null reference. Not just any method, but just special methods. In fact, these special methods would actually just be static methods that you access through unusual syntax.

The .NET Framework string class has a nice static method called IsNullOrEmpty. With it, you can make your code a little less tedious:

if (username == null || username.Length == 0) return;
// becomes
if (string.IsNullOrEmpty(username)) return;

But, I really hate calling static methods and then passing in an instance of that same class. That's just silly. You're still doing something with username; the only difference in the paradigm is that username might not actually refer to a string. With what I'm calling a "ghost function" (a name I spent about thirty seconds coming up with), it becomes much cleaner:

if (username.IsNullOrEmpty()) return;

You wouldn't want that behavior on most methods; usually, you'd still want to throw if you call a method on null. Anyway, with my new imaginary syntax, not only does the code above become clearer, the actual definition of IsNullOrEmpty becomes slightly clearer too:

public static IsNullOrEmpty(string instance)
{
return instance == null || instance.Length == 0;
}

// becomes

public ghost IsNullOrEmpty()
{
return this == null || Length == 0;
}

(Note that in the ghost method, you can use the "this" keyword and instance variables, even though it's technically a static method and not an instance method; it's just syntactic pleasantry.)

In the end, using a ghost method versus a regular static method would have identical results; in fact, for languages that didn't support ghosts, you could make them equivalent in the compiled IL. But, I think that the object.action() syntax is much more pleasant than the class.action(object) syntax. The language design team's response is that this sort of thing takes away from the clarity of the code, but I think it's just the opposite. (My original suggestion only dealt with properties, not regular methods.)

24 comments:

Ianonymous said...

Let the obfuscated runtime errors begin!! I suppose that strong typing is also less “clean” and inconvenient as well?

Anonymous said...

Because what languages need are tons and tons of special case syntax constructs so that programmers can be saved from having to type a few extra characters!

Travis said...

In any given situation I'll generally prefer whatever makes the code a more easily readable translation of what I'm thinking. Strong typing wins because the reader doesn't have to infer things and look all around to figure out what something is; they know what something is from the declaration a few lines above or by hovering their cursor over the variable or by pressing a hotkey.

It's not at all about saving typing a few extra characters. One of the things I like about VB versus C#/Java/C++ is that it involves typing more characters. But, there are times that you want to do something to an object reference even when that reference is null.

I don't see how this contributes to obfuscated runtime errors. If you call an instance method on null, you get a null reference exception. If you call a static method and pass in a null reference, you (generally) won't. This isn't going to trick people into calling instance methods on null.

Travis said...

"But, there are times that you want to do something to an object reference even when that reference is null."

I should have clarified that I didn't mean that literally, since you're not actually doing anything to null.

To me, the syntax is "Travis went to the store" versus "Someone may have gone to the store, and it was Travis." The first one's worth fighting for.

Ianonymous said...

As much as you are going to hate me saying it (and as much as I DON'T want this to sound like I think Elbaum does a good job teaching SE ... because he doesn't): you really should have paid more attention in 461.

Your ghost functions give non-design conscious developers (such as yourself) the ability to make some really bad decisions. Let me use your example of checking a username. In this case, and it does depend on your implementation, the null result may be desirable. When checking a username if I catch a NullPointerException, I may redirect to some kind of login page or invalid session page. Null is desirable here, if I had a bunch of ghost functions then I would 1) have to code some kind of null check logic at some point anyway or 2) have these methods return false and then have the application continue running as if a user were present (when in fact one is not) causing some strange runtime error later.

If you organize or control and data flow in a responsible way, you won't ever have a null user and you won't have to constantly check if null... And if you simply MUST have some clean code, you can use aspects (since technically the null check is a cross cutting concern anyway).

The bottom line is that in OO, the operations on an object (methods) must have something to operate on. If the object reference doesn't point to anything, then it makes absolutely no sense to call operations on it.

There are existing mechanisms to deal with what you are talking about here. You are essentially advocating a stupid little hack which gives clean code at the expense of safe.

Unsafe code for esthetic or feature reasons? These sorts of things haven’t cause problems for MS before … oh wait, yes they have.

Travis said...

Sigh. I used "Username" as an example of a variable that clearly contained a string. That's all.

I'm not going to go through and respond to everything in your post, because I think I already have for most of it.

My point is that there are a very specific select few operations for which the standard OO paradigms aren't appropriate, such as checking if a string is null OR empty. I'm obviously not advocating using these kinds of methods all the time... I could imagine a great deal of applications that would never need to define a single one. But for strings I feel that it's worth making an exception.

As long as we're making stupid, vague examples that pull the security card without actually providing any useful justification, how about the case where the n00b who doesn't know what he's doing just checks to see if username is null or not? If username is an empty string, that'll go past his if statement and let the application continue running as if a user was logged in. See? I can do it too.

But I don't think that there actually is even a problem in your example as I read it. Without ghost methods, you get a null reference exception like you mention. With ghost methods, you get the value "true," which means that the username is empty, so they shouldn't get to see the page. If there's a bug, it's not the fault of the IsNullOrEmpty method if the user checked the boolean result incorrectly.

By the way, bringing up the mere fact that MS has made questionable security decisions in the past is a high-quality discussion point that no one has ever thought of using before. Kudos to your originality.

Ianonymous said...

I was merely making the point that exceptions to common language constraints made for strings or anything else where you want to break the rules of a paradigm are generally bad; security being one manifestation of the "badness". Don't get all huffy about it. I could make the argument with a Vector, HashTable or any other ADT, the point still stands: breaking OO for convenience isn't a good idea.

If you want ghost functions, Spomey, then more power to you. I am just saying that it is a hack ... if you want to infer that I am calling you a hack as well, be my guest.

Anonymous said...

I think the he is just trying to say that:

Queue.elementsRemaining() == 0

and

Queue == null

are two very different states and that providing any kind of backdoor overriding functionality to obscure this, probably isn't a good idea from a design or language standpoint.

Travis said...

Great. But I already made that point in my original post. If that's all you wanted to say, you could have just said "I agree with them" and saved me some flamebait.

Anyway, I still feel that there are a ton of ways that software development could be more natural and intuitive, and I think that this is one of them. For me, at least, the more natural things become... the closer they become to the way I think about things, even non-technical things... the more solid and bug-free my programs become. It only breaks OO principles as we're used to them, which is surely a downside to the idea, but not a deal-killer. As much as I love OO principles in general, they're not holy, and they can be changed (or perverted) over time.

Off to go build a model of a nuclear power plant for that contest.

Travis said...

(Now replying to most recent comment)

If the snippet (Queue == null || Queue.elementsRemaining() == 0) kept showing up in your code, it might be helpful to have a static method on Queue called something like isNullOrEmpty. When you can use a single expression to do the work of two OR'ed together, you reduce the possibility of making a mistake in one but not the rest. Given the choice of calling that code using Queue.isNullOrEmpty(instance) or instance.isNullOrEmpty(), I prefer the latter. It could even have a different syntax; maybe Queue:isNullOrEmpty or something, if it's just the dot that's scaring people.

Anonymous said...

And here I thought that other guy was just being a dick.

You are so myopic. It isn't "the dot that’s scaring people", it is the fact that this functionality is a flagrant violation of commonly held language understanding. It is not a matter of Queue.isNullOrEmprty(q) vs. myQueue.isNullOrEmpty(). Those two behaviors are dependant on COMPLETELY different state. Queue.isNullOrEmpty(q), which would be static, and operate on the state of q, containing logic for essentially asking if ( q == null ). myQueue.isNullOrEmpty(), which would not be static, operates on itself and assumes that myQueue has state other than null, which a "ghost" method would not enforce. Basically a static call to an object is done independent of object state, therefore not requiring that an object be instantiated.

You really need to go back and read up on the fundamentals of programming languages and OO.

Travis said...

I'm really disappointed that the fact that I fully understand the difference is not abundantly clear by now. I actually say in my original post, "In fact, these special methods would actually just be static methods that you access through unusual syntax." Looking back, I technically say basically the same thing two or three times. Perhaps you should reread my post while I hunt down the Bjarne.

You're just stuck on the "typical" syntax for accessing OO members. I really don't think that changing the standard OO syntax rules a tiny bit is really as catastrophic as you two (or more?) are making it out to be. I see this idea of a "ghost method" to be very similar to a property. A property sure looks like a public field, but it's really a method or pair of methods disguised behind cute syntax. And, just like this idea, there are people who feel that you just can't have code that runs when you say "X.Y = Z". You've gotta write out the whole thing: "X.setY(Z)". I feel that nothing is lost by using the more natural first syntax, but some people feel that it's just flat-out wrong to hide the fact that you're actually running code. It kind of sounds to me that both Mr. Ianonymous and Mr./Ms. Anonymous are probably in the "use explicit getters and setters" camp. To me, it's just the same as overloading = or + in C++; I'm okay with method calls being hidden.

Ianonymous said...

I didn't realize that the "it's ok to access non-static properties directly" camp was still around. I thought they all starved to death during the winder of 2001 after not being able to find work anymore due to their inability to write safe code.

Let me try one more time, and perhaps save Mr. or Ms. Anonymous the typing trouble. The accessor (static or non-static) isn't the point here. I read your original post and see that you do in fact understand how to make calls to static methods, gold star for you. What you don’t seem to understand is that an instantiated object maintains some kind of state. This state in turn may be dependant on the state of other objects. One kind of bug, a rather common one, is a broken dependency. The problem is that what you are advocating makes false assumptions about object state and therefore lends itself to potential runtime errors in your code.

As O’Reiley says (but I actually mean it): “I’ll give you the last word”.

Travis said...

Last word?? Sweet! I can do all sorts of... well, no... I already kind of said it all in my original post.

I guess I'll just end with a quote:

"The problem is that what you are advocating makes false assumptions about object state and therefore lends itself to potential runtime errors in your code."

I hope you were laughing when you typed that. Or, at least crying from laughter. You must think that I'm some kind of robot that can't survive in the face of non sequiturs, and I'll just begin to vibrate until my head explodes in a shower of sparks and shrapnel. That's how I'll interpret your last post. Suddenly, the world is calm again.

stack said...

(regurgitate old comment)

(TROLL)

Anonymous said...

Sigh, it's like trying to explain to a small child that drinking antifreeze is bad. To them it's bright and green and looks yummy. It's just unfortunate when that child is actually a trained mechanic and has been hired by the largest auto manufacturer in the world. :(

Anonymous said...

I heard about this and had to see for myself. If I understand you correctly you are basically implementing a Facade to your methods for ghosting:

class User
{
 private String username;
 protected getUserName() { return username; }
}

public class UserProxy extends User
{
 private User _sefl;

 public UserProxy(User u)
 {
  _self = u;
 }

 public String getUserName()
 {
  return ( _self == null ) ? "" : u.getUserName();
 }
}

And you want that sort of functionality automatically wrapped around any class that has a ghost declaration on a method, at compile or runtime? Go back to school there junior, those other guys are right, that is retarded.

stack said...

1. Perhaps anonymous users should show some spine and sign their comments. Additional portmanteaus are encouraged.

2. "OO" and "safe" are adjectives.

3. Travis's idea makes for more intuitive code. The second Jesus comes down from Heaven and tells me that OO principles as they currently stand are the only way to go, then I'll believe it (among other things). Things can't stay the same forever, and we all know that computer code isn't exactly the most intuitive thing sometimes. It has to evolve at some point, just as it's evolved from punch cards to assembly language to FORTRAN to C#. Dismount from the proverbial high horse, and maybe think innovatively for a second.

Travis said...

Thanks, Stack.

Regarding the large code sample, no, you don't get it at all; sorry. It doesn't make any sense at all to use a ghost function in that situation. That's just absurd. And, the implementation doesn't require any sort of automatic wrapping in a proxy class, it just changes the way that var.method() compiles into IL/bytecode; there's no overhead. If method is a ghost, then it becomes "push var onto the stack, then invoke method"; if it's not, then it stays "invoke method on var" like it's always been. (Clearly that's simplified, but you get the picture.) Then, the declaration of method as "public ghost method()" is essentially just translated to "public static method(ThisClassType this)" and anything inside not prefixed with "this." gets it, except "this" is a parameter, not the current object, because there is no current object.

You could make a fa├žade to accomplish the same result, but if you're going to do that, you're probably better off just sticking with a static method; that's way too much work and probably isn't going to make your code any cleaner. I'm talking about something that happens automatically at compile time, and if that's what you were proposing too, I'm scared.

Travis said...

Oh, and the antifreeze comment is gold. I'm gonna have to find a way to use that one.

Travis said...

...Even though I said at the beginning that antifreeze is bad, and was just proposing that drinking a little of it is fine because it won't kill you and actually is blue and tasty. (I don't know if I've ever seen green antifreeze... though HOLY SWEET LORD you do not need to convince me that it exists.)

stack said...

Blue antifreeze?

Luke said...

You are recognizing the difference between antifreeze and windshield washer fluid, yes?

Travis said...

Yes, I believe so. A Google image did turn up primarily green.