Thursday, April 7, 2011

List Comprehensions in C

Some introductions to Haskell monads begin with the IO monad. After a lot of study and setting up a few functions, you can write lines of Haskell guaranteed to execute exactly once in the order of appearance, with errors handled appropriately.

Congratulations, you’ve waded through large tracts of theory only to acheive what C programmers take for granted. Monads are undoubtedly a formidable weapon for pure functional programmers battling with side-effects, but what do imperative programmers care?

On the other hand, list comprehensions impress veteran coders of all stripes due to their astounding conciseness, and hence are a more universally appealing showpiece for monads. Consider a simple example:

[(x,y) | x <- [1,2,3], y <- [1,2,3], x /= y]

which is:

[(1,2),(1,3),(2,1),(2,3),(3,1),(3,2)]

Wouldn’t it be great if C could do this? Our goal for this post is to get list comprehensions in C, preferably without learning anything about monads!

A spoonful of syntactic sugar

How do list comprehensions automatically try all combinations of x and y? Does Haskell have a special list comprehension parser that generates loops and stuff?

Kind of. There is a special parser, but it merely converts our list comprehension into:

do x <- [1,2,3]
y <- [1,2,3]
True <- return (x /= y)
return (x,y)

This hardly differs: some argue it is so close to the original that list comprehension should be rarely used. There are still no signs of any list operations, nor loops. We’re no closer to solving the mystery.

On the bright side, it now resembles C enough that we can consider translating it.

Let me C

A first attempt:

BEGIN_DO;
int a[] = { 1, 2, 3, 0 };
int b[] = { 1, 2, 3, 0 };
ASSIGN(x, a);
ASSIGN(y, b);
if (*x != *y) printf(" (%d, %d)", *x, *y);
END_DO;

We’ll figure out the macro definitions in a second. We’re taking a few shortcuts here. We’ve only translated the first two <- expressions. We terminate the arrays with 0. And instead of returning a list of tuples, we simply print them out.

If the ASSIGN macros could change the above into two nested for loops then we’d be done:

for(*x = a; *x; x++) {
for(*y = b; *y; y++) {
if (*x != *y) printf(" (%d, %d)", *x, *y);
}
}

Unfortunately, the ASSIGN lines are self-contained; if we translate them to for loops they’ll stay unnested. Instead, we can use computed goto to get the same effect:

#define BEGIN_DO do { \
void *stack[128]; \
int stacki = 0; \
stack[stacki] = &&label_end
#define END_DO goto *stack[stacki]; label_end: ; } while(0)
#define ASSIGN(x, a) \
int *x, *x##next = a; \
stack[++stacki] = &&label_##x; \
label_##x: if (!*(x = x##next)) goto *stack[--stacki]; else x##next = x + 1

It looks horrible, and we needed GNU C extensions, but we’ve hidden loops in the macros, just as Haskell must be doing.

Another spoonful

It turns out Haskell’s do and <- are themselves syntactic sugar. Our example in fact expands again to:

[1,2,3] >>= (\ x -> [1,2,3] >>= (\y -> return (x/=y) >>=
(\r -> case r of True -> return (x,y)
_ -> fail "")))

This is unsweetened Haskell code. Still no loops, but we’ve learned those separate lines of Haskell are really one big line: later lines are function evaluations nested within earlier lines.

Simulating nested for loops in C was on the right track after all. However, we should have used nested functions. The extra flexibility and power should help complete the conversion.

This time, we’ll build a list of elements instead of just printing them. Also, let’s replace the tuple (x,y) by the integer x+y so we can stick to integers and lists of integers; we can find a C equivalent for Haskell lists and tuples another day.

Intuition suggests all our functions should work with lists; then they’ll look alike, which should make macros easy to write. This implies we must occasionally package integers in singleton lists. We wind up with:

int *f0() {
int a[] = { 1, 2, 3, 0 };
int b[] = { 1, 2, 3, 0 };

int x;
auto int *f1();
{
int *r = list_0();
for(int *p = a; (x = *p); p++) r = list_cat(r, f1());
return r;
}

int y;
auto int *f2();
int* f1() {
int *r = list_0();
for(int *p = b; (y = *p); p++) r = list_cat(r, f2());
return r;
}

int z;
auto int *f3();
int* f2() {
int *r = list_0();
for(int *p = list_1(x != y); (z = *p); p++) r = list_cat(r, f3());
return r;
}

int* f3() {
if (z) return list_1(x + y);
return list_0();
}
}

where list_0 returns an empty list of integers, list_cat concatenates two lists of integers, and list_1 creates a singleton list containing the given integer. The functions f0 and f1 take on the roles of the two nested for loops from our previous attempt, only now they also concatenate the values they compute.

Because forward-declaring functions is difficult with macros, we introduce an array of function pointers so one function can call the next. Also, since C doesn’t do pattern matching, we’ll cheat with an ad hoc macro. The full program:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BEGIN_DO ({ \
int *(*fun[64])(int); \
int funi = 0

#define END_DO fun[0](0); \
})

#define ASSIGN(x, a) int x; \
int *x##fun(int i) { \
int *r = list_0(); \
for(int *p = a; (x = *p); p++) r = list_cat(r, fun[i+1](i+1)); \
return r; \
} \
fun[funi++] = x##fun

#define MATCH(x, a) int* match() { \
if (x) return a; \
return list_0(); \
} \
fun[funi++] = match

int *list_0() {
int *r = malloc(sizeof(*r));
*r = 0;
return r;
}

int list_len(int *a) {
int *x = a;
while(*x) x++;
return x - a;
}

int *list_cat(int *a, int *b) {
int m = list_len(a), n = list_len(b);
int *r = realloc(a, sizeof(*r) * (m + n + 1));
memcpy(r + m, b, sizeof(*r) * n);
r[m+n] = 0;
free(b);
return r;
}

int *list_1(int n) {
int *r = malloc(sizeof(*r) * 2);
*r = n;
r[1] = 0;
return r;
}

int main() {
int *r = BEGIN_DO;
int a[] = { 1, 2, 3, 0 };
int b[] = { 1, 2, 3, 0 };
ASSIGN(x, a);
ASSIGN(y, b);
ASSIGN(z, list_1(x != y)); // Memory leak.
MATCH(z, list_1(x + y));
END_DO;

// Should be 3 4 3 5 4 5.
for(int *x = r; *x; x++) printf(" %d", *x);
putchar('\n');
free(r);
exit(0);
}

At last! List comprehensions in C.

Haskell’s list monad

Haskell does not need zany macros. Instead, it has:

instance Monad [] where
return a = [a]
xs >>= f = concat (map f xs)
fail _ = []

Mystery solved. The >>= operator is like our ASSIGN macro: they both compute a function on each element of a list and concatenate all the results. The return function is our list_1 function, and fail is our list_0.

In both languages, we made looping over lists invisible by writing a routine that converts an integer into a list (just put it in a list of size one) and another that runs a function on lists of integers even though the function normally takes integers (just run the function on each integer in the list, then concatenate the results).

We can generalize this idea to hide other kinds of repetitive code. This is precisely what Haskell monads are, and why do notation exists. Roughly speaking, we augment the abilities of existing functions by describing how to convert a regular output into a new and improved type (monadic type) and how to get a function to operate on monadic types when it expects the regular type.

It’s cool that we can get the same effect in C. Nevertheless:

  • Haskell has a few lines of robust, clean code instead of fragile, ugly macros.

  • This design may never occur to a C programmer, while monads are ubiquitous in Haskell.

  • Monads mesh well with pattern matching, which C lacks.

A comprehensions compromise

Our macros were challenging to write because their combined output must be nested. It’s why we needed computed goto and a stack of labels when simulating nested for loops, and why we needed an array of function pointers when we moved to nested functions.

It would be easier if we tolerated nesting:

int* assign(int *x, int *a, int *(*fun)()) {
int *r = list_empty();
for(int *p = a; (*x = *p); p++) r = list_cat(r, fun());
return r;
}

int *lamb() {
int a[] = { 1, 2, 3, 0 };
int b[] = { 1, 2, 3, 0 };
int x;
auto int *lamb();
return assign(&x, a, lamb);
int *lamb() {
int y;
auto int *lamb();
return assign(&y, b, lamb);
int *lamb() {
int z;
auto int *lamb();
return assign(&z, list_1(x != y), lamb);
int *lamb() {
return z ? list_1(x + y) : list_empty();
}
}
}
}
int *r = lamb();

The code is verbose, because at several points, one line declares a function, another calls it, and a third defines it. All three would coalesce into a single line if C had lambda expressions. Frustratingly, GNU C supports closely related nested functions but not anonymous functions. Why not? Even standard C++ is getting lambda expressions! However, if we tolerate one macro, we can ease the pain:

#define ASSIGN(x, a) \
int x; \
auto int *lamb(); \
return assign(&x, a, lamb); \
int *lamb()

...

int *lamb() {
int a[] = { 1, 2, 3, 0 };
int b[] = { 10, 20, 30, 0 };
ASSIGN(x, a) {
ASSIGN(y, b) {
ASSIGN(z, list_1(x != y)) {
return z ? list_1(x + y) : list_empty();
}
}
}
}
int *r = lamb();

While messier than Haskell, it is still readable. Moreover, the nested braces may aid seasoned imperative programmers, who might incorrectly think the Haskell do notation represents statements executing one after another. Here, it is unmistakeably clear we are nesting function calls.

Wednesday, April 6, 2011

Convert line breaks? No!

When I moved my blog to this site, I left the default settings alone. Unfortunately one of them was "Convert line breaks". While harmless at first, it eventually caused headaches when editing HTML for fancier formatting. Turning off the option destroyed all paragraph breaks in older posts, so I had to leave it on for their sake.
I believe Blogger no longer has this problem: on a new test blog, flipping the switch appeared to have no effect on existing posts. However, my blog predates this change.
Googling yields many reports of trouble with this maleficent option. Some suggest a laborious method of disabling it: turn it off, then break paragraphs in old posts by hand!
Such advice was probably written before Blogger gained the export and import features, which are the key to a simpler but scarier solution:
  1. Export the blog, from the "Basic" tab under "Settings".
  2. In the "Edit Posts" tab under "Posting", select all posts.
  3. Delete them all!
  4. import the blog and publish all posts, again from "Basic" tab under "Settings".
Now you can say no to "Convert line breaks" without trashing ancient posts, though when editing new posts, you still have to select "Use <br /> tags" under "Post Options" to get the desired behaviour.
It works because the export format robustly represents line breaks with HTML tags whether or not the evil option was on when the post was written. Take care: after I exported my blog, I got distracted by the recently added "Spam" tab under "Comments", and found a non-spam comment sequestered within. I freed it, but forgot to re-export my blog, so after I deleted all posts and imported my blog, the comment was lost forever.
The export format has also given me ideas for my script that publishes HTML posts via the Blogger Data API, which has a hard time preserving formatted source code. Unfortunately, frequent experiments have temporarily barred my script:
Blog has exceeded rate limit or otherwise requires word verification for new posts

I’ll try again another day.

Permalink Pain

Unfortunately, I belatedly discovered serious manual editing is still required. Permalinks have changed; they now put the day of the month in the URL. Restoring old entries generates the new kind of permalink, invalidating all existing links to old posts.

Sunday, April 3, 2011

Tagline-Oriented Programming

In the land of "tl;dr", the one-liner is king. Short is sweet, and also highly contagious. A paragraph is easy to ignore, while a good tagline embeds itself in a reader’s mind against their will, spreading independently of the accompanying product.

In programming, some one-liners are equally alluring. There is only room for the bare essentials: we must distill the purest form of solutions to problems. Their brevity makes them memorable, and awe-inspiring, as shrinking code can take considerable skill; most recently, I’ve been stunned by a 1433-character chess engine in C.

Having already praised Bash one-liners, this time I’m taking a shallow look at Haskell, and in particular, a trivial function:

emp :: a -> ([b] -> a) -> [b] -> a
emp a f [] = a
emp a f (x:xs) = f (x:xs)

Or since we’re advocating one-liners:

emp a f x = if null x then a else f x

Why? In Haskell, recursion on lists often goes like this:

f []     = empty_list_case
f (x:xs) = recurse_somehow x f(xs)

The emp function let us combine these two lines into one. The fold family of functions is similar: for them, the 2nd argument handles the empty-list case, while the 1st argument is a function that describes how to process the next element. However, they are insufficiently general for our purposes.

Should we go to this trouble just to squeeze the empty-list case into the same line as the inductive step? Yes, if you share my appreciation for one-liners! Also:

  • this is a natural extension of the precedent set by the fold family

  • it’s harder to forget about the base case

  • single lines suit interactive Haskell (if you do type these in, remember to add "let" in front of the definitions).

Examples

Here’s foldl and foldr in one line each:

foldr f z = emp z $ \(x:xs) -> f x (foldr f z xs)
foldl f z = emp z $ \(x:xs) -> foldl f (f z x) xs

And the membership test:

elem x = emp False $ \(y:ys) -> x==y || (x `elem` ys)

All subsequences (though in an order differing from Data.List.subsequences):

sub = emp [[]] $ \(x:xs) -> sub xs ++ (map (x:) (sub xs))

How about all permutations? In C, where we can loop through an array and swap elements in place, an easy but impure recursive solution takes every element in turn, temporarily swaps it to the first position, then calls itself on the remainder of the elements, where the lowest level of recursion prints the whole buffer.

In Haskell, I found it easiest to do something similar by maintaining 2 lists on either side of a selected element. My first attempt came out as:

-- Invoke with: p "" "" "abc"
p a [] [] = [a]
p [] ys [] = []
p a ys [] = map (a++) (p [] [] ys)
p a ys (x:xs) = (if null a then (p [x] ys xs) else []) ++ (p a (x:ys) xs)

It’s unclear how to rewrite this in one line. Perhaps it’s best to break the problem into two sub-problems to get one-liners. The following is based on elegant code found on a mailing list:

f = emp [] $ \(x:xs) -> (x,xs):[(y,x:ys) | (y,ys) <- (f xs)]
perm = emp [[]] $ \xs -> [y:zs | (y,ys) <- (f xs), zs <- (perm ys)]

The helper function f is the FP version of iterating through a list and plucking an element out, and has uses outside this problem.

Integers

A similar definition for integers is also handy:

induct a f x = if 0==x then a else f

For starters, we can define emp with induct:

emp a f = induct a f . length

List all n-letter strings consisting of the letters in "BEN":

f = induct [""] $ \n -> [x:xs | x <- "BEN", xs <- f (n-1)]

Compute an n-bit Gray code:

g = induct [""] $ \n -> (map ('0':)) (g (n-1)) ++ (map ('1':)) (reverse (g (n-1)))

Though shallow, Haskell would gain from default induct and emp functions. (Perhaps there already are? I don’t know Haskell that well.) They bring the length of many interesting function defintions under a mystical threshold that makes them catchy and appealing.

Sunday, March 27, 2011

CMU -= OOP, CMU++;

I should have been a comedian. I have excellent timing.

I applied for Computer Science grad school during the irrationally exuberant dot-com boom, when much of my competition ran off chasing easy money. During the subsequent crash I was already a sheltered student. By the time I finished, the economy was up again. My lucky streak continued as I managed to secure a job before the global financial crisis struck.

However, I’m most thankful that I was amongst the last batch of students of my undegraduate CS program who were only taught object-oriented programming in an optional course.

It wasn’t just my alma mater. According to an interview with a CS professor emeritus at NYU, around that time, many universities bowed to pressure to switch to Java and focus less on fundamentals. This produced less capable programmers, or as the article puts it, “today’s Java-savvy college grad is tomorrow’s pizza-delivery man”. Again, my competition was decimated merely because of timing.

Apart from increased job security, I’m grateful I learned less object-oriented nonsense and more CS, as I’m deeply interested in the subject. In fact, this is why I chose to study CS; I’m just fortunate I happen to live in a time and place where coding is a marketable skill.

Now that I can afford to be less selfish, I wish universities would undo this injustice and remove object-oriented programming from introductory CS curriculums, so future students can benefit, just as Carnegie Mellon University is doing. CMU's decision inspired me to post this entry, and update my page denouncing object-oriented programming.

Business

While some applaud the move, others state that learning a language and programming paradigm rarely seen in industry leaves a student ill-prepared.

My own experience suggests otherwise. I have little trouble coding in new programming languages so long as they’re not too esoteric. Granted, the first few lines are painful, as I’ll have to constantly look up keywords in a reference while acclimatizing to error messages and other peculiarities of the development environment. But after a while, I’ll pick up the syntax for arithmetic, loops, conditionals, function calls, and so on, and that’ll be enough to keep me going. I might miss out on idioms, but I can get my point across.

The difficult part is designing the underlying data structures and algorithms. That’s where CS is most magical. Functional programming gives me an edge. It makes recursion less scary, and recursion is often a great approach to a problem: sorting, parsing, anything with trees. Also, experience with lazy evaluation increases my awareness of parallelism.

I believe it also makes my code cleaner, because treating functions as first-class citizens opens my eyes to design choices I might otherwise miss. It’s hard to say: a Java fanatic might be trained to spot perfect places for anonymous inline classes.

In interviews, I let candidates choose the language as I’m more interested in the idea being expressed. Some candidates write code comfortably, but struggle with relatively simple tasks because they can’t find the right algorithm or data structure. They’re like an author with perfect spelling but who only know boring stories.

Others quickly find a good solution, but their code may have syntax issues here and there. These mistakes hardly bother me because after a week of seeing the same compiler error messages over and over again, their syntax will be as good as mine.

Personal

Although OOP was optional for me, I studied it with relish. I read “Design Patterns” cover to cover shortly after purchasing it. Bertrand Meyer’s words were gospel. Eiffel, a pure OO language, became my favourite. I promoted OOP more fiercely than I attack it now; back then I was even more obstinate. I believed the only path to virtues such as modularity, information hiding, and code reuse was through OOP, partly because I thought OOP folks invented these concepts.

Over the years, events caused me to pause and reexamine what I had been taught. Many lines of code later, I finally discarded OOP and freed myself. I felt cheated, which is why I’m more emotionally attached to this stuff than one might expect.

There’s no harm in learning OOP as long as you avoid my mistake and arm yourself with a healthy dose of skepticism (which you should do all the time anyway, including now).

Monday, February 7, 2011

UTF-8 good, UTF-16 bad

Lately I’ve been programming in Go. I started by writing a few popular Unix tools in Go, before moving on to a lexer that supports structural regular expressions and UTF-8.

Unicode support was easy to add because Go lives and breathes UTF-8. But why did the Go designers favour UTF-8, and not UTF-16? Widespread software such as Windows, Java, and Python use UTF-16 internally, so why not Go?

Further reading revealed the answer. UTF-16 is a historical accident that persists mainly due to inertia. UTF-16 has no practical advantages over UTF-8, and it is worse in some ways. The Go designers knew what they were doing, so they chose UTF-8.

Down with UTF-16

Before, from vague hunches, I had thought that UTF-16 was simpler because each character took exactly 2 bytes (so it handled only a strict subset of Unicode), and that UTF-16 was more compact for certain languages.

The first statement is patently false. UTF-16 is a variable-length encoding, and hence not much simpler than UTF-8. As for the second: consider HTML. The amount of whitespace and tags in a typical document is high enough that UTF-8 is more compact than UTF-16. This generalizes to other formats, such as Go source code.

So there’s no upside. But there are plenty of downsides to UTF-16:

  1. We must worry about byte ordering because UTF-16 deals with 16 bits at a time. A related drawback is the loss of self-synchronization at the byte level.

  2. We lose backwards compatibility with ASCII.

  3. We lose backwards compatibility with code treating NUL as a string terminator.

  4. We cannot losslessly convert from byte streams.

  5. The encoding is inflexible: hopefully 0x110000 code points should be enough, but if it were extended (again), UTF-16 would need major surgery. In contrast, by simply allowing up to 6 bytes, UTF-8 can handle up to 231 code points.

  6. Ugly encoding: it’s the reason why U+D800 to U+DFFF is reserved (though thankfully UTF-16 is self-synchronizing with 16-bit granularity).

I tired of thinking about the drawbacks of UTF-16, but there are likely more. Thus even if we only worked with a strange set of documents where UTF-16 is more efficient, the savings are too small to justify the complexity.

A living dead standard

How did so many projects settle on such a horrible standard? It’s a sad story. The original Unicode supported exactly 0x10000 code points, making UCS-2 (the precursor to UTF-16) attractive. Each character neatly corresponded to one 16-bit word. Although UTF-8 might still be preferable thanks to byte ordering and ASCII compatibility, UCS-2 is certifiably sane. People built systems based on UCS-2, and rejoiced that the codepage Stone Age was over.

Then an unnatural disaster struck: the Unicode Consortium decided more code points were needed. The supple UTF-8 got away unscathed, but there’s no way to squeeze more than 16 bits into UCS-2. Apart from abandoning UCS-2, the only recourse was to reserve a chunk of values for encoding the higher code points. Thus UTF-16 was born. Hopefully, developers could simply tweak masses of existing UCS-2 code for the new standard. Sadly, various UTF-16 bugs suggest that much code remains unaware UTF-16 is a variable-length encoding.

Despite the trauma it would cause, they should have killed UCS-2 when they had the chance. Now we have a grotesque encoding scheme that shambles alongside UTF-8.

Gauss was here

According to Wikipedia, UTF-8 was built on ASCII by Rob Pike and Ken Thompson. ASCII was developed from telegraph codes by a committee. The telegraph codes stemmed from a Western Union code, which evolved from a code by Donald Murray, who modified a 5-bit code invented by Émile Baudot, who based his work on a code designed by Carl Friedrich Gauss and Wilhelm Weber. It’s awe-inspiring that the Prince of Mathematicians was involved. And if he were alive today, I bet he’d disparage UTF-16!

Tuesday, October 12, 2010

Spooked by a corrupt initrd image

Halloween is approaching. It is apt I have a scary tale to tell.

I left my laptop on in the sun, on a surface with poor conductivity. As I opened it, I glimpsed it complaining of heat exhaustion before it powered off. (That’s happened to me before, too!) I let it cool off, but on booting Ubuntu, a terrifying message was all that appeared on the screen:

Error 16: Inconsistent filesystem structure

Uh oh. Did I lose everything? What haven’t I backed up lately? Indeed, what have I backed up lately?

I searched online for the error message, and took heart when others reported no data loss in various forums. Running fsck was suggested, though in at least one case they resorted to reinstalling the OS. No way I’m doing that!

My first instinct was to dust off my USB DVD writer and burn a Ubuntu rescue CD. This turned out to be unnecessary, but at least I was reassured when I used it to mount the Linux partition and found my files intact. Firing up fsck had no effect: I was still defeated by the same error message.

Playing with the GRUB command-line (by pressing “c” instead of selecting an kernel to boot) showed that the initrd image was corrupted, as selecting the newest image with the “initrd” command triggered the same error message.

The solution was easy. Boot up an older kernel, then download a fresh copy of the initrd image by running:

$ sudo apt-get install --reinstall linux-image-2.6.32-25-generic

Thursday, October 7, 2010

Shameless plugs

Better the head of a chicken than the tail of an ox. This Chinese proverb captures entrepreneurial thoughts I’ve long harboured. It’s a common affliction around my neck of the woods.

Unfortunately, I’m disinclined to abandon my cushy job to work my fingers to the bone for an uncertain reward. My dreams are likely doomed to remain dreams.

To compensate, I’m living vicariously through friends who are being their own boss. I’m devoting this post to them; may it contribute at least an iota to their success.

Ethan lived on the ground floor of my apartment at Stanford. Early in my degree, he finished his Masters, in financial mathematics I think. I’m uncertain because we rarely chatted about school! I’m sure his latest venture will succeed, and not just because he’s capable.

I was in Ottawa for a conference in '03. I skipped half of it to hang out with Ethan, who gave me a tour of the city where he grew up, which became challenging when a widespread blackout hit the region. On top of all this, I had FedExed a visa document to his parent’s house (which I needed to return to the US), and he went out of his way to deliver it to me.

A few years later, I was in China on vacation, mainly because I wanted to see the Guangzhou Trade Fair. I met up with Ethan. I thought we’d do little more than catch up over a meal. Well, we did chat over dinner: Ethan had worked in Hong Kong’s finance sector for a while, and was now living off savings while exploring China. I was still a starving student.

But then Ethan took us to a health spa in Shenzhen which was cheaper yet far more luxurious than the hotels we had been considering. For about $20 a day I got massages, food, spas, haircuts, movies, a place to sleep, etc. I never thought I’d get a pedicure in my life, but because it was effectively free, I had to try it.

Then in Hong Kong, we crashed at his place, which saved a lot of money. He took us on a whirlwind shopping and eating tour. Soon after, we had to part ways, but not before he showed us a website offering discounted rates at hotels in Guangzhou. We got a great deal at a great hotel across the street from the trade fair. At the check-in counter, the guy in front of us was livid when he learned how little we were paying.

He offered to take me on a tour of China after the trade fair was over, but I declined. I deeply regret my decision. What’s a few weeks of missed grad school? Ethan spoke three or more Chinese languages. He knew where to go. He had contacts everywhere. Clearly, the China luxury travel business is perfect for him.

Rahul lived a few apartments away from me in grad school. He mysteriously vanished one year. I heard later it was because he was “saving babies”. This raised yet more questions! Wasn’t he an engineer?

Certain problematic births are far less problematic when an incubator is available. Unfortunately, the cost of typical incubators often places them beyond the reach of poor remote villages, contributing to higher infant mortality. Rahul helped design a vastly cheaper incubator, and hopes to eventually supply one wherever babies are born.

I met Kumaran through his wife. I was a Teaching Assistant for an introductory computer science class she was taking. Kumaran wanted to meet partly because he wanted help recruiting people for his company. I was of no help: at the time, my engineer friends were already employed, and they were busy recruiting people themselves!

Total Phase’s biggest hit is a USB protocol analyzer, an invaluable tool when developing USB products. Kumaran loves being an engineer designing for engineers. They never bothered advertising since engineers tend to use logic to determine purchases, and their products are better and cheaper than those of their competitors. I saw parallels when I read about the HP200A, an audio oscillator that was profitable because it was the best and the cheapest.

My cousin Wade designed a simple tool to render CDs and DVDs unreadable. An easy motion etches deep parallel scratches into the media: Freddy Krueger for discs. Quick and safe. No batteries. No electricity. The disc stays in one piece, ready for recycling.

He used to work on hard drive platters at IBM (before they sold the division to Hitachi) so he knows what he’s doing. He verified his claims with AcoDisc, a data recovery company. After a single application of the Disc Eraser to a CD, the data was deemed unrecoverable.

I don’t burn much, let alone anything confidential, so it’s not something I need. However, I’m grateful for a sample he gave me: as a kind of therapy, I savagely mutilate CDs I receive as junk mail.

Dave was yet another electrical engineering student in my neighbourhood during grad school. I never would have guessed he would join the dark side and become a Pointy-haired Boss of a startup. Seriously though, I like tech companies with an engineer at the helm. And Dave’s hair is not pointy.

Years ago, a friend once pointed out that two people in the same room cannot easily get their laptops to talk to each other. Sure, there’s Bluetooth, ad-hoc wireless networking, and other hacks, but he was right: even now, it’s easiest for one party to verbally ask for contact details of the other. This is somehow troubling.

I remember a light-hearted news report I watched once, which described a gadget in Japan. You’d enter a few personal details, and carry it around. It would alert you if someone of the opposite sex nearby had the same gadget and had similar interests to you. What became of this? Couldn’t they do something like this so you would never have to spell out someone’s email address or type in a phone number?

Enter Bump, an app for iPhone and Android that attacks this problem in a cute way. Both sides run the app, then lightly bump their phones together to swap contact details, or other data. No physical contact is actually needed as the phones just have to be close enough, but smashing your fists together is more macho!

Kestrel lives a few streets away, which benefits me greatly because she is a good chef and a keen gardener. She also loves making hair accessories, and recently started selling them. Each handcrafted piece is unique and beautiful, like their creator.