Is there a way to simulate empty constraints with GHC < 7.8.1? Something
similar to
{-# LANGUAGE NullaryTypeClasses #-}
class HasCallStack
instance HasCallStack
so that e.g.
foo :: HasCallStack => Int -> Int
is technically the same as
foo :: Int -> Int
GHC 8.0.1 introduces HasCallStack as:
HasCallStack = (?callStack :: CallStack)
For a compatibility shim I want to define HasCallStack for all versions of
GHC back to 7.0.1.
For versions of GHC that have support for implicit parameter based call stacks,
HasCallStack should be functionally equivalent to what we have in GHC 8.0.1.
For versions of GHC that lack support for implicit parameter based call stacks
(that is all versions of GHC < 7.10.2), HasCallStack should be functionally
equivalent to the empty constraint.
For completeness, here is the code that we could use if we only care about GHC
7.8.1 and later:
#if MIN_VERSION_base(4,8,1)
type HasCallStack = (?callStack :: CallStack)
#else
class HasCallStack
instance HasCallStack
#endif
Is there a way to make this work for older versions of GHC?
For reference, I'll add my current solution as an answer. But I'd love to get input on other ways to tackle this.
I don't know of any way to support the exact same syntax we have in GHC
8.0.1. However, it's possible to optionally add a constraint by using a type
synonym and Rank2Types. The following code works for all versions of GHC
back to 7.0.1.
#if MIN_VERSION_base(4,8,1)
type HasCallStack a = (?callStack :: CallStack) => a
#else
type HasCallStack a = a
#endif
The example from your question
foo :: HasCallStack => Int -> Int
becomes
foo :: HasCallStack(Int -> Int)
Note that this is quite robust. I haven't encountered any situation where this would not work, it e.g. even works when foo has other constraints or when foo is a class method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With