为什么我的功能的签名不起作用?

时间:2015-10-27 12:22:50

标签: haskell typeclass

function :: (Floating a, RealFrac a, Integral b) => a -> b
function x = floor (sqrt x)

好的,如果你看一下这个函数,我指定a可以是RealFracFloating Typeclasses,然后bIntegral。 这是因为floorsqrt

的签名
floor :: (Integral b, RealFrac a) => a -> b 
sqrt :: Floating a => a -> a

可以看出sqrt只需要Floating s而floor只需要RealFrac。所以我将a设置为RealFracFloating。 然后我指定bIntegral,因为floor为我们提供了Integral

所以我的问题是

newfunction :: (Integral a, RealFrac a, Integral b) => a -> b 
newfunction a = floor (sqrt (fromIntegral a))

为什么这不起作用?

实际的正确类型签名是

newfunction :: (Integral a, Integral b) => a -> b 

1 个答案:

答案 0 :(得分:8)

  

为什么这不起作用?

......但确实如此:

Prelude> :{
Prelude| let newfunction :: (Integral a, RealFrac a, Integral b) => a -> b
Prelude|     newfunction a = floor (sqrt (fromIntegral a))
Prelude| :}
Prelude> :t newfunction 
newfunction :: (Integral a, Integral b, RealFrac a) => a -> b

只是如果你让GHC自动推断出类型签名,它会为你提供一个更简单/更好的签名:

Prelude> let newfunction a = floor (sqrt (fromIntegral a))
Prelude> :t newfunction 
newfunction :: (Integral b, Integral a) => a -> b

但这并不意味着手动指定的一个是错误的 - 它可能不是那么普遍,可能会或可能不会是您所追求的,具体取决于具体情况。

关于什么意思更通用 - 当它做出更少的假设并减少对其输入的要求/约束​​时;通过要求更少,它可以使用更多种类的输入,这被称为“更通用”

同样,请注意在这种情况下较不通用的版本是无效的,因为您无法真正使用任何调用它,但Haskell仍允许您定义此类函数 - 失败将发生在呼叫站点。

Prelude> :i RealFrac
class (Real a, Fractional a) => RealFrac a where
    ...
    -- Defined in ‘GHC.Real’
instance RealFrac Float -- Defined in ‘GHC.Float’
instance RealFrac Double -- Defined in ‘GHC.Float’

Prelude> :i Integral
class (Real a, Enum a) => Integral a where
    ...
    -- Defined in ‘GHC.Real’
instance Integral Word -- Defined in ‘GHC.Real’
instance Integral Integer -- Defined in ‘GHC.Real’
instance Integral Int -- Defined in ‘GHC.Real’

- 了解单个类型如何同时属于RealFracIntegral?如果您手动定义instance RealFrac Integer where ...,它会起作用,但这可能没有意义,并且可能违反某些类型类法则,从而导致代码不一致/错误。

所以作为一般准则:从GHC推断类型签名开始,然后才添加其他约束或使签名更加单态/更少多态。

此外,您还可以定义这些函数point free,我认为这些函数更具可读性。我在下面展示了如何将当前的括号内的版本转换为免费版本(也称为tacit):

function x = floor (sqrt x)
function x = floor . sqrt $ x
function   = floor . sqrt

newfunction a = floor (sqrt (fromIntegral a))
newfunction a = floor . sqrt . fromIntegral $ a
newfunction   = floor . sqrt . fromIntegral