【发布时间】: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约束。如果镜头类型不受限制,那么它实际上可能会起作用。 -
我能得到任何类型检查的唯一方法是将类型参数添加到
Blah为data 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