I think Hungarian Notation even preceded DOS — if memory serves, Simonyi came up with it while writing the Bravo word processor at PARC.
It’s kind of like C++ name mangling done by hand. I imagine it was useful in languages with weak type checking, like pre-ANSI C without function prototypes (although Bravo wasn’t written in C), where the compiler wouldn’t warn you if you passed the wrong types to a function. In modern languages it seems not worth the trouble, especially with editor support for showing parameters as you type!
I worked for Charles at Intentional Software from 2006-2011. We worked in C# and used Hungarian notation exclusively. Every time this subject pops up somewhere, I feel annoyingly compelled to refute this misconception. I wish I could stop! I really do. I don’t even use it in my own programming anymore. I have no interest in converting anyone. But here goes anyway:
Hungarian notation is not a replacement for missing type systems. It is a tool for thought, centering around naming concepts unambiguously and using those names consistently. Even in a very sophisticated type system with a powerful IDE, you still need to name the types, and Hungarian is very opinionated about how you should do that.
Hungarian’s core principles are:
the name you use for a concept should be unique across your codebase
it should be short enough that when you type or say it out loud to your teammates, you are not tempted to abbreviate it
Notably, it does not include the suggestion that a name should be familiar or evocative - in fact it is often encouraged that names be cryptic and unintelligible to newcomers. This is seen as a feature; when you don’t understand something, it is obvious to you - you’re not mixing it up with some similar-but-subtly-different concept and have no risk of going off the rails with incorrect assumptions about what it is and how it should work.
Once you have accepted that as a basis, then we can layer on formulae for generating names in standard ways, which should also be applied consistently. For example, a function that takes a Foo and transforms it to a Bar should always be named BarFromFoo(), a dictionary that maps between Foos and Bars should always be named mpfoobar, etc.
We can also define some standard names for concepts that are used in most codebases - for example, c for a count of things, vs. i for an index into a list of things. (Both kinds of values will be typed as bare integers in any programming language I’ve ever used, but they relate to each other in very specific ways, and the Hungarian prefix encodes that even though the type system doesn’t.)
Once you got used to working this way, the clarity of discussion when collaborating on the codebase was genuinely incredible. It does work. It also makes you seem like you’ve basically been indoctrinated into a cult to any other programmer. It took years of deprogramming before I stopped using it in my personal codebases.
It’s also very difficult to communicate how to do it well†. Notably, the version of Hungarian that most people were exposed to is commonly referred to as “Systems Hungarian”, and was used by the win32 API. It directly maps its prefixes to implementation types - like lpsz for “long pointer to zero-terminated string”. Doing things this way misses the point. Charles led the apps division inside Microsoft, which is why the correct version is called “Apps Hungarian”.
† An alternate view is possible here, which is that Charles was bad at explaining it. I think both are probably true to some extent. At one point while I worked there, out of frustration about being misunderstood, he printed out the transcript of the scene from “Monty Python and the Holy Grail” where the king repeatedly tries and fails to tell his guards to make sure the prince doesn’t leave his room, and posted it up in a common area. He would often say things that he thought were completely straightforward - and when I understood what he meant, they generally were! - but my own preconceptions made me completely miss his point.
Thanks for the clarification! I still don’t think I agree with HN, but I understand it better.
the name you use for a concept should be unique across your codebase
I do agree with this, and it’s good to define a type for each concept, ideally one that’s not implicitly convertible to another type. (E.g. an enum class in C++ or a distinct type in Nim.)
it does not include the suggestion that a name should be familiar or evocative - in fact it is often encouraged that names be cryptic and unintelligible to newcomers.
I wonder if this was an influence on Urbit, which is full of weird 4-letter names (not all unintelligible though.) This was one of the things that fascinated me about it, before I discovered the ugly fascist underpinnings. Urbit was of course also influenced by the writings of Borges, and I also figured out after a while that the 4-letter theme comes from using 32-bit ints to represent identifiers (an old trick from the classic MacOS.)
a function that takes a Foo and transforms it to a Bar should always be named BarFromFoo()
Here I disagree. The types/roles of the inputs and outputs do not uniquely identify a function. Moreover, I don’t think they need to be part of the name of the function, when they’re a part of the function’s signature.
Sorry, not trying to continue any debate. Thanks again for the info.
Except that it was intended to mainly capture things that couldn’t be expressed in the type system in C, such as columns versus rows, speed versus distance, and so on. It was only after the Windows team started using it that we ended up with the pointless version that just mangled the type.
It’s less useful in a language with rich types and the ability to prevent accidental casts.
In addition to the compiler, think about the debugging experience. In an era before symbolic debugging, developers needed to reason about machine code, and it’s helpful to easily distinguish types at each access because it corresponds to the expected assembly generated at that point.
Many Bothans died in the zeros to help extinguish this, and you bring it right back into our house… :)
I think Hungarian Notation even preceded DOS — if memory serves, Simonyi came up with it while writing the Bravo word processor at PARC.
It’s kind of like C++ name mangling done by hand. I imagine it was useful in languages with weak type checking, like pre-ANSI C without function prototypes (although Bravo wasn’t written in C), where the compiler wouldn’t warn you if you passed the wrong types to a function. In modern languages it seems not worth the trouble, especially with editor support for showing parameters as you type!
I worked for Charles at Intentional Software from 2006-2011. We worked in C# and used Hungarian notation exclusively. Every time this subject pops up somewhere, I feel annoyingly compelled to refute this misconception. I wish I could stop! I really do. I don’t even use it in my own programming anymore. I have no interest in converting anyone. But here goes anyway:
Hungarian notation is not a replacement for missing type systems. It is a tool for thought, centering around naming concepts unambiguously and using those names consistently. Even in a very sophisticated type system with a powerful IDE, you still need to name the types, and Hungarian is very opinionated about how you should do that.
Hungarian’s core principles are:
Notably, it does not include the suggestion that a name should be familiar or evocative - in fact it is often encouraged that names be cryptic and unintelligible to newcomers. This is seen as a feature; when you don’t understand something, it is obvious to you - you’re not mixing it up with some similar-but-subtly-different concept and have no risk of going off the rails with incorrect assumptions about what it is and how it should work.
Once you have accepted that as a basis, then we can layer on formulae for generating names in standard ways, which should also be applied consistently. For example, a function that takes a
Foo
and transforms it to aBar
should always be namedBarFromFoo()
, a dictionary that maps betweenFoo
s andBar
s should always be namedmpfoobar
, etc.We can also define some standard names for concepts that are used in most codebases - for example,
c
for a count of things, vs.i
for an index into a list of things. (Both kinds of values will be typed as bare integers in any programming language I’ve ever used, but they relate to each other in very specific ways, and the Hungarian prefix encodes that even though the type system doesn’t.)Once you got used to working this way, the clarity of discussion when collaborating on the codebase was genuinely incredible. It does work. It also makes you seem like you’ve basically been indoctrinated into a cult to any other programmer. It took years of deprogramming before I stopped using it in my personal codebases.
It’s also very difficult to communicate how to do it well†. Notably, the version of Hungarian that most people were exposed to is commonly referred to as “Systems Hungarian”, and was used by the win32 API. It directly maps its prefixes to implementation types - like
lpsz
for “long pointer to zero-terminated string”. Doing things this way misses the point. Charles led the apps division inside Microsoft, which is why the correct version is called “Apps Hungarian”.† An alternate view is possible here, which is that Charles was bad at explaining it. I think both are probably true to some extent. At one point while I worked there, out of frustration about being misunderstood, he printed out the transcript of the scene from “Monty Python and the Holy Grail” where the king repeatedly tries and fails to tell his guards to make sure the prince doesn’t leave his room, and posted it up in a common area. He would often say things that he thought were completely straightforward - and when I understood what he meant, they generally were! - but my own preconceptions made me completely miss his point.
Thanks for the clarification! I still don’t think I agree with HN, but I understand it better.
I do agree with this, and it’s good to define a type for each concept, ideally one that’s not implicitly convertible to another type. (E.g. an enum class in C++ or a
distinct
type in Nim.)I wonder if this was an influence on Urbit, which is full of weird 4-letter names (not all unintelligible though.) This was one of the things that fascinated me about it, before I discovered the ugly fascist underpinnings. Urbit was of course also influenced by the writings of Borges, and I also figured out after a while that the 4-letter theme comes from using 32-bit ints to represent identifiers (an old trick from the classic MacOS.)
Here I disagree. The types/roles of the inputs and outputs do not uniquely identify a function. Moreover, I don’t think they need to be part of the name of the function, when they’re a part of the function’s signature.
Sorry, not trying to continue any debate. Thanks again for the info.
Except that it was intended to mainly capture things that couldn’t be expressed in the type system in C, such as columns versus rows, speed versus distance, and so on. It was only after the Windows team started using it that we ended up with the pointless version that just mangled the type.
It’s less useful in a language with rich types and the ability to prevent accidental casts.
In addition to the compiler, think about the debugging experience. In an era before symbolic debugging, developers needed to reason about machine code, and it’s helpful to easily distinguish types at each access because it corresponds to the expected assembly generated at that point.
Also see Joel’s take on it: https://www.joelonsoftware.com/2005/05/11/making-wrong-code-look-wrong/