function :: (Floating a, RealFrac a, Integral b) => a -> b
function x = floor (sqrt x)
好的,如果你看一下这个函数,我指定a
可以是RealFrac
和Floating
Typeclasses,然后b
是Integral
。
这是因为floor
和sqrt
:
floor :: (Integral b, RealFrac a) => a -> b
sqrt :: Floating a => a -> a
可以看出sqrt
只需要Floating
s而floor
只需要RealFrac
。所以我将a
设置为RealFrac
和Floating
。
然后我指定b
为Integral
,因为floor
为我们提供了Integral
。
所以我的问题是
newfunction :: (Integral a, RealFrac a, Integral b) => a -> b
newfunction a = floor (sqrt (fromIntegral a))
为什么这不起作用?
实际的正确类型签名是
newfunction :: (Integral a, Integral b) => a -> b
答案 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’
- 了解单个类型如何同时属于RealFrac
和Integral
?如果您手动定义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