5

I Java I appended this to my print statements and they had a stack trace...

How can we print line numbers to the log in java

public static int getLineNumber() {
    // The second row of the stack trace had the caller file name, etc.
    return Thread.currentThread().getStackTrace()[2];
}

How do I do this is Haskell?

Community
  • 1
  • 1
Michael Lafayette
  • 2,972
  • 3
  • 20
  • 54

3 Answers3

3

One option appears to be the use of a library like loc-th where you can, for example, write an error message with the line information:

{-# LANGUAGE TemplateHaskell #-}
-- app/Main.hs
module Main where

import Debug.Trace.LocationTH

main :: IO ()
main = do
    $failure "Error"
    putStrLn "Hello"

gives me

my-exe: app/Main.hs:10:5-12: Error

It also provides a string which one could look at, in order to determine the line number. However, I'd imagine that's a bit frowned upon, depending on your use-case. For example, I wouldn't want to see this method used to just log line numbers.

There's more on Haskell debugging techniques here.

Honestly, though, maybe this isn't the greatest idea. What are you planning on doing with the line number?

Noon Silk
  • 54,084
  • 6
  • 88
  • 105
  • The way I like to do debugging is I sprinkle these in crucial areas of the code (i.e. controllers that correspond to actions) and I make them print out in such a way that the stack traces are click-able. These clickable stack traces leave a trail that I can go forward or backward through, allowing me to map regions of code to functionality without me having to worry about accidentally skipping a break-point. – Michael Lafayette Feb 08 '16 at 02:43
  • I see. I guess in some sense that's a reasonably interesting way to go about debugging. Maybe it would be nice to have a pluggable thing that can be used on certain applications (I'm guessing websites?) In any case, in that situation I think probably this isn't the best way to go about it. You'll probably want to consider some template haskell to get the line number. See this answer: http://stackoverflow.com/a/13407182/154152 It'd be interesting to see if you can make this a nice tool to aid debugging. I'm not sure how robust all this is under inlining of functions, etc. – Noon Silk Feb 08 '16 at 02:50
  • I've actually made it into a really cute tool, with logging levels and the ability to disable printing of markers when a debug flag is set ( https://github.com/JohnReedLOL/JohnsUtils - https://github.com/JohnReedLOL/JohnsUtils/blob/master/src/info/collaboration_station/utilities/Printer.java ) . I like it a lot better than going through a large code base line by line in a single direction. The IDE requires more than just a line number - it wants a full line from a stack trace (like the same as a java stack trace) so that when you click on it you go to that line. – Michael Lafayette Feb 08 '16 at 03:19
  • 4
    This template-haskell based approach has essentially been obsoleted by [modern ghc features](http://haddock.stackage.org/lts-5.2/base-4.8.2.0/GHC-Stack.html#t:CallStack). – user2407038 Feb 08 '16 at 11:17
  • @user2407038 - I think I found this function called "traceStack" that "prints a call stack if one is available". It would be nice if the "traceStack" function only printed a single one line stack trace and it formatted it same way Java formats its exception stack traces so that it makes a clickable hyperlink in the IntelliJ terminal. It would be nice if these functions also had an "off" switch - like a value that I could set to "false" to turn off all printing to the terminal. – Michael Lafayette Feb 08 '16 at 18:51
  • @NoonSilk - I forgot the second most important thing. Every print statement also contains the name of the thread from whence the statement originated. So if you have one thread called "main", another called "database", and a third called "gui", you can see what they're doing and what function is being called by what thread without needing to make use of fancy multi-threaded debuggers. – Michael Lafayette Feb 08 '16 at 20:11
  • @user2407038 does that work in ghc 7.10? (7.8.4 at least complains about the `?`). Btw, you might want to add it as an answer, since it doesn't require `-prof`, which it seems traceStack does. – unhammer Jul 07 '17 at 07:53
  • 1
    @unhammer It works on every version of GHC still available on the GHC website, going back 9 (!) years. [Here](https://downloads.haskell.org/~ghc/6.10.1/docs/html/users_guide/other-type-extensions.html#implicit-parameters) is the oldest version of the docs I could find which describe it (of course, read the modern version - that link is just for the curious). You need to enable an extension, `-XImplicitParams`, but you can use `CallStack` directly without it. If you feel it warrants an answer, feel free to write the answer of your choosing :) – user2407038 Jul 07 '17 at 18:53
3

I think I found a solution:

Debug.Trace: Functions for tracing and monitoring execution.

traceStack :: String -> a -> a Source

like trace, but additionally prints a call stack if one is available.

In the current GHC implementation, the call stack is only availble if the program was compiled with -prof; otherwise traceStack behaves exactly like trace. Entries in the call stack correspond to SCC annotations, so it is a good idea to use -fprof-auto or -fprof-auto-calls to add SCC annotations automatically.

Since: 4.5.0.0

^ https://hackage.haskell.org/package/base-4.8.2.0/docs/Debug-Trace.html

Michael Lafayette
  • 2,972
  • 3
  • 20
  • 54
2

As noted in @user2407038's comment, modern GHC makes a CallStack available, see docs at https://www.stackage.org/haddock/lts-17.13/base-4.14.1.0/GHC-Stack.html#t:HasCallStack

Print the callstack like this:

import           GHC.Stack

msgStacktraced :: HasCallStack => String -> IO ()
msgStacktraced msg = putStrLn (msg ++ "\n" ++ prettyCallStack callStack)

You'll need that HasCallStack constraint on anything that can call msgStacktraced as well, or it'll be hidden from the call stack.

unhammer
  • 4,306
  • 2
  • 39
  • 52