Proposal: add HasCallStack for all partial functions in base
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.
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.
HasCallStackconstraint for all partial functions in base package
- suggest all programmers to add
HasCallStackconstraint for their exported partial functions when they release a package
- provide a compiler option to toggle off the effect of
HasCallStackconstraint in case somebody need best performance
There is an issue talking about inferring HasCallStack, and it says:
The downside is that even with
-finfer-hascallstackyou 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 in
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.