I’m looking for a design pattern that will let me broadcast a function over two input lists, returning a 2D array of the output of the function for all possible pairs from the two lists.

I know you specifically asked about two inputs, but just as a general note, you can combine your idea of Iterators.product and @marius311’s use of comprehensions for any number of arguments:

[f(args...) for args in Iterators.product(x, y, z, a, b, c)]

would give you the 6-dimensional cube over all combinations of arguments.

I’d like to point out to anyone referencing this that replacing

f.(permutedims(x), y)

with

f.(x', y)

does not work. It errors when x is a Vector of length one. It is trying to take the Adjoint (transpose) of the element itself in that case. I’m not sure if this is a bug or is intentional.

Its intentional, adjoint is recursive like the mathematical construct, while permutedims is not (that was why I used the latter). I think this has some discussion of why, if I remember right. Fwiw, I don’t think vector length matters, e.g. ["a","b"]' is also an error.