There are 400 million native English speakers, but over a billion people who speak English as a second language. … At any given instant on this planet, most people who are speaking English are not native speakers.
…
Perhaps we should take a good look at common forms of incorrect grammar and see if they actually make our language easier to learn. Maybe we should give a loose leash to those who are trying to make English more accessible.
I am going to try to use simple language and limited slang in my writing. When one considers the population of the world, it seems rather rude to address only the native English speakers.
September 26, 2008
Simple English
September 2, 2008
You Can Fool Some of the People
Preconceptions are a powerful thing.
In one recent test, psychologists asked 32 volunteers to sample strawberry yogurt. To make sure the testers made their judgments purely on the basis of taste, the researchers said, they needed to turn out the lights. Then they gave their subjects chocolate yogurt. Nineteen of the 32 praised the strawberry flavor. One said that strawberry was her favorite flavor and she planned to switch to this new brand.
August 31, 2008
Better List Termination
As I’ve written before, using nil
to terminate a variable-length list (eg [NSArray arrayWithObject:foo,bar,nil]
) is a bad choice because of how easy it is to get a nil
value in Objective-C without knowing it. (Unlike, say C++, doing stuff with nil
won’t cause an exception, leading to some subtle bugs). Well, here is a solution.
A Better Sentinel
#define END_OF_LIST ((id) -1)
is my current choice of a good sentinel-value. It is an invalid pointer, so unlike nil
, you should never get it back from some library. Sending it any message will fail fast, and recognizably (you will see 0xFF...FF
as an illegal address somewhere in the back-trace).
Example:
id aNilObject = nil;
NSArray *standard = [NSArray arrayWithObjects:@"1", @"2", aNilObject, @"4", nil];
LOG_ID(standard);
NSArray *better = [NSArray arrayWithSentinelTerminatedObjects:@"1", @"2", aNilObject, @"4", END_OF_LIST];
LOG_ID(better);
standard = (
1,
2
)
better = (
1,
2,
<null>,
4
)
The Code:
#define END_OF_LIST ((id) -1)
@implementation NSArray (SentinelTerminatedConstructors)
+ (NSArray*) arrayWithSentinelTerminatedObjects:(id) firstObject, ...
{
NSMutableArray *array = [NSMutableArray array];
if (firstObject != END_OF_LIST)
{
if(!firstObject)
firstObject = [NSNull null];
[array addObject:firstObject];
id eachObject;
va_list argumentList;
va_start(argumentList, firstObject);
while (END_OF_LIST != (eachObject = va_arg(argumentList, id)))
{
if(!eachObject)
eachObject = [NSNull null];
[array addObject:eachObject];
}
va_end(argumentList);
}
return array;
}
@end
Some Downsides
“arrayWithSentinelTerminatedObjects
” is too long of a name. If you’ve got a better one, please share!
You won’t get a compiler-warning if you leave out END_OF_LIST
, but if you compile with -Wformat
, you will get a warning if you leave out nil
. The full story
There are six collection creation methods in Foundation which require nil termination to function properly. They are:
+[NSArray arrayWithObjects:]
-[NSArray initWithObject:]
+[NSDictionary dictionaryWithObjectsAndKeys:]
-[NSDictionary initWithObjectsAndKeys:]
+[NSSet setWithObjects:]
-[NSSet initWithObjects:]
These methods have been decorated with theNS_REQUIRES_NIL_TERMINATION
macro, which adds an additional check to invocations of those methods to make sure the nil has been included at the end of the argument list. This warning is only available when compiling with-Wformat
.
What do you think?
Frankly, I haven’t used END_OF_LIST
-terminated collection creation enough yet to have a final verdict. So please let me know what you think! Was this code useful? Did it cause problems, or preempt them? As always, if you have a better solution, please share!
August 23, 2008
Don’t Buy Down Pillows
After I saw duckling’s jumping out of trees (from Planet Earth), I had to try some down pillows. And they are great, except that the tips of the feathers eventually work their way out of the pillowcase and poke you. The pokes are so disruptive that they ruin sleep more then a slightly-less-comfortable pillow. I’m sure there’s a good metaphor in there somewhere.
August 21, 2008
Are Line Numbers Just Clutter?
I think numbering every line in a source-code editor is (very mildly) harmful. It adds visual clutter; and that’s a bad thing.
Often you need to find a particular line number, say to locate a failed assert()
— but every programming editor has a “Go to line#” command, which is faster to use.
Showing the line number the cursor is on is worthwhile, because almost no display space is lost.
If you have your left-hand column set to show you line numbers, try turning it off and see what happens. You may be able to focus on your code ever so slightly better.
August 20, 2008
Hardwired Strings
With every programming language/development environment I know of, you have to do extra work to make a string localizable. For example,
errorMessage = NSLocalizedString(@"This is hard!",@"");
not simply,
errorMessage = @"This is hard!";
And it’s not just extra work, it’s a ridiculous amount of work. E.g. to create an Objective-C string, you just put an @
in front of a standard C string. But to create a localized string, you have to surround your string in ()
‘s, and prefix it with a seventeen character mixed-case function-name, and finally add an empty “note” for the translator. (Rarely, you may want to add a note for the translator, and it’s good that you can, but you should not be forced to write something when it is not necessary.)
There isn’t really an excuse for this; it’s just how languages have lazily evolved.
Non-localizable (“hardcoded”) strings are a huge issue, even though they are trivially simple for programmers to fix. The problem is that translators do not work with source code, they work with resource files. So when they find a “hardwired” string that must be changed in code, they tell a programmer to fix it. And that communication is slow and costly. Especially when you consider that good localizers are probably native to the place they are localizing for — meaning that having a real-time conversation involves at least one party staying up past 3AM.
I dream of a day where "this would be localized by default"
; "and"("syntactic sugar for adding optional translator notes would be there too")
; and creating C"literal C-strings"
would be easy.
“Unicode support libraries” are doomed to institutionalized-failure, because they will never make it easier to do the right thing. It must be so easy to create a localizable program that you wouldn’t dream of doing anything else.
Localizing In Xcode
Let me say this again in slow motion: NEVER type in ANY English string without typing
NSLocalizedString()
around it! This will save you SO MUCH HASSLE later on when your app is popular. Remember that enterprising polyglots can localize your code from just the binary you ship if you follow a few rules of localization, so you may wake up one day and find that someone from across the world has mailed you a your app in another language. It’s a fuzzy feeling and it gets you instant market-share.
August 12, 2008
Aesthetics Matter
.. aesthetics (are) important in UI. If you begin to look at something and want to avert your eyes, the site has failed.
I highly recommend the book Emotional Design, which makes this point in much more detail. A person’s emotional state has a quantifiable impact on how successful they will be at a task. Aesthetics are still the most direct way to manipulate emotion.
August 6, 2008
Distorting Text for Readability?
This is an award winning example. Compared to traditional “flat” text, the distorted text is certainly striking, looks more three-dimensional, and since it’s drawn on top of objects that would normally obscure signage it’s larger. But it can only be seen correctly from a few angles.
This example from 1939 looks a little confusing — more like a POTS sign then a STOP sign.
August 5, 2008
Simplified Logging
I have noticed a pattern in my Cocoa code, which I have been able to simplify. I often print out the value of a variable for debugging. 99 times out of 100, the code looks like this: NSLog(@"actionURL = %@", actionURL);
, where actionURL
is some variable.
But using the macros, I can say LOG_ID(actionURL);
. This is shorter, and non-repetitive.
The macros I use to simplify debugging (2008-09-16):
#define FourCharCode2NSString(err) NSFileTypeForHFSTypeCode(err)
#define LOG_4CC(x) NSLog(@"%s = %@", # x, FourCharCode2NSString(x))
#define LOG_FUNCTION() NSLog(@"%s", __FUNCTION__)
#define LOG_ID(o) NSLog(@"%s = %@", # o, o)
#define LOG_INT(i) NSLog(@"%s = %d", # i, i)
#define LOG_INT64(ll) NSLog(@"%s = %lld", # ll, ll)
#define LOG_FLOAT(f) NSLog(@"%s = %f", # f, f)
#define LOG_LONG_FLOAT(f) NSLog(@"%s = %Lf", # f, f)
#define LOG_OBJECT(o) LOG_ID(o)
#define LOG_POINT(p) NSLog(@"%s = %@", # p, NSStringFromPoint(p))
#define LOG_RECT(r) NSLog(@"%s = %@", # r, NSStringFromRect(r))
#define LOG_SIZE(s) NSLog(@"%s = %@", # s, NSStringFromSize(s))
Look in assert.h
for insight on how to roll your own debugging macros.