Haskell与函数类型定义。几个问题。

3

如果我执行any isUpper "asBsd",我将得到True
这里,any的第二个元素是一个字符串。
但是,如果我执行以下操作:

any ("1" `isInfixOf`) ["qas","123","=-0"]

第二个元素是any函数中的字符串列表。
这两个函数之间的差异是如何和为什么产生的?

另一个例子。
如果我写filter isUpper "asdVdf",我会得到"V"
在这里,filter的第二个元素是一个字符串。
但是,如果我写下面的代码:
filter (isUpper . head) ["abc","Vdh","12"],我会得到["Vdh"]
正如您所看到的,filter的第二个元素现在是一个字符串列表。
为什么会有不同,并且Haskell如何知道在这两种情况下都是正确的?

总之:
我不明白在同一个函数中,一次Haskell会获得一个字符串作为第二个元素,而另一次Haskell会获得一个字符串列表作为第二个元素。
一次发生在any函数中,另一次发生在filter函数中。
Haskell(和我)如何知道在这两种情况下都是正确的?

谢谢 :-).


问题确切是什么?类型推断是 Haskell 的一个重要特性,任何字面量甚至参数模式都会触发它。 - Max Shawabkeh
我不理解为什么在同一个函数中,Haskell有一次会得到一个作为第二个参数的字符串,而另一次则会得到一个字符串列表。其中一次出现在any函数中,而另一次则出现在filter函数中。那么,Haskell(以及我)怎么知道在这两种情况下都是正确的呢?我需要解释一下 :) - Azriel
1
这是一个很好的问题,KennyTM的答案非常出色。但是如果您能编辑问题并包含您的澄清,那将会更有帮助。这样,未来任何发现这个问题的人都能更好地理解。 - Paul Johnson
我可以将其更改为其他内容,如果您认为不清楚的话 :-). - Azriel
有一些可能会派上用场的东西。我不知道这是 GHC 的一个特性还是所有 Haskell 实现都可以做到,但你可以输入 :t <something> 来查找某个东西的类型。例如,如果你输入 :t any,你会得到:any :: (a -> Bool) -> [a] -> Bool。因此,它接受一个类型为 a -> Bool 的函数和一个 a 类型的列表,并返回一个 Bool 类型的值。 - Tyler
是的,我知道我可以这样做,但我仍然不明白为什么会有两种不同的情况,相同的函数会得到不同的第二个元素,因此我才会问的 :-)。 - Azriel
3个回答

12

由于isUpper是一个Char -> Bool函数,而"1" ‘isInfixOf‘isUpper . head都是[Char] -> Bool函数。

"1" `isInfixOf` xxx

可以重写为

isInfixOf "1" xxx

我们知道 isInfixOf 的类型是 [a] -> [a] -> Bool1。现在传递给 isInfixOf 的第一个参数是类型为 [Char]"1",因此我们可以推断出 aChar 类型:

     isInfixOf :: [a]    -> [a] -> Bool
       "1"     :: [Char]
//∴ a = Char and
 isInfixOf "1" ::           [a] -> Bool
                =        [Char] -> Bool

这意味着isInfixOf "1"现在是一个[Char] -> Bool函数。

现在,any的类型是(a -> Bool) -> [a] -> Bool函数。与上面类似,

               any :: (a      -> Bool) -> [a] -> Bool
     isInfixOf "1" :: ([Char] -> Bool)
 //∴ a = [Char] and

任意(isInfixOf“1”):: [a] -> Bool = [[Char]] -> Bool

为了满足 any (isInfixOf "1") 的类型限制,参数必须是一个字符串列表。


现在考虑isUpperisUpper 的类型是Char -> Bool。因此:

              any :: (a    -> Bool) -> [a] -> Bool
          isUpper :: (Char -> Bool)
//∴ a = Char and
      any isUpper ::                   [a] -> Bool
                   =                [Char] -> Bool

所以any isUpper需要仅接受一个字符串,而非字符串列表。


最后是isUpper . head。在Haskell中,相关函数的类型为:

 filter :: (a -> Bool) -> [a] -> [a]
   head :: [a] -> a
isUpper :: Char -> Bool
    (.) :: (b -> c) -> (a -> b) -> a -> c

因此对于filter isUppera = Char,类型为[Char] -> [Char],即需要接受一个字符串作为参数。

And2:

            (.) :: (b    -> c   ) -> (a   -> b) -> a -> c
        isUpper :: (Char -> Bool)
           head ::                   ([b] -> b)
//∴ c = Bool, b = Char, a = [b] = [Char], and
 isUpper . head ::                                 a -> c
                =                             [Char] -> Bool
因此对于filter (isUpper . head),我们有a = [Char],类型为[[Char]] -> [[Char]],即它需要接受一个字符串列表作为参数。注意:1. isInfixOf的类型实际上是(Eq a) => [a] -> [a] -> Bool,因为相等性必须对类型a有效,但这在我们的分析中是无关紧要的。2. 我们暂时将变量a更改为b,但这并不重要。

首先,非常感谢您的出色答案和非常详细的解释! 我想我明白了您的意思,但我有一个问题。 在 filter (isUpper . head) 中,a = [Char],由于 filter 的类型签名是 filter :: (a -> Bool) -> [a] -> [a],因此 (a -> Bool) 函数实际上是一个 ([Char] -> Bool) 函数,因此我们应该在 filter 的第二个元素中有 [[Char]],因为如果 a 实际上是 [Char],那么 [a] 实际上比 a 高一级,应该是 [[Char]]。 我说得对吗?非常感谢 :-) - Azriel
还有一件事,我不知道你答案中的 isIndexOfindexOf 是什么意思。我猜你把它和 isInfixOf 混淆了,所以当我看到 indexOf 时,我用 isInfixOf 替换了它以便理解。如果我错了,请告诉我 :-)。 - Azriel
@moshe:是的。而且是,isInfixOf - kennytm

5

字符串实际上只是字符列表,即[Char]。因此,如果你喜欢,“hello”就是['h', 'e', 'l', 'l', 'o']的简写。因此,在这两种情况下,你都得到了某种东西的列表,只是一种情况下是Char列表,另一种情况下是String列表(或者如果你愿意,是Char列表的列表)。


4
好的,这就是多态性。 any 的类型是 (a -> Bool) -> [a] -> Bool,其中类型变量 a 可以是任何你喜欢的类型。在第一个例子中,它是 Char - 因为第二个参数是一组 Char - 并且类型变成了 (Char -> Bool) -> String -> Bool(请记住,[Char]String 相同!); 在第二个例子中,aString,类型变成了 (String -> Bool) -> [String] -> Bool

filter 的推理类似。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接