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

Cha*_*ana 1 haskell typeclass

function :: (Floating a, RealFrac a, Integral b) => a -> b
function x = floor (sqrt x)
Run Code Online (Sandbox Code Playgroud)

好的,所以如果你看一下这个函数,我指定的a可以是两个RealFracFloatingTypeclasses,然后b是一个Integral.这是因为签名floorsqrt:

floor :: (Integral b, RealFrac a) => a -> b 
sqrt :: Floating a => a -> a
Run Code Online (Sandbox Code Playgroud)

可以看出sqrt只需要花费Floating而且floor只需要花费RealFrac.所以我设置aRealFracFloating.然后我指定那bIntegral因为floor给了我们一个Integral.

所以我的问题是

newfunction :: (Integral a, RealFrac a, Integral b) => a -> b 
newfunction a = floor (sqrt (fromIntegral a))
Run Code Online (Sandbox Code Playgroud)

为什么这不起作用?

实际的正确类型签名是

newfunction :: (Integral a, Integral b) => a -> b 
Run Code Online (Sandbox Code Playgroud)

Eri*_*lun 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
Run Code Online (Sandbox Code Playgroud)

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

Prelude> let newfunction a = floor (sqrt (fromIntegral a))
Prelude> :t newfunction 
newfunction :: (Integral b, Integral a) => a -> b
Run Code Online (Sandbox Code Playgroud)

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

至于什么对于更普遍的东西意味着什么呢?当它做出更少的假设并减少对其输入的要求/约束​​时; 通过要求更少,它可以使用更多种类的输入,这被称为"更一般".


此外,请注意,在这种情况下较不通用的版本是无效的,因为你不能真正用anying调用它,但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’
Run Code Online (Sandbox Code Playgroud)

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

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


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

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
Run Code Online (Sandbox Code Playgroud)