哈斯克尔。匹配模式问题。无法输入带有空列表“print $ note1 []”的函数的 IO 值 - 编译失败

For*_*eld 1 haskell

哈斯克尔。匹配模式问题。无法输入空列表函数的 IO 值

print $ note1 []
Run Code Online (Sandbox Code Playgroud)

无法编译,但在 ghci 中工作正常?!另外,print $ note1 [1]工作正常,编译罚款了。只有空列表的问题:

print $ note1 []
Run Code Online (Sandbox Code Playgroud)

(注意我是 Haskell 的新手)我有一个匹配的模式函数

note1          :: (Show a) => [a] -> String
note1 []       = "Empty"
note1 (x:[])   = "One"
Run Code Online (Sandbox Code Playgroud)

print $ note1 []无法编译,但在 ghci 解释器中完美运行?!

我在 MacOS 上使用 stack 2.3.1 和 ghc 8.8.3。

这是编译器产生的编译错误。

    /Users/admin1/Haskell/PROJECTS/orig1/src/Lib.hs:18:13: error:
    • Ambiguous type variable ‘a0’ arising from a use of ‘note1’
      prevents the constraint ‘(Show a0)’ from being solved.
      Probable fix: use a type annotation to specify what ‘a0’ should be.
      These potential instances exist:
        instance Show Ordering -- Defined in ‘GHC.Show’
        instance Show Integer -- Defined in ‘GHC.Show’
        instance Show a => Show (Maybe a) -- Defined in ‘GHC.Show’
        ...plus 22 others
        ...plus 15 instances involving out-of-scope types
        (use -fprint-potential-instances to see them all)
    • In the second argument of ‘($)’, namely ‘note1 []’
      In a stmt of a 'do' block: print $ note1 []
      In the expression:
        do putStrLn "someFunc"
           putStrLn $ show (1)
           putStrLn $ show $ length ("a" :: String)
           putStrLn $ show (length' "a")
           ....   |
18 |     print $ note1 []
Run Code Online (Sandbox Code Playgroud)

dfe*_*uer 7

问题是(在这种情况下是不必要的)对 的Show a约束note1。这就是发生的事情。当 GHC 进行类型检查时print $ note1 [],它需要确定与哪个 Show实例一起使用note1。这通常是从它传递的列表中元素的类型推断出来的。但是它传递的列表......没有任何元素。所以 typechecker 没有特定的方法来选择一个实例,只是放弃了。这在 GHCi 中起作用的原因是 GHCi 默认启用ExtendedDefaultRules语言扩展,它扩展了类型默认规则。因此,类型检查器不会举手,而是选择()列表元素的类型,一切正常。当您使用时,会发生类似的事情[1]. 在这种情况下,标准的默认规则开始发挥作用:数字类型默认为Integer,因此类型检查器会选择该类型。

你应该如何解决这个问题?你可以手动写

print $ note1 ([] :: [()])
Run Code Online (Sandbox Code Playgroud)

使您的代码编译,但如果那是您的真实代码,则最好删除不必要的约束:

note1          :: [a] -> String
note1 []       = "Empty"
note1 (x:[])   = "One"
Run Code Online (Sandbox Code Playgroud)

附带说明一下,由于您不使用x变量,因此最好通过使用特殊_模式来明确说明这一事实:

note1          :: [a] -> String
note1 []       = "Empty"
note1 (_:[])   = "One"
Run Code Online (Sandbox Code Playgroud)

或用下划线作为变量名的前缀:

note1          :: [a] -> String
note1 []       = "Empty"
note1 (_x:[])   = "One"
Run Code Online (Sandbox Code Playgroud)

这表明,对于其他程序员(例如几个小时后的您自己)和编译器,您故意不使用该值。

此外,您可以(并且可能应该)使用列表语法来阐明第二种模式:

note1 [_] = "One"
Run Code Online (Sandbox Code Playgroud)

最后,该note1函数有一点问题:如果您向它传递一个包含多个元素的列表,它将产生模式匹配失败。哎呀!如果可以,通常最好编写完整的函数。如果不能,通常最好使用显式error调用来指示出了什么问题。我建议使用-Wall标志编译您的代码以帮助捕获错误。