Prefixing names using some notation

Started by
96 comments, last by phresnel 13 years, 11 months ago
Personaly, and although I think Microsoft's "systems" hungarian is retarded, I don't so much have a problem with the scheme you've outlined. It seems simple enough, concise, and avoids being so terribly specific (which is the fault with Microsoft's Hungarian) if its what you really want to do -- its your code after all, so if it helps *YOU* and *YOUR TEAM*, then all other oppinions be damned as long as you're willing to live with the consequences, both good and bad.


I regularly use 'm', 's' and 'g' prefixs to denote scope (member, static (class), and global, respectively) as I find that hinting the scope gives me information that intellisense doesn't always, at least with a simple mouse-over.

'p' and 'r' I could maybe see myself using (although I don't, since intellisense will tell me this), as I could tell at a glance whether calls against said object affect a long-lived object that is referenced.

I don't see a point to 'c', but if you're inclined to keep it I might suggest 't' instead, for 'type', which is perhaps more nuetral (presuming you didn't want to reserve that prefix for typedefs, but its probably a bad idea to call out typedefs in this way anyhow, as that goes to 'form rather than function'.

I usually use the rule that arrays are pluralized, so adding an 'a' would be redundant for me.

Your use of 'f' and 'i' aren't my cup of tea (again, because intellisense already tells me this stuff easily), but they're probably neutral and non-specific enough to be egregiously annoying, and migrating from Integers to Reals (or vice-versa) is rare (as opposed to say float to double or char to short, which wouldn't affect the naming scheme) -- in short, you can argue that this is closer to function than form, if you squint at it a little. That said, I'd probably distinguish between signed and unsigned ('s' and 'u') integers, as well as booleans ('b'), if you're going to have this sort of arrangement at all. I'd prefer 'r' over 'f' for real values if it weren't already taken, and I can't think of a substitute prefix for references.

This last part leaves us with what to do with your 'n' -- and I say keep it, but use it only for counters since that represents additional useful information to what intellisense gives you. I'd want a distinct prefix 'u' for unsigned values which are not counters (if I were so inclined) -- say a "volume" variable, which (probably) shouldn't be able to go negative, but isn't a 'counting' variable in any way.


Personally, I find that my scope-based prefixes, combined with literate programming (I consult Thesaurus.com not-infrequently when programming) and a few other conventions: arrays have plural names, boolean values and functions begin with an assertive "helping" verb like 'is' or 'has' (perhaps these could be called 'stateful' verbs) really serve my needs quite well, so I don't really embelish beyond that.

Other prefixes might be more useful, as in the original meaning of hungarian notation -- its certainly not a bad use of prefixation, but I prefer longer, more literal variable names.

throw table_exception("(? ???)? ? ???");

Advertisement
Quote:Original post by Giedrius
So the main reason for not using these prefixes is that some extremely widespread variable might need its type changed?


I have another. TMI! (Too Much Information)

Simply put: I don't need to know what exact type a variable is, that's a compiler problem! Unnecessary information is a distraction, thus a waste of energy. That alone is enough, for me.

I like what Antheus said:
Quote:...variable names should reflect the function, not the form.


If i have a variable, it's type should be implicate by the name. While the exact type may not be, its general abilities should still be obvious.

But, i don't really like the Hungarian notation expressed in Antheus's link since it adds prerequisite knowledge to interpreting the code. (For example, i think it's a bad thing that a programmer has to learn that us = unsafe string in the article.) Abbreviations just create more abstractions that have to be managed. I think the purposes of variables can be expressed without them.

PS: I program without an IDE. I do not have a tool-tip or whatever. I still find Hungarian notation in every form unnecessary.
Quote:I personally find it easier to read or write code if I have such information available without having to explicitly look it up.
Well, it sounds like your mind is already made up, more or less. For what it's worth though, I'll go ahead and throw in another vote for not using Hungarian (or other similar naming schemes).
Quote:So the main reason for not using these prefixes is that some extremely widespread variable might need its type changed?
In well-structured code, I would imagine 'extremely widespread variables' would be relatively rare. Still, Antheus' example:
Quote:
// MUST BE 7const int MAX = 4;
Remains relevant, I think.

The type of a variable (which seems to be what we're talking about here) is inherent to that variable. If you incorporate the type into the variable name, you now have two pieces of information, both describing the same property of the variable, that must be kept in sync manually.

To me, this alone is enough reason to avoid such naming conventions. IMO, redundancy in code is best avoided where possible, and anything that has to be kept in sync manually - a comment as in Antheus' example, an enumeration whose value is dependent on that of a previous enumeration, etc. - is burdensome and has the potential for falling out of sync and therefore becoming meaningless at best, and outright misleading and incorrect at worst.

I also find code that uses such naming schemes difficult and tiresome to read. Personally, I like code to read like a narrative, and I find:
The prince rode off on the horse.
Much easier to read than:
aThe nPrince vRode aOff pOn aThe nHorse.
Which is what Hungarian (of the type we're talking about) looks like to me.
I once had to maintain a piece of software (a hardware driver written in FORTRAN) in which all variable names were prefixed with the letter 'o'.

I learned much from that exercise.

What prefix would you use for a reference to an array of doubles?

Stephen M. Webb
Professional Free Software Developer

Quote:Original post by Giedrius
So the main reason for not using these prefixes is that some extremely widespread variable might need its type changed?

The main reason for not using them is that they're ugly, unnecessary, make code harder to read, make code harder to update, and inevitably get out of sync with reality.

They made some sense in ancient versions of C that had little to no type checking. Or perhaps in assembly code for the same reason. Those days are largely gone.

I still use prefixes for member variables but this is an issue of scope, not type. Even then there are other ways (e.g. in C++ you can do this->foo instead of m_foo). I was a holdout on pointers for a while but eventually decided that -> vs . is a trivial distinction readily apparent from surrounding code and even if I got it wrong the compiler would tell me. I held out even longer on pointers-to-pointers but found that I needed them less and less often as I embraced exceptions.

The bit about "even if I got it wrong the compiler would tell me" is the important bit. Hungarian arose largely because the compiler would not tell you so anything that helped you avoid type conflicts was something that helped you not ship bugs. This isn't the 80's anymore. The compiler will tell you the vast majority of time if you get it wrong.
-Mike
Quote:Original post by nullsquaredYou mean the 0.2 second mouse-over tool-tip delay? Or the 0.2 second right-click -> go to declaration/definition?

Hungarian notation was useful in the past when IDE functionality was next to non-existent. Nowadays it only hinders development. If you don't believe me, start a project using Hungarian notation. Keep developing it and I guarantee you'll either forget to use Hungarian notation or get tired of it.

As mentioned before you simply overstated the ease of looking up variable types to make your side seem stronger, but my reply is more about some guarantees you have erroneously made.

I started a very large project (L. Spiro Engine) with over 500 files and 250 classes/structures/etc., all written from scratch by myself.
Every single member, parameter, local, etc., is named with a very strict naming convention essentially Hungarian.
The next 500 files will also follow this convention 100% strictly and I will not forget nor get tired of it.



#1:
Rather than supporting any specific naming convention, I rather detest having no naming convention.
In another example posted by nullsquared he mentions ending up in a random function without pre-existing knowledge etc.
Well this is simply false to the Nth degree, at least if you actually code for a living.
I frequently have to view others’ code and find myself in functions I do not recognize, trying to understand a system designed by someone else.
In these cases, nothing pisses me off more than not being to immediately recognize the types and scopes of the variables I am seeing.
Types of variables are only one problem, but it is frustrating seeing a value being used and not immediately knowing whether it is a local, a parameter, a member, or, God forbid, a global.

And it is not just about being able to quickly identify the type/scope of variables you have already spotted.
If members were clearly marked m_*, I could easily scan the code and spot lines where the class itself is being modified. Being able to do this often allows one to focus just on code in one region of the function, rather than having to scan the whole function line-by-line.

If I want to quickly find places where a certain variable is accessed or modified, and if that variable happens to begin with _* (due to being a parameter) or m_* (member) or g_* (global), I am in luck. These prefixes are easier to spot on quick scans and makes for much easier data tracking.



#2:
But what is more annoying in a general sense is inconsistency. If you are going to use a style, use it throughout your code. No one can trust your code/naming conventions if you forget things or make mistakes. Even the Windows® API has problems with this.



#3:
Type data can be tricker than you think.
In the field of 3D math you will be using vectors and reals frequently, and you will encounter overloaded operators.
Cross products return vectors while dot products return reals (usually float).
But not everyone overloads vector operators the same way, and even if they did, the overloaded operators themselves often ambiguate the code, along with misleading names.

final = ballVel * dir;

dir is surely a vector, but is this vector DOT vector, vector CROSS vector, or float TIMES vector??
Perhaps this would make each case clearer:

fFinal = vBallVel * vDir; // Overloaded * must be DOT, since this returns a float.

vFinal = vBallVel * vDir; // Overloaded * must be CROSS since this returns a vector.

vFinal = fBallVel * vDir; // Overloaded * is just multiplication, and this is scaling a vector by a real.




Why even argue that naming conventions do not make code clearer? It always seems to me that lack of some naming convention or other just causes headaches and no gain, except that the one guy (or girl) who writes the code can be a bit lazier, deferring the headache to the unlucky fellows who must later read his or her code.
What kind of person knowingly writes ambiguous hard-to-understand-at-a-glance code and justifies it by telling others to waste time following tooltips, right-click to find definitions, and jump back and forth between windows?
Isn’t programming already a tricky business enough? What logic tells one not to add all the clarity possible even if just a little?

Point in case: Pick a convention. It doesn’t matter which one, but it should be one that attempts to give as much information as possible about the code you are writing. “Ugly” or not is not a factor when there are so many problems and consequences with hard-to-read code these days.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

I find it handy to prefix stuff to a small degree as it makes naming more consistant

Data
pData // pointer to Data


Whatever convention makes it easy to remember is great

HOWEVER,

Full blown Hungarian is a poor way to name stuff IMHO.


Just a couple of comments, for the sake of discussion.
Quote:Types of variables are only one problem, but it is frustrating seeing a value being used and not immediately knowing whether it is a local, a parameter, a member, or, God forbid, a global.

And it is not just about being able to quickly identify the type/scope of variables you have already spotted.
If members were clearly marked m_*, I could easily scan the code and spot lines where the class itself is being modified. Being able to do this often allows one to focus just on code in one region of the function, rather than having to scan the whole function line-by-line.

If I want to quickly find places where a certain variable is accessed or modified, and if that variable happens to begin with _* (due to being a parameter) or m_* (member) or g_* (global), I am in luck. These prefixes are easier to spot on quick scans and makes for much easier data tracking.
For what it's worth, I think most of the discussion here has been related to type prefixes rather than scope prefixes/suffixes.

I still use m_ prefixes in my code, for the very reason you mention above. However, others on these forums have made compelling arguments for dropping even scope prefixes, and if I were to start a new code base from scratch, I might try going without them.
Quote:Type data can be tricker than you think.
In the field of 3D math you will be using vectors and reals frequently, and you will encounter overloaded operators.
Cross products return vectors while dot products return reals (usually float).
But not everyone overloads vector operators the same way, and even if they did, the overloaded operators themselves often ambiguate the code, along with misleading names.

final = ballVel * dir;

dir is surely a vector, but is this vector DOT vector, vector CROSS vector, or float TIMES vector??
Perhaps this would make each case clearer:

fFinal = vBallVel * vDir; // Overloaded * must be DOT, since this returns a float.

vFinal = vBallVel * vDir; // Overloaded * must be CROSS since this returns a vector.

vFinal = fBallVel * vDir; // Overloaded * is just multiplication, and this is scaling a vector by a real.
That's an argument against ambiguous operator overloading, not an argument for type prefixes.

Many (including myself) would argue that overloading the * operator for the dot, cross, or member-wise product is a bad idea, for this very reason. Using named functions for these operators clears up any ambiguity and obviates the need for explanatory comments or type prefixes (in this context at least).
Quote:Why even argue that naming conventions do not make code clearer?
We're not arguing that naming conventions don't make code clearer. We're just arguing as to whether Hungarian-style naming conventions make code clearer.
Quote:It always seems to me that lack of some naming convention or other just causes headaches and no gain, except that the one guy (or girl) who writes the code can be a bit lazier, deferring the headache to the unlucky fellows who must later read his or her code.
For what it's worth, I've read and analyzed a lot of other people's code, and I always prefer reading code that doesn't use Hungarian-like notation.
Quote:What kind of person knowingly writes ambiguous hard-to-understand-at-a-glance code and justifies it by telling others to waste time following tooltips, right-click to find definitions, and jump back and forth between windows?
Code without type prefixes needn't be ambiguous or hard to understand. In fact, with good variable names and good code structure, it can be quite clear and easy to read.
Quote:What logic tells one not to add all the clarity possible even if just a little?
I prefer to take the approach of stripping away noise and redundant information until the code is as clean and straightforward as possible while still being clear and readable. (Or, to paraphrase Einstein, until the code is as 'simple as possible, but not simpler'.)

In any case, I don't think you can reasonably argue that Hungarian notation (of the type under discussion here) is 'the' right way to do things. Obviously it works for you, but it's equally clear that not everyone likes it, and that good code (even for large projects of the kind you described) can be written without it.
I’m not for Hungarian (I just use my own derivative of it), but rather for any naming convention that tries to give at-a-glance information, hopefully useful.

Most of the discussions have been about type, but I think the original poster was open to any high/low points to each style.
And usually type information is not so hard to guess, but scoping is trickier. I think this is something else to consider when thinking about what a naming convention should convey, but it rarely gets mentioned, so I wanted to bring it up.

It seems the main difference between our views is that mine assume the author of the code is inconsistent, not necessarily using the best coding practices, etc., whereas yours assume the author is adding lots of comments, using decent coding structure, etc.
It may just be my own experience, but I tend to find code written in the former more often than in the latter. To be honest, I am not so creative as to have made up my previous examples; they came from actual experiences I have had repeatedly enough (at work) that I remember those cases all-too-well.

If you are lucky enough to be surrounded by reliable coders, maybe naming conventions are not as necessary.
But in my experience, most programmers are not reliable, and at least some confusion can be cleared up through naming conventions.
If naming prefixes are hard to read it just means you haven’t read much of it.



And while it is on my mind, another example of naming-convention helpfulness: 64-bit portability and other 64-bit issues.
These days my types include size.
LSUINT32 ui32Value;

This may hurt some eyes (but that really doesn’t matter when code is about functionality).
This has made a lot of code easier to manage when size information, not just type information, is valuable. Overflows and other issues become quite easy to find when I have type and size information at a glance on every variable.
Without a naming convention, you are asked, on top of the headache of following the code to find bugs, to keep a dictionary in your head as to what the types (and sizes) of each variable are. And when you originally checked the type of “varX”, you may have not have paid close attention to its size, unknowning that that would be a key factor to a bug later in the code.

Anyway, as I said, you have to decide if naming conventions are for you or not. But if you pick one, stick with it, reliably.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

Quote:Original post by YogurtEmperor
#3:
Type data can be tricker than you think.
In the field of 3D math you will be using vectors and reals frequently, and you will encounter overloaded operators.
Cross products return vectors while dot products return reals (usually float).
But not everyone overloads vector operators the same way, and even if they did, the overloaded operators themselves often ambiguate the code, along with misleading names.
final = ballVel * dir;

dir is surely a vector, but is this vector DOT vector, vector CROSS vector, or float TIMES vector??
Perhaps this would make each case clearer:
fFinal = vBallVel * vDir; // Overloaded * must be DOT, since this returns a float.vFinal = vBallVel * vDir; // Overloaded * must be CROSS since this returns a vector.vFinal = fBallVel * vDir; // Overloaded * is just multiplication, and this is scaling a vector by a real.

Or:

final = ballVel.Dot(dir);
final = ballVel.Cross(dir);
final = ballVel * dir;

And this way I don't have to backwards-deduce the nature of the operation by means of its return type. The only place where ambiguity might arise is in the last line (where ballVel could be either a float or a vector), but this is easily fixed by properly naming the variable:

final = ballSpeed * dir.

This topic is closed to new replies.

Advertisement