That’s great, thanks. But it doesn’t explain why it is used in the definition of head. Could you expand on why it’s used in head and other functions?
Ah sure, I can answer that question: head is a partial function: which means that for a valid input value (the empty list), it will throw an exception. HasCallStack is a mechanism to produce a stack trace upon exceptions, so that you may get a use site for the exception that is thrown:
ghci> head []
*** Exception: Prelude.head: empty list
CallStack (from HasCallStack):
error, called at libraries/base/GHC/List.hs:1646:3 in base:GHC.List
errorEmptyList, called at libraries/base/GHC/List.hs:85:11 in base:GHC.List
badHead, called at libraries/base/GHC/List.hs:81:28 in base:GHC.List
head, called at <interactive>:1:1 in interactive:Ghci1
It’s a wart. You shouldn’t have to change the type signature to get a proper error message.
It’s also not really an exhaustive mechanism to mark partial functions. A partial function may or may not have it. You can assign it to a total function too.
What’s the alternative? Implicitly add a HasCallStack to every definition?
It’s also not really an exhaustive mechanism to mark partial functions. A partial function may or may not have it. You can assign it to a total function too.
That’s the same with pretty much every Haskell construct isn’t it? A function with a HasCallStack constraint may or may not use it (by throwing an exception). A function may or may not use its argument. A value of type IO a may or may not do IO.
I don’t know. But it seems to me like implementation details leaking. As an end user I don’t really care what magic the compiler conjures to present me with reasonable call stack in the error message.
Why is this offloaded to core libraries?
Yeah, although “maybe partial or not” is really not useful to end users or is it? “This function has access to IO, but may not actually do IO” isn’t that confusing to me.
There was a discussion once to add a Partial constraint to base, following a HasCallStack discussion. So yeah.
The documentation says it is a lightweight method of obtaining a partial call-stack which probably means that it is not zero cost. Can GHC optimize the book-keeping away for functions that do not explicitly use this constraint? If not, I agree with @hasufell that it is pointless to make it explicit in the type signature.
NOTE: The implicit parameter ?callStack :: CallStack is an implementation detail and should not be considered part of the CallStack API, we may decide to change the implementation in the future.
Not sure how much Iike that wording, given how prominently HasCallStack is exposed.
It was specified 35 years ago, drawing on decades of precedent, probably going back all the way to Lisp, when programmers and language designers had bigger things to worry about. It’s debatable whether it would be designed this way if Haskell were started today. For example, Purescript’s version of head has a Partial constraint that can only be dispatched with a use of unsafePartial.
As Tom says, that decision is out of our hands, so we have to make do with the tools we have now: using uncons if List must be the interface type, or using NonEmpty in order to enforce the invariants earlier in the program.
PureScript has this, and we’re probably stuck with it at this point, but I think it was a bad decision.
The problem with encoding partiality as a constraint is that unsafePartial head (where unsafePartial :: (Partial => a) -> a) resolves the constraint to the compiler’s satisfaction without actually reducing the thing that is partial (the intent is that you’d always call unsafePartial (head foo) instead, but mistakes happen and type checkers are for catching them). You’re left with something having the type of a pure function, and you can pass that pure function anywhere else and it may blow up on you there.
And you can say, well, you used something called unsafe somewhere in your code; any subsequent blow-ups are on you. But there’s no way to use Partial-constrained types without using unsafePartial somewhere in your code, so this amounts to always knowing which functions are partial and manually keeping track of when they’re fully reduced—which is exactly what we’d have to do if partiality wasn’t encoded in the type system at all, so what have we gained?