Мне было интересно, почему функция ошибки возвращает это в ghci:
Сначала одним звонком:
Prelude> error ""
*** Exception:
CallStack (from HasCallStack):
error, called at <interactive>:3:1 in interactive:Ghci1
Потом с двумя:
Prelude> error (error "")
*** Exception: *** Exception:
CallStack (from HasCallStack):
error, called at <interactive>:2:8 in interactive:Ghci1
А с тремя и так далее:
Prelude> error (error ( error ""))
*** Exception: *** Exception: *** Exception:
CallStack (from HasCallStack):
error, called at <interactive>:1:16 in interactive:Ghci1
Почему многие «*** Exception:» печатаются как ошибки?
Я сделал эту функцию, которая также получает число и печатает исключение:
printException n = foldr (\c r -> c error r) id (replicate n (.)) ""
Но мне также было интересно, почему
until (const False) error ""
Не печатает Exception бесконечно.
Пусть e
будет любым выражением типа String
. Затем error e
вызывает исключение, которое (в нормальных условиях) печатает *** Exception:
, за которым следует строка e
.
Следовательно, e
оценивается сейчас (а не раньше!). Это может включать в себя оценку другого error e'
, который напечатает еще один *** Exception:
, за которым следует e'
.
И так далее.
Ключевым моментом здесь является то, что e
оценивается лениво, только когда оно печатается, поэтому мы наблюдаем *** Exception:
перед ним.
Что касается последнего вопроса, обратите внимание, что until (const False) f x
никогда не будет оценивать f (f (f ... (f x))
. Это потому, что по лени const False x
, const False (f x)
, const False (f (f x))
, ... не нужно оценивать x
, ни f x
, ни f (f x)
,...
Следовательно, в вашем примере error
никогда не называется.
У вас ошибка в сообщении об ошибке. Из-за лени, когда он печатает внешнюю ошибку, он вызывает внутреннюю ошибку, которая напечатает вторую.
Таким образом, он начнет печатать сообщение:
*** Exception: …
но затем он начинает putStrLn
сообщения. Обратите внимание, что это сообщение еще не было оценено. Действительно, Haskell ленив, поэтому он будет оценивать сообщение только в том случае, если он собирается его напечатать, поэтому теперь возникает вторая ошибка, и поэтому он начинает печатать второе сообщение.
Но мне также было интересно, почему
until (const False) error ""
Не печатает исключение бесконечно.
Здесь он начинает печатать строку, но никогда ничего не генерирует. Действительно, он создаст выражение, похожее на error (error (error (… (error "") …)))
, но никогда не будет вычислять ни одно error
, даже внешнее, поскольку постоянно занят построением гигантского дерева выражений.
error
ленив, и String
также оценивается лениво. Кроме того, поскольку error :: String -> a
, результат error
может быть входом функции error
. И строки также печатаются лениво, символ за символом.
Может быть, чтобы лучше понять ситуацию, вы можете подумать, например, о такой функции:
errorsFrom n = error (show n ++ "\n" ++ errorsFrom (n+1))
Затем оцените на консоли errorsFrom 0
, и вы увидите это:
*** Exception: 0
*** Exception: 1
*** Exception: 2
*** Exception: 3
*** Exception: 4
*** Exception: 5
*** Exception: 6
*** Exception: 7
*** Exception: 8
*** Exception: 9
*** Exception: 10
...
Возможно, вам также будет интересно прочитать ответы на один из моих вопросов, заданных несколько лет назад.