Skip to content

GitLab

  • Projects
  • Groups
  • Snippets
  • Help
    • Loading...
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
  • Sign in / Register
GHC
GHC
  • Project overview
    • Project overview
    • Details
    • Activity
    • Releases
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
    • Locked Files
  • Issues 4,322
    • Issues 4,322
    • List
    • Boards
    • Labels
    • Service Desk
    • Milestones
    • Iterations
  • Merge Requests 368
    • Merge Requests 368
  • Requirements
    • Requirements
    • List
  • CI / CD
    • CI / CD
    • Pipelines
    • Jobs
    • Schedules
  • Security & Compliance
    • Security & Compliance
    • Dependency List
    • License Compliance
  • Operations
    • Operations
    • Incidents
    • Environments
  • Analytics
    • Analytics
    • CI / CD
    • Code Review
    • Insights
    • Issue
    • Repository
    • Value Stream
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Members
    • Members
  • Collapse sidebar
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
  • Glasgow Haskell Compiler
  • GHCGHC
  • Issues
  • #11035

Closed
Open
Opened Oct 29, 2015 by Eric Seidel@gridaphobeReporter

Add implicit call-stacks to partial functions in base

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

head [] = error "bad"
head (x:xs) = x

would get a CallStack, but

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.
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.

  1. 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.

Trac metadata
Trac field Value
Version 7.10.2
Type FeatureRequest
TypeOfFailure OtherFailure
Priority normal
Resolution Unresolved
Component Compiler
Test case
Differential revisions
BlockedBy
Related
Blocking
CC bgamari
Operating system
Architecture
Assignee
Assign to
None
Milestone
None
Assign milestone
Time tracking
None
Due date
None
Reference: ghc/ghc#11035