首页 文章

当从当前签名或从另一个函数返回类型获得时,Haskell类型不会被视为相同

提问于
浏览
2

我按照this answer中的建议开始学习Haskell . 所以我只是实现了简单的列表函数,我偶然发现了编译器行为的差异,我无法向自己解释:

-- Impl 1
elementAt :: (Integral b) => [a] -> b -> a
elementAt xs id = xs !! (fromIntegral(id-1))

-- Impl 2
elementAt' :: (Num b) => [a] -> b -> a
elementAt' xs id = xs !! (id-1)

使用以下签名:

fromIntegral :: (Integral a, Num b) => a -> b
(!!) :: [a] -> Int -> a

我只为第二个实现 elementAt' 收到错误 .

Could not deduce (b ~ Int)
from the context (Num b)

如果我理解正确,这意味着运算符(!!)期望 Int 实例作为其第二个参数(从签名中看到),但我们只保证提供的参数符合 Num 类型类(从 elemenAt' 签名推断) ),比 Int 更宽 .

考虑到这一点,我不明白为什么第一个实现实际工作,知道 fromIntegral 也返回一个只符合 Num 类型类的值 .

1 回答

  • 5

    fromIntegral 返回 Num 类的任何实例 . 即,无论你需要什么实例,它都是类型变量的想法,'s basically an extra compile-time argument which the caller of the function gets to choose. That'为什么 elemAt 工作原理:编译器知道我们需要 Int ,所以它告诉 fromIntegral ,然后知道该怎么做 .

    但是,因此,如果您使用签名 Num b => ... 定义一个函数,您还需要允许调用者为 b 选择任何类型的选项,前提是它位于 Num 类中 . 在这种情况下,您无需询问特定实例 Int ,但需要接受调用者给您的任何内容 . 这就是区别 .

    实际上, Num b => [a] -> b -> a 不是可以有效定义此函数的签名 . 你如何用复数,甚至是无限维矩阵或其他什么来索引列表?你能做的是 Integral b => [a] -> b -> a .

相关问题