Довольно легко определить оператор вроде
(@) :: [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]]
, который не является допустимым вводом для @@
. Нет очевидного способа узнать, через сколько уровней списков нужно дойти до функции; очевидно, что этот подход тупиковый.
Я пробовал несколько других возможных сигнатур типов, но ни одна из них не приближает меня к ответу. Может ли кто-нибудь дать мне решение или объяснить, почему его не существует?