为什么Haskell中更高级别的类型如此脆弱

时间:2016-08-30 06:40:21

标签: haskell type-inference higher-rank-types impredicativetypes

我正在弄乱runST功能。其类型为(forall s. ST s a) -> a,似乎试图以任何方式使用它而不是任何间接直接应用它会以非常恶劣的方式打破它。

runST :: (forall s. ST s a) -> a
const :: a -> b -> a

所以通过在a中用const代替forall s. ST s a,你应该得到const runST的类型

const runST :: b -> (forall s. ST s a) -> a

但GHC表示它不能将a(forall s. ST s a) -> a匹配,但由于a字面意思是forall a. a,所以每个类型都会感到满意{39} ;看看那是什么无效。

事实证明,使用\_ -> runST实际上工作正常,并提供正确的类型。

一旦我constST具有正确的类型,我想看看我是否可以uncurry,但不出所料,这也会打破。但它似乎真的不应该,但不可否认,这种情况似乎不像前一个那么明显:

constST :: b -> (forall s. ST s a) -> a
uncurry :: (a -> b -> c) -> (a, b) -> c

那么肯定a中的uncurry可以替换为b中的constSTb中的uncurry可以替换使用(forall s. ST s a)中的constSTa中的uncurry可以替换为c中的constST。这给了我们:

uncurry constST :: (b, forall s. ST s a) -> a

现在不可否认,这种类型是不可预测的,我知道这是非常有问题的。但技术上Just mempty在直接替换而不移动隐式forall量化时也是不可预测的。

Just :: a -> Maybe a
mempty :: forall a. Monoid a => a
Just mempty :: Maybe (forall a. Monoid a => a)

forall会自动浮出来给你:

Just mempty :: forall a. Monoid a => Maybe a

现在,如果我们为uncurry constST做同样的事情,我们应该明白并且据我所知正确的类型:

uncurry constST :: (forall s. (b, ST s a)) -> a

哪个级别更高但不具有预测性。

有人可以向我解释为什么基本上没有上述内容实际上与GHC 8一起使用,是否有更基本的东西使得上述情况在一般情况下非常复杂?因为如果不是这样的话,如果只是为了$而仅仅为了runST而摆脱forall的烦人的特殊外壳似乎真的很好。

另一方面,我们可以代替所有ImpredicativeTypes浮动而不是让Just mempty正常工作。它正确地将Maybe (forall a. Monoid a => a)的类型推断为MonoLocalBinds但似乎实际上使用它并不容易。我听说impredicative类型推断不是真的可行,但它会以某种方式限制对谓词类型的类型推断,除非你提供类型签名来指示其他情况。类似于If you select a single character on the line, tab/shift tab will do the 默认情况下为了类型推断而使局部绑定单态化。

2 个答案:

答案 0 :(得分:11)

您已回答了自己的问题:

  

...将a中的const替换为forall s. ST s a ...

这是 impredictive polymorphism 的定义 - 使用polytype实例化类型变量的能力,它是(松散地)具有forall量词的类型最左边的那种。

来自主题的GHC trac页:

  

GHC(尚未)支持不可预测的多态性

此外

  

我们已经做了各种尝试来支持不信任,因此有一个标志-XImpredicativeTypes。但它不起作用,绝对不受支持。如果你使用它,你就是独立的;我对将要发生的事情没有任何承诺。

所以不要使用ImpredictiveTypes - 它不会帮助。

现在有了血淋淋的细节 - 为什么所有具体的例子都像他们一样工作?

您已注意到在Just mempty表达式中,不推断出不可预测的类型Maybe (forall a. Monoid a => a);相反,forall已经浮出水面'。您还注意到,对uncurry constST执行相同的过程会给出一个类型"这是一个更高的等级,但不是不可预测的"。 GHC user guide可以说更高级别的类型:

  

通常,任意等级类型的类型推断是不可判定的。 ...

     

对于lambda-bound或case-bound变量x,程序员为x提供显式多态类型,或GHC的类型推断将假设x的类型中没有foralls。

所以你真的必须提供相当多的帮助,这通常会排除使用高阶函数(注意上面说的 nothing 关于任意应用程序,只关于绑定变量 - 和{{1没有绑定变量!)。 uncurry constST的正确类型是等级1,因此无论是否有其他类型签名,都无法推断它。

例如,您可以编写Just mempty函数(至少在GHC 8.0.1上):

(forall s. (b, ST s a)) -> a

并且还要注意,您甚至无法对该对进行模式匹配,因为这会立即实例化绑定的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 类型var:

b

使用输入的孔,你得到:

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

请注意,对于某些新鲜类型变量 * 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 b的类型为ST s0 a,而不是s0所需的forall s . ST s a 。没有办法让旧类型回来。

这类事情的最简单的解决方案可能是定义runST,正如GHC trac页面所暗示的那样:

newtype

并存储准备在此容器中运行的newtype RunnableST a = RST (forall s . ST s a) rrunST :: RunnableST a -> a rrunST (RST a) = runST a 个操作:

ST

答案 1 :(得分:0)

为了能够编写const runST,您需要激活Impredicative类型扩展(在GHCi上::set -XImpredicativeTypes)。