Simple debugging in Haskell

前端 未结 5 2125
醉话见心
醉话见心 2021-02-07 19:04

I am new to Haskell. Previously I have programmed in Python and Java. When I am debugging some code I have a habit of littering it with print statements in the midd

相关标签:
5条回答
  • 2021-02-07 19:45

    Other answers link the official doco and the Haskell wiki but if you've made it to this answer let's assume you bounced off those for whatever reason. The wikibook also has an example using Fibonacci which I found more accessible. This is a deliberately basic example which might hopefully help.

    Let's say we start with this very simple function, which for important business reasons, adds "bob" to a string, then reverses it.

    bobreverse x = reverse ("bob" ++ x)
    

    Output in GHCI:

    > bobreverse "jill"
    "llijbob"
    

    We don't see how this could possibly be going wrong, but something near it is, so we add debug.

    import Debug.Trace
    
    bobreverse x = trace ("DEBUG: bobreverse" ++ show x) (reverse ("bob" ++ x))
    

    Output:

    > bobreverse "jill"
    "DEBUG: bobreverse "jill"
    llijbob"
    

    We are using show just to ensure x is converted to a string correctly before output. We also added some parenthesis to make sure the arguments were grouped correctly.

    In summary, the trace function is a decorator which prints the first argument and returns the second. It looks like a pure function, so you don't need to bring IO or other signatures into the functions to use it. It does this by cheating, which is explained further in the linked documentation above, if you are curious.

    0 讨论(0)
  • 2021-02-07 19:53

    You can use Debug.Trace for that.

    0 讨论(0)
  • 2021-02-07 19:59

    I was able to create a dual personality IO / ST monad typeclass, which will print debug statements when a monadic computation is typed as IO, them when it's typed as ST. Demonstration and code here: Haskell -- dual personality IO / ST monad? .

    Of course Debug.Trace is more of a swiss army knife, especially when wrapped with a useful special case,

    trace2 :: Show a => [Char] -> a -> a
    trace2 name x = trace (name ++ ": " ++ show x) x
    

    which can be used like (trace2 "first arg" 3) + 4

    edit

    You can make this even fancier if you want source locations

    {-# LANGUAGE TemplateHaskell #-}
    import Language.Haskell.TH
    import Language.Haskell.TH.Syntax as TH
    import Debug.Trace
    
    withLocation :: Q Exp -> Q Exp
    withLocation f = do
        let error = locationString =<< location
        appE f error
        where
            locationString :: Loc -> Q Exp
            locationString loc = do
                litE $ stringL $ formatLoc loc
    
    formatLoc :: Loc -> String
    formatLoc loc = let file = loc_filename loc
                        (line, col) = loc_start loc
                    in concat [file, ":", show line, ":", show col]
    
    trace3' (loc :: String) msg x =
        trace2 ('[' : loc ++ "] " ++ msg) x
    trace3 = withLocation [| trace3' |]
    

    then, in a separate file [from the definition above], you can write

    {-# LANGUAGE TemplateHaskell #-}
    tr3 x = $trace3 "hello" x
    

    and test it out

    > tr3 4
    [MyFile.hs:2:9] hello: 4
    
    0 讨论(0)
  • 2021-02-07 20:00

    I really liked Dons short blog about it: https://donsbot.wordpress.com/2007/11/14/no-more-exceptions-debugging-haskell-code-with-ghci/

    In short: use ghci, example with a program with code called HsColour.hs

     $ ghci HsColour.hs
        *Main> :set -fbreak-on-exception
        *Main> :set args "source.hs"
    

    Now run your program with tracing on, and GHCi will stop your program at the call to error:

     *Main> :trace main
        Stopped at (exception thrown)
    

    Ok, good. We had an exception… Let’s just back up a bit and see where we are. Watch now as we travel backwards in time through our program, using the (bizarre, I know) “:back” command:

      [(exception thrown)] *Main> :back
        Logged breakpoint at Language/Haskell/HsColour/Classify.hs:(19,0)-(31,46)
        _result :: [String]
    

    This tells us that immediately before hitting error, we were in the file Language/Haskell/HsColour/Classify.hs, at line 19. We’re in pretty good shape now. Let’s see where exactly:

     [-1: Language/Haskell/HsColour/Classify.hs:(19,0)-(31,46)] *Main> :list
        18  chunk :: String -> [String]
            vv
        19  chunk []    = head []
        20  chunk ('\r':s) = chunk s -- get rid of DOS newline stuff
        21  chunk ('\n':s) = "\n": chunk s
                                           ^^
    
    0 讨论(0)
  • 2021-02-07 20:03

    Read this. You can use Debug.Trace.trace in place of print statements.

    0 讨论(0)
提交回复
热议问题