Наблюдение ленивости в Haskell

Можно ли написать функцию Haskell, которая зависит от того, рассчитаны ли значения уже или являются thunks? Например. если lazyShow :: [Int] -> String показывает thunks как ? и вычисленные значения обычно, в GHCi мы увидим

> let nats = [0..]

> lazyShow nats
0 : ?

> nats !! 5    
5

> lazyShow nats
0 : 1 : 2 : 3 : 4 : ? 

Ответ 1

Очевидно, что lazyShow не может иметь тип, который вы укажете. Если строка должна зависеть от текущего состояния оценки, тогда IO String в результате - лучшее, на что вы можете надеяться.

Если все, что вас интересует, использует это для отладки, то я думаю, что пакет ghc-heap-view (и, возможно, графический интерфейс, такой как ghc-vis) полезны для этой цели. Он определяет команду GHCi :printHeap, которая может использоваться для отображения описания того, как значение выглядит в куче GHC. Это, возможно, немного более низкоуровневое, чем то, что вы намеревались, но может быть очень полезно лучше понять, как работают ленивые оценки и совместная работа:

Prelude> let nats = [0..]
Prelude> :printHeap nats
(_bco (D:Enum _fun _fun _fun _fun _fun _fun _fun _fun) _fun)()
Prelude> null nats
False
Prelude> System.Mem.performGC
Prelude> :printHeap nats
let x1 = S# 0
in x1 : _thunk x1 (S# 1)
Prelude> nats !! 5
5
Prelude> System.Mem.performGC
Prelude> :printHeap nats
let x1 = S# 5
in S# 0 : S# 1 : S# 2 : S# 3 : S# 4 : x1 : _thunk x1 (S# 1)

Я явно вызываю сборщик мусора через System.Mem.performGC (как рекомендовано в документации по ghc-heap-view), чтобы немного очистить представление.

Ответ 2

Вам может быть интересно выкопать реализацию ": sprint" в GHCi, которая имеет возможность смотреть в thunks:

> let a = map (+1) [1..10]
> :sprint a
a = _

> length a
10

> :sprint a
a = [_,_,_,_,_,_,_,_,_,_]

> take 5 a
[2,3,4,5,6]

> :sprint a
a = [2,3,4,5,6,_,_,_,_,_]