【问题标题】:Why I get: Could not deduce (Fractional a) arising from a use of ‘/’?为什么我得到:由于使用“/”而无法推断出(小数)?
【发布时间】:2020-02-01 15:52:12
【问题描述】:

我在 .hs 文件中有以下代码

module TypeInference1 where
f :: Num a => a -> a -> a
f x y = x + y + 3

然后如果我检查f的类型,我得到以下结果,这是可以的:

*TypeInference1> :t f
f :: Num a => a -> a -> a

如果我将一个小数类型的参数传递给 f 并检查我获得的类型:

*TypeInference1> :t f 1.0
f 1.0 :: Fractional a => a -> a

但另一方面,如果我通过对其一个参数设置除法运算来更改 f,如下所示: f x y = x/2 + y + 3

我收到以下错误:

5-typeInference1.hs:4:9: error:
    • Could not deduce (Fractional a) arising from a use of ‘/’
      from the context: Num a
        bound by the type signature for:
                   f :: forall a. Num a => a -> a -> a
        at 5-typeInference1.hs:3:1-25
      Possible fix:
        add (Fractional a) to the context of
          the type signature for:
            f :: forall a. Num a => a -> a -> a
    • In the first argument of ‘(+)’, namely ‘x / 2’
      In the first argument of ‘(+)’, namely ‘x / 2 + y’
      In the expression: x / 2 + y + 3

为什么会发生这种情况,为什么我改变上面的函数f时不能推断出类型?

【问题讨论】:

    标签: haskell


    【解决方案1】:

    让我们记住这种类型类层次结构:

                 Num a
                 /   \  
                /     \ 
               /       \
      (Fractional a)  (Integral a)
          /   \           /   \  
         /     \         /     \ 
        /       \       /       \
     Double   Float   Integer   Int
    

    现在如果你说:

    f :: Num a => a -> a -> a
    

    您是说a 将使用的所有函数都来自Num 类型类,这些在这里:

    (+), (*), abs, signum, fromInteger, (negate | (-))
    

    如果你检查你的函数体:

    f :: Num a => a -> a -> a
    f x y = x/2 + y + 3
    

    您正在使用(/) 函数,并且该函数属于NumFractionalFractional 的“子”类型类,允许这些函数:

    fromRational, (recip | (/))
    

    结论:

    如果您使用一个属于层次结构底部的一个函数,则您将类型限制为该级别。

    修复

    好消息是,如果你仔细阅读堆栈错误,它的某些部分会说:

      Possible fix:
        add (Fractional a) to the context of
          the type signature for:
    

    让我们试试吧:

    f :: Fractional a => a -> a -> a
    f x y = x/2 + y + 3
    

    【讨论】:

      【解决方案2】:

      简答:通过指定Num a => a -> a -> a,您声称f 可以处理所有 as,它们是Num 类型类的成员,但是(/) 只能与属于 Fractional 类型类成员的类型一起使用。

      FractionalNum 的“子”类型类。这意味着所有属于Fractional 成员的类型都是Num 的成员,但这确实反之亦然。

      通过指定为 f 的类型:

      f :: Num a => a -> a -> a
      

      您说f 可以处理a所有 类型,它们是Num 类型类的成员。但如果将f 定义为f x y = x/2 + y + 3,这是不正确的,因为x/2 意味着x 应该是Fractional 的成员类型。事实上(/) 的类型为(/) :: Fractional a => a -> a -> a。因此,您应该使f 更具限制性,这样您只能传递a 类型的值,而aFractional 类型类的成员:

      f :: Fractional a => a -> a -> a
      f x y = x/2 + y + 3

      【讨论】:

      • 你能否解释一下为什么对于 *TypeInference1> :t f 1.0 我们得到: f 1.0 :: Fractional a => a -> a;尽管函数 f 已被声明为 f :: Num a => a -> a -> a ?我明白你的解释,但这方面仍然让我感到困惑。
      • 因为FractionalNum 的子类型,所以可以。因此,它得出您的参数1.0 :: Fractional b => bNum a => a -> a -> a 中的a 相同,因此结果的类型是(Fractional a, Num a) => a -> a 或更简单的Fractional a => a -> a。因此f 1.0 只能用于Fractional 的所有类型。
      • 多态类型的基本原则是函数的调用者可以在函数类型给定的约束范围内选择要提供的类型。你原来的f 可以在任何数字类型上工作,并且它的所有参数必须是相同的类型。当它提供1.0 时,它可以是任何Fractional 类型,这很好,并导致Fractional 约束,因为f 的定义中的所有as 都是相同的。但是您的第二个f,通过使用/ 运算符,突然不能再适用于所有数字类型。你的类型签名说它可以,这就是 GHC 拒绝它的原因。
      • 谢谢大家的详细解释。现在我明白了。
      猜你喜欢
      • 2013-03-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-16
      • 1970-01-01
      • 1970-01-01
      • 2019-01-23
      • 2022-08-03
      相关资源
      最近更新 更多