Why are higher rank types so fragile in Haskell

徘徊边缘 提交于 2019-12-03 12:27:09

You have answered your own question:

... by substituting a in const for forall s. ST s a ...

This is the definition of impredictive polymorphism - the ability to instantiate a type variable with a polytype, which is (loosely) a type with a forall quantifier at the leftmost side of the type.

From the GHC trac page on the subject:

GHC does not (yet) support impredicative polymorphism

and furthermore

We've made various attempts to support impredicativity, so there is a flag -XImpredicativeTypes. But it doesn't work, and is absolutely unsupported. If you use it, you are on your own; I make no promises about what will happen.

So do not use ImpredictiveTypes - it won't help.


Now for the gory details - why do all the specific examples work as they do?

You've noted that in the expression Just mempty, the impredictive type Maybe (forall a. Monoid a => a) is not inferred; instead, the forall is 'floated out'. You also noted that performing the same process for uncurry constST gives a type "which is higher rank but not impredicative". The GHC user guide has this to say about higher rank types:

In general, type inference for arbitrary-rank types is undecidable. ...

For a lambda-bound or case-bound variable, x, either the programmer provides an explicit polymorphic type for x, or GHC’s type inference will assume that x’s type has no foralls in it.

So you really have to help it quite a bit, and that usually precludes using higher-order functions alltogether (note the above says nothing about arbitrary applications, only about bound variables - and uncurry constST has no bound variables!). The correct type for Just mempty is rank 1, so there is no issue inferring it, with or without additional type signatures.

For example, you can write your (forall s. (b, ST s a)) -> a function as such (on GHC 8.0.1 at least):

constST' :: forall a b . (forall s . (b, ST s a)) -> a
constST' z = runST z' 
  where z' :: forall s . ST s a
        z' = snd z

and also note that you cannot even pattern match on the pair, because this immediately instantiates the bound b type var:

constST' :: forall a b . (forall s . (b, ST s a)) -> a
constST' (a,b) = _res 

Using typed holes, you get:

* Found hole: _res :: a
  Where: `a' is a rigid type variable bound by
           the type signature for:
             constST' :: forall a b. (forall s. (b, ST s a)) -> a
* In the expression: _res
  In an equation for constST': constST' (a, b) = _res
* Relevant bindings include
    b :: ST s0 a 
    a :: b 

Note the type of b is ST s0 a for some fresh type variable s0, not the required forall s . ST s a for runST. There is no way to get the old type back.


The simplest solution to such things is probably to define a newtype, as the GHC trac page suggests:

newtype RunnableST a = RST (forall s . ST s a)

rrunST :: RunnableST a -> a 
rrunST (RST a) = runST a 

And store your ST actions which are ready to run in this container:

doSomething :: RunnableST X
doSomething = RST $ do ...

To be able to write const runST, you need to active the Impredicative types extension (on GHCi: :set -XImpredicativeTypes).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!