[GHC] #11035: Add implicit call-stacks to partial functions in base

GHC ghc-devs at haskell.org
Thu Oct 29 16:32:28 UTC 2015


#11035: Add implicit call-stacks to partial functions in base
-------------------------------------+-------------------------------------
           Reporter:  gridaphobe     |             Owner:
               Type:  feature        |            Status:  new
  request                            |
           Priority:  normal         |         Milestone:
          Component:  Compiler       |           Version:  7.10.2
           Keywords:                 |  Operating System:  Unknown/Multiple
       Architecture:                 |   Type of failure:  None/Unknown
  Unknown/Multiple                   |
          Test Case:                 |        Blocked By:
           Blocking:                 |   Related Tickets:
Differential Rev(s):                 |         Wiki Page:
-------------------------------------+-------------------------------------
 From
 https://www.reddit.com/r/haskell/comments/3qpefo/a_very_unfortunate_error_message/

 {{{
 ghci> minimumBy compare []
 *** Exception: Prelude.foldr1: empty list
 CallStack:
   error, called at libraries/base/GHC/List.hs:999:3 in
 base-4.8.2.0:GHC.List
 }}}

 This is not a very useful call-stack. It tells us that error was called in
 GHC.List, and if we happen to have the source we can even see where it was
 called.

 https://github.com/ghc/ghc/blob/master/libraries/base/GHC/List.hs#L999

 But that's '''still''' not very helpful because:

 1. It points us to some generic `errorEmptyList` function. This function
 always diverges, so by our current rule it ought to get a CallStack
 constraint. Oops!

 2. Even if we add the CallStack to `errorEmptyList`, the next culprit will
 (presumably) be `foldr1`, where the stack ends again. `foldr1` is partial,
 but it doesn't '''always''' diverge, so our current rule would say it
 shouldn't get a CallStack.

 This is quite unfortunate because the CallStack will point the finger at
 `foldr1`, but the error message itself '''already does that'''. So we
 haven't really gained anything by using the CallStack-aware `error` in
 base.

 What we really want to know is where `foldr1` was called, which just so
 happens to be `minimumBy` itself!

 Ben Gamari pinged me earlier today on IRC with a similar instance in
 GHC.Arr.


 ----

 So, I think we should '''consider''' expanding the use of CallStacks in
 base by one level, to partial functions. By "partial" I specifically mean
 functions that directly call `error` or one of its wrappers (like
 `errorEmptyList`). That means that

 {{{#!haskell
 head [] = error "bad"
 head (x:xs) = x
 }}}

 would get a CallStack, but

 {{{#!haskell
 minimumBy cmp = foldr1 min'
   where min' x y = case cmp x y of
                         GT -> y
                         _  -> x
 }}}

 would '''not''', even though `minimumBy` is also partial in the
 traditional sense.

 I recall three arguments against broader use of CallStacks:

 1. '''Performance concerns''': CallStacks exist at runtime in the form of
 an extra parameter to each CallStack-aware function. This is a valid
 concern and we should certainly do some benchmarking to see what the
 effects are.

 2. '''Readability concerns''': Adding CallStacks will clutter otherwise
 simple type signatures, e.g.

 {{{#!haskell
 head :: [a] -> a
 head :: (?callStack :: CallStack) => [a] -> a
 }}}

     Also a valid concern, especially considering that base functions are
 the first novices will encounter. But I think we can mitigate this one in
 two steps. (1) The `:type` command in ghci already suppresses the
 CallStacks (because it happens to invoke the constraint solver), but
 `:info` shows them. I think this is fine as is. (2) If haddock shows
 CallStacks (I'm not sure if it does), we could patch haddock to render
 them differently. For example, instead of rendering the full type, just
 render

 {{{
 head :: [a] -> a
 }}}

     with a badge that indicates that `head` is location-aware. That would
 reduce the cognitive overhead of the larger type signature, while
 retaining the important data.

 3. '''Slippery slope''': Where do we draw the line? Why should `head` get
 a CallStack but not `minimumBy`? I don't have a good answer to this one
 yet, apart from a suspicion that my proposal will get us a closer to an
 80/20 balance.

 I'm sure this would need to go through the Core Libraries Committee, but
 I'd also like feedback from fellow GHC devs.

--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/11035>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler


More information about the ghc-tickets mailing list