【问题标题】:Haskell Type Error - Ambiguous Type Variable Arising from Use of `Print`Haskell 类型错误 - 使用“打印”引起的类型变量不明确
【发布时间】:2018-04-16 18:20:41
【问题描述】:

所以我正在尝试写一点 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 导致另一个错误)

【问题讨论】:

  • 你为什么在这里使用case,而只使用警卫?
  • 哈哈哈我不知道当我删除案例时它根本不起作用
  • 不幸的是,这个错误经常出现。 return 不是 Haskell 中的 关键字:它是用于 monad 的函数。因此,我建议您不要使用它,除非您知道自己在做什么。
  • 任何我可以使用而不是 return 来打印出字符串而不会导致此错误?
  • 你应该从它们的类型开始编写这些函数。如果返回类型是Double,则不能返回字符串(当然也不能在像return "abc" 这样的monad 中返回字符串)。如果你想出错,你可以使用if ... then 2.71 else error "message",但要明白这个错误会在评估时使程序崩溃,调用者将无法捕捉到错误并处理它。有时这已经足够了。表达式error ".." 适合任何类型。

标签: debugging haskell


【解决方案1】:

通常,从 Haskell 开始的人会使用 return :: Monad m =&gt; a -&gt; m a 函数,因为在命令式世界中,几乎所有 [命令式] 编程语言都为 return 分配了几乎相同的语义关键字。然而,在 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类型,所以不匹配。我们可以通过在printGrade 的结果上调用show 来解决这个问题(我建议你重命名该函数,因为该函数不 打印任何内容),这样我们将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))

现在程序将编译,但它相当不优雅。例如,您在printGrade 函数中使用了case,但实际上并没有执行任何模式匹配。我们可以使用 guards 代替,例如:

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 函数使用相同的技术:使用模式和守卫来匹配值:

note :: (Ord a, Num a) => Bool -> Bool -> a -> a -> String
note _ _ c d | d > 100 || c > 20 = "Wrong input"
note False False _ d = show (printGrade d)
note _ _ c d = show (printGrade (c + d))

【讨论】:

    猜你喜欢
    • 2020-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-03
    • 1970-01-01
    相关资源
    最近更新 更多