Приложение вложенных функций

Довольно легко определить оператор вроде

(@) :: [x -> y] -> [x] -> [y]

, который принимает список функций и список входов и возвращает список выходов. Есть два очевидных способа реализовать это:

  1. Применить первую функцию к первому входу, вторую функцию - ко второму входу и т. Д.
  2. Применить каждую функцию к каждому входу.

И то, и другое одинаково тривиально определить.Приятно то, что теперь вы можете делать что-то вроде

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]]

, который не является допустимым вводом для @@ . Нет очевидного способа узнать, через сколько уровней списков нужно дойти до функции; очевидно, что этот подход тупиковый.

Я пробовал несколько других возможных сигнатур типов, но ни одна из них не приближает меня к ответу. Может ли кто-нибудь дать мне решение или объяснить, почему его не существует?

16
задан Dan Burton 16 December 2011 в 19:35
поделиться