【问题标题】:Type error with rank-n types and lensesrank-n 类型和镜头的类型错误
【发布时间】:2015-03-03 11:49:58
【问题描述】:

我有一个简单的多态数据类型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)’ 中消失了,但我不明白为什么。

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

【问题讨论】:

  • 好吧,我不确定我能否给出足够正确的解释来解释为什么需要完整的答案,但这似乎是Blah 的范围界定问题。如果您将forall c. 移出构造函数为data Blah = forall c. Blah (Lens' (Foo c) c),则map Blah 将进行类型检查,但map Blah [bar] 不会。如果您扩展Lens' 类型并手动执行data Blah = forall c f. Functor f => Blah ((c -> f c) -> Foo c -> f (Foo c)),那么您将:t map Blah 作为Functor f => [(c -> f c) -> Foo c -> f (Foo c)] -> [Blah]:t [bar, baz] 作为[(c -> f c) -> Foo c -> f (Foo c)]...
  • ... 但它无法统一map Blah [bar, baz] 中的Functor 约束。我相信这是因为Blah 构造函数对类型检查器隐藏了太多(本质上),所以它无法弄清楚如何合并Functor 约束。如果镜头类型不受限制,那么它实际上可能会起作用。
  • 我能得到任何类型检查的唯一方法是将类型参数添加到Blahdata Blah f c where Blah :: Functor f => ((c -> f c) -> Foo c -> f (Foo c)) -> Blah f c,然后您可以执行map Blah [bar, baz] 并拥有Functor f => [Blah f c] 类型,但是这个并不意味着没有办法隐藏这些类型变量。我认为这里重要的是f,因为Functor 约束。编译器必须为Functor 选择一个实例,但无法只选择一个,因此类型检查失败。
  • 您希望[bar] 具有[forall c . Lens' (Foo c) c] 类型,但它实际上具有Functor f => [(c0 -> f c0) -> Foo c0 -> f (Foo c0)] 类型。您可以强制[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' 也是如此。
  • 顺便说一句,我不建议使用不可预测类型 - 您必须为每个不可预测类型编写类型签名,它不会给您太多权力。

标签: haskell haskell-lens higher-rank-types


【解决方案1】:

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

您可以强制[bar] 使用前一种类型,只需指定该类型签名并启用ImpredicativeTypesmap 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,它为所有常见的镜头类型声明了新类型,以便您可以在容器中拥有镜头。在这个特定的用例中,这实际上对您没有帮助,因为您还希望将Lens' (Foo c) c 中的c 绑定在列表构造函数中,但它通常很有用。

【讨论】:

  • 这是“不可预测的”——而不是“不可预测的”。从“谓词”一词。
  • 为了迂腐,伤害推理的不是不可预测性(理论上),而是更高级别的类型。 rank-2 类型的推断是不切实际的,对于 rank-3 及以上类型是不可判定的。在实践中,不可预测性破坏了 Haskell 中的类型推断,因为它的集成很差。还有其他类型系统可以更有机地处理禁言类型,例如。 G。 HMF、HML 或 MLF。
猜你喜欢
  • 1970-01-01
  • 2012-01-10
  • 2018-06-05
  • 1970-01-01
  • 1970-01-01
  • 2016-05-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多