使用rank-n类型和镜头键入错误

时间:2015-03-02 18:33:02

标签: haskell lens higher-rank-types

我有一个简单的多态数据类型Foo

{-# LANGUAGE TemplateHaskell #-}
import Control.Lens

data Foo c = 
    Foo {
        _bar :: c,
        _baz :: c,
        _quux :: c
    }   

makeLenses ''Foo

当然,生成的镜片在c中是多态的。 GHCi的类型是:

*Main> :t bar
bar :: Functor f => (c0 -> f c0) -> Foo c0 -> f (Foo c0)

我做了一个数据类型Blah来包裹镜头。这在简单的情况下工作正常(当然,扩展名为RankNTypes):

data Blah = Blah (forall c . Lens' (Foo c) c)

orange :: Blah
orange = Blah bar

但任何稍微复杂的东西都不起作用,例如

cheese :: [Blah]
cheese = map Blah [bar]

最后一段代码给出了GHC的错误:

    Couldn't match type ‘(c0 -> f0 c0) -> Foo c0 -> f0 (Foo c0)’
                  with ‘forall c (f :: * -> *).
                        Functor f =>
                        (c -> f c) -> Foo c -> f (Foo c)’
    Expected type: ((c0 -> f0 c0) -> Foo c0 -> f0 (Foo c0)) -> Blah
      Actual type: (forall c. Lens' (Foo c) c) -> Blah
    In the first argument of ‘map’, namely ‘Blah’
    In the expression: map Blah [bar]

似乎forall c f .已从‘(c0 -> f0 c0) -> Foo c0 -> f0 (Foo c0)’消失,但我不明白为什么。

为什么这不能编译,我该怎么做才能让这样的东西工作?

1 个答案:

答案 0 :(得分:2)

您希望[bar]具有[forall c . Lens' (Foo c) c]类型,但它实际上具有forall f c . Functor f => [(c -> f c) -> Foo c -> f (Foo c)]类型。编译器推断后一种类型的签名,因为它是谓词。您可以在(im)预测类型的技术细节上找到resources。简而言之,类型推断在存在不可预测的类型时是不可判定的 - 因此类型签名成为强制性的 - 因此默认情况下它们是不允许的。不可预测的类型是类型构造函数(如forall)中出现[]的类型。

您可以通过简单地指定类型签名并启用[bar]来强制ImpredicativeTypes拥有前一种类型。同样适用于map Blah - 它也有一个不可预测的类型,因此您还需要手动指定它。

bar' :: [forall c . Lens' (Foo c) c]
bar' = [bar] 

mapBlah :: [forall c . Lens' (Foo c) c] -> [Blah]
mapBlah = map Blah 

然后是以下类型检查:

> mapBlah bar'

甚至

> (map Blah :: [forall c . Lens' (Foo c) c] -> [Blah]) 
    ([bar] :: [forall c . Lens' (Foo c) c])

事实上,处理不可预测类型的问题lens包括模块Control.Lens.Reified,它声明所有常见镜头类型的新类型,以便您可以在容器中安装镜头。这实际上不会在这个特定的用例中帮助你,因为你也希望c中的Lens' (Foo c) c绑定在列表构造函数中,但它通常很有用。