Ответ 1
Ну, имея как доступный, а не только один, дает программисту более жесткий контроль над областью. С let x = e1 in e2
привязка присутствует только в среде e2
, а при let rec x = e1 in e2
привязка присутствует в средах e1
и e2
.
(Edit: Я хочу подчеркнуть, что это не проблема производительности, это не имеет никакого значения.)
Вот две ситуации, когда использование этой нерекурсивной привязки полезно:
-
Затенение существующего определения с уточнением, использующим старую привязку. Что-то вроде:
let f x = (let x = sanitize x in ...)
, гдеsanitize
- это функция, которая гарантирует, что вход имеет какое-то желательное свойство (например, он принимает норму, возможно, ненормированного вектора и т.д.). Это очень полезно в некоторых случаях. -
метапрограммирование, например, макросъемка. Представьте, что я хочу определить макрос
SQUARE(foo)
, который desugars вlet x = foo in x * x
, для любого выраженияfoo
. Мне нужна эта привязка, чтобы избежать дублирования кода в выходе (я не хочу, чтобыSQUARE(factorial n)
вычислялfactorial n
дважды). Это только гигиенично, если привязкаlet
не рекурсивна, иначе я не мог бы написатьlet x = 2 in SQUARE(x)
и получить правильный результат.
Поэтому я утверждаю, что очень важно иметь доступ как рекурсивную, так и нерекурсивную привязку. Теперь поведение по умолчанию let-binding является детерминированным. Можно сказать, что let x = ...
является рекурсивным, и нужно использовать let nonrec x = ...
для получения нерекурсивного связующего. Выбор одного по умолчанию или другого - это вопрос, какой стиль программирования вы хотите одобрить, и есть веские причины сделать любой выбор. Haskell страдает¹ из недоступности этого нерекурсивного режима, а OCaml имеет точно такой же дефект на уровне уровня: type foo = ...
является рекурсивным, и нет нерекурсивного варианта - см. это сообщение в блоге.
¹: когда Google Code Search был доступен, я использовал его для поиска в коде Haskell для шаблона let x' = sanitize x in ...
. Это обычное обходное решение, когда нерекурсивное связывание недоступно, но оно менее безопасно, потому что вы рискуете написать x
вместо x'
по ошибке позже - в некоторых случаях вы хотите иметь оба доступных, поэтому выбираете другую имя может быть добровольным. Хорошей идиомой было бы использовать более длинное имя переменной для первого x
, например unsanitized_x
. Во всяком случае, просто поиск x'
litterally (нет другого имени переменной) и x1
показал много результатов. Эрланг (и весь язык, который пытается сделать переменную тень трудной: Coffeescript и т.д.) Имеет еще худшие проблемы такого рода.
Тем не менее, выбор привязки Haskell, рекурсивный по умолчанию (а не нерекурсивный), безусловно, имеет смысл, поскольку он согласуется с ленивой оценкой по умолчанию, что делает его очень простым для создания рекурсивных значений - по умолчанию языки имеют больше ограничений, на которых рекурсивные определения имеют смысл.