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.12.1/docs/users_guide/exts/callstack.html

Spec-Zone.ru

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