It's quite easy to define an operator like
(@) :: [x -> y] -> [x] -> [y]
which takes a list of functions and a list of inputs and returns a list of outputs. There are two obvious ways to implement this:
- Apply the first function to the first input, the second function to the second input, etc.
- Apply every function to every input.
Either is equally trivial to define. The nice thing about it is that you can now do something like
foo :: X -> Y -> Z -> R
bar :: [X] -> [Y] -> [Z] -> [R]
bar xs ys zs = [foo] @@ xs @@ ys @@ zs
This generalises to an arbitrary number of function arguments.
So far so good. Now for the problem: How to I change the type signature for @@
such that the type signature for bar
becomes
bar :: [X] -> [Y] -> [Z] -> [[[R]]]
It's not hard to implement a function with this type; either of these will do it:
bar xs ys zs = map (\ x -> map (\ y -> map (\ z -> foo x y z) zs) ys) zs
bar xs ys zs = map (\ z -> map (\ y -> map (\ x -> foo x y z) xs) ys) zs
I'm not fussy about which result I get. But I can't figure out how to tweak the @@
operator to do this.
An obvious thing to try is
(@@) :: [x -> y] -> [x] -> [[y]]
It's not hard to implement this, but it won't help you. Now you have
[foo] @@ xs :: [[Y -> Z -> R]]
which is not a valid input to @@
. There's no obvious way to know how many levels of lists to reach through to get to the function; clearly this approach is a dead end.
I've tried several other possible type signatures, but none of them takes me closer to the answer. Can somebody either give me a solution, or explain why none exists?