Ответ 1
Позвольте мне ответить на этот вопрос, показывая, как GHC на самом деле это делает, используя библиотеку ghc-heap-view
. Вероятно, вы можете воспроизвести это с помощью ghc-vis
и получить хорошие снимки.
Я начинаю с создания структуры данных со значением исключения где-то:
Prelude> :script /home/jojo/.cabal/share/ghc-heap-view-0.5.1/ghci
Prelude> let x = map ((1::Int) `div`) [1,0]
Вначале это просто бит (который, как представляется, включает в себя различные классы типов):
Prelude> :printHeap x
let f1 = _fun
in (_bco [] (_bco (D:Integral (D:Real (D:Num _fun _fun _fun _fun _fun _fun _fun) (D:Ord (D:Eq _fun _fun) _fun _fun _fun _fun _fun _fun _fun) _fun) (D:Enum _fun _fun f1 f1 _fun _fun _fun _fun) _fun _fun _fun _fun _fun _fun _fun) _fun) _fun)()
Теперь я оцениваю части, не содержащие исключения:
Prelude> (head x, length x)
(1,2)
Prelude> System.Mem.performGC
Prelude> :printHeap x
[I# 1,_thunk (_fun (I# 1)) (I# 0)]
Второй элемент списка - это всего лишь "нормальный" удар. Теперь я оцениваю это, получаю исключение и снова смотрю на него:
Prelude> last x
*** Exception: divide by zero
Prelude> System.Mem.performGC
Prelude> :printHeap x
[I# 1,_thunk (SomeException (D:Exception _fun (D:Show _fun _fun _fun) _fun _fun) DivideByZero())]
Вы можете видеть, что теперь это thunk, который ссылается на объект SomeException
. Конструктор данных SomeException
имеет тип forall e . Exception e => e -> SomeException
, поэтому вторым параметром конструктора является конструктор DivideByZero
ArithException
исключение, а первый параметр - соответствующий экземпляр класса Exception
.
Этот thunk может быть передан так же, как и любое другое значение Haskell, и, если он будет оцениваться, снова вызовет исключение. И, как и любое другое значение, исключение может быть общим:
Prelude> let y = (last x, last x)
Prelude> y
(*** Exception: divide by zero
Prelude> snd y
*** Exception: divide by zero
Prelude> System.Mem.performGC
Prelude> :printHeap y
let x1 = SomeException (D:Exception _fun (D:Show _fun _fun _fun) _fun _fun) DivideByZero()
in (_thunk x1,_thunk x1)
То же самое происходит с потоками и MVars, ничего особенного там.