Легко определить такой оператор, как
(@) :: [x -> y] -> [x] -> [y]
который принимает список функций и список входов и возвращает список выходов. Существует два очевидных способа реализовать это:
- Применить первую функцию к первому входу, вторую функцию ко второму входу и т.д.
- Применять каждую функцию к каждому входу.
Либо одинаково тривиально определить. Самое приятное в том, что теперь вы можете сделать что-то вроде
foo :: X -> Y -> Z -> R
bar :: [X] -> [Y] -> [Z] -> [R]
bar xs ys zs = [foo] @@ xs @@ ys @@ zs
Это обобщает на произвольное число аргументов функции.
Пока все хорошо. Теперь для проблемы: Как изменить сигнатуру типа для @@
так, чтобы сигнатура типа для bar
становилась
bar :: [X] -> [Y] -> [Z] -> [[[R]]]
Не сложно реализовать функцию с этим типом; любой из них сделает это:
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
Я не суетливый, какой результат я получаю. Но я не могу понять, как настроить этот оператор @@
.
Очевидная вещь, которую нужно попробовать:
(@@) :: [x -> y] -> [x] -> [[y]]
Это не сложно реализовать, но это вам не поможет. Теперь у вас есть
[foo] @@ xs :: [[Y -> Z -> R]]
который не является допустимым входом для @@
. Нет очевидного способа узнать, сколько уровней списков доступно для доступа к функции; очевидно, что этот подход является тупиком.
Я пробовал несколько других возможных типов подписей, но ни один из них не приближает меня к ответу. Может ли кто-нибудь дать мне решение или объяснить, почему никто не существует?