Spec-Zone.ru › Haskell 9

6.19.6. HasCallStack

GHC.Stack.HasCallStack — это лёгкий способ получить частичный стек вызовов в любой точке программы.

Функция может запросить место своего вызова с помощью ограничения HasCallStack и получить его в виде значения Haskell, используя callStack.

Затем можно использовать функции из GHC.Stack для просмотра стека вызовов или его красивой печати (как показано в f ниже).

f :: HasCallStack => IO ()
f = putStrLn (prettyCallStack callStack)

g :: HasCallStack => IO ()
g = f

Вычисление f напрямую показывает стек вызовов с одной записью, тогда как вычисление g, которое также запрашивает место своего вызова, показывает две записи — по одной для каждого вычисления, «аннотированного» с помощью HasCallStack.

ghci> f
CallStack (from HasCallStack):
  f, called at <interactive>:19:1 in interactive:Ghci1
ghci> g
CallStack (from HasCallStack):
  f, called at <interactive>:17:5 in main:Main
  g, called at <interactive>:20:1 in interactive:Ghci2

Функция error из Prelude поддерживает вывод стека вызовов, приведших к ошибке, вместе с обычным сообщением об ошибке:

ghci> error "bad"
*** Exception: bad
CallStack (from HasCallStack):
  error, called at <interactive>:25:1 in interactive:Ghci5

Здесь стек вызовов состоит из одной записи, указывающей на место вызова error. Однако аннотирование нескольких вычислений с помощью HasCallStack значительно упрощает выяснение точных обстоятельств и последовательностей вызовов, приводящих к вызову error, как показано в простом примере ниже.

f :: HasCallStack => IO ()
f = error "bad bad bad"

g :: HasCallStack => IO ()
g = f

h :: HasCallStack => IO ()
h = g
ghci> h
*** Exception: bad bad bad
CallStack (from HasCallStack):
  error, called at call-stack.hs:4:5 in main:Main
  f, called at call-stack.hs:7:5 in main:Main
  g, called at call-stack.hs:10:5 in main:Main
  h, called at <interactive>:28:1 in interactive:Ghci1

CallStack расширяется только настолько, насколько это позволяют типы, например:

myHead :: HasCallStack => [a] -> a
myHead []     = error "empty"
myHead (x:xs) = x

bad :: Int
bad = myHead []
ghci> bad
*** Exception: empty
CallStack (from HasCallStack):
  error, called at Bad.hs:8:15 in main:Bad
  myHead, called at Bad.hs:12:7 in main:Bad

содержит место вызова error в myHead и myHead в bad, но не место вызова bad в приглашении GHCi.

GHC разрешает ограничения HasCallStack в два этапа:

  1. Если в области видимости есть CallStack — то есть если для охватывающего определения задано ограничение HasCallStack, — GHC добавит новое место вызова в существующий CallStack.
  2. В противном случае GHC разрешит ограничение HasCallStack для синглтона CallStack, содержащего только текущее место вызова.

Важно: GHC никогда не выводит ограничение HasCallStack автоматически — его необходимо запрашивать явно.

CallStack остаётся абстрактным типом, но GHC предоставляет функцию

getCallStack :: CallStack -> [(String, SrcLoc)]

для доступа к отдельным местам вызова в стеке. String — это имя вызванной функции, а SrcLoc содержит название пакета, модуля и файла, а также номера строки и столбца.

GHC.Stack также экспортирует функцию withFrozenCallStack, позволяющую пользователям зафиксировать текущий CallStack и предотвратить влияние любых последующих операций добавления. Авторы библиотек могут использовать её, чтобы не допустить раскрытия CallStack ненужных подробностей реализации. Рассмотрим приведённый выше пример с myHead: строка error в выведенном стеке не особенно информативна, поэтому можно скрыть её, зафиксировав CallStack, передаваемый в error.

myHead :: HasCallStack => [a] -> a
myHead []     = withFrozenCallStack (error "empty")
myHead (x:xs) = x
ghci> myHead []
*** Exception: empty
CallStack (from HasCallStack):
  myHead, called at Bad.hs:12:7 in main:Bad

ПРИМЕЧАНИЕ: Внимательный пользователь может заметить, что HasCallStack — это просто псевдоним неявного параметра ?callStack :: CallStack. Это деталь реализации, и её не следует считать частью API CallStack; в будущем мы можем изменить реализацию.

6.19.6.1. Сравнение с другими источниками трассировок стека

HasCallStack не взаимодействует с RTS и не требует компиляции с -prof. С другой стороны, поскольку CallStack явно формируется с помощью ограничений HasCallStack, обычно он содержит меньше информации, чем моделируемые стеки вызовов, которые поддерживает RTS.

© 2002–2007 The University Court of the University of Glasgow. All rights reserved.
Licensed under the Glasgow Haskell Compiler License.
https://downloads.haskell.org/~ghc/9.14.1/docs/users_guide/exts/callstack.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API