我正在做一个数学项目,一直需要从类型中提取规范的“基”,因此我定义了下面的类型类。
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
相同?
3条答案
按热度按时间nfg76nw01#
在 这种 情况 下 , 最 简单 的 方法 就是 * 使用 * 某 个 东西 的 基础 , 因为 这样 类型 检查 器 就 可以 推断 出 您 所 指 的 是 哪个
a
:愚蠢 的 例子 :
中 的 每 一 个
否则 , 您 也 可以 使用 型别 注解 ( 需要
ScopedTypeVariables
扩充 , 否则 函 式 主体 内 的a
就 不会 是 签章 中 的a
) :格式
使用
ScopedTypeVariables
+TypeApplications
, 您 还 可以 将 类型 参数a
显 式 传递 给getBasis
:格式
n3ipq98p2#
最初的错误发生的原因可能是代码中没有向编译器指示
theBasis
类型的内容。例如,如果只在show theBasis
这样的表达式中使用它,那么编译器只知道该类型应该有一个Show
示例,但它仍然不知道该类型是什么。但是,您尝试修复的问题更有趣。它揭示了潜在的问题。即使您显式指定类型应为
[a]
,编译器仍然感到困惑。为什么会这样?您说它是[a]
,编译器还需要什么?答案是
(Basis a)
中的a
和:: [a]
中的a不是同一个!在Haskell中,默认情况下,类型变量的作用域仅限于使用它们的类型签名。因此,在
foo
的类型签名中,它是一个a
,对该类型签名是局部的。而在(getBasis n)
的类型签名中,它是一个完全不同的a
,对该类型签名是局部的。但是有一种方法可以告诉编译器它应该是相同的
a
,即forall
:显式使用时,
forall
为其中列出的类型变量创建了一个更大的作用域。对于函数类型签名,作用域是函数的整个主体。因此,:: [a]
中的a
最终与foo
的签名中的a
相同。(note您需要the
ScopedTypeVariables
extension才能正常工作)q9yhzks03#
使用
ScopedTypeVariables
。此扩展允许在函数体中使用类型签名中的类型变量。注意,在某些上下文中,Ia
可以从f
推导出来,因此可以消 debugging 误