Одна из основных причин явного использования rec
связана с выводом типа Хиндли-Милнера, который лежит в основе всех языков программирования с типичным типизированным программированием (хотя и изменен и расширен различными способами).
Если у вас есть определение let f x = x
, вы можете ожидать, что он будет иметь тип 'a -> 'a
и применим к различным типам 'a
в разных точках. Но в равной степени, если вы напишете let g x = (x + 1) + ...
, вы ожидаете, что x
будет рассматриваться как int
в остальной части тела g
.
То, как вывод Hindley-Milner имеет дело с этим различием, заключается в явном обобщении. В некоторых случаях при обработке вашей программы система типов останавливается и говорит «ok», типы этих определений будут обобщены в этот момент, так что, когда кто-то их использует, любые свободные переменные типа в их типе будут свежее, и, таким образом, не будет мешать другим применениям этого определения ».
Оказывается, что разумное место для этого обобщения - это проверка взаимно-рекурсивного набора функций. Раньше, и вы слишком много обобщаете, что приводит к ситуациям, когда типы могут действительно сталкиваться. Позднее, и вы слишком мало обобщаете, создавая определения, которые нельзя использовать с несколькими экземплярами экземпляров.
Итак, учитывая, что проверяющий тип должен знать, какие наборы определений взаимно рекурсивные, что он может сделать? Одна из возможностей - просто провести анализ зависимостей во всех определениях в области и переупорядочить их в наименьшие возможные группы. Haskell на самом деле это делает, но в таких языках, как F # (и OCaml и SML), которые имеют неограниченные побочные эффекты, это плохая идея, потому что она может также изменить порядок побочных эффектов. Таким образом, вместо этого он просит пользователя явно указать, какие определения взаимно рекурсивные, и, следовательно, путем расширения, где должно происходить обобщение.
См. Также http://stackoverflow.com/questions/3739628/whats-the-reason-of-marking-a-recursive-function-as-rec-in-f – Brian