Одна из важнейших причин явного использования rec
заключается в выводе типа Хиндли-Милнера, который лежит в основе всех статически типизированных функциональных языков программирования (хотя и измененных и расширенных различными способами).
Если у вас есть определение let f x = x
, вы ожидаете, что оно будет иметь тип 'a -> 'a
и будет применимо к различным 'a
типам в разных точках. Но в равной степени, если вы напишите let g x = (x + 1) + ...
, вы ожидаете, что x
будет рассматриваться как int
в остальной части тела g
.
Способ, которым вывод Хиндли-Милнера имеет дело с этим различием, заключается в явном обобщении шаге. В определенные моменты при обработке вашей программы система типов останавливается и говорит: «Хорошо, типы этих определений будут обобщены на этом этапе, поэтому, когда кто-то их использует, любые переменные свободного типа в их типе будут freshly , и, следовательно, не будет мешать любому другому использованию этого определения. "
Оказывается, разумным местом для этого обобщения является проверка взаимно рекурсивного набора функций. Раньше, и вы будете обобщать слишком много, что приведет к ситуациям, когда типы могут фактически сталкиваться. Позже, и вы будете слишком мало обобщать, делая определения, которые нельзя использовать с множественными экземплярами типов.
Итак, учитывая, что средство проверки типов должно знать, какие наборы определений являются взаимно рекурсивными, что оно может делать? Одна из возможностей - просто провести анализ зависимостей для всех определений в области и переупорядочить их в наименьшие возможные группы. Haskell на самом деле делает это, но в таких языках, как F # (и OCaml и SML), которые имеют неограниченные побочные эффекты, это плохая идея, потому что это может также изменить порядок побочных эффектов. Поэтому вместо этого он просит пользователя явно пометить, какие определения являются взаимно рекурсивными, и, следовательно, путем расширения, где должно произойти обобщение.