
Hi, I would like to learn a reasonable way (ie how others do it) to debug programs in Haskell. Is it possible to "see" what's going on when a function is evaluated? Eg in f a b = let c = a+b d = a*b in c+d evaluating f 1 2 would output something like f called with values 1 2 c is now (+) a b => (+) 1 2 => 3 d is now (*) a b => (*) 1 2 => 2 ... Or maybe I am thinking the wrong way, and functional programming has its own debugging style... Thanks, Tamas

Hi Tamas There are several ways to debug a Haskell program. The most advanced ones are based in offline analysis of traces, I think Hat [1] is the most up-to-date tool for this. There is a Windows port of Hat at [5]. Another approach is to simply use Debug.Trace. A more powerful alternative for this approach is Hood [2]. Even if it hasn't been updated in some time, Hood works perfectly with the current ghc distribution. Even more, Hugs has it already integrated [3]. You can simply import Observe and use observations directly in your program. For instance: import Observe f' = observe "f" f f a b = .... And then in hugs the expression:
f' 1 2
would output what you want.
Finally, the GHCi debugger project [4] aims to bring dynamic
breakpoints and intermediate values observation to GHCi in a near
future. Right now the tool is only available from the site as a
modified version of GHC, so unfortunately you will have to compile it
yourself if you want to try it.
Cheers
pepe
1. www.haskell.org/hat
2. www.haskell.org/hood
3. http://cvs.haskell.org/Hugs/pages/users_guide/observe.html
4. http://haskell.org/haskellwiki/GHC/GHCiDebugger
5. http://www-users.cs.york.ac.uk/~ndm/projects/windows.php
On 06/09/06, Tamas K Papp
Hi,
I would like to learn a reasonable way (ie how others do it) to debug programs in Haskell. Is it possible to "see" what's going on when a function is evaluated? Eg in
f a b = let c = a+b d = a*b in c+d
evaluating
f 1 2
would output something like
f called with values 1 2 c is now (+) a b => (+) 1 2 => 3 d is now (*) a b => (*) 1 2 => 2 ...
Or maybe I am thinking the wrong way, and functional programming has its own debugging style...
Thanks,
Tamas _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

mnislaih:
Hi Tamas
There are several ways to debug a Haskell program.
The most advanced ones are based in offline analysis of traces, I think Hat [1] is the most up-to-date tool for this. There is a Windows port of Hat at [5].
Another approach is to simply use Debug.Trace. A more powerful alternative for this approach is Hood [2]. Even if it hasn't been updated in some time, Hood works perfectly with the current ghc distribution. Even more, Hugs has it already integrated [3]. You can simply import Observe and use observations directly in your program. For instance:
import Observe
f' = observe "f" f f a b = ....
And then in hugs the expression:
f' 1 2
would output what you want.
Finally, the GHCi debugger project [4] aims to bring dynamic breakpoints and intermediate values observation to GHCi in a near future. Right now the tool is only available from the site as a modified version of GHC, so unfortunately you will have to compile it yourself if you want to try it.
Pepe, would you like to put up a page on the haskell.org wiki about debugging in Haskell? You could use the above mail as a start :) -- Don

Thanks for the suggestion Don,
I started the wiki page at http://haskell.org/haskellwiki/Debugging
On 06/09/06, Donald Bruce Stewart
mnislaih:
Hi Tamas
There are several ways to debug a Haskell program.
The most advanced ones are based in offline analysis of traces, I think Hat [1] is the most up-to-date tool for this. There is a Windows port of Hat at [5].
Another approach is to simply use Debug.Trace. A more powerful alternative for this approach is Hood [2]. Even if it hasn't been updated in some time, Hood works perfectly with the current ghc distribution. Even more, Hugs has it already integrated [3]. You can simply import Observe and use observations directly in your program. For instance:
import Observe
f' = observe "f" f f a b = ....
And then in hugs the expression:
f' 1 2
would output what you want.
Finally, the GHCi debugger project [4] aims to bring dynamic breakpoints and intermediate values observation to GHCi in a near future. Right now the tool is only available from the site as a modified version of GHC, so unfortunately you will have to compile it yourself if you want to try it.
Pepe, would you like to put up a page on the haskell.org wiki about debugging in Haskell? You could use the above mail as a start :)
-- Don

On Wed, Sep 06, 2006 at 11:34:05AM +0200, Pepe Iborra wrote:
Hi Tamas
There are several ways to debug a Haskell program.
The most advanced ones are based in offline analysis of traces, I think Hat [1] is the most up-to-date tool for this. There is a Windows port of Hat at [5].
Another approach is to simply use Debug.Trace. A more powerful alternative for this approach is Hood [2]. Even if it hasn't been updated in some time, Hood works perfectly with the current ghc distribution. Even more, Hugs has it already integrated [3]. You can simply import Observe and use observations directly in your program.
Dear Pepe, Thank you for the information. I finally ended up working with Debug.Trace, and found the bug very quickly. I also tried Hood, but couldn't load it in ghci: import Observe can't find the library, but % locate Observe /usr/lib/ghc-6.4.2/hslibs-imports/util/Observe.hi /usr/lib/ghc-6.4.2/hslibs-imports/util/Observe.p_hi /usr/lib/hugs/libraries/Hugs/Observe.hs /usr/lib/hugs/oldlib/Observe.hs Does importing from hslibs-imports require something special? Quite a bit of philosophical discussion erupted as a result of my original question. I understand the arguments of those who dislike debuggers, but I don't think I could have done without some debugging. It turns out that the problem was not in the algorithm, but in the specification of the equation itself. I was solving a continuous time Hamilton-Jacobi-Bellman equation, and there was a point in the state space where the supremum was infinity, giving stuff like 0/Inf and their ilk. I respecified the problem and now it works. For those who think that it is always possible to break the problem up into small pieces you can test individually (without a debugger): you can't, at least not when solving functional equations. The corner cases (and nonconvergence, when using non-contraction methods, such as PEA) are difficult to catch and reproduce. Thanks for all the suggestions, Tamas

On 07/09/2006, at 10:53, Tamas K Papp wrote:
Dear Pepe,
Thank you for the information. I finally ended up working with Debug.Trace, and found the bug very quickly. I also tried Hood, but couldn't load it in ghci: import Observe can't find the library, but
% locate Observe /usr/lib/ghc-6.4.2/hslibs-imports/util/Observe.hi /usr/lib/ghc-6.4.2/hslibs-imports/util/Observe.p_hi /usr/lib/hugs/libraries/Hugs/Observe.hs /usr/lib/hugs/oldlib/Observe.hs
Does importing from hslibs-imports require something special?
Hi Tamas I'm glad to hear that you fixed it! GHC includes Observe (the hs-libs-imports file you are seeing) only in the hidden 'util' package, which I believe is deprecated or not present in 6.6. If you want to use it, launch ghci with the flag '- package util'. Or download Observe.hs from the Hood website and place it somewhere in the path. Hmm, it would be handy to have a Cabal Hood package... Cheers pepe

Tamas K Papp
I would like to learn a reasonable way (ie how others do it) to debug programs in Haskell. Is it possible to "see" what's going on when a function is evaluated? Eg in
f a b = let c = a+b d = a*b in c+d
evaluating
f 1 2
would output something like
f called with values 1 2 c is now (+) a b => (+) 1 2 => 3 d is now (*) a b => (*) 1 2 => 2
But why should c and d exist at runtime? They're only used once each, so the compiler is free to replace f with \a b -> (a+b)+ a*b and indeed, it's quite likely that f should disappear too. So if your debugger outputs what you want, it's not debugging the programme that you would otherwise have run.
Or maybe I am thinking the wrong way, and functional programming has its own debugging style...
I've said this before, but I think it's worth repeating: in most cases, if you need to debug your programme it's because it's too complicated for you to understand, so the correct thing to do is to simplify it and try again. That's not to say that I don't think you should test programmes... in fact I'm all for testing little bits. You can define f above and try it on a few arguments in ghci or hugs, and you can use QuickCheck and/or HUnit to automate this. But there's no reason for you to care what happens while f is evaluating. Remember there's very little (often no) overhead for defining subsidiary functions, so break complicated stuff into parts you can understand. -- Jón Fairbairn Jon.Fairbairn@cl.cam.ac.uk http://www.chaos.org.uk/~jf/Stuff-I-dont-want.html (updated 2006-07-14)

Hi
But why should c and d exist at runtime? They're only used once each, so the compiler is free to replace f with
\a b -> (a+b)+ a*b
Yes, and if the compiler is this clever, it should also be free to replace them back at debug time.
I've said this before, but I think it's worth repeating: in most cases, if you need to debug your programme it's because it's too complicated for you to understand, so the correct thing to do is to simplify it and try again.
That makes it impossible to ever improve - initially all programs are beyond you, and only by pushing the boundary do you get anywhere. As for me, I always write programs I can't understand, and that no one else can. Perhaps I understood it when I originally wrote it, perhaps not, but that was maybe over a year ago. It's been my experience that debugging is a serious weakness of Haskell - where even the poor mans printf debugging changes the semantics! And everyone comes up with arguments why there is no need to debug a functional language - that sounds more like excuses about why we can't build a good lazy debugger :) [Sorry for the slight rant, but I've used Visual Studio C++ so I know what a good debugger looks like, and how indispensable they are] Thanks Neil

I've also used Visual Studio, and I wouldn't mind having something like that for Haskell. But I have to agree with Jon, I think the best way of debugging is to understand your code. I think people who come from imperative programming come with a mind set that you understand your code by stepping through it in the debugger. But I find this paradigm much less useful for functional code. -- Lennart On Sep 6, 2006, at 06:22 , Neil Mitchell wrote:
Hi
But why should c and d exist at runtime? They're only used once each, so the compiler is free to replace f with
\a b -> (a+b)+ a*b
Yes, and if the compiler is this clever, it should also be free to replace them back at debug time.
I've said this before, but I think it's worth repeating: in most cases, if you need to debug your programme it's because it's too complicated for you to understand, so the correct thing to do is to simplify it and try again.
That makes it impossible to ever improve - initially all programs are beyond you, and only by pushing the boundary do you get anywhere. As for me, I always write programs I can't understand, and that no one else can. Perhaps I understood it when I originally wrote it, perhaps not, but that was maybe over a year ago.
It's been my experience that debugging is a serious weakness of Haskell - where even the poor mans printf debugging changes the semantics! And everyone comes up with arguments why there is no need to debug a functional language - that sounds more like excuses about why we can't build a good lazy debugger :)
[Sorry for the slight rant, but I've used Visual Studio C++ so I know what a good debugger looks like, and how indispensable they are]
Thanks
Neil _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On Wed, Sep 06, 2006 at 06:33:32AM -0400, Lennart Augustsson wrote:
I've also used Visual Studio, and I wouldn't mind having something like that for Haskell. But I have to agree with Jon, I think the best way of debugging is to understand your code. I think people who come from imperative programming come with a mind set that you understand your code by stepping through it in the debugger. But I find this paradigm much less useful for functional code.
At this point, I need debugging not because I don't understand my code, but because I don't understand Haskell ;-) Most of the mistakes I make are related to indentation, precedence (need to remember that function application binds tightly). The compiler and the type system catches some mistakes, but a few remain. Thanks for the suggestions. Tamas

So it sounds like what you need at the moment is more of a Haskell pretty printer than a debugger. :) IDEs, like Visual Studio, can be very helpful when writing code in a language you don't really know, I've noticed this writing VBA. I wish Visual Haskell were ready for real use. -- Lennart On Sep 6, 2006, at 08:13 , Tamas K Papp wrote:
On Wed, Sep 06, 2006 at 06:33:32AM -0400, Lennart Augustsson wrote:
I've also used Visual Studio, and I wouldn't mind having something like that for Haskell. But I have to agree with Jon, I think the best way of debugging is to understand your code. I think people who come from imperative programming come with a mind set that you understand your code by stepping through it in the debugger. But I find this paradigm much less useful for functional code.
At this point, I need debugging not because I don't understand my code, but because I don't understand Haskell ;-) Most of the mistakes I make are related to indentation, precedence (need to remember that function application binds tightly). The compiler and the type system catches some mistakes, but a few remain.
Thanks for the suggestions.
Tamas _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

Tamas K Papp
Most of the mistakes I make are related to indentation,
I use Emacs, which has a reasonably decent mode for this. Hit TAB repeatedly to show the possible indentations.
precedence (need to remember that function application binds tightly).
It's not always intuitive, but at least there is a fixed rule that's easy to remember. -k -- If I haven't seen further, it is by standing in the footprints of giants

Tamas K Papp
On Wed, Sep 06, 2006 at 06:33:32AM -0400, Lennart Augustsson wrote:
I've also used Visual Studio, and I wouldn't mind having something like that for Haskell. But I have to agree with Jon, I think the best way of debugging is to understand your code. I think people who come from imperative programming come with a mind set that you understand your code by stepping through it in the debugger. But I find this paradigm much less useful for functional code.
At this point, I need debugging not because I don't understand my code, but because I don't understand Haskell ;-) Most of the mistakes I make are related to indentation, precedence (need to remember that function application binds tightly). The compiler and the type system catches some mistakes, but a few remain.
I don't think you need a debugger for that. In addition to what Lennart and Ketil have said, I think you need to take on board the usefulness of breaking functions up into small pieces that can be run at the GHCi or Hugs command line. And if you aren't sure about precedence, you can type something that demonstrates it, like this sort of thing: *Main> let f = (+1) *Main> f 1 * 2 4 (That particular example only works in GHCi, but you could use let f = (+1) in f 1 * 2 in Hugs) On top of that, in GHCi you can do this: *Main> :info + class (Eq a, Show a) => Num a where (+) :: a -> a -> a ... -- Imported from GHC.Num infixl 6 + *Main> :info * class (Eq a, Show a) => Num a where ... (*) :: a -> a -> a ... -- Imported from GHC.Num infixl 7 * *Main> Note in particular the two infixl lines, which say that both operators bind to the left and that “*” binds more tightly than “+” And there's always Hoogle (and the documentation it links to) if you want to find something of a particular type. Kudos to Neil for producing this, by the way (though I do hope his remarks about the readability of his code were a more self deprecating than accurate). -- Jón Fairbairn Jon.Fairbairn@cl.cam.ac.uk

"Neil Mitchell"
Hi
But why should c and d exist at runtime? They're only used once each, so the compiler is free to replace f with
\a b -> (a+b)+ a*b
Yes, and if the compiler is this clever, it should also be free to replace them back at debug time.
And where does that get us? You snipped the salient bit where I said that you'd be debugging a different programme. If the debugger serialises stuff and refers to variables, it reinforces an imperative view of the world and supports the erroneous view of variables as things that exist at runtime. AFAIK, no implementation nowadays compiles to pure combinators, but such would still be a valid implementation of Haskell. You need to develop a way of thinking about programmes that produces insights that apply to Haskell, not to a particular implementation.
I've said this before, but I think it's worth repeating: in most cases, if you need to debug your programme it's because it's too complicated for you to understand, so the correct thing to do is to simplify it and try again.
That makes it impossible to ever improve - initially all programs are beyond you, and only by pushing the boundary do you get anywhere.
I offer myself as a counter example. I'm not the world's best Haskel programmer (that might be Lennart?;-), but I understand it pretty well, I think. At no point have I ever used a debugger on a Haskell programme. So clearly not impossible (I didn't come into existence a fully fledged functional programmer, which is the only other possibility admitted by your argument).
As for me, I always write programs I can't understand, and that no one else can. Perhaps I understood it when I originally wrote it, perhaps not, but that was maybe over a year ago.
No comment.
It's been my experience that debugging is a serious weakness of Haskell - where even the poor mans printf debugging changes the semantics! And everyone comes up with arguments why there is no need to debug a functional language - that sounds more like excuses about why we can't build a good lazy debugger :)
It may sound like that to you, but the arguments why debugging is a bad thing are quite strong. There are almost certainly bugs in my Haskell code. Would a debugger help me find them? Not in the least, because none of the testcases I've used provokes them. Would it help when one is provoked? I don't think so, because either the logic is right and there's a slip up in the coding, which is usually easy to find from the new testcase, or the logic is tortuous and needs replacement.
[Sorry for the slight rant, but I've used Visual Studio C++ so I know what a good debugger looks like, and how indispensable they are]
So I'm ranting back at you. I've used debuggers on C and other old languages (particularly when writing in assembler) and indeed they are useful when the language provides so many opportunities for mistakes. When writing Haskell I've never felt the need. Here's a story: A long time ago, when I was an undergraduate, I encountered a visitor to the Computing Service who often asked for help with the idiosyncrasies of the local system. I was happy to help, though somewhat bemused by all the muttering at his terminal while he was programming, which involved some sort of debugging, for sure. After some weeks he finished his programme and whatever kept him in Cambridge and disappeared. Being curious, I dug (strictly against regulations) a listing of his programme out of the scrap bin. It was in PL/I, and several millimetres thick. It took me quite a while (hours, though, not weeks) to work out what the programme did, but eventually I understood it well enough that I could write a version in Algol68 (my favourite language at the time). It came to one sheet of listing paper. This wasn't because A68 is more expressive than PL/I (it is, but not by that much), it was because I understood the problem before I started to write the code. Now, you could argue that the first programmer spent most of his time working out what the problem was (it might even be true, but given that it boiled down to 1 page of A68, I'm not sure), but my point is that if you proceed by debugging rather than rewriting, you are likely to end up with this sort of mess. Personally, I don't mind too much if that kind of programmer finds Haskell too hard. Elitist? Certainly! Immorally so? No. -- Jón Fairbairn Jon.Fairbairn@cl.cam.ac.uk http://www.chaos.org.uk/~jf/Stuff-I-dont-want.html (updated 2006-07-14)

Hi
Yes, and if the compiler is this clever, it should also be free to replace them back at debug time.
And where does that get us? You snipped the salient bit where I said that you'd be debugging a different programme.
In Visual C there are two compilation modes. In debug mode, if you create a variable "i" it WILL be there in the compiled version. It won't be merged with another variable. It won't be constant folded. It won't have its value globablly computed and stored. It won't be placed only in the register. In release mode all these things can (and do) happen. I'm not saying Haskell compilers aren't free to optimize, but maybe there is a need for one that is this "debug mode" style.
At no point have I ever used a debugger on a Haskell programme.
Because you couldn't find a working debugger? ;) If one had been there, just a click away, would you never have been tempted?
It may sound like that to you, but the arguments why debugging is a bad thing are quite strong. There are almost certainly bugs in my Haskell code. Would a debugger help me find them? Not in the least, because none of the testcases I've used provokes them. Would it help when one is provoked? I don't think so, because either the logic is right and there's a slip up in the coding, which is usually easy to find from the new testcase, or the logic is tortuous and needs replacement.
Let take for example a bug I spent tracking down in Haskell this weekend. The bug can be summarized as "Program error: pattern match failure: head []". And indeed, thats all you get. A quick grep reveals there are about 60 calls to head in the program. In this instance I have the choice between 1) crying, 2) a debugger, 3) a lot of hard work. [Of course, knowing this situation arises, I had already prepared my getout plan, so it wasn't a massive problem] Thanks Neil

"Neil Mitchell"
Hi
Yes, and if the compiler is this clever, it should also be free to replace them back at debug time.
And where does that get us? You snipped the salient bit where I said that you'd be debugging a different programme.
In Visual C there are two compilation modes. In debug mode, if you create a variable "i" it WILL be there in the compiled version. It won't be merged with another variable. It won't be constant folded. It won't have its value globablly computed and stored. It won't be placed only in the register. In release mode all these things can (and do) happen. I'm not saying Haskell compilers aren't free to optimize, but maybe there is a need for one that is this "debug mode" style.
I think this chiefly indicates that you think of variables as things, which in Haskell they are not.
At no point have I ever used a debugger on a Haskell programme.
Because you couldn't find a working debugger? ;) If one had been there, just a click away, would you never have been tempted?
Not in the least. The highest level language I've ever wanted to use a debugger on was C, and that was for debugging a pointer-reversing garbage collector, which ties in with what Andrae says.
Let take for example a bug I spent tracking down in Haskell this weekend. The bug can be summarized as "Program error: pattern match failure: head []". And indeed, thats all you get. A quick grep reveals there are about 60 calls to head in the program. In this instance I have the choice between 1) crying, 2) a debugger, 3) a lot of hard work. [Of course, knowing this situation arises, I had already prepared my getout plan, so it wasn't a massive problem]
H'm. I've never been completely convinced that head should be in the language at all. If you use it, you really have to think, every time you type in the letters h-e-a-d, “Am I /really/ /really/ sure the argument will never be []?” Otherwise you should be using a case expression or (more often, I think) a subsidiary function with a pattern match. Most of the occurrences of “head” in my code seem to be applied to infinite lists, or commented out, or rebindings of head to something else, sloppy bits of nonce programming or references in comments to the top of my own noddle. -- Jón Fairbairn Jon.Fairbairn@cl.cam.ac.uk http://www.chaos.org.uk/~jf/Stuff-I-dont-want.html (updated 2006-07-14)

Hi I think that when it comes to debuggers and Haskell its fairly safe to say: 1) There aren't any which are production quality, regularly work "out of the box" and are avaiable without much effort. There may be ones which are debuggers (Hat/GHCi breakpoints), but we probably haven't got to the stage where they are "polished" (being exceptionally vague about what that means). 2) We disagree if they would be useful. I say yes. Others say no. I guess this is an academic argument until we fix 1, and I guess no one who says "no" to 2 is going to bother fixing 1.
H'm. I've never been completely convinced that head should be in the language at all. If you use it, you really have to think, every time you type in the letters h-e-a-d, "Am I /really/ /really/ sure the argument will never be []?" Otherwise you should be using a case expression or (more often, I think) a subsidiary function with a pattern match.
I disagree (again...) - head is useful. And as you go on to say, if you apply it to the infinite list, who cares if you used head. Head is only unsafe on an empty list. So the problem then becomes can you detect unsafe calls to head and let the programmer know. Answer, not yet, but again, its being worked on: http://www.cl.cam.ac.uk/~nx200/research/escH-hw.ps http://www-users.cs.york.ac.uk/~ndm/projects/catch.php Thanks Neil

"Neil Mitchell"
I wrote:
H'm. I've never been completely convinced that head should be in the language at all. If you use it, you really have to think, every time you type in the letters h-e-a-d, "Am I /really/ /really/ sure the argument will never be []?" Otherwise you should be using a case expression or (more often, I think) a subsidiary function with a pattern match.
I disagree (again...) - head is useful.
Given that a programming language remain computationally complete, utility is never a sufficient reason for including something in it. Guns are occasionally useful, but if you walk around with a loaded automatic down the front of your pants, you are *far* more likely to blow your own balls off than encounter circumstances where the gun would be useful. A large part of good programming language design is about preventing you from blowing your balls off (or anyone else's).
And as you go on to say, if you apply it to the infinite list, who cares if you used head. Head is only unsafe on an empty list. So the problem then becomes can you detect unsafe calls to head and let the programmer know.
No, that's the wrong approach because it relies on detecting something that (a) the programmer probably knows already and (b) is undecidable in general. It would be far better for the programmer to express this knowledge in the programme. In Haskell it's not possible for the two datatypes data NonFiniteList t = (:) {head::t, tail:: NonFiniteList t} and data List t = (:) {head::t, tail:: List t} | [] to coexist, and though it would even then have been possible to use a type system where all the operations on List were also applicable to NonFiniteList, the means available then would have lost type inference. So the consensus at the time was that doing clever stuff with types (making one a transparent supertype of the other) was too problematic, and keeping them separate would have meant providing all the list operations twice. Once typeclasses came along, this argument no longer held, but redoing lists and nonfinite lists as special cases of a sequence class would have been hard work. I think it's hard work that should have been done, though. As I said in the post to which you replied, if you use head, you really have to have a convincing argument that it isn't going to be applied to []. If you don't have one, use something other than head (it's always possible). If your programme crashes with a "head []", the only reasonable thing to do is to go through all 60 uses and check them. If you don't have time for that right now, doing an automatic replace is a reasonable interim measure, so long as you don't replace them back once you've found the particular bug. 1 i import Control.Exception . s/head/(\l -> assert (not $ null l) (head l))/g ... Ugly, and *** Exception: /tmp/foo.hs:4:7-12: Assertion failed is really ugly, but not as ugly as *** Exception: Prelude.head: empty list :-) And the ugliness might encourage a rethink of particular uses of head next time you have to look at that bit of code, and that's a good thing even if all you end up doing is manually substituting a let for the lambda and adding a comment on the lines "I really can't see how this list could ever be empty, but I can't prove it". -- Jón Fairbairn Jon.Fairbairn@cl.cam.ac.uk

On 9/6/06, Neil Mitchell
Let take for example a bug I spent tracking down in Haskell this weekend. The bug can be summarized as "Program error: pattern match failure: head []". And indeed, thats all you get. A quick grep reveals there are about 60 calls to head in the program. In this instance I have the choice between 1) crying, 2) a debugger, 3) a lot of hard work. [Of course, knowing this situation arises, I had already prepared my getout plan, so it wasn't a massive problem]
I don't know what your getout plan was but when I'm in this situation I do the following (hopefully listing this trick will help the OP): head' :: String -> [a] -> a head' s [] = error $ "head' empty list at " ++ s head' _ xs = head xs Then I do the tedious thing of replacing all the heads in my program with head' and giving each a unique string to print out, such as head' "line 5". You could even take this a step further and let the type system help you find the places to replace head by doing something like: import Prelude hiding (head) import qualified Prelude (head) head :: String -> [a] -> a head s [] = error $ "head empty list at " ++ s head _ xs = Prelude.head Now you should get type errors anywhere you're using head. Or maybe even more extreme you could use template haskell or the c preprocessor to fill in the line number + column. HTH, Jason

On Wed, Sep 06, 2006 at 09:56:17AM -0700, Jason Dagit wrote:
Or maybe even more extreme you could use template haskell or the c preprocessor to fill in the line number + column.
Which is precisely what darcs does for fromJust (which we use a lot): we define a C preprocessor macro fromJust. It's ugly, but beats any other choice I'm aware of. I wish that built in functions that call error could be automatically this way... -- David Roundy

David Roundy wrote:
On Wed, Sep 06, 2006 at 09:56:17AM -0700, Jason Dagit wrote:
Or maybe even more extreme you could use template haskell or the c preprocessor to fill in the line number + column.
Which is precisely what darcs does for fromJust (which we use a lot): we define a C preprocessor macro fromJust. It's ugly, but beats any other choice I'm aware of. I wish that built in functions that call error could be automatically this way...
This old mailing list message may help: http://www.mail-archive.com/haskell-cafe@haskell.org/msg13034.html
Suggested by a question from sethk on #haskell irc channel. Solves an FAQ where people have often resorted to cpp or m4: a `trace' that prints line numbers.
-- Chris

David Roundy
On Wed, Sep 06, 2006 at 09:56:17AM -0700, Jason Dagit wrote:
Or maybe even more extreme you could use template haskell or the c preprocessor to fill in the line number + column.
Which is precisely what darcs does for fromJust (which we use a lot): we define a C preprocessor macro fromJust.
Curiously, the only bug in darcs that has bitten me so far was a use of fromJust. Perhaps that indicates a weakness in the style, rather than the tools? -- Jón Fairbairn Jon.Fairbairn@cl.cam.ac.uk

On Thu, Sep 07, 2006 at 06:21:01AM +0100, Jn Fairbairn wrote:
David Roundy
writes: On Wed, Sep 06, 2006 at 09:56:17AM -0700, Jason Dagit wrote:
Or maybe even more extreme you could use template haskell or the c preprocessor to fill in the line number + column.
Which is precisely what darcs does for fromJust (which we use a lot): we define a C preprocessor macro fromJust.
Curiously, the only bug in darcs that has bitten me so far was a use of fromJust. Perhaps that indicates a weakness in the style, rather than the tools?
Yeah, in general fromJust is a dangerous business, and most of the uses of it in darcs can lead to trouble for partial repositories, for instance. I was just yesterday discussing with Jason the possibility of switching away from a Maybe approach for lazily reading patches. -- David Roundy

Hi
I don't know what your getout plan was but when I'm in this situation I do the following (hopefully listing this trick will help the OP):
I have headNote, fromJustNote, fromJustDefault, lookupJust, lookupJustNote, tailNote - a whole set of functions which take an extra note parameter - or for the example of lookupJust still give a crash, but have both the thing you were searching for and the list of things you were searching in - lookupJust requires a Show, so can usually tell you roughly where you are. I also always write it out as Module.Name.function - since line 5 has a habit of being moved to a different line a lot more than names changes. And because I am used to this, maybe 95% of all unsafe functions are already in Note form, so I just change the new ones I added since last time I broke down and cried. However, its useful, but hacky. Its not something I should have to do manually. Thanks Neil

Here's another story. A few weeks ago I was working on dynamic breakpoints for GHC. At that moment, I was trying to capture the bindings introduced by implicit parameters in the type of a function, in order to make them available at a breakpoint site. Ok, disregard the stuff about me working on dynamic breakpoints. It is completely orthogonal to what I'm trying to convey here. So, in order to capture these bindings, I had to introduce some code in dsBinds, the function that munches type constraints in the GHC desugarer. Or was it...? Doh, I don't even remember accurately and it was only a few days ago. The problem is that this function would build on a lot of abstractions below it, particularly on a datatype called HsBind. There were some comments located near the datatype definition trying to give some insight about its meaning, but they were almost completely opaque to me. Even more, they referred to a paper, the one about Static Semantics of Haskell, which, even if interesting, was discouragingly long. I was faced with the disjunctive of trying to completely understand the whole thing, or be more pragmatic and handle only the cases specific to my problem. A few traces showing me the structure received at dsBinds were incredibly productive: in a few minutes I was ready to test my solution. The morale of the story is, imho, that sometimes the problem is not Haskell, the language; or C, the language. The problem is complexity and the size of the programs we are dealing with. It can easily be the case that you are dealing with code written by another person and you cannot afford the time necessary to completely understand its insides. A good debugger may be counterproductive for issues in your own code, but it can be immensely helpful in a myriad other situations. Cheers pepe On 06/09/2006, at 16:39, Jón Fairbairn wrote:
Here's a story:
A long time ago, when I was an undergraduate, I encountered a visitor to the Computing Service who often asked for help with the idiosyncrasies of the local system. I was happy to help, though somewhat bemused by all the muttering at his terminal while he was programming, which involved some sort of debugging, for sure. After some weeks he finished his programme and whatever kept him in Cambridge and disappeared. Being curious, I dug (strictly against regulations) a listing of his programme out of the scrap bin. It was in PL/I, and several millimetres thick. It took me quite a while (hours, though, not weeks) to work out what the programme did, but eventually I understood it well enough that I could write a version in Algol68 (my favourite language at the time). It came to one sheet of listing paper. This wasn't because A68 is more expressive than PL/I (it is, but not by that much), it was because I understood the problem before I started to write the code.
Now, you could argue that the first programmer spent most of his time working out what the problem was (it might even be true, but given that it boiled down to 1 page of A68, I'm not sure), but my point is that if you proceed by debugging rather than rewriting, you are likely to end up with this sort of mess. Personally, I don't mind too much if that kind of programmer finds Haskell too hard. Elitist? Certainly! Immorally so? No.

"scientists, who ought to know, assure us that it must be so, oh, let us never, never, doubt, what nobody is sure about." (or something like that..;-) as everyone else in this thread, I have my own experiences and firm opinions on the state of debugging support in Haskell (in particular on the apparent ease with which operational semantics or stepwise reduction are often dismissed), but just a few points: - if you're sure about anything, that probably just means you've stopped thinking about that thing - not a good position for imposing your assured opinion on others, imho. - a debugger is (mainly) a tool for finding and removing bugs: - if you're crawling through the machine's circuitboards in search of a real bug, that might be a screwdriver, a flashlight, and a circuitdiagram/floorplan - if you don't like to use computers to augment your mental processes, that might be pencil and paper - if you do like to use computers to augment your mental processes, that might be some piece of software - what kind of software might be helpful in debugging depends as much on what you are debugging as on your individual approach to debugging - assuming that you're not debugging the hardware, compiler, runtime system, or foreign code, functional languages free you from many sources of bugs. but not of all sources. - simplifying the code until it becomes easily comprehensible is a good habit anyway, and it does help to expose bugs that creep in while you're juggling too many balls at once (is the code "obviously correct" or "not obviously wrong"?). for those so inclined, tools can help here, too: they can expand our limits of comprehension, they can assist in the simplification, they can highlight big-balls-of-mud in your code, etc. - often, finding bugs is linked to comprehending the program's operational behaviour, so that you can be sure that it is going to do all that you need it to do, and nothing else. that in itself does not imply, however, that you need to include your language's implementation into the set of things to debug. - it is perfectly possible to study the operational behaviour of functional programs without resorting to implementation details - that falls under operational semantics, and last time I checked, it had become at least as well respected as denotational semantics, not least because of its successes in reasoning about programs in concurrent languages. - a useful equivalent to observing instruction-by-instruction state changes when debugging imperative programs is to observe reduction-by-reduction program transformations when debugging functional programs. based on operational semantics, tool support for this approach is not just possible, but was used with good success in the past (interested parties might like to browse, eg, the 1994 user's guide for PI-RED, or the 1996 papers on teaching experience, in FPLE, and system overview, in JFP: http://www.informatik.uni-kiel.de/~wk/papers_func.html ), just not for Haskell. note that this constitutes a semantics-based inspection at the language level, completely independent of the actual implementation below. hiding implementation details while presenting a high-level operational semantics is a non-trivial exercise that includes such topics as variables, bindings and substitution, as well as retranslating low-level machine states to corresponding intermediate high-level programs. so, while there are reasonable and less reasonable ways of using debuggers, the question is not whether or not there should be debuggers, but what kind of tools we have or need to help debugging and, more generally, comprehending, Haskell programs. and if the state of the art indicates that such tools are either kludges (affecting program semantics, exposing low-level machine details, etc), do not cover the whole Haskell-in-use, or simply don't help those who'd like to use them, then that state is not good. it is a lot better than it used to be, but far from optimal. thinking helps, but claiming that tools can't help doesn't. claus

"Claus Reinke"
thinking helps, but claiming that tools can't help doesn't.
Lets be absolutely clear about this: I've never claimed that tools can't help. In this thread I've been using the term debugger in the narrow sense implied by the OP's question -- something that steps through the execution of the code. Such a debugger is inappropriate for Haskell programmers, and doubly so for beginners. -- Jón Fairbairn Jon.Fairbairn@cl.cam.ac.uk

thinking helps, but claiming that tools can't help doesn't.
Lets be absolutely clear about this: I've never claimed that tools can't help. In this thread I've been using the term debugger in the narrow sense implied by the OP's question -- something that steps through the execution of the code. Such a debugger is inappropriate for Haskell programmers, and doubly so for beginners.
well, then it is clear that we disagree on this. imho, being able to step through reductions is very appropriate for all functional programmers (**), and an essential exercise for beginners. and if that is true for tiny examples on paper, then there should be tool support for applying it to larger programs. from experience with the PI-RED systems, there are few cases where one can apply such a tool without thinking (eg. let it run till it gets stuck on an error, go a few steps back to see where that erroneous part was constructed; or let it run for a large number of steps, then check why and where the program it is still growing instead of terminating). (*) after the initial learning phase, where such a stepper helps to form the student's mental model of program evaluation, the majority of debugging cases need to combine thinking with experimentation, but dropping either of these two ingredients makes the problem much harder. and it is nice to be able to do the experiments without having to switch tools or mindsets (although there are many ways in which the old PI-RED systems could have been improved, not to mention lessons learned from other tools, like Hat, that were developed for Haskell because there were no reduction systems for it). claus (*) note that the programmer never saw thunks, or stacks, let alone heap objects or abstract machine code, only high-level program text, transformed by reductions. intermediate programs were fully editable, in no way distinguished from the original programs entered by the programmer, so one could make some local observations, changes and reductions, then continue with the overall reduction, or return to the original program. compilation, decompilation, and presentation were implicit, under the hood. for the PI-RED developers, though, there were *separate* debugging tools that would allow them to inspect the abstract machine's stack, heap, etc.. and only if those failed to indicate the problem, would they have to resort to C-level debugging tools, another level lower. (**) in fact, Berkling used to argue that was true for all declarative programmers, and he extended his ideas and machines to functional logic languages

On 06/09/2006, at 8:22 PM, Neil Mitchell wrote:
It's been my experience that debugging is a serious weakness of Haskell - where even the poor mans printf debugging changes the semantics! And everyone comes up with arguments why there is no need to debug a functional language - that sounds more like excuses about why we can't build a good lazy debugger :)
[Sorry for the slight rant, but I've used Visual Studio C++ so I know what a good debugger looks like, and how indispensable they are]
I simply can't let this pass without comment. It's irrelevant if you're using a functional or imperative language, debuggers are invariably a waste of time. The only reason to use a debugger is because you need to inspect the contents of a processes address- space; so either you're using it as a disassembler, or you're using it to examine the consequences of heap/stack corruption. Consequently, if you're using Java, C#, Scheme, Haskell, Erlang, Smalltalk, or any one of a myriad of languages that don't permit direct memory access, there's no reason for you to be using a debugger. Jon understates it by implying this is a Functional/Haskell specific quality - it's not. Debuggers stopped being useful the day we finally delegated pointer handling to the compiler/vm author and got on with writing code that actually solves real problems. It's just that historically functional programmers have tended to already be experienced programmers who realise this. Why would they waste their time building a tool that no-one needs? It's a truism to say if your code doesn't work it's because you don't understand it; clearly if you did understand it, you wouldn't have included the bug that's causing you difficulty. Therefore either 1) The code is poorly structured and you need to restructure it to better represent your understanding of the problem or 2) Your understanding of the problem is flawed, so you need to sit back and reexamine your thinking on this problem in light of the counter-example you have found (the bug). Spending your time tracing through individual lines of code is counter-productive in both cases. Andrae Muys P.S. It is worth noting that I am here talking about the sort of debugger raised in the original post. I am not talking about using a separate tool to extract a stracktrace from a core file in a C/C++ program or equivalent - I'm talking about runtime debugging with variable watches, breakpoints, and line-by-line stepping. -- Andrae Muys andrae@netymon.com Principal Kowari Consultant Netymon Pty Ltd

I simply can't let this pass without comment. It's irrelevant if you're using a functional or imperative language, debuggers are invariably a waste of time. The only reason to use a debugger is because you need to inspect the contents of a processes address-space;
That's a very narrow definition of "debugger". Stefan

Andrae Muys
It's a truism to say if your code doesn't work it's because you don't understand it; ...
Indeed, but tracing the execution of the code, on the test example where it fails, will often give insight into one's misunderstanding. And often, the person trying to fix some code is not even the person who wrote it. Help with understanding a program is the job of tracing tools like Hat, Hood, or Buddha. (These are often called debuggers.) And I believe this is what the original poster was asking for - a tool to reveal what his code _really_ does as opposed to what he _thinks_ it ought to do. Is that so terrible? Many people seem to think that banishing some misunderstanding of a piece of code is as simple as staring at it until enlightenment hits. I can vouch for the fact that, when the code is complex (and necessarily so, because the problem logic is complex), then tools are needed for full comprehension of the logic. For example, when presented with a bug report for the nhc98 compiler (which I maintain but did not create), using Hat has been invaluable to me in narrowing down which function is to blame in mere minutes. Prior to Hat, it sometimes took several days of reading sources by hand, manually tracking possible stacks of function calls over large data structures, before even finding the site of the error. Large software systems are complex. Tool support for understanding their behaviour is not needed because we humans are stupid, but because there is just too much information context to keep in one head-ful at a time.
P.S. It is worth noting that I am here talking about the sort of debugger raised in the original post. - I'm talking about runtime debugging with variable watches, breakpoints, and line-by-line stepping.
I don't believe that is necessarily what the original poster was asking for. And reduction-by-reduction stepping can be extremely useful for beginners to understand how lazy evaluation works. Regards, Malcolm

On 06/09/2006, at 17:10, Andrae Muys wrote:
On 06/09/2006, at 8:22 PM, Neil Mitchell wrote:
It's been my experience that debugging is a serious weakness of Haskell - where even the poor mans printf debugging changes the semantics! And everyone comes up with arguments why there is no need to debug a functional language - that sounds more like excuses about why we can't build a good lazy debugger :)
[Sorry for the slight rant, but I've used Visual Studio C++ so I know what a good debugger looks like, and how indispensable they are]
I simply can't let this pass without comment. It's irrelevant if you're using a functional or imperative language, debuggers are invariably a waste of time. The only reason to use a debugger is because you need to inspect the contents of a processes address- space; so either you're using it as a disassembler, or you're using it to examine the consequences of heap/stack corruption. Consequently, if you're using Java, C#, Scheme, Haskell, Erlang, Smalltalk, or any one of a myriad of languages that don't permit direct memory access, there's no reason for you to be using a debugger.
Jon understates it by implying this is a Functional/Haskell specific quality - it's not. Debuggers stopped being useful the day we finally delegated pointer handling to the compiler/vm author and got on with writing code that actually solves real problems.
You seem to base everything on the assumption that a debugger is a program that lets you, and I quote your words below, "trace through individual lines of code". A debugger in the sense that this post regards it is any kind of program that helps you to understand a piece of code. A debugger is the program that tries to answer the following questions: "What information can we provide to the programmers about how a program is running?" "What information will help the programmer most?" If it happens that traditionally debuggers are based in inspecting the memory, this is an unavoidable situation considering the history of programming languages. But certainly there are many other possibilities that can help a programmer to manage the complexity of a running program, and it seems as if you disregard them all completely in your argument !
It's just that historically functional programmers have tended to already be experienced programmers who realise this. Why would they waste their time building a tool that no-one needs?
This whole block is offensive to the rest of the world. Fortunately it has nothing to do with reality: - the recent GHC survey uncovered "some kind of debugger" as the most demanded tool - Other functional languages have seen magnificent efforts in the debugging camp, such as the awesome Ocaml debugger or the now sadly defunct ML Time-Travel debugger from A. Tolmach - The Lispish languages, which are arguably on the functional side too, have always enjoyed impressive online debugging tools.
It's a truism to say if your code doesn't work it's because you don't understand it; clearly if you did understand it, you wouldn't have included the bug that's causing you difficulty.
Therefore either
1) The code is poorly structured and you need to restructure it to better represent your understanding of the problem
or
2) Your understanding of the problem is flawed, so you need to sit back and reexamine your thinking on this problem in light of the counter-example you have found (the bug). Spending your time tracing through individual lines of code is counter-productive in both cases.
Andrae Muys
P.S. It is worth noting that I am here talking about the sort of debugger raised in the original post. I am not talking about using a separate tool to extract a stracktrace from a core file in a C/C+ + program or equivalent - I'm talking about runtime debugging with variable watches, breakpoints, and line-by-line stepping.
-- Andrae Muys andrae@netymon.com Principal Kowari Consultant Netymon Pty Ltd
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On 2006-09-06, Andrae Muys
Jon understates it by implying this is a Functional/Haskell specific quality - it's not. Debuggers stopped being useful the day we finally delegated pointer handling to the compiler/vm author and got on with writing code that actually solves real problems.
Of course, these sometimes have bugs which we need to track down, in which case having a debugger to show just how the vm is messing up can be very helpful. -- Aaron Denney -><-
participants (15)
-
Aaron Denney
-
Andrae Muys
-
Chris Kuklewicz
-
Claus Reinke
-
David Roundy
-
dons@cse.unsw.edu.au
-
Jason Dagit
-
Jón Fairbairn
-
Ketil Malde
-
Lennart Augustsson
-
Malcolm Wallace
-
Neil Mitchell
-
Pepe Iborra
-
Stefan Monnier
-
Tamas K Papp