不能构造无限型

时间:2017-09-12 09:27:20

标签: haskell compiler-errors

我想使用应用功能并尝试如下:

*ReaderExercise Control.Applicative> (+4) <*> (+3)

然后收到以下错误消息:

<interactive>:51:11: error:
    * Occurs check: cannot construct the infinite type: a ~ a -> b
      Expected type: (a -> b) -> a
        Actual type: a -> a
    * In the second argument of `(<*>)', namely `(+ 3)'
      In the expression: (+ 4) <*> (+ 3)
      In an equation for `it': it = (+ 4) <*> (+ 3)
    * Relevant bindings include
        it :: (a -> b) -> b (bound at <interactive>:51:1)

我期望的是带有一个参数的返回函数 什么意思是无限类型?

1 个答案:

答案 0 :(得分:3)

错误&#34;发生检查:无法构造[an]无限类型&#34;当Haskell确定一个类型变量(由程序员明确给出或由Haskell隐式引入)必须满足一个条件时,它必须以一种导致无限的方式递归地定义它自身的条件。深&#34; type(即类型变量&#34;出现&#34;在其自己的定义中)。

它通常是由于编程人员的拼写错误或概念错误导致混淆了两个不同的结构级别&#34;在一个程序中。

作为一个简单的例子,一个int列表(类型[Int])是一个有效的Haskell类型,因此是一个int列表([[Int]])或列表列表整数列表([[[[[Int]]]]]),但只允许有限数量的列表级别。您无法获得列表列表列表的列表,一直向下 - 这将是无限类型。如果Haskell认为你想要它构建这样一种类型,它会给你一个&#34;发生检查&#34;错误。

以下定义:

yuck (x:xs) = x == xs

出于这个原因给出了这个错误。 Haskell从左侧知道yuck获取了一些未知元素类型a的列表,其中变量x是类型a的头部和变量xs }是[a]类型的尾部。在RHS中,运算符(==)强制xxs具有相同的类型 - 换句话说,它意味着约束a ~ [a],其中代字号表示&#34 ;类型相等&#34;。没有有限类型(没有具有有限数量的列表级别的类型)具有此属性,只有无效的无限类型[[[[...forever...]]]]可以允许您删除外部列表级别并且仍然具有相同的类型,因此您可以获得错误。

这里的问题是程序员混淆了两个级别的结构:列表xs和元素x

在您的具体示例中,错误的原因类似,但难以解释。接线员:

(<*>) :: (Applicative f) => f (a -> b) -> f a -> f b

采用不同基础类型的两个应用操作:左侧具有应用于基础类型f的应用仿函数a -> b给出的类型;右侧具有应用于基础类型f的相同应用程序仿函数b给出的类型。

你还没有告诉Haskell你打算使用哪个适用的仿函数f,所以Haskell试图推断它。因为LHS的类型为:

(+4) :: (Num n) => n -> n

Haskell尝试将n -> n类型与f (a -> b)匹配。使用(->)类型运算符的前缀形式编写这些类型可能更清楚:Haskell正在尝试将(->) n nf ((->) a b)匹配,其中f是一个应用函数。< / p>

幸运的是,(->) t适用于任何类型t的应用程序仿函数实例。所以,Haskell认为你想要的应用函子是f = (->) n,它成功地将(->) n n = f nf ((->) a b)匹配。这意味着n等于((->) a b)。然后Haskell尝试匹配RHS上的类型,将(->) n n = f n(->) n a = f a匹配。这有效,这意味着n等于a

现在我们遇到了问题。 n同时等于a -> b(来自LHS)和a(来自RHS)。这意味着创建一个无限函数类型,类似于:

(((... forever ...)->b)->b)->b)->b

这是唯一可以移除外部...->b并保留相同类型的方法。这是一个不可能的无限类型,所以你得到错误。

底层问题是您发生了概念错误。鉴于您正在使用ReaderExample,我认为您打算使用(->) n applicative functor实例,因此您和Haskell就这一点达成一致。在这方面:

(+4) :: (Num n) -> n -> n

是一个读取器操作,它从读取器读取一个数字并向其中添加四个。类似地,(+3)是一个读取器操作,它从读取器读取一个数字并向其中添加三个。

然而,(<*>)是一个操作员,它对LHS进行读取操作,从读取器读取以生成函数(不是数字!)然后应用于结果使用RHS从阅读器读取产生数字。例如,如果您定义:

multiplyByReader :: (Num n) -> n -> n -> n
multiplyByReader readerNum input = readerNum * input

然后:

multiplyByReader <*> (+4)

或更简单的版本:

(*) <*> (+4)

会有意义。预期的含义是:构造一个读者动作,(1)使用LHS从读者读取一个数字,以创建一个与读者相乘的函数;然后(2)将此函数应用于将RHS应用于读者所产生的数字。

这相当于\r -> r * (r + 4),您可以看到:

> ((*) <*> (+4)) 5   -- same a 5 * (5 + 4)
45
>

当您编写(+3) <*> (+4)时,您将两个不同的结构级别混合在一起:LHS阅读器产生一个数字,但应该产生一个可以应用于数字的函数

我最好的猜测是,您要创建一个阅读器操作,将(+4)应用于阅读器以获取数字,然后将(+3)应用于该结果。在这种情况下,(+3)不是读者行动;它只是您想要应用于阅读器操作(+4)结果的功能,相当于fmap ping阅读器操作:

(+3) <$> (+4)

当然,您可以直接将其直接写为:

(+3) . (+4)

两者都是复合阅读器动作,它们为读取的数字增加了七个:

> ((+3) <$> (+4)) 5
12
> ((+3) . (+4)) 5
12
>