2016-11-28 11 views
7

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ń?

Odpowiedz

2

W kodzie foo jest statyczna wartość typu CallStack. Zauważ, że ma ona , a nie mieć ograniczenie więzów HasCallStack.

Nie ma znaczenia, jak i gdzie używasz foo, zawsze będzie się to odnosić do tego konkretnego CallStack. Nie ma znaczenia, że ​​sam foo jest zdefiniowany przy użyciu bar, który wykorzystuje maszynę HasCallStack - można po prostu statycznie zdefiniować foo = [("bar",….

Spróbuj dodać podpis typu HasCallStack => do foo. Czy teraz zachowuje się tak, jak oczekujesz?

+0

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ę. –

+0

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ń. –

+0

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. –

Powiązane problemy