简体   繁体   English

Haskell - “应用程序中的类型错误”:“统一会给出无限类型”

[英]Haskell - “Type error in application”: “unification would give infinite type”

I'm beginning work with Haskell, but this equality check I'm attempting to make isn't working out. 我正在开始使用Haskell,但是我试图制作的这种平等检查并没有成功。

I have a function, countLetter a [b] c , where a is a char, b is a list of chars and c is an int. 我有一个函数countLetter a [b] c ,其中a是char, b是chars列表, c是int。 (The type declarations passed through fine.) But I run into problems with this expression: (类型声明通过罚款。)但是我遇到了这个表达式的问题:

if a == head(b)

Gives me the following message: 给我以下消息:

Type error in application

*** Expression     : a == head b
*** Term           : a
*** Type           : [a]
*** Does not match : a
*** Because        : unification would give infinite type

My code in full, if it's needed, is this: 如果需要,我的代码是完整的:

countLetter :: char -> [char] -> int

countLetter a [b] c = if null b

                       then []
                       else
                       if a == head(b)
                       then countLetter a tail(b) c+1
                    else
                    countLetter head(b) tail(b) c

Any help or advice would be greatly appreciated. 任何帮助或建议将不胜感激。 Thank you. 谢谢。

First of all, types in Haskell start with capital letters. 首先,Haskell中的类型以大写字母开头。 If you use identifiers starting with lower case letters in a type signature, they're interpreted as type variables. 如果在类型签名中使用以小写字母开头的标识符,则将它们解释为类型变量。 So your type char -> [char] -> int is the same as a -> [a] -> b , which is not a sensible type for your function. 所以你的类型char -> [char] -> inta -> [a] -> b ,这对你的函数来说不是一个合理的类型。 Your want Char -> [Char] -> Int . 你想要的Char -> [Char] -> Int

Secondly [] is not a valid value of type Int . 其次, []不是Int类型的有效值。 Fixing your type signature should produce an error message that tells you this in less ambiguous terms. 修复您的类型签名应该会产生一条错误消息,告诉您不那么模糊的术语。

It should then also tell you that null b is a type error because b is of type Char (the second argument of your function is of type [Char] and you've matched it against the pattern [b] , binding b to the single Char contained in that list) and null takes a list, not a Char, as its argument. 然后,它也应该告诉你, null b是一种错误,因为b是类型的Char (你的函数的第二个参数是类型[Char]与你匹配它与模式[b]结合b的单包含在该列表中的Char )和null将列表而不是Char作为其参数。

To clarify the last point a bit: 最后一点澄清:

The second argument to your function is a list. 函数的第二个参数是一个列表。 By writing [b] in your parameter list, you're matching that argument against the pattern [b] . 通过在参数列表中编写[b] ,可以将该参数与模式[b]匹配。 In other words writing countLetter a [b] c = blabla is the same as writing: 换句话说,写countLetter a [b] c = blabla与写入相同:

countLetter a theList c =
    case theList of
        [b] -> blabla

So you're saying: "the second argument to the function must be a list with one single element and that element shall be called b ". 所以你要说:“函数的第二个参数必须是一个包含一个元素的列表,该元素应该被称为b ”。 That is not what you want to be saying. 这不是你想要说的。 What you want to be saying is "the second argument to the function (which, incidentally, is a list) shall be called b ". 你想要说的是“函数的第二个参数(顺便提一下,它是一个列表)应该被称为b ”。 For that you simply writie countLetter abc = blabla . 为此,你只需写入countLetter abc = blabla

Parameters of type list don't have to be denoted any differently than other types of parameters. 类型列表的参数不必以与其他类型的参数不同的方式表示。

Another error you will encounter is this: 您将遇到的另一个错误是:

countLetter head(b) tail(b) c

This is, by the rules, the same as 按照规则,这是相同的

countLetter head b tail b c

ie you call your countLetter function with 5 arguments, when it only takes 3 (according to the first equation) or 2 (according to your type signature). 即你只用3个参数调用你的countLetter函数,当它只需要3个(根据第一个等式)或2个(根据你的类型签名)。 (This is another point the compiler will be very unhappy with.) (这是编译器非常不满意的另一点。)

You probably want this: 你可能想要这个:

countLetter (head b) (tail b) c

Likewise: 同样:

countLetter a tail(b) c+1

is the same as 是相同的

(countLetter a tail b c) + 1

but you probably want: 但你可能想要:

countLetter a (tail b) (c+1)

Aside from the other answers, which provide ways to fix your function, you may want to consider writing this in a more compositional style. 除了提供修复函数的其他答案之外,您可能还需要考虑以更具成分的方式编写它。 Specifically, look for ways you could write a function by building up other, standard functions. 具体来说,通过构建其他标准函数来寻找编写函数的方法。 Suppose you have a function that will remove everything that isn't equal to the test letter from a list. 假设您有一个函数将删除与列表中的测试字母不相等的所有内容。

myFilter :: Char -> [Char] -> [Char
myFilter = ...

After using myFilter , you'd be left with a list with only the element you're checking for. 使用myFilter ,您将只剩下一个列表,其中只包含您要检查的元素。 At this point, you can just use length to get the length of the list: 此时,您可以使用length来获取列表的长度:

countLetter :: Char -> [Char] -> Int
countLetter a b = length $ myFilter a b

So now you just need to define myFilter , which can be done with the standard Prelude function filter 所以现在你只需要定义myFilter ,这可以使用标准的Prelude函数filter来完成

myFilter a b = filter (==a) b

For a function this small, creating our own definition is hardly worth it, as you can just write 对于一个小的函数,创建我们自己的定义几乎不值得,因为你可以写

countLetter a b = length $ filter (== a) b

Now, define this in ghci to see what type it finds for the function countLetter 现在,在ghci中定义它以查看它为函数countLetter找到的类型

Prelude> let countLetter a b = length $ filter (== a) b
Prelude> :t countLetter
countLetter :: Eq a => a -> [a] -> Int

No Char in sight! 看见没有Char The implementation doesn't depend on the elements being letters, only that they can be compared for equality (this is true of your approach also). 实现不依赖于元素是字母,只是可以比较它们的相等性(这也适用于您的方法)。 So ghci reflects that in the calculated type. 所以ghci在计算的类型中反映出来。 But you can see that this is exactly the type you want by substituting Char for a . 但你可以通过用Char代替a来看到这正是你想要的类型。

Many functional programmers tend to find this approach, ie building functions by composing smaller parts, particularly easy to reason about, so it can be quite common. 许多功能程序员倾向于找到这种方法,即通过组合较小的部件来构建功能,特别容易推理,因此它很常见。 Especially when working with lists, you might want to see if you can write an implementation using so-called higher-order functions, such as map , filter , or a fold, instead of using recursion. 特别是在使用列表时,您可能希望查看是否可以使用所谓的高阶函数(例如mapfilter或fold)编写实现,而不是使用递归。 Sometimes recursion is the clearest way, but I expect you will frequently find function composition to be a better approach. 有时递归是最清晰的方式,但我希望你经常会发现函数组合是一种更好的方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM