Quantcast

Printing call site for partial functions

classic Classic list List threaded Threaded
11 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Printing call site for partial functions

Michael Snoyman
I had a bug in a site of mine[1] for a few weeks, where it would just print:

    Prelude.head: empty list

It took a long time to track down the problem, as it came from some
other library I was depending on. Eventually I tracked it down,
reported it, and the problem was fixed the next day. The weak link in
this chain was identifying what package was to blame.

I noticed the other day that the docs for undefined[2] state:

> It is expected that compilers will recognize this and insert error messages which are more appropriate to the context in which undefined appears.

I'm not sure if the current output of `Prelude.undefined` is meant to
fit the criterion of "appropriate to the context", but in my opinion
it does not. I'd like to propose a bit of a strawman to improve the
situation for not just `undefined`, but any arbitrary partial function
(including `head`). What we really want is to define some function
like:

    headContext :: String -> [a] -> a
    headContext context [] = error $ "Prelude.head: empty list (" ++
context ++ ")"
    headContext _ (x:_) = x

And then have the compiler automatically include (optional) package
name, module name, and line number where `headContext` was called. How
about we borrow a bit from rewrite rules, and have a pragma such as:

    {-# WITH_CONTEXT head headContext #-}

If the compiler supports the feature, and is able to provide context
in the given location, it will call `headContext` with an appropriate
`String`. Otherwise, normal `head` would be called.

I'm sure there are many better ways to approach the problem, and I
can't speak to the complexity of implementation within GHC. I *can*
say, however, that this would have saved me a lot of time in the
example I gave above, and I'd bet many Haskellers have similar
stories. This could be a huge debugging win across the board.

Michael

[1] It was actually yesodweb.com, and resulted in empty Javascript
files being generated, thus disabling some features of the site.
[2] http://hackage.haskell.org/packages/archive/base/4.5.0.0/doc/html/Prelude.html#v:undefined

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Printing call site for partial functions

Evan Laforge
> And then have the compiler automatically include (optional) package
> name, module name, and line number where `headContext` was called. How
> about we borrow a bit from rewrite rules, and have a pragma such as:
>
>    {-# WITH_CONTEXT head headContext #-}

This seems similar to the SRCLOC_ANNOTATE pragma supported by jhc.

I'd love to have this feature.  I'd just like to point out it's not
just useful for partial functions like 'head', but also for logging
and "expected" exceptions like Left.  It's also extremely useful for
tests, when I get "check failed: 3 /= 4" it's really important to see
filename and line number.

There are various hacks out there to get this, but they all involve
annotating the call sites with some magic, either an 'assert' (and
then runtime overhead on every function that uses it) or a TH splice
(and then you have to turn TH on in every module).  I have literally
thousands of lines of tests that would all need an extra boilerplate
argument.

I implemented a preprocessor that is effectively like SRCLOC_ANNOTATE
and it works ok for me, but it's not a good general solution because
it's hardcoded to certain symbols and slows down compilation too much.
 And being specific to my app of course it wouldn't help in your case
:)

But it won't happen unless one of us few people who care about it just
goes and implements it.  I don't think it's much of a priority with
others.

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Printing call site for partial functions

Ozgur Akgun
In reply to this post by Michael Snoyman
Hi,

On 25 April 2012 16:36, Michael Snoyman <[hidden email]> wrote:
   Prelude.head: empty list

Recent versions of GHC actually generate a very helpful stack trace, if the program is compiled with profiling turned on and run with -xc.

See: http://community.haskell.org/~simonmar/slides/HIW11.pdf (Ironically titled "Prelude.head: empty list")

HTH,
Ozgur


_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Printing call site for partial functions

Simon Peyton Jones
In reply to this post by Evan Laforge
Tristan Allwood got quite a long way with this a couple of years ago.
http://research.microsoft.com/en-us/um/people/simonpj/papers/stack-trace/DebugTraces.pdf

But not enough to finish it off! The paper describes the tricky points... Simon M is more of an expert than I.

Moreover the work Simon has done on improving profiling is relevant too.

Simon

| -----Original Message-----
| From: [hidden email] [mailto:haskell-cafe-
| [hidden email]] On Behalf Of Evan Laforge
| Sent: 25 April 2012 17:08
| To: Michael Snoyman
| Cc: Haskell Cafe
| Subject: Re: [Haskell-cafe] Printing call site for partial functions
|
| > And then have the compiler automatically include (optional) package
| > name, module name, and line number where `headContext` was called. How
| > about we borrow a bit from rewrite rules, and have a pragma such as:
| >
| >    {-# WITH_CONTEXT head headContext #-}
|
| This seems similar to the SRCLOC_ANNOTATE pragma supported by jhc.
|
| I'd love to have this feature.  I'd just like to point out it's not just
| useful for partial functions like 'head', but also for logging and "expected"
| exceptions like Left.  It's also extremely useful for tests, when I get
| "check failed: 3 /= 4" it's really important to see filename and line number.
|
| There are various hacks out there to get this, but they all involve
| annotating the call sites with some magic, either an 'assert' (and then
| runtime overhead on every function that uses it) or a TH splice (and then you
| have to turn TH on in every module).  I have literally thousands of lines of
| tests that would all need an extra boilerplate argument.
|
| I implemented a preprocessor that is effectively like SRCLOC_ANNOTATE and it
| works ok for me, but it's not a good general solution because it's hardcoded
| to certain symbols and slows down compilation too much.
|  And being specific to my app of course it wouldn't help in your case
| :)
|
| But it won't happen unless one of us few people who care about it just goes
| and implements it.  I don't think it's much of a priority with others.
|
| _______________________________________________
| Haskell-Cafe mailing list
| [hidden email]
| http://www.haskell.org/mailman/listinfo/haskell-cafe
_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Printing call site for partial functions

Simon Marlow-7
In reply to this post by Ozgur Akgun
On 25/04/2012 17:28, Ozgur Akgun wrote:
 > Hi,
 >
 > On 25 April 2012 16:36, Michael Snoyman <[hidden email]
 > <mailto:[hidden email]>> wrote:
 >
 >         Prelude.head: empty list
 >
 >
 > Recent versions of GHC actually generate a very helpful stack trace, if
 > the program is compiled with profiling turned on and run with -xc.

Right.  Also don't forget to add -fprof-auto.

There's an API to get access to the stack trace too:

http://www.haskell.org/ghc/docs/latest/html/libraries/base-4.5.0.0/GHC-Stack.html

 > See: http://community.haskell.org/~simonmar/slides/HIW11.pdf
 > (Ironically titled "Prelude.head: empty list")

A more recent talk about this with more details is here:

http://community.haskell.org/~simonmar/Stack-traces.pdf


Cheers,
        Simon


_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Printing call site for partial functions

Michael Snoyman
In reply to this post by Ozgur Akgun
On Wed, Apr 25, 2012 at 7:28 PM, Ozgur Akgun <[hidden email]> wrote:

> Hi,
>
> On 25 April 2012 16:36, Michael Snoyman <[hidden email]> wrote:
>>
>>    Prelude.head: empty list
>
>
> Recent versions of GHC actually generate a very helpful stack trace, if the
> program is compiled with profiling turned on and run with -xc.
>
> See: http://community.haskell.org/~simonmar/slides/HIW11.pdf
> (Ironically titled "Prelude.head: empty list")
>
> HTH,
> Ozgur
>

I certainly think that stack traces are going to be a powerful
debugging tool, and I'm looking forward to using them. But I think
this feature would be useful in and of itself. I'm presuming it won't
be the default to have stack traces turned on due to performance
overhead (is that a faulty assumption?). This feature would be
available for all builds and give you a very high amount of useful
information- though not quite as much as a stack trace- without any
(serious) performance impact.

Michael

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Printing call site for partial functions

Joachim Breitner-2
In reply to this post by Michael Snoyman
Hi,

Am Mittwoch, den 25.04.2012, 18:36 +0300 schrieb Michael Snoyman:
> I'm sure there are many better ways to approach the problem, and I
> can't speak to the complexity of implementation within GHC. I *can*
> say, however, that this would have saved me a lot of time in the
> example I gave above, and I'd bet many Haskellers have similar
> stories. This could be a huge debugging win across the board.

using TH (which I only reluctantly advocate for general usage) you can
get good location information behaviour, see
http://hackage.haskell.org/packages/archive/stm-stats/0.2.0.0/doc/html/Control-Concurrent-STM-Stats.html#v:trackThisSTM
(and its source) for one example. One would use this approach maybe with
http://hackage.haskell.org/packages/archive/safe/0.3.3/doc/html/Safe.html#v:headNote

Greetings,
Joachim


--
Joachim Breitner
  e-Mail: [hidden email]
  Homepage: http://www.joachim-breitner.de
  Jabber-ID: [hidden email]

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

signature.asc (205 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Printing call site for partial functions

Michael Snoyman
On Thu, Apr 26, 2012 at 5:05 PM, Joachim Breitner
<[hidden email]> wrote:

> Hi,
>
> Am Mittwoch, den 25.04.2012, 18:36 +0300 schrieb Michael Snoyman:
>> I'm sure there are many better ways to approach the problem, and I
>> can't speak to the complexity of implementation within GHC. I *can*
>> say, however, that this would have saved me a lot of time in the
>> example I gave above, and I'd bet many Haskellers have similar
>> stories. This could be a huge debugging win across the board.
>
> using TH (which I only reluctantly advocate for general usage) you can
> get good location information behaviour, see
> http://hackage.haskell.org/packages/archive/stm-stats/0.2.0.0/doc/html/Control-Concurrent-STM-Stats.html#v:trackThisSTM
> (and its source) for one example. One would use this approach maybe with
> http://hackage.haskell.org/packages/archive/safe/0.3.3/doc/html/Safe.html#v:headNote
>
> Greetings,
> Joachim
>
>
> --
> Joachim Breitner
>  e-Mail: [hidden email]
>  Homepage: http://www.joachim-breitner.de
>  Jabber-ID: [hidden email]
>
> _______________________________________________
> Haskell-Cafe mailing list
> [hidden email]
> http://www.haskell.org/mailman/listinfo/haskell-cafe
>

We actually use a similar technique in Yesod for logging[1]. But it
requires actively using it in all libraries. In other words, unless
the author of the library in question explicitly used $headLoc, this
wouldn't help.

[1] http://hackage.haskell.org/packages/archive/yesod-core/1.0.1.1/doc/html/Yesod-Core.html#v:logDebug

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Printing call site for partial functions

Evan Laforge
In reply to this post by Simon Peyton Jones
On Thu, Apr 26, 2012 at 12:20 AM, Simon Peyton-Jones
<[hidden email]> wrote:
> Tristan Allwood got quite a long way with this a couple of years ago.
> http://research.microsoft.com/en-us/um/people/simonpj/papers/stack-trace/DebugTraces.pdf

While stack traces are undoubtably useful, I think this is a different
problem.  At least the one I'm thinking of is.  It's probably never
going to be reasonable to get a whole stack trace on every call to a
logger, but all that's actually needed is the direct call site.
That's a different (and much simpler!) problem.  And at least in my
case, the logging and exception functions are all pure, so using
currentCallStack under an unsafePerformIO would be sketchy.

On Thu, Apr 26, 2012 at 7:05 AM, Joachim Breitner
<[hidden email]> wrote:
> using TH (which I only reluctantly advocate for general usage) you can
> get good location information behaviour, see

Yeah, this is the TH solution I mentioned.  It requires boilerplate at
every call site and put a TH dependency on every module (which seems
to slow down compilation quite a lot!), but on the other hand it
*does* provide zero overhead caller information.  I still prefer my
hacky preprocessor, even though I don't like it much either.

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Printing call site for partial functions

Ketil Malde-5
In reply to this post by Michael Snoyman
Michael Snoyman <[hidden email]> writes:

> I had a bug in a site of mine[1] for a few weeks, where it would just print:
>
>     Prelude.head: empty list
>
> It took a long time to track down the problem

+1: I've been arguing this for something like ten years :-)

One half-baked quasi-solution is to use:

#define head (\xs -> case xs of { (x:_) -> x ; _ -> error("head: empty list at"++__FILE__++show __LINE__)})

Downsides are that it depends on CPP, and, CPP being a C preprocessor,
it doesn't blend well with lines with single apostrophes on them (e.g.:
head x')

-k
--
If I haven't seen further, it is by standing in the footprints of giants

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Printing call site for partial functions

Henk-Jan van Tuyl
On Mon, 30 Apr 2012 10:31:01 +0200, Ketil Malde <[hidden email]> wrote:

> One half-baked quasi-solution is to use:
>
> #define head (\xs -> case xs of { (x:_) -> x ; _ -> error("head: empty  
> list at"++__FILE__++show __LINE__)})
>
> Downsides are that it depends on CPP, and, CPP being a C preprocessor,
> it doesn't blend well with lines with single apostrophes on them (e.g.:
> head x')
>
> -k

There is a Haskell solution: cpphs[0], you can invoke this preprocessor by  
specifying the flags
   -cpp  -pgmPcpphs  -optP--cpp
for GHC.

Regards,
Henk-Jan van Tuyl


[0] http://hackage.haskell.org/package/cpphs


--
http://Van.Tuyl.eu/
http://members.chello.nl/hjgtuyl/tourdemonad.html
Haskell programming
--

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