Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use GHCi breakpoint debugger to find intermediate results?

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>
like image 553
Maarten Faddegon Avatar asked May 27 '13 12:05

Maarten Faddegon


People also ask

How do breakpoints work in GHCi?

When a breakpoint is set on a particular line, GHCi sets the breakpoint on the leftmost subexpression that begins and ends on that line. If two complete subexpressions start at the same column, the longest one is picked.

How does the debugger work in GHCi?

The debugger is integrated into GHCi, and is turned on by default: no flags are required to enable the debugging facilities. There is one major restriction: breakpoints and single-stepping are only available in interpreted modules; compiled code is invisible to the debugger [5]. The debugger provides the following:

How to pass arguments to the main function while testing in GHCi?

However, we cannot simply pass the arguments to the main function while we are testing in ghci, as the main function doesn’t take its arguments directly. Instead, we can use the :main command.

How do you debug a breakpoint in C++?

For C++ code, you can turn on highlighting of breakpoint and current lines by selecting Tools (or Debug) > Options > Debugging > Highlight entire source line for breakpoints and current statement (C++ only). When you debug, execution pauses at the breakpoint, before the code on that line is executed. The breakpoint symbol shows a yellow arrow.


2 Answers

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

like image 76
user2141650 Avatar answered Jan 02 '23 06:01

user2141650


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.

like image 27
user2407038 Avatar answered Jan 02 '23 05:01

user2407038