为什么 Haskell 的作用域类型变量不允许在模式绑定中绑定类型变量?



我注意到GHC的ScopedTypeVariables能够绑定函数模式中的类型变量,但不能让模式。

作为最小示例,请考虑类型

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绑定实际上是伪装的函数绑定,为什么上述两个代码段不等效?

(在此示例中,其他解决方案是用typeOf x创建TypeRep,或在顶级函数中直接绑定变量为x :: a。这些选项都不可用回答问题。)

最大的是,函数是伪装中的 case表达式,而不是 let表达式。case匹配和let匹配具有不同的语义。这也是为什么您无法匹配在let表达式中键入改进的GADT构造函数。

区别在于,case在继续之前评估了审查者,而let匹配匹配的thunk在堆上,上面写着"在需要结果时进行此评估"。GHC不知道如何在所有潜在的懒惰与它们相互作用的方式中保留本地分类的类型(例如示例中的a),因此它不会尝试。如果涉及本地分类类型,请使用case表达式,以使懒惰不会成为问题。

至于您的代码,ScopedTypeVariables实际上为您提供了更简洁的选项:

{-# Language ScopedTypeVariables, GADTs #-}
import Data.Typeable
import Data.Proxy
data Foo where
    Foo :: Typeable a => a -> Foo
fooType :: Foo -> TypeRep
fooType (Foo (x :: a)) = typeRep (Proxy :: Proxy a)

最新更新