Reputation: 785
Is it possible to stop the GHCi debugger when the result of a function is known?
For example, consider the following snippet of code:
blabla :: [Int] -> Int
bla :: Int -> Int
papperlap :: Int -> Int -> Int
bla x = x+x
papperlap y x = ((y *) . bla) x
blabla xs = foldl papperlap 0 x
Now, I would like to see the results of 'papperlap' and 'bla'. But remember I want to stop when the result is evaluated. Therefore using ':force' is out of the question since it changes the order of evaluation.
When I use ':break' the debugger stops but _result is not yet evaluated yet. Please find my GHCi session below, which does not yield the desired intermediate results:
GHCi, version 7.6.3: http://www.haskell.org/ghc/ :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
[1 of 1] Compiling Main ( bla1.hs, interpreted )
Ok, modules loaded: Main.
*Main> :break blabla
Breakpoint 0 activated at bla1.hs:7:1-36
*Main> :break papperlap
Breakpoint 1 activated at bla1.hs:6:1-31
*Main> :break bla
Breakpoint 2 activated at bla1.hs:5:1-19
*Main> blabla [1,2,3]
Stopped at bla1.hs:7:1-36
_result :: Int = _
[bla1.hs:7:1-36] *Main> :step
Stopped at bla1.hs:7:17-36
_result :: Int = _
xs :: [Int] = [1,2,3]
[bla1.hs:7:17-36] *Main> :step
Stopped at bla1.hs:6:1-31
_result :: Int = _
[bla1.hs:6:1-31] *Main> :step
Stopped at bla1.hs:6:17-31
_result :: Int = _
x :: Int = 3
y :: Int = _
[bla1.hs:6:17-31] *Main> :step
Stopped at bla1.hs:6:1-31
_result :: Int = _
[bla1.hs:6:1-31] *Main> :step
Stopped at bla1.hs:6:17-31
_result :: Int = _
x :: Int = 2
y :: Int = _
[bla1.hs:6:17-31] *Main> :step
Stopped at bla1.hs:6:1-31
_result :: Int = _
[bla1.hs:6:1-31] *Main> :step
Stopped at bla1.hs:6:17-31
_result :: Int = _
x :: Int = 1
y :: Int = 0
[bla1.hs:6:17-31] *Main> :step
Stopped at bla1.hs:5:1-19
_result :: Int = _
[bla1.hs:5:1-19] *Main> :step
Stopped at bla1.hs:5:17-19
_result :: Int = _
x :: Int = 1
[bla1.hs:5:17-19] *Main> :step
Stopped at bla1.hs:5:1-19
_result :: Int = _
[bla1.hs:5:1-19] *Main> :step
Stopped at bla1.hs:5:17-19
_result :: Int = _
x :: Int = 2
[bla1.hs:5:17-19] *Main> :step
Stopped at bla1.hs:5:1-19
_result :: Int = _
[bla1.hs:5:1-19] *Main> :step
Stopped at bla1.hs:5:17-19
_result :: Int = _
x :: Int = 3
[bla1.hs:5:17-19] *Main> :step
0
*Main>
Upvotes: 9
Views: 3539
Reputation: 2857
Might be a bit late for your immediate debugging concerns, but here's what I would do:
blabla :: [Int] -> Int
bla :: Int -> Int
papperlap :: Int -> Int -> Int
bla x = (undefined :: Int)
papperlap y x = ((y *) . bla) x
blabla xs = foldl papperlap 0 xs
Now we know we'll get an exception in the evaluation of bla
. So let's drop into ghci:
[1 of 1] Compiling Main ( temp.hs, interpreted )
Ok, modules loaded: Main.
λ: :set -fbreak-on-exception
λ: :trace blabla [1,5,17]
Stopped at <exception thrown>
_exception :: e = _
λ: :hist
-1 : bla (temp.hs:6:17-35)
-2 : bla (temp.hs:6:1-35)
-3 : papperlap (temp.hs:7:17-31)
-4 : papperlap (temp.hs:7:1-31)
-5 : papperlap (temp.hs:7:17-31)
-6 : papperlap (temp.hs:7:1-31)
-7 : papperlap (temp.hs:7:17-31)
-8 : papperlap (temp.hs:7:1-31)
-9 : blabla (temp.hs:8:13-32)
-10 : blabla (temp.hs:8:1-32)
<end of history>
λ: :back
Logged breakpoint at temp.hs:6:17-35
_result :: a
λ: :list
5
6 bla x = (undefined :: Int )
^^^^^^^^^^^^^^^^^^^
7 papperlap y x = ((y *) . bla) x
λ: :back
Logged breakpoint at temp.hs:6:1-35
_result :: a
λ: :list
5
6 bla x = (undefined :: Int )
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
7 papperlap y x = ((y *) . bla) x
λ: :back
Logged breakpoint at temp.hs:7:17-31
_result :: Int
x :: Int
y :: Int
λ: :list
6 bla x = (undefined :: Int )
7 papperlap y x = ((y *) . bla) x
^^^^^^^^^^^^^^^
8 blabla xs = foldl papperlap 0 xs
And so we can see the stack leading up to the evaluation of the right-hand-side of bla
.
This isn't perfect, since putting in undefined
s everywhere seems tacky and hacky, but :hist
gives you quite a bit to work on already, and using :list
as you step back makes things even clearer.
Upvotes: 3
Reputation: 14578
Haskell doesn't step through literal expressions for you. Something like
e = 2*(2+2)
will immediately evaluate to 8, because the compiler will optimize any expressions composed of literals it can find just 'laying around' at compile time (this type of expression is called a constant applicative form).
Now you have to realize that foldl
is lazy. If you call foldl f
on a list, it won't perform a single evaluation of f
until it is absolutely forced to do so.
>foldl (+) 0 [1,2,3]
>foldl (+) a1 [2,3]
where a1 = 0+1
>foldl (+) a2 [3]
where a2 = a1+2
where a1 = 0+1
eventually we have ((0+1)+2)+3)
and the compiler says "ok, we have exhausted every list and expanded every evaluation to the most primitive form we can. lets evaluate". And we already know that Haskell won't step through the evaluation of a CAF.
If you want to see the intermediate values of the evaluation you have to actually produce them first place. The way you do this is the following strict variant of foldl
:
foldl' f z [] = z
foldl' f z (x:xs) = let z' = z `f` x
in seq z' $ foldl' f z' xs
I will leave you to figure out how it works, but seq a b
will fully evalutate a
before continuing to lazily evaluate b
.
Do everything else as before, except change foldl
to foldl'
. When you step through the evaluation, you will see intermediate when you are paused inside the foldl'
function.
Upvotes: 1