Haskell在类型检查中出现意外的“forall”错误

qq24tv8q  于 2022-11-14  发布在  其他
关注(0)|答案(3)|浏览(138)

我正在做一个数学项目,一直需要从类型中提取规范的“基”,因此我定义了下面的类型类。

class Basis a where
    getBasis :: Int -> [a]

我想在一个单独的函数中使用这个基础:

foo :: (Basis a) => Int -> (a -> b) -> IO ()
foo n f = do
    let theBasis = getBasis n
    undefined
    -- Do some stuff with the basis using the function f.
    -- theBasis should have type [a].

但是,这将无法进行类型检查,从而产生Could not deduce (Basis a0) arising from the use of 'getBasis' from the context: Basis a形式的错误。
我试图通过显式地编写预期的类型来帮助类型检查器:

foo n f = do
    let theBasis = (getBasis n) :: [a]
    undefined

但是错误仍然存在,并且将类型注解移动到该行中的不同术语并不能修复错误。
我如何告诉类型检查器我希望getBasis n的类型与foo签名中出现的a相同?

nfg76nw0

nfg76nw01#

在 这种 情况 下 , 最 简单 的 方法 就是 * 使用 * 某 个 东西 的 基础 , 因为 这样 类型 检查 器 就 可以 推断 出 您 所 指 的 是 哪个 a
愚蠢 的 例子 :

foo :: Basis a => Int -> (a -> b) -> IO ()
foo n f = do
    let theBasis = getBasis n
        _        = map f theBasis
    undefined

中 的 每 一 个
否则 , 您 也 可以 使用 型别 注解 ( 需要 ScopedTypeVariables 扩充 , 否则 函 式 主体 内 的 a 就 不会 是 签章 中 的 a ) :

foo :: forall a b. Basis a => Int -> (a -> b) -> IO ()
foo n f = do
    let theBasis = getBasis n :: [a]
    undefined

格式
使用 ScopedTypeVariables + TypeApplications , 您 还 可以 将 类型 参数 a 显 式 传递 给 getBasis

foo :: forall a b. Basis a => Int -> (a -> b) -> IO ()
foo n f = do
    let theBasis = getBasis @a n
    undefined

格式

n3ipq98p

n3ipq98p2#

最初的错误发生的原因可能是代码中没有向编译器指示theBasis类型的内容。例如,如果只在show theBasis这样的表达式中使用它,那么编译器只知道该类型应该有一个Show示例,但它仍然不知道该类型是什么。
但是,您尝试修复的问题更有趣。它揭示了潜在的问题。即使您显式指定类型应为[a],编译器仍然感到困惑。为什么会这样?您说它是[a],编译器还需要什么?
答案是(Basis a)中的a:: [a]中的a不是同一个!
在Haskell中,默认情况下,类型变量的作用域仅限于使用它们的类型签名。因此,在foo的类型签名中,它是一个a,对该类型签名是局部的。而在(getBasis n)的类型签名中,它是一个完全不同的a,对该类型签名是局部的。
但是有一种方法可以告诉编译器它应该是相同的a,即forall

foo :: forall a b. (Basis a) => Int -> (a -> b) -> IO ()
foo n f = do
    let theBasis = (getBasis n) :: [a]
    undefined

显式使用时,forall为其中列出的类型变量创建了一个更大的作用域。对于函数类型签名,作用域是函数的整个主体。因此,:: [a]中的a最终与foo的签名中的a相同。
(note您需要the ScopedTypeVariables extension才能正常工作)

q9yhzks0

q9yhzks03#

使用ScopedTypeVariables。此扩展允许在函数体中使用类型签名中的类型变量。注意,在某些上下文中,I a可以从f推导出来,因此可以消 debugging 误

{-# LANGUAGE ScopedTypeVariables #-}

class Basis a where
    getBasis :: Int -> [a]

foo :: forall a b. (Basis a) => Int -> (a -> b) -> IO ()
foo n f = do
    let theBasis = getBasis n :: [a]
    undefined
    -- Do some stuff with the basis using the function f.
    -- theBasis should have type [a].

相关问题