В GHCi let y = y + 1 компилируется отлично, но когда я пытаюсь оценить y, я получил *** Exception: <<loop>>
Почему нет ошибки компиляции и что это значит <<loop>>?
Ответ 1
Haskell let, where и привязки верхнего уровня рекурсивны по умолчанию, даже если они не для функции. Таким образом, let y = y + 1 определяет бесконечный цикл добавления 1 к числу. GHC представляет такие петли, как это, с исключением <<loop>> - если он может их поймать, конечно!
Это полезно для ленивых операций, поскольку позволяет нам легко определять такие вещи, как бесконечные списки (let xs = 0:xs), которые хорошо определены и действительно полезны для обычного кода. Тем не менее, он не может работать для строгих операций, таких как + (для большинства числовых типов), потому что им необходимо сразу оценить всю (бесконечную) вещь.
Ответ 2
Я хотел добавить минимальный пример того, где это может быть полезно.
data Peano = Z | S Peano
instance Num Peano where
p + Z = p
p + (S q) = S (p + q)
fromInteger 1 = S Z
instance Eq Peano where
Z == Z = True
Z == _ = False
_ == Z = False
(S p) == (S q) = p == q
instance Ord Peano where
Z `compare` Z = EQ
Z `compare` _ = LT
_ `compare` Z = GT
(S p) `compare` (S q) = p `compare` q
y :: Peano
y = y + 1
main :: IO ()
main = print $ 1 < y
В этом случае y бесконечно, с точностью до изоморфизма. (Хотя мой fromInteger явно слабая реализация, поэтому изменение литерального 1 на что-то другое без исправления fromInteger не удастся.)
Обратите внимание, что это работает только потому, что (+), который я определил, ленив в этом первом аргументе, и compare, который я определил, также достаточно ленив.