A few newbie questions about tracing/debugging and order of execution

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

A few newbie questions about tracing/debugging and order of execution

Hunter Kelly
Heya, I decided to play around with Haskell and see what it's like.
I used a small problem to explore it.  Basically, given two words,
find the least number of 1 letter changes that will go from one
word to the other (e.g.  for "fig" and "dog" either fig -> fog -> dog  or
fig -> dig -> dog).

I came up with a solution, but I have to say it was quite difficult to
get any debugging information, and when I did, the result was fairly
surprising!

Here's the out put I got:

dagger:~/stuff$ ./figt /usr/share/dic/twords fig dog
Word: dog
        visited:
["bit","bin","bid","bib","bug","bog","beg","bag","fix","fit","fir","fin","fie","fib","fog","fag","wig","rig","pig","jig","gig","dig","big","fig"]

Word: dig
        visited: ["big","fig"]

Word: fig
        visited: []

fig -> dig -> dog


The first thing I found surprising was that the debug output comes in
the "reverse" order - I would have expected it to be the output for
fig, then dig, then dog.

Secondly, I'm not entirely sure why I only got output for the
statements along the "solution" path.  If I try a combination that has
no solution, I get no output at all.

I got the program working, and I'm quite intrigued, but I figure it's
probably important to figure out why the above things happen...

I'm using GHC 6.4.1, but I get the same thing if I use runhugs.

Thanks!

--------- figt.hs --------------

import System
import System.IO
import Data.Set
import Debug.Trace

type Word = String
data Path = None | Path Word Path

instance Show Path where
  showsPrec _ path = showsPath path
    where
    showsPath :: Path -> ShowS
    showsPath None = ("<None>"++)
    showsPath (Path w None) = (w ++)
    showsPath (Path w p) = (showsPath p) . ((" -> " ++ w)++)

findPath :: Word -> Word -> Set String -> Path
findPath orig target allWords =
    let gen = singleton orig
    in iteratePath [(Path orig None)] gen []
    where
      iteratePath :: [Path] -> Set String -> [String] -> Path
      iteratePath [] _ _ = None
      iteratePath (None: _) _ _ = None
      iteratePath (fullPath@(Path w _):remain) generated visited =
          let
              allPossibilities = findPossibleWords w
              existant = Prelude.filter (\p -> member p allWords)
allPossibilities
              new = Prelude.filter (\e -> not (member e generated)) existant
              marked = foldl (\x y -> insert y x) generated new
              thingum = trace ("Word: " ++ w ++ "\n\tvisited: " ++
(show visited) ++ "\n") fullPath
              todo = remain ++ Prelude.map (\u -> (Path u thingum)) new
          in if target == w
             then thingum
             else if length todo > 0 then iteratePath todo marked (w:visited)
                  else None

findPossibleWords :: String -> [String]
findPossibleWords word = concatMap enumChars (splits)
    where enumChars (prefix, _:cs) = Prelude.map (\c -> prefix ++ [c]
++ cs) ['a'..'z']
          splits = Prelude.map (\x -> splitAt x word) [0..((length word) -1)]

findAllWordsSizeN :: String -> Int -> Set String
findAllWordsSizeN content wordLength =
    fromDistinctAscList (Prelude.filter (\x -> wordLength == (length
x)) (lines content))

main :: IO ()
main = do [wordsFile, orig, target] <- getArgs
          contents <- readFile wordsFile
          allWordsSizeN <- (return (findAllWordsSizeN contents (length orig)))
          putStrLn (show (findPath orig target allWordsSizeN ))
_______________________________________________
Haskell mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell
Reply | Threaded
Open this post in threaded view
|

Re: A few newbie questions about tracing/debugging and order of execution

Bugzilla from robdockins@fastmail.fm

On Dec 28, 2005, at 6:10 AM, Hunter Kelly wrote:

> Heya, I decided to play around with Haskell and see what it's like.
> I used a small problem to explore it.  Basically, given two words,
> find the least number of 1 letter changes that will go from one
> word to the other (e.g.  for "fig" and "dog" either fig -> fog ->  
> dog  or
> fig -> dig -> dog).
>
> I came up with a solution, but I have to say it was quite difficult to
> get any debugging information, and when I did, the result was fairly
> surprising!


I see you are using Debug.Trace to generate your debug messages.  The  
'trace' function is a sort of strange one, because it breaks the  
usual rules that Haskell follows; it allows you to generate output in  
the middle of a pure computation.  It works by generating output  
_when it is evaluated_.  However, without the IO monad to make  
everything sequenced and well-behaved, it can be difficult to predict  
when that will occur.  In the particular program you posted, the  
'trace' thunk is not evaluated until after the recursive call has  
completed, which gives the reversed output.  Furthermore, when there  
is no solution, the 'trace' thunk isn't evaluated at all (the magic  
of laziness!), so you never see that output.

To make your traces show up where you expect, you need to make sure  
that your trace function gets forced earlier and on both success and  
failure paths.  Since your function is written as a big let...in  
if ...  block you can do something like this:

   ..... =
     let ...
          ....
     in trace traceString
          (if ...
              then
              else
           )

That way, the trace will be output before the 'if' is evaluated, so  
you will get output for both branches.  I can't tell from a quick  
inspection if it will return the results in the order you expect, but  
I think it may.



Rob Dockins

Speak softly and drive a Sherman tank.
Laugh hard; it's a long way to the bank.
           -- TMBG

_______________________________________________
Haskell mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell
Reply | Threaded
Open this post in threaded view
|

Re: A few newbie questions about tracing/debugging and order of execution

Hunter Kelly
Yes, thank you, that did the trick!  It produced the output for all steps,
and in the order I would expect.

Are there other techniques that people use to get debugging output?

H

On 12/28/05, Robert Dockins <[hidden email]> wrote:

>
> On Dec 28, 2005, at 6:10 AM, Hunter Kelly wrote:
>
> > Heya, I decided to play around with Haskell and see what it's like.
> > I used a small problem to explore it.  Basically, given two words,
> > find the least number of 1 letter changes that will go from one
> > word to the other (e.g.  for "fig" and "dog" either fig -> fog ->
> > dog  or
> > fig -> dig -> dog).
> >
> > I came up with a solution, but I have to say it was quite difficult to
> > get any debugging information, and when I did, the result was fairly
> > surprising!
>
>
> I see you are using Debug.Trace to generate your debug messages.  The
> 'trace' function is a sort of strange one, because it breaks the
> usual rules that Haskell follows; it allows you to generate output in
> the middle of a pure computation.  It works by generating output
> _when it is evaluated_.  However, without the IO monad to make
> everything sequenced and well-behaved, it can be difficult to predict
> when that will occur.  In the particular program you posted, the
> 'trace' thunk is not evaluated until after the recursive call has
> completed, which gives the reversed output.  Furthermore, when there
> is no solution, the 'trace' thunk isn't evaluated at all (the magic
> of laziness!), so you never see that output.
>
> To make your traces show up where you expect, you need to make sure
> that your trace function gets forced earlier and on both success and
> failure paths.  Since your function is written as a big let...in
> if ...  block you can do something like this:
>
>    ..... =
>      let ...
>           ....
>      in trace traceString
>           (if ...
>               then
>               else
>            )
>
> That way, the trace will be output before the 'if' is evaluated, so
> you will get output for both branches.  I can't tell from a quick
> inspection if it will return the results in the order you expect, but
> I think it may.
>
>
>
> Rob Dockins
>
> Speak softly and drive a Sherman tank.
> Laugh hard; it's a long way to the bank.
>            -- TMBG
>
>
_______________________________________________
Haskell mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell
Reply | Threaded
Open this post in threaded view
|

Re: A few newbie questions about tracing/debugging and order of execution

Cale Gibbard
Hi,

Mostly I just use ghci. If something isn't working (or even sometimes
if it is), I break it down into smaller parts until I can understand
and test each part separately. Usually once the functions are broken
into small, easy to manage pieces, it becomes obvious where the error
is.

Sometimes though, in certain kinds of algorithms, you have some code
which runs for quite a long time before producing an example of data
on which it fails. For this purpose, Debug.Trace is quite handy for
extracting a report of the input data where things are breaking, but I
usually find that these cases are pretty rare, and Debug.Trace is
still no substitute for decomposing your problem further.

 - Cale

On 28/12/05, Hunter Kelly <[hidden email]> wrote:

> Yes, thank you, that did the trick!  It produced the output for all steps,
> and in the order I would expect.
>
> Are there other techniques that people use to get debugging output?
>
> H
>
> On 12/28/05, Robert Dockins <[hidden email]> wrote:
> >
> > On Dec 28, 2005, at 6:10 AM, Hunter Kelly wrote:
> >
> > > Heya, I decided to play around with Haskell and see what it's like.
> > > I used a small problem to explore it.  Basically, given two words,
> > > find the least number of 1 letter changes that will go from one
> > > word to the other (e.g.  for "fig" and "dog" either fig -> fog ->
> > > dog  or
> > > fig -> dig -> dog).
> > >
> > > I came up with a solution, but I have to say it was quite difficult to
> > > get any debugging information, and when I did, the result was fairly
> > > surprising!
> >
> >
> > I see you are using Debug.Trace to generate your debug messages.  The
> > 'trace' function is a sort of strange one, because it breaks the
> > usual rules that Haskell follows; it allows you to generate output in
> > the middle of a pure computation.  It works by generating output
> > _when it is evaluated_.  However, without the IO monad to make
> > everything sequenced and well-behaved, it can be difficult to predict
> > when that will occur.  In the particular program you posted, the
> > 'trace' thunk is not evaluated until after the recursive call has
> > completed, which gives the reversed output.  Furthermore, when there
> > is no solution, the 'trace' thunk isn't evaluated at all (the magic
> > of laziness!), so you never see that output.
> >
> > To make your traces show up where you expect, you need to make sure
> > that your trace function gets forced earlier and on both success and
> > failure paths.  Since your function is written as a big let...in
> > if ...  block you can do something like this:
> >
> >    ..... =
> >      let ...
> >           ....
> >      in trace traceString
> >           (if ...
> >               then
> >               else
> >            )
> >
> > That way, the trace will be output before the 'if' is evaluated, so
> > you will get output for both branches.  I can't tell from a quick
> > inspection if it will return the results in the order you expect, but
> > I think it may.
> >
> >
> >
> > Rob Dockins
> >
> > Speak softly and drive a Sherman tank.
> > Laugh hard; it's a long way to the bank.
> >            -- TMBG
> >
> >
> _______________________________________________
> Haskell mailing list
> [hidden email]
> http://www.haskell.org/mailman/listinfo/haskell
>
_______________________________________________
Haskell mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell
Reply | Threaded
Open this post in threaded view
|

Re: A few newbie questions about tracing/debugging and order of execution

Bugzilla from robdockins@fastmail.fm
In reply to this post by Hunter Kelly

On Dec 28, 2005, at 12:07 PM, Hunter Kelly wrote:

> Yes, thank you, that did the trick!  It produced the output for all  
> steps,
> and in the order I would expect.
>
> Are there other techniques that people use to get debugging output?
>

Well, if you are writing code in the IO monad, obviously you can just  
insert 'putStrLn', just like in your favorite imperative language.  
For pure code, I generally try to write small functions and then  
compose them to do larger tasks.  That way I can exercise each  
smaller function individually and see that I get the results I  
expect.  It is also more likely that your functions will just be  
"obviously correct".

If that isn't possible for some reason I go to Debug.Trace and try to  
find good places to sprinkle in the calls to 'trace'.  I find that  
attaching them to control structures usually works best.

If that doesn't cut it for you, there are also some interactive  
Haskell debuggers. (http://www.haskell.org/libraries/#tracing).



Rob Dockins

Speak softly and drive a Sherman tank.
Laugh hard; it's a long way to the bank.
           -- TMBG



_______________________________________________
Haskell mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell
Reply | Threaded
Open this post in threaded view
|

Re: A few newbie questions about tracing/debugging andorder of execution

Srinivas Nedunuri
In reply to this post by Hunter Kelly
I agree with the suggestions of the other posters. Sometimes I'll also use
"error <data to print>" to get a "one time" breakpoint :-)
cheers

"Hunter Kelly" <[hidden email]> wrote in message
news:[hidden email]...
Yes, thank you, that did the trick!  It produced the output for all steps,
and in the order I would expect.

Are there other techniques that people use to get debugging output?

H

On 12/28/05, Robert Dockins <[hidden email]> wrote:

>
> On Dec 28, 2005, at 6:10 AM, Hunter Kelly wrote:
>
> > Heya, I decided to play around with Haskell and see what it's like.
> > I used a small problem to explore it.  Basically, given two words,
> > find the least number of 1 letter changes that will go from one
> > word to the other (e.g.  for "fig" and "dog" either fig -> fog ->
> > dog  or
> > fig -> dig -> dog).
> >
> > I came up with a solution, but I have to say it was quite difficult to
> > get any debugging information, and when I did, the result was fairly
> > surprising!
>
>
> I see you are using Debug.Trace to generate your debug messages.  The
> 'trace' function is a sort of strange one, because it breaks the
> usual rules that Haskell follows; it allows you to generate output in
> the middle of a pure computation.  It works by generating output
> _when it is evaluated_.  However, without the IO monad to make
> everything sequenced and well-behaved, it can be difficult to predict
> when that will occur.  In the particular program you posted, the
> 'trace' thunk is not evaluated until after the recursive call has
> completed, which gives the reversed output.  Furthermore, when there
> is no solution, the 'trace' thunk isn't evaluated at all (the magic
> of laziness!), so you never see that output.
>
> To make your traces show up where you expect, you need to make sure
> that your trace function gets forced earlier and on both success and
> failure paths.  Since your function is written as a big let...in
> if ...  block you can do something like this:
>
>    ..... =
>      let ...
>           ....
>      in trace traceString
>           (if ...
>               then
>               else
>            )
>
> That way, the trace will be output before the 'if' is evaluated, so
> you will get output for both branches.  I can't tell from a quick
> inspection if it will return the results in the order you expect, but
> I think it may.
>
>
>
> Rob Dockins
>
> Speak softly and drive a Sherman tank.
> Laugh hard; it's a long way to the bank.
>            -- TMBG
>
>



_______________________________________________
Haskell mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell