为什么这两个haskell功能不相同?

时间:2014-10-07 18:49:30

标签: haskell

我一直在研究这些功能:

import Data.Digits (digits)
numberDivider (a,b) = a / b
numberDivider2 (num,denom) = num / denom
                              where
                                a = head $ digits 10 num
                                b = head . tail $ digits 10 denom

我们可以看一下这些函数的类型:

λ> :t numberDivider2
numberDivider2 :: (Integral a, Fractional a) => (a, a) -> a
λ> :t numberDivider
numberDivider :: Fractional a => (a, a) -> a

numberDivider做了你认为它做的事情。 numberDivider2给出:

No instance for (Show a0) arising from a use of ‘print’
The type variable ‘a0’ is ambiguous
Note: there are several potential instances:
  instance Show Double -- Defined in ‘GHC.Float’
  instance Show Float -- Defined in ‘GHC.Float’
  instance (Integral a, Show a) => Show (GHC.Real.Ratio a)
    -- Defined in ‘GHC.Real’
  ...plus 34 others
In a stmt of an interactive GHCi command: print it

用正确的参数调用时(例如(48,98))。现在我不知道如何制作更具体的东西会导致模糊不清?我觉得我在某种程度上是愚蠢的。 (我也不知道为什么函数需要从(a,a) -> a开始,因为我认为它是(a,a) -> b,其中a是Integral而b是浮点数等等。 / p>

我尝试过输入类型注释来强制它使用Float来获得结果。

有人可以指出我在这里失踪的东西吗?

2 个答案:

答案 0 :(得分:6)

这可能来自digits的定义,我猜测它将Integrala => a作为其参数之一。然后,将Integral的附加约束放在numberDivider2的参数上。事实证明,没有一种类型是FractionalIntegral的实例。但是,当您输入数字文字时,它会尝试从Num a => a转换为(Integral a, Fractional a) => a,而在GHCi中,有一些特殊规则可以尝试查找也使用Show的实例,以便您可以将其打印到屏幕上。由于不存在此类型,因此会出现错误。

现在,真正的问题似乎是由于对Haskell号码系统的误解。您无法对所有号码使用/,例如Int,因为/未针对这些类型定义。您只能对小数类型使用/,因此Fractional类型类。如果您想将IntInteger转换为FloatDouble以执行浮点除法,可以使用fromIntegral将它们转换为任意值Num类型,例如

a = head $ digits 10 $ fromIntegral num
b = head . tail $ digits 10 $ fromIntegral denom

这应该从函数中删除Integral约束。


在查看digits的类型后,我发现这不会起作用。相反,你可能想要像

这样的东西
numberDivider2 :: (Integral a, Fractional b) => (a, a) -> b
numberDivider2 (num, denom) = fromIntegral num / fromIntegral denom
    where
        a = head $ digits 10 num
        b = head . tail $ digits 10 denom

请注意fromIntegral的位置,它们会在您要执行Integral操作的位置转换每个Fractional值。

答案 1 :(得分:4)

  

现在我不知道如何制作更具体的内容会导致模糊不清?

在这两种情况下都不明确,但在numberDivider的情况下,可以使用Haskell的默认规则解决歧义。在此特定实例中,这些规则基本上表示如果可能有多种数字类型且其中一种是Integer,请选择Integer。如果无法Integer,但Double是,请选择Double。如果两者都不可能,那么模糊性仍然存在。

如果numberDivider Integer不可能,因为Integer不是Fractional的实例,而DoubleDouble。因此,numberDivider被选中。

如果Integral也不可能,因为根本没有FractionalFractional的类型。因此,模棱两可仍然存在。

你可能会争辩说,一组正好0种可能的类型不是模糊的,但根本不可能,所以错误信息应该是不同的,但它也必须考虑可能在别处定义的实例。也就是说,虽然标准库中没有Integral和{{1}}的任何类型,但它们可能会在其他地方定义,因此我们无法排除这种可能性(甚至虽然没有意义)。