Boolean parameters to API functions considered harmful (2011)(jlebar.com)
jlebar.com
Boolean parameters to API functions considered harmful (2011)
http://jlebar.com/2011/12/16/Boolean_parameters_to_API_functions_considered_harmful..html
83 comments
Alternate (better) solution to the author's problem: Use a language where all parameters are named, like Smalltalk or Objective-C.
document addObserver:observer forNotificationNamed:'load' isWeak:false.
xhr openURL:options url type:options type async:options sync negatedBut even there, I would prefer:
[document addWeakObserver: observer forNotificationNamed: 'load']
and
[document addObserver: observer forNotificationNamed: 'load']
Assuming for a second, that the non-weak observer should be the normal case.
[document addWeakObserver: observer forNotificationNamed: 'load']
and
[document addObserver: observer forNotificationNamed: 'load']
Assuming for a second, that the non-weak observer should be the normal case.
Alternate (even better) solution: Use a language where you can optionally name parameters, like C#.
xhr.OpenUrl(url, async: true);Actually, even in Objective-C it's technically optional to give your parameters names. Feel free to knock yourself out with method signatures like:
- (void)doMysteriousThingsWithParameters:(int)x :(BOOL)y :(NSString*)z;
I've come across this recently when creating an HTML5 <canvas> context object to be called from within JavaScriptCore. This needs to expose JS-friendly methods like: - (void)strokeRect:(CGFloat)x :(CGFloat)y :(CGFloat)w :(CGFloat)h;
- (void)arc:(CGFloat)x :(CGFloat)y :(CGFloat)radius :(CGFloat)startAngle :(CGFloat)endAngle :(BOOL)antiClock;Interesting. I don't use Object-C, but always heard parameter names are actually considered to be part of the method name there. Was that wrong?
Ah, or do you mean the name is just empty? How does that work on the implementation side? I.e. how do you refer to the passed arguments?
Ah, or do you mean the name is just empty? How does that work on the implementation side? I.e. how do you refer to the passed arguments?
Parameters don't have names, the message has a format, and th only rule of that format(referred to as a "selector") is that parameters are preceded by a :
More, more parentheses!
So wordy and repetitive and wordy.
Much bugfree
Not a problem if autocomplete is working.
The issue with autocomplete is that it makes writing a lot of code really easy but offers no help when reading the code. Syntax/semantic highlighting may help a little but the issue is still there.
Given that more time is spent reading than writing code, autocomplete is only a partial solution to this problem.
Given that more time is spent reading than writing code, autocomplete is only a partial solution to this problem.
This comment makes sense for the original problem, but not for the message you're replying to, as keyword parameters help tremendously with readability.
I've never worked in those languages, but I suspect I'd end up with a lot code like this:
foo(author: author, title: title, price: price)
The problem in the article is real, but it is the exception. If it's hard to see what 5% of your parameters mean, forcing 100% of them is probably a cure worse than the disease.
When I worked in Java with IntelliJ, the solution was to simply hover over the call, and the declaration would appear.
foo(author: author, title: title, price: price)
The problem in the article is real, but it is the exception. If it's hard to see what 5% of your parameters mean, forcing 100% of them is probably a cure worse than the disease.
When I worked in Java with IntelliJ, the solution was to simply hover over the call, and the declaration would appear.
While I can't speak to what you would end up with, my experience (~quarter of a century) is that this is not the case. To verify, I just did
Dan Ingalls explains the syntax really nicely in this video:
I ramble on a bit about the relationship between keyword syntax and (lack of) operator overloading:
ack '\[.*:' | less
to search my code-base for message sends with parameters. Scanning the first hundred or so results shows the pattern you mention to be rare.Dan Ingalls explains the syntax really nicely in this video:
https://youtu.be/P2mh92d-T3Y?t=600
With this type of syntax, most APIs turn into EDSLs all by themselves, and often read like fairly natural sentences.I ramble on a bit about the relationship between keyword syntax and (lack of) operator overloading:
http://blog.metaobject.com/2015/03/why-overload-operators.htmlIDEs fix both problems: writing verbose code and reading terse code. The thing is, code isn't always examined in a working IDE, and it tends to be read outside of an IDE more than it tends to be written outside of an IDE.
The disease is far worse than the cure. I frequently (at least once a day) find myself wishing code was written in the smalltalk style but I've only found the smalltalk style inconvenient on a small number of occasions.
The disease is far worse than the cure. I frequently (at least once a day) find myself wishing code was written in the smalltalk style but I've only found the smalltalk style inconvenient on a small number of occasions.
I consider verbose code to be more readable in general. However one has to take more time when naming their functions and parameters.
So memorable.
too much verbose.
I prefer python approach, where you can set "optionally named" and "kwarg only" parameters" on a per function basis.
For a description, see https://www.python.org/dev/peps/pep-3102/
I prefer python approach, where you can set "optionally named" and "kwarg only" parameters" on a per function basis.
For a description, see https://www.python.org/dev/peps/pep-3102/
Oh come on, we both know that if you give people a choice the feature will be underused. If you want proof just look at python code!
I don't understand. What you mean?
> Use enums
For internal but not external APIs. Enums create tight coupling and are not extensible. Booleans and Enums should be avoided for external APIs.
For internal but not external APIs. Enums create tight coupling and are not extensible. Booleans and Enums should be avoided for external APIs.
What are you talking about? How is using an enum more tightly coupled/less extensible than a function name?
I usually don't care if my booleans are extensible or not.
Well that's just a lack of ambition on your part:
http://thedailywtf.com/articles/What_Is_Truth_0x3f_
http://thedailywtf.com/articles/What_Is_Truth_0x3f_
Oh man, i should not have started reading DailyWTF. When booleans attack:
http://thedailywtf.com/articles/tri-state-boolean
http://thedailywtf.com/articles/Special-Delivery
http://thedailywtf.com/articles/tri-state-boolean
http://thedailywtf.com/articles/Special-Delivery
Instead of booleans or enums why not have different methods for each value of the bool/enum, e.g.:
openAsync(...),
open(...)
I've seen this in practice a lot in jQuery and many people with jQuery experience. You end up with methods like showFoo / hideFoo, enableFoo / disableFoo, and then you have to continuously write if-statements like this:
if (someCondition) {
showFoo();
} else {
hideFoo();
}
Instead of: setFooVisible(someCondition);
The point being, creating lots of different methods for each variant reduces one's ability to use other abstraction tools, like variables, to control parameters.> I've seen this in practice a lot in jQuery and many people with jQuery experience.
A bit odd given jQuery often provides both options e.g. show[0]/hide[1] and toggle(bool)[2].
[0] http://api.jquery.com/show/
[1] http://api.jquery.com/hide/
[2] http://api.jquery.com/toggle/#toggle-display
A bit odd given jQuery often provides both options e.g. show[0]/hide[1] and toggle(bool)[2].
[0] http://api.jquery.com/show/
[1] http://api.jquery.com/hide/
[2] http://api.jquery.com/toggle/#toggle-display
Yes (and toggleClass too), but they were only added in 1.3.
Functions are first-class values, so what's wrong with:
(someCondition? showFoo : hideFoo)();
You can also pass in arguments if they both accept the same parameters, eg. (someCondition? show : hide)(foo, duration);Except they're methods so you have to write
(someCondition ? foo.show : foo.hide)();
except the language does not auto-bind so you have to write (someCondition ? foo.show : foo.hide).call(foo);
or maybe (someCondition ? foo.show.bind(foo) : foo.hide.bind(foo))();Or:
foo[someCondition? 'show' : 'hide']();
Although looking up functions using raw strings is less than elegant. I'd be tempted to abstract the pattern using a function, but at that point I'd be doing functional programming anyway and wouldn't be using methods ;)Different names don't scale well beyond a single boolean argument.
Instead of having different methods like open/openAsync why not have enums?
Boolean parameters aren't the problem. It's that people use booleans when they mean "something that has two choices", they're too lazy to enumerate what those choices are, and most crucially, it cannot be easily inferred from context what the boolean argument means.
It's perfectly fine if your function takes a boolean argument where the parameter either means "TRUE" or "FALSE": EnableThing(bool) is self-descriptive (as long as passing TRUE enables the thing... too many APIs fall victim to this total failure as well).
The real problem is when you start using booleans where you lose the meaning. Mapping boolean to things that take on similar meanings { 0, 1 }, { NO, YES }, { OFF, ON }, and maybe stretching it a bit, { BLACK, WHITE } is probably okay as long as it is clearly spelled out what the flag is doing, and likely it should be the only parameter to the function (and definitely not when there are multiple flags, and especially definitely not when those flags have some interaction). Mapping boolean to { PURPLE, YELLOW }, { LEFT TO RIGHT, RIGHT TO LEFT }, or { SYNCHRONOUS, ASYNCHRONOUS } is asking for people to misuse your API, and worse - painting yourself into the corner when it comes time to handle BLUE, TOP TO BOTTOM, or ISOSYNCHRONOUS. In many of these cases, you're far better off just throwing in the ten second enum. ("Functions are cheap" is true for applications, but it's categorically false for libraries, especially those that must stand up to the test of time, so I would definitely stick to the enum - types really are cheap.)
His given example is definitely an example of doing it wrong, but I have a better one that I come across every day at my job:
#2. One boolean changes the behavior of the other. We don't have a complete mapping; only three of the four states are meaningful.
#3. The API is not expressive enough to tell you what expand/fill mean without looking at the documentation, so you have to "do the matrix" and start seeing blonde, brunette, redhead by memorizing the order of the flags.
An enumeration would tell you immediately, and be roughly as character efficient as "FALSE, FALSE", "TRUE, FALSE" or "TRUE, TRUE":
It's perfectly fine if your function takes a boolean argument where the parameter either means "TRUE" or "FALSE": EnableThing(bool) is self-descriptive (as long as passing TRUE enables the thing... too many APIs fall victim to this total failure as well).
The real problem is when you start using booleans where you lose the meaning. Mapping boolean to things that take on similar meanings { 0, 1 }, { NO, YES }, { OFF, ON }, and maybe stretching it a bit, { BLACK, WHITE } is probably okay as long as it is clearly spelled out what the flag is doing, and likely it should be the only parameter to the function (and definitely not when there are multiple flags, and especially definitely not when those flags have some interaction). Mapping boolean to { PURPLE, YELLOW }, { LEFT TO RIGHT, RIGHT TO LEFT }, or { SYNCHRONOUS, ASYNCHRONOUS } is asking for people to misuse your API, and worse - painting yourself into the corner when it comes time to handle BLUE, TOP TO BOTTOM, or ISOSYNCHRONOUS. In many of these cases, you're far better off just throwing in the ten second enum. ("Functions are cheap" is true for applications, but it's categorically false for libraries, especially those that must stand up to the test of time, so I would definitely stick to the enum - types really are cheap.)
His given example is definitely an example of doing it wrong, but I have a better one that I come across every day at my job:
gtk_box_pack_start (Box, Widget, bool expand, bool fill, int padding);
#1. Two boolean flags, so you must remember the ordering, or look it up every time.#2. One boolean changes the behavior of the other. We don't have a complete mapping; only three of the four states are meaningful.
#3. The API is not expressive enough to tell you what expand/fill mean without looking at the documentation, so you have to "do the matrix" and start seeing blonde, brunette, redhead by memorizing the order of the flags.
An enumeration would tell you immediately, and be roughly as character efficient as "FALSE, FALSE", "TRUE, FALSE" or "TRUE, TRUE":
enum GtkBoxPackingOptions
{
PACK_SHRINK,
PACK_EXPAND_PADDING,
PACK_EXPAND_WIDGET
};
(And the latter enumeration is exactly what the gtkmm C++ bindings do.)Somewhat related to the "use an enum" approach: Value Objects.
Not quite an enum since it's not a design-time constraint, but they can let you "wrap" a scalar (or series of scalars) into something much more self-documenting, and prevent mismatches, like where someone substitutes length for mass.
For your example it'd be overkill since there are only four states, but if there was some additional related piece of information, like a number for tolerance, you could make an immutable object to group it all together.
Not quite an enum since it's not a design-time constraint, but they can let you "wrap" a scalar (or series of scalars) into something much more self-documenting, and prevent mismatches, like where someone substitutes length for mass.
For your example it'd be overkill since there are only four states, but if there was some additional related piece of information, like a number for tolerance, you could make an immutable object to group it all together.
It also depends on the language. In c# enums aren't required to have defined value. But it would help as documentation for what values you were expecting.
This gets especially horrible with multiple arguments, each of which is a bool.
The »two methods, distinguished in name« works fine, as long as you only have a single boolean argument and API growth won't ever happen around that point. Other options that often scale better would be enums, or a parameter object encompassing options, if there are many.
foo.frob(true, false, true);
and now try to figure out what each of them means. It can get better with explicitly stating argument names, but that's only a temporary stopgap.The »two methods, distinguished in name« works fine, as long as you only have a single boolean argument and API growth won't ever happen around that point. Other options that often scale better would be enums, or a parameter object encompassing options, if there are many.
Something I occasionally saw people do in lisp was
Another option would be
EDIT: And the D approach posted above inspires this idea in python:
It looks like D enforces the attribute name, so you can't get the arguments mixed up when writing and say No.sneaky, Yes.quick. It would be nice to get that as well.)
(frob foo :quick (not :sneaky) :force)
which could be done with strings in other languages foo.frob('quick', not 'sneaky', 'force')
It's not ideal, because it makes it look like the arguments mean something. But if someone remembers that frob takes three boolean arguments, but doesn't remember what order they come in, this could be handy.Another option would be
foo.frob(bool('quick'), not bool('sneaky'), bool('force'))
but that's kind of verbose.EDIT: And the D approach posted above inspires this idea in python:
class _ConstAttr:
def __init__(self, const):
self.const = const
def __getattr__(self, attr):
return self.const
Yes = _ConstAttr(True)
No = _ConstAttr(False)
foo.frob(Yes.quick, No.sneaky, Yes.force)
(It wouldn't be much use in Python, because of keyword arguments, but there might be languages without keyword arguments that could implement something like this. E.g. C macros YES(x) -> 1, NO(x) -> 0.It looks like D enforces the attribute name, so you can't get the arguments mixed up when writing and say No.sneaky, Yes.quick. It would be nice to get that as well.)
One solution I've seen is to use two unique string values instead of True/False, and reject all other string values.
eg.
eg.
foo.frob("quick", "loud", "noforce")
foo.frob("slow", "sneaky", "force")
foo.frob("fast", "sneaky", "force") # this errors
EDIT: Naturally I'd prefer a type-safe approach in languages that offer it, but this is a common solution I've seen in the python world.We used to call that "boolean salad" at an old job.
I've seen people suggest enums, strings, and named parameters. I'm surprised that nobody seems to have mentioned binary flags, because sometimes they work really well.
/* caller */
doSomething (arg1, arg2, XYZ_SYNC);
/* platform 1 */
#define XYZ_SYNC 0
#define XYZ_ASYNC 0x01
/* platform 2 */
#define XYZ_SYNC 0x01
#define XYZ_ASYNC 0
For extra credit, you can define flags so that one or the other must be specified, so both can't be, etc. Then you can still pack multiple flags into a single parameter, to reduce argument-passing time and space. I know it's all old-school and everything, but it has worked well for a lot of people over the years.hall of api shame: the boolean trap, by Ariya (of Esprima and PhantomJS) covers the same topic but is probably a more fun read.
http://ariya.ofilabs.com/2011/08/hall-of-api-shame-boolean-t...
I like D solution very much: http://dlang.org/phobos/std_typecons.html#.Flag which results in calls like:
document.addObserver(observer, "load", Yes.isWeak);
or xhr.OpenUrl(url, options, No.sync)Wow that looks really good. Can you use any String after the dot?
P.S. I don't use D
P.S. I don't use D
Yes, you declare the parameter as
Flag!"async"
And then the possible values are Yes.async and No.async
This exploits D dispatch operator: http://dlang.org/operatoroverloading.html#dispatch which in general is great for implementing this sort of dynamic feature in a strongly statically typed language as D - everything is checked as compile time!I don't see how this has any relation to API. The same argument could be true for any function — after all, your code would only benefit if you develop any class or function in your code base like an API endpoint.
And also, it is not the parameter type that is harmful here, but rather how the parameter is named and used. Let me just quote 2nd edition of Code Complete, which was written over 10 years ago: "give boolean variables names that imply true or false". Very simple, almost obvious piece of advice that summaries the problem on hand better than all this post.
And also, it is not the parameter type that is harmful here, but rather how the parameter is named and used. Let me just quote 2nd edition of Code Complete, which was written over 10 years ago: "give boolean variables names that imply true or false". Very simple, almost obvious piece of advice that summaries the problem on hand better than all this post.
"API" has a much broader meaning than the HTTP/REST/JSON variant that everyone is familiar with. Any class, 3rd-party lib, C header file, etc. that you include in your code can be considered to have an API - an application programming interface - as opposed to a application binary interface. I find it a great exercise when developing new code to first plan out the external API that other classes or programs will use to talk to it.
That's exactly what I meant. Ideally, I first write classes public interface (it's especially easy in C/C++, it's just an almost complete header file), then write some code that uses it to ensure that it is easy and intuitive to use, then unit tests, and only after that get to implementation. It's not always possible to follow this flow all the time (I work in the game industry and unit testing is seldom used, unfortunately), but I always find that I write better code when I follow it.
Not a problem with named arguments.
Too bad that they really screwed that up with ES6:
I really don't understand why they prioritized this kind of excessive flexibility over tooling. Now you can put entire programs into a function's signature. Why would anyone want that?
Too bad that they really screwed that up with ES6:
function foo({a = 1, b = 2} = {}) {...}
Dart's syntax is a lot easier to remember: foo({a: 1, b: 2}) {...}
Another very important detail: ES6's syntax is not declarative. You aren't restricted to "compile-time" constants and you can do all kinds of bogus stuff there. function foo({a = 1, b = 2, c} = {c: 3}) {
console.log(a, b, c);
}
foo(); // 1 2 3
foo({a: 'a'}); // a 2 undefined
foo({c: 'c'}); // 1 2 c
But wait! There is more! function foo({length = 2} = 'foobar') {
console.log(length);
}
foo(); // 6
foo({length: 0}); // 0
foo({x: 'x'}); // 2
Just look at that! Those short Hello World examples are already super confusing brain teasers.I really don't understand why they prioritized this kind of excessive flexibility over tooling. Now you can put entire programs into a function's signature. Why would anyone want that?
It's still a problem with named parameters if the name doesn't dramatically break the symmetry between "true" and "false". Even if it does, booleans are still dangerous because they're harder to check statically.
Which isn't to say named parameters don't help a bunch over boolean positional parameters!
A lovely trick I found with libdispatch: when the async-nature is specified dynamically, invoke via a function pointer determined by the ternary operator:
(async ? dispatch_async : dispatch_sync)(queue, ^{
/* stuff */
});
The reader does a double-take, but it's so damn pretty!Boolean is harmful, period.
https://existentialtype.wordpress.com/2011/03/15/boolean-bli...
https://existentialtype.wordpress.com/2011/03/15/boolean-bli...
I'll agree that Boolean positional arguments are bad. But nothing wrong with keyword args like myFunc(async=true).
My exact thought. Ruby ftw! :D
An enum/symbol makes more sense,
Xhr.open(type, url, Xhr.ASYNC);It goes for all primitive parameters. I can't tell you how many days I've burnt on this kind of bug. But I can tell you it stole 3 hours of my time yesterday. https://twitter.com/badgerhunt/status/587787102684758016
An alternative using ES6 object parameters:
function addObserver(observer, func, { weak = false } = {}) {
if (weak) {
// ...
}
}
addObserver(observer, 'load', { weak: true });To some extent, this is also true of any bare type. In C, I try to wrap primatives in a single-element struct that means something, particularly at function boundaries. On a modern compiler this doesn't change the emitted code, but it makes it much easier to avoid shooting yourself in the foot in ways like this.
[deleted]
Usually this is good advice. But there are exceptions:
enableOutput(bool)
is pretty easy to understand. If the function name explains the parameter then its ok.Why not enableOutput() and disableOutput()? enableOutput(false) is also not very clear.
...one interpretation might be enableOutput(prefixDate):
enableOutput(true) -> enable output with the date prefixed on each line
enableOutput(false) -> enable output, date is not prefixed on each line
disableOutput() -> disables all output
enableOutput(true) -> enable output with the date prefixed on each line
enableOutput(false) -> enable output, date is not prefixed on each line
disableOutput() -> disables all output
Alternate solution: use a language with shortcut syntax for named parameters. foo(sync => true) or foo(=>sync).
I like Objective-C for that.
This is programming 101. Kind of odd that this hits the front page of HN.
Might be programming 101 but you'd be amazed how much code out there is at the programming 100 level.
It's always a freakout moment when dealing with flags.
Boolean parameters aren't always bad. It depends on the language.
This is a more superficial explanation of why they're bad: they're hard to name. I agree. More deeply: they also scale horribly. Boolean parameters often are used to switch off to a meaningfully different function, and when they accumulate, you have 2^n different cases. At n = 6, you have two anti-patterns: a function with at least 6 parameters and probably more, and quite possibly 64 different use cases. How likely is it that those parameters all play well together? It's quite probable that there are combinations of the parameters that don't make any sense together.
They also don't extend well. If you move from 2 cases to 3, the common behavior (by a maintenance programmer who doesn't understand the code well and is working to deadline, so forgive him) is to add another boolean parameter rather than refactor it to an enum or union. Then you have a "case2" and "case3" boolean parameter and get the problem described above: 4 possibilities, 1 of which is meaningless.
With named, optional arguments, boolean parameters can be fine-- if you know that there will never be more than 2 cases. Sometimes they're the right thing. That said, they're a code smell (not always bad, but suggestive that you might be doing something wrong) and I'd almost never use them in Haskell, which (although a great language) doesn't have named arguments, because it's so easy to create your own datatype (that is easier to extend than a boolean).
This is a more superficial explanation of why they're bad: they're hard to name. I agree. More deeply: they also scale horribly. Boolean parameters often are used to switch off to a meaningfully different function, and when they accumulate, you have 2^n different cases. At n = 6, you have two anti-patterns: a function with at least 6 parameters and probably more, and quite possibly 64 different use cases. How likely is it that those parameters all play well together? It's quite probable that there are combinations of the parameters that don't make any sense together.
They also don't extend well. If you move from 2 cases to 3, the common behavior (by a maintenance programmer who doesn't understand the code well and is working to deadline, so forgive him) is to add another boolean parameter rather than refactor it to an enum or union. Then you have a "case2" and "case3" boolean parameter and get the problem described above: 4 possibilities, 1 of which is meaningless.
With named, optional arguments, boolean parameters can be fine-- if you know that there will never be more than 2 cases. Sometimes they're the right thing. That said, they're a code smell (not always bad, but suggestive that you might be doing something wrong) and I'd almost never use them in Haskell, which (although a great language) doesn't have named arguments, because it's so easy to create your own datatype (that is easier to extend than a boolean).
Languages without type-checking considered harmful.
Seriously, it's 2015, I shouldn't have to worry about getting my arguments out of order and slipping into production code. Test your code, pick a better language, or hell, use enums. Instead we get one hell of a click-bait headline and a very specious argument.
Seriously, it's 2015, I shouldn't have to worry about getting my arguments out of order and slipping into production code. Test your code, pick a better language, or hell, use enums. Instead we get one hell of a click-bait headline and a very specious argument.
Commenting without RTFA considered harmful.
TFA talks about passing false instead of true or vice versa.
TFA talks about passing false instead of true or vice versa.
Commenting without knowing that I read TFA is also considered harmful, but sorry I hit a nerve. I'm sure your Python/Ruby/Node is going to be nice and maintainable for years to come. Let's just hope all of your parameter names are meaningful, because hey, we have no other means to glean what should be passed in.
Unless your magical statically-typed pet language can read your mind and throw compilation error when you mistakenly write "x + y" when you should have "x - y", I'm not sure what your point is.
The problem is that the type checker won't catch you when you accidentally call foo(true, false) instead of foo(false, true). Using enums is a valid solution, though.
So you propose a) Having a "boolean" enum which does't solve the problem or having dozens of unique "boolean" enums which is a maintenance nightmare. Instead, I can use my IDE to tell me the names and ordering of parameters, and a type checker to verify everything at compilation. You'll never completely stop the problem of logical errors, and enums are certainly no panacae.
I think in languages like ML or Haskell, people are encouraged to be trigger-happy about creating enums with meaningful names for every tiny use case. That's a viable approach because defining a new enum literally takes one line, and working with enums is very natural due to pattern matching. The maintenance nightmare doesn't seem to happen. Though in languages like Java that approach would be much less convenient.
I can understand that line of thinking, but in given the OP is talking in JS, and and that I've only ever written such languages at Uni or for practice, I don't find them applicable to my day to day. I deal with Java, Scala, JS, and a few other C-style languages. These are how I think and work, these are, and I'm going to assume (make an ass of you and me ;)) that these are what the majority of HNers deal in.
Good to see that someone agrees with me for a change.
Good to see that you agree with someone for a change.
Alternate (better) solution to the author's problem: Use enums