haskell “约束中的非类型变量参数”的真正含义是什么?

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

例如:

map (+1) 2

以ghci产量计

<interactive>:23:1: error:
* Non type-variable argument in the constraint: Num [b]
  (Use FlexibleContexts to permit this)
* When checking the inferred type
    it :: forall b. (Num b, Num [b]) => [b]

我见过许多类似的问题,但似乎都只是回答我们由此可以推断出什么(map的第二个参数的类型是错误的),以及如何修复它--而不是错误实际 * 意味着 * 什么。

km0tfn4u

km0tfn4u1#

错误是在你的陈述式的类型推导过程中出现的。

  1. (+1)的类型为Num a => a -> a
  2. 2的类型为Num a => a
  3. map的类型为(a -> b) -> [a] -> [b]
    我们知道map (+1)的类型必须是(Num b) => [b] -> [b],因此map (+1) 2的类型必须是(Num b, Num [b]) => [b],但是[b]不仅仅是一个类型变量,它是某个类型变量的 List,其中list是一个 * 数据构造函数 *,在Haskell的另一个版本中,没有列表的语法糖存在,我们可以写(Num b, Num (List b))
    这是一个问题,因为默认情况下,Haskell不支持 * 非类型变量参数的约束 *。因此,问题的确切本质不是Haskell不知道如何Map数字-而是它不允许我们的函数调用产生的类型的值。
    但这条规则并不是严格需要的。通过在调用ghci时添加-XFlexibleContexts,我们的方法产生的类型现在被允许了。原因是Haskell中的文字2并不真正表示数字-它表示Num a => a类型的对象。它是用fromIntegral从积分2构造的。所以语句map (+1) 2等价于map (+1) (fromIntegral (2::Integer))。这意味着文字“2”可以表示任何东西,只要给出适当的示例化-包括列表。
weylhg0b

weylhg0b2#

2具有类型Num a => a;我们没有指定a * 是什么 *,只是它必须有一个Num示例。
map (+1)具有类型Num b => [b] -> [b];我们已经指定了b是什么,只是它必须有一个Num示例。
当我们确定map (+1) 2的类型时,我们基本上统一了Num a ~ Num b => [b]

2 ::                  Num a    => a
map (+1) ::    Num b           => [b] -> [b]
map (+1) 2 :: (Num b, Num [b]) =>        [b]

这就是问题所在。Num需要ab之类的类型变量作为其参数,而不是[b]之类的多态类型。

x33g5p2x

x33g5p2x3#

以下是实际答案:

GHC试图自己推断类型,但失败了,或者用GHC的行话来说:

When checking the inferred type …
基本上,冲突是:

· GHC很乐意将任何值作为map (+1)的参数,只要它与map (+1)的类型兼容,而map (+1)需要一个数字列表,因为+需要两个数字,而1已经是一个数字,而map总是需要一个与之兼容的值列表。
·关键在于:**GHC很乐意让2为任何类型!**因为为了保存总是必须指定文字2是否为特定类型的麻烦,GHC只将数字文字视为Integer,并将其替换为fromInteger 2。因此,由于fromInteger来自Num类,2可以是实现类Num的任何内容!因此2也可以是列表!
所以GHC一直在尝试将这些东西组合在一起,并告诉你:

  • “它必须是某个值,也就是一个数字......而这些值的列表也是一个数字!"*

或者在 haskell :

it :: forall b. (Num b, Num [b]) => [b]

但没有示例,使列表数字!
或者用GHC的行话来说:

Non type-variable argument in the constraint: Num [b]

这就是为什么你现在得到这个,是的,肯定是设计糟糕,令人困惑的错误信息.
(几乎所有的GHC错误信息都是一个残酷的笑话。要理解它们,最好的办法就是 * 从下到上 * 阅读它们!了解所有Haskell的类型系统自由和GHC扩展,这些扩展迫使GHC不要做出假设,否则这些错误将成为更简单的错误子类。)

**很明显,**你通常只需要用一个实际的列表来替换2。但是我们在这里不会这样做。既然GHC想要疯狂,GHC就应该疯狂:

愚蠢的决议

因此,既然它给我们留下了制作数字列表的选项,那就是我们要做的:

instance  Num b => Num [b] where
  (+) = zipWith (+)
  (*) = zipWith (*)
  abs = map abs
  signum = map signum
  fromInteger i = [fromInteger i]
  -- or repeat [fromInteger i], if we’re evil. ;)
  negate = map negate

所以现在,它运行得非常好:

>  map (+1) 2
[3]

因为2变成了fromInteger 2 :: Num a => [a],它把2放到一个列表([2])中,然后map (+1)很高兴,把它变成了[2+1],它的求值结果是[3]

**这种可能性就是 * 为什么 * 错误消息不是简单得多

  • “错误:2不是一个列表,但map需要一个列表!"*.**

相关问题