(Haskell) Can't understand error : No instance for (Ord a) arising from a use of ‘’

(Haskell) Can't understand error : No instance for (Ord a) arising from a use of ‘’

我正在尝试编写一个函数,该函数接受一个字符串列表(包含要在元组内的列表上执行的操作)和一个包含 2 个 Int 列表的元组作为参数 (testReturn)。 该函数应该对 Int 列表执行操作,检查元组中的第一个列表是否按升序排列,然后 returns 一个布尔值取决于结果。

此代码无法编译,我不确定一些事情。

我的代码:

testReturn :: [[Char]] -> ([a], [b]) -> Bool
testReturn [] b = orderedCheck $ fst b
testReturn (a:as) b
    | a == "sa" = testReturn as (saFunc $ fst b, snd b)

orderedCheck :: (Ord a) => [a] -> Bool
orderedCheck []  = True
orderedCheck [x] = True
orderedCheck (x:y:xs) = x <= y && orderedCheck (y:xs)

saFunc :: [a] -> [a]
saFunc x
    | length x <= 1 = x
saFunc (x:y:xs) = (y:x:xs)

我知道 orderedCheck 和 saFunc 函数有效

我担心的是:

  1. [[Char]] : 不知道这样写对不对,也不确定是不是真正的字符串列表。
  2. orderedCheck $ fst b :这是我遇到 GHC 错误的部分:“(Ord a) 没有因使用‘orderedCheck’而产生的实例”,我环顾四周,我无法理解是什么意思是
  3. 我在别处听说 fst 和 snd 的使用可能有点急躁(是吗?),但我不知道没有它怎么办。

你们能帮我理解我的错误以修复我的代码吗? 谢谢!

  1. [[Char]] : Not sure if writing this is right or not, and not sure if it really means a list of strings.

是的,它确实表示一个字符串列表——即类型[String]。这与 [[Char]] 相同,因为在 Haskell 中,String[Char] 是类型同义词,从字面上看是指代完全相同事物的不同方式。因此,在每个方括号中加上一对方括号(表示该类型的列表)也会导致两个等价类型。

在编写 Haskell 代码时,

String 通常被认为比 [Char] 更地道,但这没什么大不了的。 (即使你在任何地方都使用 String,编译器在报告错误时可能仍然使用 [Char],所以了解这种等价性很重要。)

  1. orderedCheck $ fst b : this is the part where I have a GHC error : "No instance for (Ord a) arising from a use of ‘orderedCheck’ ",I looked around and I can't understand what does it mean.

正如@Ismor 在评论中所说的那样 - 但由于我在回答中有更多 space,我将尝试给出稍微更长、更清晰的解释。

您正在使用值 fst b 调用 orderedCheck。根据你自己的(正确的)类型签名,orderedCheck 的类型为 (Ord a) => [a] -> Bool。这意味着它的参数必须是一个列表,其元素的类型 a 满足约束 Ord a.

但是您在 testReturn 中的 fst b 上使用它。 b 是函数的第二个参数,从它的类型签名来看,它的类型必须是 ([a], [b])。这意味着 fst b 必须是 [a] 类型——这是一个列表,到目前为止还不错。但是正如刚才提到的,那个类型 a 必须有一个 Ord 的实例。不幸的是,我们不能保证这一点,因为您的类型签名 - [[Char]] -> ([a], [b]) -> Bool - 允许 ab 绝对是任何东西。如果此代码已编译,该类型签名将允许我调用该函数,例如 testReturn ["Example"] ([putStrLn "foo"], []) - 但 putStrLn "foo" 具有类型 IO (),它肯定没有 Ord 实例. (除非你在你的代码中提供一个,这听起来不是一个好主意 - 但即使你这样做了,你也可以很容易地找到另一种没有 Ord 实例的类型,这会导致同样的问题。)

解决此问题的唯一方法是修复 testReturn 的类型签名 - 它实际上不能接受任何类型作为 a,因为该类型必须具有 Ord实例。所以包括这个要求,你会没事的:

testReturn :: (Ord a) => [[Char]] -> ([a], [b]) -> Bool

(正如@MarkSeemann 指出的那样,如果您遗漏了任何类型签名,这正是 GHC 会为您推断出的签名。我当然不建议为顶级函数保留类型签名,但如果您如果您不确定签名应该是什么,这种将其省略然后让 GHCi 使用 :t 为您推断的技术几乎可以保证给您想要的东西。)

  1. I heard somewhere else that the use of fst and snd may be a bit edgy (is it?), but I don't know how to do without it.

我不确定在这种情况下“前卫”是什么意思。它当然不是“错误的”,也不像 headtail 用于列表那样危险——当给定一个空列表时,这些函数将崩溃(在运行时),而 fstsnd 适用于任何二元组(如果给定的值不是二元组,您的代码将无法编译 - 因此运行时崩溃的风险为零)。

但是,与模式匹配相比,它不是很优雅,这里更像是其他语言中的“解构”。我认为大多数 Haskell 用户会像这样编写您的函数,这等同于您的代码并且希望不言自明:

testReturn [] (b, _) = orderedCheck b
testReturn (a:as) (b, c)
    | a == "sa" = testReturn as (saFunc b, c)

(请注意,此函数似乎不完整,如果使用第一个元素不是 "sa" 的非空第一个参数调用,将会崩溃。)