GHC 8 zapewnia HasCallStack
z modułu GHC.Stack
, który pozwala funkcjom na żądanie rejestrowania ramki stosu po wywołaniu. Zapewnia także funkcję withFrozenCallStack
, która "zawiesza" stos wywołań, dzięki czemu nie można do niego dodać kolejnych klatek.Dlaczego program HasCallStack nadal dodaje klatki stosu podczas korzystania zFrozenCallStack?
W prostych scenariuszach działa to tak, jak powinienem oczekiwać. Na przykład:
ghci> let foo :: HasCallStack => CallStack
foo = callStack
ghci> foo
[("foo",SrcLoc {srcLocPackage = "interactive", srcLocModule = "Ghci2", srcLocFile = "<interactive>", srcLocStartLine = 8, srcLocStartCol = 1, srcLocEndLine = 8, srcLocEndCol = 4})]
ghci> withFrozenCallStack foo
[]
Kiedy zadzwonić foo
normalnie, mam ramkę stosu, ale kiedy owinąć ją withFrozenCallStack
, ja nie. Idealny. Jednak, gdy tylko przykładem dostaje nieco bardziej skomplikowane, to przestaje zachowywać się jak ja się spodziewać:
ghci> let foo :: CallStack
foo = bar
bar :: HasCallStack => CallStack
bar = callStack
ghci> foo
[("bar",SrcLoc {srcLocPackage = "interactive", srcLocModule = "Ghci9", srcLocFile = "<interactive>", srcLocStartLine = 24, srcLocStartCol = 11, srcLocEndLine = 24, srcLocEndCol = 14})]
ghci> withFrozenCallStack foo
[("bar",SrcLoc {srcLocPackage = "interactive", srcLocModule = "Ghci9", srcLocFile = "<interactive>", srcLocStartLine = 24, srcLocStartCol = 11, srcLocEndLine = 24, srcLocEndCol = 14})]
Dodając tę prostą warstwę zadnie, ramka stosu nadal pobiera zawarte, pomimo mojego użytkowania withFrozenCallStack
. Czemu?
Podsumowując, moje rozumienie HasCallStack
polega na tym, że jest to jak niejawne użycie pushCallStack
na bieżącym stosie połączeń, a pushCallStack
nie ma wpływu na zablokowany stos wywołań. Dlaczego więc nie można zapobiec dodaniu powyższej ramki stosu do stosu wywołań?
Ahh, oczywiście, to ma sens. Moja intuicja dotycząca ukrytych parametrów w Haskell nie jest oczywiście najlepsza, ponieważ dotyczyłaby dowolnego niejawnego parametru, a nie tylko 'HasCallStack'. Mimo wszystko dziękuję. –
Prawie, ale nie do końca. Przy normalnym ukrytym parametrze pojawi się błąd, jeśli nie zostanie jawnie powiązany, gdy przestaniesz używać ograniczenia. Ale z 'HasCallStack' kompilator wykrywa to i magicznie wiąże początkowy stos wywołań. –
Aha, tak, to ma sens - znowu masz rację. :) Magiczna natura 'HasCallStack' jest z pewnością trochę zagmatwana. Nadal nie sądzę, że całkowicie rozumiem zawiłości tego, jak wywodzą się ograniczenia "HasCallStack", ale na szczęście to nie wydaje mi się zbyt dużym problemem w praktyce, haha. –