我有一个简单的多态数据类型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)'
中消失了,但我不明白为什么。
为什么这个代码无法编译?有什么方法可以让它正常工作吗?
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)]
... - bheklilrmap Blah [bar, baz]
中的Functor
约束。我认为这是因为Blah
构造函数对类型检查器隐藏了太多的信息,所以它无法确定如何整合Functor
约束。如果镜头类型没有约束,那么它可能会真正起作用。 - bheklilrBlah
中,如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
实例,但没有办法只选择一个,所以它无法通过类型检查。 - bheklilr[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'
也将通过类型检查。 - user2407038