这个Haskell函数的类型签名是什么?

时间:2009-05-20 03:33:41

标签: haskell types type-inference

我写了一个函数来检查数字是否为素数:

prime n = prime' n 2 (floor (sqrt n))
     where prime' n c u | n `mod` c == 0 = False
                        | c > u = True
                        | otherwise = prime' n (c+1) u

我无法弄清楚这个函数的类型签名应该是什么。起初我认为应该是这样的:

prime :: Integral a => a -> Bool

但是我在编译时遇到错误,因为sqrt期望Floating afloor期望RealFrac a而不是Integral a。当我删除类型签名时,它会编译,但该函数不起作用:

*Euler> :t prime
prime :: (Integral a, RealFrac a, Floating a) => a -> Bool
*Euler> prime 5

<interactive>:1:0:
    Ambiguous type variable `t' in the constraints:
      `Floating t' arising from a use of `prime' at <interactive>:1:0-6
      `RealFrac t' arising from a use of `prime' at <interactive>:1:0-6
      `Integral t' arising from a use of `prime' at <interactive>:1:0-6
    Probable fix: add a type signature that fixes these type variable(s)

如何使此功能有效?

4 个答案:

答案 0 :(得分:12)

问题是您在sqrt上使用n,这会强制n为浮点数;并且您还在mod上使用n,这会强制n为整数。直观地说,通过查看代码,n 应该是一个整数,因此您无法直接在其上调用sqrt。相反,您可以使用类似fromIntegral的内容将其从整数转换为另一种数字类型。

prime :: (Integral a) => a -> Bool
prime n = prime' n 2 (floor (sqrt (fromIntegral n)))
     where prime' n c u | n `mod` c == 0 = False
                        | c > u = True
                        | otherwise = prime' n (c+1) u

答案 1 :(得分:3)

最后一点,其他答案没有涵盖......

*Euler> :t prime
prime :: (Integral a, RealFrac a, Floating a) => a -> Bool

只要primeaa的实例,并且Integral可以采用RealFrac类型的参数,一次Floating个课程。

*Euler> prime 5

<interactive>:1:0:
    Ambiguous type variable `t' in the constraints:
      `Floating t' arising from a use of `prime' at <interactive>:1:0-6
      `RealFrac t' arising from a use of `prime' at <interactive>:1:0-6
      `Integral t' arising from a use of `prime' at <interactive>:1:0-6
    Probable fix: add a type signature that fixes these type variable(s)

然而,当您向prime 5提出要求时,它会抱怨5的默认类型都不能满足这些条件。

你很可能自己编写

instance (Integral a, RealFrac b, Floating b) => Integral (Either a b) where ...
instance (Integral a, RealFrac b, Floating b) => RealFrac (Either a b) where ...
instance (Integral a, RealFrac b, Floating b) => Floating (Either a b) where ...

(您还必须添加NumOrdRealFractional等实例),然后prime 5将是可以接受,因为存在满足类型条件的5 :: Either Integer Float

答案 2 :(得分:3)

或者,您可以更改上限测试:

prime n = prime' n 2
    where prime' n c | n `mod` c == 0 = False
                     | c * c > n = True
                     | otherwise = prime' n (c+1)

顺便说一句,你不需要n作为prime'的参数,因为它在所有调用中都是不变的。

答案 3 :(得分:2)

您可以将(sqrt n)更改为(sqrt (fromInteger n)),以使该功能按预期工作。这是必需的,因为sqrt的类型是:

sqrt :: (Floating a) => a -> a

所以这是错误的,例如:

sqrt (2 :: Int)