【发布时间】:2017-09-06 14:09:07
【问题描述】:
我注意到 GHC 的 ScopedTypeVariables 能够在函数模式中绑定类型变量,但不能在 let 模式中。
作为一个最小的例子,考虑类型
data Foo where Foo :: Typeable a => a -> Foo
如果我想访问 Foo 中的类型,以下函数无法编译:
fooType :: Foo -> TypeRep
fooType (Foo x) =
let (_ :: a) = x
in typeRep (Proxy::Proxy a)
但是使用这个技巧将类型变量绑定移动到函数调用,它可以正常工作:
fooType (Foo x) =
let helper (_ :: a) = typeRep (Proxy::Proxy a)
in helper x
既然let绑定实际上是变相的函数绑定,为什么上面两个代码sn-ps不等价呢?
(在本例中,其他解决方案是使用typeOf x 创建TypeRep,或在顶级函数中直接将变量绑定为x :: a。这些选项在我的真实代码中均不可用,并且使用它们并不能回答问题。)
【问题讨论】:
-
您根本不需要显式引入任何类型变量:
fooType (Foo x) = typeRep [x]也可以使用,因为typeRep的proxy可以是任何函子:不一定是微不足道的Proxy,也可以是实际包含a值的东西! -
@leftaroundabout 如果我们要摆脱类型变量,您可以更进一步,只需说
fooType (Foo x) = typeOf x。我继续假设真正的用例是访问类型变量是重要部分。 -
@Carl 是的,但可能并不为人所知的是,带有
proxy x参数的 any 函数也可以使用包含实际x值的具体容器来调用。 -
确实,我意识到在这种情况下我可以使用
typeOf或其他解决方案(请参阅括号中问题的最后一段),但在我的真实代码中不可用。我需要通过将类型变量放在类型引号中来获得模板 HaskellType。我也不能直接在顶层函数中绑定a,因为我要获取的类型实际上是隐藏类型的函数依赖。辅助函数解决方案工作正常,我只是不明白为什么。 -
let不能这样做,因为它使用惰性模式匹配,并且不能将类型类字典带入范围。这是stackoverflow.com/a/23540431/3234959的同一个问题