Haskell类型错误 - 使用“打印”引起的模糊类型变量

时间:2018-04-16 18:20:41

标签: debugging haskell

所以我想写一些Haskell,我遇到了这个让我想把头撞在墙上的问题。

printGrade points = case points of            
    points | 0 <= points && points < 50 -> 5.0
    points | 50 <= points && points < 54 -> 4.0
    points | 54 <= points && points < 58 -> 3.7      
    points | 58 <= points && points < 62 -> 3.3
    points | 62 <= points && points < 66 -> 3.0
    points | 66 <= points && points < 70 -> 2.7
    points | 70 <= points && points < 74 -> 2.3
    points | 74 <= points && points < 78 -> 2.0
    points | 78 <= points && points < 82 -> 1.7
    points | 82 <= points && points < 86 -> 1.3
    points | 86 <= points && points < 100 -> 1.0

note a b c d = 
            if d > 100 || c > 20
            then return "Wrong input"
            else if a == False || b == False 
            then printGrade d
            else printGrade (c + d) 

当我尝试运行代码时,它编译没有问题,但实际调用该函数会带来此错误

<interactive>:91:1: error:
• Ambiguous type variable ‘m0’ arising from a use of ‘print’
  prevents the constraint ‘(Show (m0 [Char]))’ from being solved.
  Probable fix: use a type annotation to specify what ‘m0’ should be.
  These potential instances exist:
    instance Show a => Show (Maybe a) -- Defined in ‘GHC.Show’
    instance (Show a, Show b) => Show (a, b) -- Defined in ‘GHC.Show’
    instance (Show a, Show b, Show c) => Show (a, b, c)
      -- Defined in ‘GHC.Show’
    ...plus 13 others
    ...plus two instances involving out-of-scope types
    (use -fprint-potential-instances to see them all)
• In a stmt of an interactive GHCi command: print it

我知道它与“返回错误的输入”有关,但我不知道解决这个问题的方法,因为我必须在某个时候打印出一个字符串。 (我尝试过show / print / putStrLn导致另一个错误)

1 个答案:

答案 0 :(得分:5)

通常,以Haskell开头的人使用return :: Monad m => a -> m a函数,因为在命令式世界中,几乎所有[命令式]编程语言都为return 关键字指定了几乎相同的语义< / em>的。但是在Haskell中,return函数(不是关键字),它在 monads 的上下文中使用。虽然monad非常强大,但我建议首先使用return来看看这个结构在之前是如何工作的。一个提示:它并不像在命令式世界中那样有效。

我们可以删除return功能,例如:

note a b c d = 
    if d > 100 || c > 20
        then "Wrong input"
        else if a == False || b == False 
            then printGrade d
            else printGrade (c + d)

然后我们得到一个新错误:

<interactive>:20:18: error:
    • Could not deduce (Fractional [Char])
        arising from a use of ‘printGrade’
      from the context: (Num a, Ord a)
        bound by the inferred type of
                 note :: (Num a, Ord a) => Bool -> Bool -> a -> a -> [Char]
        at <interactive>:(16,1)-(21,35)
    • In the expression: printGrade d
      In the expression:
        if a == False || b == False then
            printGrade d
        else
            printGrade (c + d)
      In the expression:
        if d > 100 || c > 20 then
            "Wrong input"
        else
            if a == False || b == False then
                printGrade d
            else
                printGrade (c + d)

现在Haskell遇到了返回类型的问题。确实printGrade会返回1.0之类的值,这是Fractional类型(后面可以指定的类型,但文字表明它应该是Fractional类型)。并且它表示您也返回一个字符串("Wrong input"),并且由于String不是Fractional类型,因此存在不匹配。我们可以通过在show的结果上调用printGrade来解决这个问题(我建议你重命名该函数,因为函数打印任何东西),这样我们就可以转换{ {1}}输入Fractional,现在我们得到:

String

现在程序将编译,但它相当不优雅。例如,您在note a b c d = if d > 100 || c > 20 then "Wrong input" else if a == False || b == False then show (printGrade d) else show (printGrade (c + d))函数中使用case,但实际上并未执行任何模式匹配。我们可以使用 guards ,例如:

printGrade

因此,我们在每个案例中使用一个警卫,而且不需要检查该值是否例如小于grade :: (Num a, Ord a, Fractional b) => a -> b grade points | points < 0 = error "too small" | points < 50 = 5.0 | points < 54 = 4.0 | points < 58 = 3.7 | points < 62 = 3.3 | points < 66 = 3.0 | points < 70 = 2.7 | points < 74 = 2.3 | points < 78 = 2.0 | points < 82 = 1.7 | points < 86 = 1.3 | points < 100 = 1.0 | otherwise = error "too large",因为在这种情况下,前一个警卫会被解雇。

我们可以对0函数使用相同的技术:使用模式和保护来匹配值:

note