zer*_*ing 1 haskell compiler-errors
我想使用应用函数并尝试如下:
*ReaderExercise Control.Applicative> (+4) <*> (+3)
Run Code Online (Sandbox Code Playgroud)
然后收到以下错误消息:
<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)
Run Code Online (Sandbox Code Playgroud)
我期望的是一个带有一个参数的返回函数。
无限类型是什么意思?
当 Haskell 确定类型变量(由程序员显式给出或由 Haskell 隐式引入)必须满足一个条件时,会导致错误“发生检查:无法构造 [an] 无限类型”以某种方式会导致无限“深”类型(即,类型变量“出现”在其自己的定义中)。
它通常是由程序员的拼写错误或概念错误引起的,这些错误与混淆程序中的两个不同“结构级别”有关。
作为一个简单的例子,一个整数列表 (type [Int]) 是一个有效的 Haskell 类型,一个整数列表列表 ( [[Int]]) 或一个整数列表列表列表列表( ) 也是一个有效的 Haskell 类型,[[[[[Int]]]]]但只有有限数量的允许列表级别。你不能有一个列表列表的列表列表等等 - 这将是一个无限类型。如果 Haskell 认为您希望它构造这样的类型,它会给您一个“发生检查”错误。
以下定义:
yuck (x:xs) = x == xs
Run Code Online (Sandbox Code Playgroud)
正是出于这个原因给出了这个错误。Haskell 从左侧知道它yuck接受一些未知元素类型的列表,a其中 variablex是 type 的头部,avariablexs是 type 的尾部[a]。从 RHS 中,运算符(==)强制x和xs具有相同的类型——换句话说,它暗示a ~ [a]了波浪号表示“类型相等”的约束。没有有限类型(没有有限数量的列表级别的类型)具有此属性,只有无效的无限类型[[[[...forever...]]]]可以允许您删除外部列表级别并仍然保留相同的类型,因此您会收到错误。
这里的问题是程序员混淆了两个层次的结构:列表xs和元素x。
在您的具体示例中,错误原因类似,但更难解释。运营商:
(<*>) :: (Applicative f) => f (a -> b) -> f a -> f b
Run Code Online (Sandbox Code Playgroud)
采取两个具有不同底层类型的应用操作:左侧的类型由f应用于底层类型的应用函子给出a -> b;右侧的类型由f应用于基础类型的相同应用函子给出b。
你还没有告诉 Haskellf你打算使用哪个应用函子,所以 Haskell 试图推断它。因为 LHS 有类型:
(+4) :: (Num n) => n -> n
Run Code Online (Sandbox Code Playgroud)
Haskell 尝试将类型n -> n与f (a -> b). 这可能是更清晰的写使用的前缀形式,这几种(->)类型的操作:Haskell是尝试匹配(->) n n与f ((->) a b)在那里f是一个适用函子。
幸运的是,有一个适用(->) t于任何类型的应用函子实例t。因此,Haskell 认为您想要的应用函子是f = (->) n,并且它成功地匹配(->) n n = f n到f ((->) a b)。这意味着n等于((->) a b)。哈斯克尔然后再尝试匹配的RHS的类型,匹配(->) n n = f n带(->) n a = f a。这是有效的,它意味着n等于a。
现在我们有一个问题。 n同时等于a -> b(来自 LHS)和a(来自 RHS)。这意味着创建一个无限函数类型,如下所示:
(((... forever ...)->b)->b)->b)->b
Run Code Online (Sandbox Code Playgroud)
这是您可以移除外部...->b并保留相同类型的唯一方法。这是一个不可能的无限类型,所以你会得到错误。
该潜在的问题是,你已经有了一个概念上的错误。鉴于您正在处理一个ReaderExample,我认为您打算使用(->) napplicative functor 实例,因此您和 Haskell 在这一点上是一致的。在这种情况下:
(+4) :: (Num n) -> n -> n
Run Code Online (Sandbox Code Playgroud)
是一个阅读器动作,它从阅读器中读取一个数字并将其加四。同样(+3)是一个读取器操作,它从读取器读取一个数字并将其加三。
但是,(<*>)是一个运算符,它在 LHS 上执行读取器操作,从读取器读取以生成一个函数(不是数字!),然后将其应用于使用 RHS 从读取器读取以生成数字的结果。例如,如果您定义:
multiplyByReader :: (Num n) -> n -> n -> n
multiplyByReader readerNum input = readerNum * input
Run Code Online (Sandbox Code Playgroud)
然后:
multiplyByReader <*> (+4)
Run Code Online (Sandbox Code Playgroud)
或者更简单的版本:
(*) <*> (+4)
Run Code Online (Sandbox Code Playgroud)
会有意义。预期含义是:构造一个读取器操作,该操作(1)使用 LHS 从读取器读取数字以创建乘以读取器的 函数;然后 (2) 将此函数应用于将 RHS 应用于阅读器所产生的数字。
这相当于\r -> r * (r + 4),如您所见:
> ((*) <*> (+4)) 5 -- same a 5 * (5 + 4)
45
>
Run Code Online (Sandbox Code Playgroud)
当您编写 时(+3) <*> (+4),您混淆了两个不同的结构级别:LHS 读取器生成一个数字,但应该生成一个可应用于数字的函数。
我最好的猜测是,您想要创建一个读者操作,(+4)该操作适用于读者以获取数字,然后应用于(+3)该结果。在这种情况下,(+3)不是读者操作;它只是一个你想应用到 reader action 结果的函数(+4),相当于fmapping 过 reader action:
(+3) <$> (+4)
Run Code Online (Sandbox Code Playgroud)
当然,您可以等效地直接将其写为:
(+3) . (+4)
Run Code Online (Sandbox Code Playgroud)
两者都是复合读取器操作,将读取的数字加七:
> ((+3) <$> (+4)) 5
12
> ((+3) . (+4)) 5
12
>
Run Code Online (Sandbox Code Playgroud)