Можно ли сделать foldLeft
для списка аргументов, где начальное значение, передаваемое в свертку, является полностью каррированной функцией, оператором это apply
, а список представляет собой список аргументов, которые необходимо передать функции f
?
Например, предположим, что f определяется как:
scala> val f = (i: Int, j: Int, k: Int, l: Int) => i+j+k+l
f: (Int, Int, Int, Int) => Int = <function4>
Что мы можем из конечно, используйте напрямую:
scala> f(1, 2, 3, 4)
res1: Int = 10
Или карри и применяйте аргументы по одному:
scala> f.curried
res2: Int => Int => Int => Int => Int = <function1>
scala> f.curried.apply(1).apply(2).apply(3).apply(4)
res3: Int = 10
На первый взгляд это выглядит как работа для foldLeft
.
Моя первая попытка описать последовательность apply
с использованием foldLeft
выглядит так:
scala> List(1, 2, 3, 4).foldLeft(f.curried)({ (g, x) => g.apply(x) })
Однако это приводит к следующей ошибке:
<console>:9: error: type mismatch;
found : Int => Int => Int => Int
required: Int => Int => Int => Int => Int
List(1, 2, 3, 4).foldLeft(f.curried)({ (g, x) => g.apply(x) })
Мое прочтение сообщения об ошибке неверно. этот вывод типа потребует некоторой подсказки для g
.
Решение, которое я ищу, оставляет все неизменным в моем исходном выражении, кроме типа g
:
List(1, 2, 3, 4).foldLeft(f.curried)({ (g: ANSWER, x) => g.apply(x) })
Моя первая мысль заключалась в том, что здесь будет полезен тип объединения. Я видел, как Майлз Сабин выводил типы объединения с помощью Карри-Ховарда, так что, если эта первая догадка верна, то, похоже, у меня есть базовый механизм, необходимый для решения проблемы.
Однако: даже если типы объединения являются ответом, было бы полезно, если бы я мог сослаться на «Объединение всех типов от полностью каррированного типа функции до типа каррированной функции со всеми, кроме последнего предоставленного аргумента» . Другими словами, способ превратить тип:
T1 => ... => Tn
в тип объединения:
(T1 => ... => Tn) |∨| ... |∨| (Tn-1 => Tn)
будет полезен в качестве типа для g
выше.
Выполнение foldLeft
в списке
ограничивает обсуждение случаем, когда от T1
до Tn-1
все одинаковы. Обозначения, подобные
(T1 =>)+ Tn
, описывают тип, который я хочу предоставить для g
.
Конкретный случай, о котором я спрашиваю, не требует произвольно длинных цепочек, поэтому мы могли бы указать границы итератора, используя
(T1 =>){1,4} Tn
Забегая вперед, желая сделать это для цепочек типов, которые не равны, хотя, возможно, некоторая магическая функция для типов, которая разбивает цепочку на набор всех суффиксов, более полезна:
Suffixes(T1 => ... => Tn)
Реализация этого выходит далеко за рамки моих возможностей Scala на данный момент. Любые намеки на то, как это сделать, будут оценены. Я не знаю, можно ли это сделать с расширенным использованием существующей системы типов Scala или через плагин компилятора, или ни то, ни другое.
Как было отмечено в комментариях ниже, называть результат «тип объединения» не совсем подходит для этого варианта использования. Я не знаю, как это еще назвать, но это самая близкая идея, которая у меня есть на данный момент.Есть ли у других языков особая поддержка этой идеи? Как это будет работать в Coq и Agda?
Назвать эту проблему и понять, где она находится по отношению к более широкой картине (теории типов, разрешимости и так далее), для меня важнее, чем наличие работающей реализации ] ОТВЕТ
, хотя и то, и другое было бы неплохо. Бонусные баллы всем, кто может связать себя со Scalaz, моноидами или теорией категорий в целом.