Proposal: add HasCallStack for all partial functions in base
Motivation
Partial functions in base (especially Prelude) often cause runtime errors and is hard to locate. My current workaround is define my own wrapper functions with HasCallStack
constraint for some mostly used partial functions.
e.g.
head' :: HasCallStack => [a] -> a
head' = head
fromJust' :: HasCallStack => Maybe a -> a
fromJust' = fromJust
I have been thinking about how should we use the HasCallStack
mechanism to gain a better debugging experience. i.e. How HasCallStack
is supposed to be used by the programmers. And through my programming practice I think add HasCallStack
for partial (non-total) functions could be a good balance for better debugging experience and less runtime overhead.
Proposal
- add
HasCallStack
constraint for all partial functions in base package - suggest all programmers to add
HasCallStack
constraint for their exported partial functions when they release a package - provide a compiler option to toggle off the effect of
HasCallStack
constraint in case somebody need best performance
Other Considerations
There is an issue talking about inferring HasCallStack, and it says:
The downside is that even with
-finfer-hascallstack
you would not get full call-stacks for functions in an imported module that was compiled normally. So this would not help with e.g. partial functions inbase
.
And the downside no more exists when working with this proposal.
With these two proposal work together, we finally get an almost perfect solution for call stack in Haskell: When developing a package, we can use the -finfer-hascallstack
to get very detailed call stack information, and the printed call stack will just end up properly at the importing boundary, so we can know the path how we reach an improper call to a partial function provided by some other imported package, and the detailed call stack inside the imported package will not be printed, which is disturbing (this is much better than other languages' call stack solution). And when the package is released, it will be compiled without -finfer-hascallstack
and only the exported partial functions (which is expected to be marked with the HasCallStack
constraint) will appears in the call stack.