The GHC user's guide describes the impredicative polymorphism extension with reference to the following example:
f :: Maybe (forall a. [a] -> [a]) -> Maybe ([Int], [Char])
f (Just g) = Just (g [3], g "hello")
f Nothing = Nothing
However, when I define this example in a file and try to call it, I get a type error:
ghci> f (Just reverse)
<interactive>:8:9:
Couldn't match expected type `forall a. [a] -> [a]'
with actual type `[a0] -> [a0]'
In the first argument of `Just', namely `reverse'
In the first argument of `f', namely `(Just reverse)'
In the expression: f (Just reverse)
ghci> f (Just id)
<interactive>:9:9:
Couldn't match expected type `forall a. [a] -> [a]'
with actual type `a0 -> a0'
In the first argument of `Just', namely `id'
In the first argument of `f', namely `(Just id)'
In the expression: f (Just id)
Seemingly only undefined, Nothing, or Just undefined satisfies the type-checker.
I have two questions, therefore:
- Can the above function be called with
Just ffor any non-bottomf? - Can someone provide an example of a value only definable with impredicative polymorphism, and usable in a non-trivial way?
The latter is particularly with the HaskellWiki page on Impredicative Polymorphism in mind, which currently makes a decidedly unconvincing case for the existence of the extension.
Here's an example of how one project, const-math-ghc-plugin, uses
ImpredicativeTypesto specify a list of matching rules.The idea is that when we have an expression of the form
App (PrimOp nameStr) (Lit litVal), we want to look up the appropriate rule based upon the primop name. AlitValwill be either aMachFloat dorMachDouble d(dis aRational). If we find a rule, we want to apply the function for that rule todconverted to the correct type.The function
mkUnaryCollapseIEEEdoes this for unary functions.The first argument needs to have a Rank-2 type, because it will be instantiated at either
FloatorDoubledepending on the literal constructor. The list of rules looks like this:This is ok, if a bit too much boilerplate for my taste.
However, there's a similar function
mkUnaryCollapsePrimIEEE. In this case, the rules are different for different GHC versions. If we want to support multiple GHCs, it gets a bit tricky. If we took the same approach, thesubsdefinition would require a lot of CPP, which can be unmaintainable. Instead, we defined the rules in a separate file for each GHC version. However,mkUnaryCollapsePrimIEEEisn't available in those modules due to circular import issues. We could probably re-structure the modules to make it work, but instead we defined the rulesets as:By using
ImpredicativeTypes, we can keep a list of Rank-2 functions, ready to use for the first argument tomkUnaryCollapsePrimIEEE. The alternatives would be much more CPP/boilerplate, changing the module structure (or circular imports), or a lot of code duplication. None of which I would like.I do seem to recall GHC HQ indicating that they would like to drop support for the extension, but perhaps they've reconsidered. It is quite useful at times.