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

标签 haskell ghc

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

作为一个最小的例子,考虑类型

data Foo where Foo :: Typeable a  => a -> Foo

如果我想访问 Foo 内部的类型,则以下函数无法编译:

fooType :: Foo -> TypeRep
fooType (Foo x) =
    let (_ :: a) = x
    in typeRep (Proxy::Proxy a)

但是使用这个技巧将类型变量绑定(bind)移动到函数调用,它可以正常工作:

fooType (Foo x) =
    let helper (_ :: a) = typeRep (Proxy::Proxy a)
    in helper x

既然 let 绑定(bind)实际上是变相的函数绑定(bind),为什么上面的两个代码片段不等价呢?

(在此示例中,其他解决方案是使用 typeOf x 创建 TypeRep,或者直接将变量绑定(bind)为 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)

关于haskell - 为什么 Haskell 的作用域类型变量不允许在模式绑定(bind)中绑定(bind)类型变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46077442/

相关文章:

haskell - ghc-7.8 中 `reads` 的歧义错误

multithreading - 我可以限制 Haskell 中每个函数/monad/线程的内存使用吗?

scala - Scala 和 Haskell 中的高级类型

haskell - 是否有惰性 `ByteString` 的参数版本?

haskell - 为什么这个单射类型族实际上不是单射的?

haskell - 编译器优化后如何分析 Haskell?

haskell - Cabal 使用 enable-shared 标志重建所有内容

haskell 如果错误

haskell - 根据 foldr 定义 foldl

haskell - 如何在 Haskell 中构建无限网格状数据结构?