Hashing functions should return falsy values if they don't match and otherwise
should return the string id they should hash into.

If a or b is an array, they are treated as a path through the document to
obtain the hashed element starting at the document's 'type' field and deeply
traversing for the rest of the arguments. For example ['beep','boop','x',2]
would resolve to the string "ghi" for this document:

{

"type":"beep",

"boop":{"x":["abc","def","ghi"]},

"z":55

}

If a or b is a string, they are converted to [a,'id'] or [b,'id']
accordingly so that you can more tersely express a foreign key relation on the
"id" field.

Compute a reduce over all the pairs that resolve to the same hash id with
fn(acc, a, b) for an accumulator acc and the objects a and b that match
the hashing functions supplied to or inferred from join() in the same order.

fn(acc, a, b) should return the new accumulator value acc.
Each time a new value is derived, the 'result' event fires with the new
accumulator result.