Threading automatically with Listable functions requires the argument expressions to have the same length (or for one of them to be atomic). For nested lists the threading will continue down through the levels, provided the lengths are the same at each level. So, for example, these all work because the Dimensions of the two lists are the same at the first $n$ levels:
Array[#&, {10, 5, 3}] + Array[#&, {10}];
Array[#&, {10, 5, 3}] + Array[#&, {10, 5}];
Array[#&, {10, 5, 3}] + Array[#&, {10, 5, 3}];
whereas this doesn't work because the outer Dimensions don't match ($10\neq 5$):
Array[#&, {10, 5, 3}] + Array[#&, {5, 3}];
(* Thread::tdlen: Objects of unequal length ... cannot be combined. *)
But there is an obvious interpretation of the above code, which is to map the addition over the outer level of the first argument, i.e. to add the second 5x3 array to each of the ten 5x3 arrays in the first argument.
A more easily visualised example is adding an offset to a list of coordinates:
coords = {{1, 2}, {3, 4}, {5, 6}, {7, 8}};
offset = {0, 10};
One way is to explicity Map the addition over the coordinate list:
result = # + offset & /@ coords
(* {{1, 12}, {3, 14}, {5, 16}, {7, 18}} *)
If the coordinate list was very long, a more efficient approach using Transpose might be preferred:
result = Transpose[Transpose[coords] + offset]
(* {{1, 12}, {3, 14}, {5, 16}, {7, 18}} *)
Neither of these is particularly readable though. It would be nice to have a "smart" threading function that would identify that the second dimension of coords (length 2) matches the first dimensions of offset (also length 2), allowing the code to be written very readably:
result = smartThread[ coords + offset ]
(* {{1, 12}, {3, 14}, {5, 16}, {7, 18}} *)
How can I write such a smartThread function, which will take an expression of the form func_[a_, b_] and match up the various dimensions in a and b to do this kind of flexible threading?

smartThread[f[{a}, {{b}, {c}}]]swaps the order and returns{{f[b, a]}, {f[c, a]}}– Rojo Apr 16 '13 at 17:28Orderlessfunctions... – Simon Woods Apr 16 '13 at 18:00aandbhave been swapped, but I'm trying to think of a neater way to handle it. – Simon Woods Apr 16 '13 at 18:10smartThread@f[{{x[1], x[2]}}, {{y[1]}, {y[2]}}]andsmartThread@f[{{x[1]}, {x[2]}}, {{y[1], y[2]}}]what you expect? I mean, the dimensions of the output can depend on the order of the lists? Or is that a bonus case that doesn't need to be handled? – Rojo Apr 16 '13 at 18:18smartThread@f[h@1, {2}]– Rojo Apr 16 '13 at 18:29ArrayDepth, but if they both have the same depth the output takes the shape of the first input. – Simon Woods Apr 16 '13 at 19:42List- I should put an?ArrayQcheck in the pattern. – Simon Woods Apr 16 '13 at 19:44