Ответ 1
-
Определение Haskell не является однородным. В Racket функция для обеих складок имеет одинаковый порядок входов, поэтому вы можете просто заменить
foldl
наfoldr
и получить тот же результат. Если вы сделаете это с помощью версии Haskell, вы получите другой результат (обычно) - и вы увидите это в разных типах двух.(На самом деле, я думаю, что для правильного сравнения вы должны избегать этих игрушечных числовых примеров, где обе переменные типа являются целыми числами.)
-
У этого есть хороший побочный продукт, где вам предлагается выбрать
foldl
илиfoldr
в соответствии с их семантическими различиями. Я предполагаю, что с ордером Haskell вы, вероятно, будете выбирать в соответствии с операцией. У вас есть хороший пример для этого: вы использовалиfoldl
, потому что хотите вычесть каждое число, и что такой "очевидный" выбор позволяет легко упустить тот факт, чтоfoldl
обычно является плохим выбором в ленивом язык. -
Другое отличие состоит в том, что версия Haskell является более ограниченной, чем версия Racket обычным способом: она работает только с одним списком, тогда как Racket может принимать любое количество списков. Это делает более важным иметь единообразный порядок аргументов для входной функции).
-
Наконец, неправильно предположить, что Racket отклонился от "многих других функциональных языков", поскольку сложение далеко не новое, а у Racket есть корни, которые намного старше, чем Haskell (или эти другие языки). Таким образом, вопрос может идти по-другому: почему Haskell
foldl
определен странным образом? (И нет,(-)
не является хорошим оправданием.)
Историческое обновление:
Так как это, кажется, беспокоит людей снова и снова, я немного поработал. Это никоим образом не является окончательным, просто мои предположения из вторых рук. Не стесняйтесь редактировать это, если вы знаете больше или даже лучше, пишите соответствующим людям и спрашивайте. В частности, я не знаю даты, когда эти решения были приняты, поэтому следующий список находится в грубом порядке.
-
Сначала было Lisp, и не упоминалось о "сворачивании" любого рода. Вместо этого Lisp имеет
reduce
, который очень неравномерен, особенно если вы рассматриваете его тип. Например,:from-end
- это аргумент ключевого слова, который определяет, будет ли это левое или правое сканирование, и использует различные функции аккумулятора, что означает, что тип аккумулятора зависит от этого ключевого слова. Это в дополнение к другим хакам: обычно первое значение берется из списка (если вы не укажете:initial-value
). Наконец, если вы не укажете:initial-value
, и список пуст, он фактически применит функцию к нулевым аргументам, чтобы получить результат.Все это означает, что
reduce
обычно используется для того, что предлагает его название: сокращение списка значений в одно значение, где оба типа обычно одинаковы. Вывод здесь состоит в том, что он служит своего рода аналогичной целью для свертывания, но он не так полезен, как общая конструкция итераций списка, которую вы получаете при складывании. Я предполагаю, что это означает, что нет сильной связи междуreduce
и более поздними операциями fold. -
Первым соответствующим языком, который следует за Lisp и имеет правильную сгиб, является ML. Выбор, который был сделан там, как отмечено в нижеприведенном ответе ниже, состоял в том, чтобы перейти к версии унифицированных типов (то есть, что использует Racket).
-
Следующая ссылка - Bird and Wadler ItFP (1988), которая использует различные типы (как в Haskell). Однако они отмечают в приложении, что Миранда имеет тот же тип (как в Racket).
-
Миранда позже переключила порядок аргументов (т.е. переместилась из ордера Racket в Haskell). В частности, в этом тексте говорится:
ПРЕДУПРЕЖДЕНИЕ - это определение foldl отличается от определения в более ранних версиях Miranda. Здесь тот же, что и у Bird and Wadler (1988). У старого определения были два аргумента "op". Обратный.
-
Хаскелл взял много вещей из Миранды, включая разные типы. (Но, конечно, я не знаю дат, поэтому, возможно, изменение Миранды произошло из-за Хаскелла.) В любом случае, на данном этапе ясно, что консенсуса нет, следовательно, имеет место обратный вопрос.
-
OCaml пошел с направлением Haskell и использовал разные типы
-
Я предполагаю, что "Как создавать программы" (aka HtDP) был написан примерно в тот же период, и они выбрали тот же тип. Однако нет мотивации или объяснения - и на самом деле после этого упражнения он просто упоминается как одна из встроенных функций.
Реализация фреймов fold операций была, конечно же, "встроенными", которые упоминаются здесь.
-
Затем появился SRFI-1, и выбор заключался в использовании версии того же типа (как Racket). Это решение было вопросом Джона Дэвида Стоуна, который указывает на комментарий в SRFI, в котором говорится
Примечание: MIT Scheme и Haskell переверните F arg для своих функций
reduce
иfold
.Olin позже обратился к этому: все, что он сказал, было:
Хорошая точка, но я хочу согласованность между двумя функциями. state-value first: srfi-1, SML state-value last: Haskell
Обратите внимание, в частности, на его использование значения состояния, которое предлагает представление, где согласованные типы являются, возможно, более важной точкой, чем порядок оператора.