Да, я думаю, что это хорошая практика.Если вы компилируете с профилированием, вы можете просто использовать
currentCallStack :: IO [String]
из GHC.Stack
.Обратите внимание, что он в IO
, но я бы посоветовал unsafePerformIO
, если вы выдадите ошибку, если вы в чистом коде.Поскольку все основания равны по смыслу, на самом деле нет никаких нарушений чистоты.
Но если вы хотите получать стеки вызовов без профилирования (скажем, вы хотите включить его в сообщение журнала при производстве), вам необходимосделать больше.Вы должны включать ограничения HasCallStack
везде, где вы хотите, чтобы о стеке сообщалось.Так что
main :: IO ()
main = print f
f :: Int
f = g
g :: HasCallStack => Int
g = h
h :: HasCallStack => Int
h = error (show callStack)
сообщит о стеке вызовов до g
, но пропустит f
.К сожалению,
Если в области действия нет CallStack и определение включения имеет явную сигнатуру типа, GHC решит ограничение HasCallStack для одноэлементного CallStack, содержащего только текущий сайт вызова.
Это означает, что он пропустит всех вызывающих абонентов f
, даже если они имеют HasCallStack
только потому, что f
не имеет такого ограничения.Я нахожу это ужасно громоздким.Это довольно недавняя функция, поэтому я надеюсь, что команда GHC имеет в виду что-то лучшее, что они используют это, чтобы двигаться в направлении.