I have a simple recursive program that doesn't quite work. So I'm trying to use the ghci debugger to figure out what's going on. I set breakpoints on all lines of functions recurse
, progress
and shaded
, and it catches the first few. But when I get to the second line of progress
, every invocation of :continue drops me back on that same line of code, even though I'm calling recurse
and shaded
from there and expecting my breakpoints to work. Here's the code:
import System.Environment
type Pos = (Int,Int)
type Acc = ([[Pos]], [Pos])
main = do
getArgs >>= putStrLn . show . length . combos . read . head
combos n = recurse n [] (allPos n) []
recurse :: Int -> [[Pos]] -> [Pos] -> [Pos] -> [[Pos]]
recurse n done avail inProg
| length inProg == n = inProg:done
| null avail = done
| otherwise = fst $ foldr (progress n inProg) (done,avail) avail
progress :: Int -> [Pos] -> Pos -> Acc -> Acc
progress n inProg pos (done, avail) =
(recurse n done (filter (not . shaded pos) remain) (pos:inProg), remain)
where remain = tail avail
allPos n = [ (i,j) | i <- [0..n-1], j <- [0..n-1] ]
shaded :: Pos -> Pos -> Bool
shaded (i,j) (k,l) =
k == i
|| l == j
|| k+l == i+j
|| k-l == i-j
|| abs (k-i) < 3 && abs (l-j) < 3
Why will the ghci debugger not stop on breakpoints in functions called by progress
? Is there something like "non-reentrancy" turning them off? How can I get the debugger to break on every recursive call to these functions?
GHCi version 7.8.4
Update: I suspect this may have something to do with cached results, but it's not obvious to me that these functions have been called with the same arguments twice. Could be a bug in my code?
I think it is working as expected, but you have to take into account lazy evaluation.
If you break at the guard lines in recurse
(lines 13 -- 15) and the call to recurse in progress (line 19) you'll see this pattern when the program is run with :main 2
:
Stopped at prog0.hs:13:6-23 - recurse
Stopped at prog0.hs:14:6-15 - recurse
Stopped at prog0.hs:15:18-67 - recurse, call to foldr progress
Stopped at prog0.hs:19:3-74 - in progress, pos = (1,1)
Stopped at prog0.hs:19:3-74 - in progress, pos = (1,0)
Stopped at prog0.hs:19:3-74 - in progress, pos = (0,1)
Stopped at prog0.hs:19:3-74 - in progress, pos = (0,0)
Stopped at prog0.hs:13:6-23 - in recurse
Stopped at prog0.hs:14:6-15
Stopped at prog0.hs:13:6-23 - in recurse
Stopped at prog0.hs:14:6-15
Stopped at prog0.hs:13:6-23 - in recurse
Stopped at prog0.hs:14:6-15
Stopped at prog0.hs:13:6-23 - in recurse
Stopped at prog0.hs:14:6-15
However, if you change progress
to force its result:
import Control.DeepSeq
progress n inProg pos (done, avail) = let
result = (recurse n done (filter (not . shaded pos) remain) (pos:inProg), remain)
in deepseq result result
where remain = tail avail
then the break point pattern is:
Stopped at prog1.hs:14:6-23 - recurse
Stopped at prog1.hs:15:6-15 - recurse
Stopped at prog1.hs:16:18-67 - recurse, call to foldr progress
Stopped at prog1.hs:21:6-26 - progress, pos = (1,1)
Stopped at prog1.hs:14:6-23 - recurse
Stopped at prog1.hs:15:6-15
Stopped at prog1.hs:21:6-26 - progress, pos = (1,0)
Stopped at prog1.hs:14:6-23 - recurse
Stopped at prog1.hs:15:6-15
Stopped at prog1.hs:21:6-26 - progress, pos = (0,1)
Stopped at prog1.hs:14:6-23 - recurse
Stopped at prog1.hs:15:6-15
Stopped at prog1.hs:21:6-26 - progress, pos = (0,0)
Stopped at prog1.hs:14:6-23 - recurse
Stopped at prog1.hs:15:6-15
The recursive calls to recurse
are now interleaved because we are forcing them right away.