我正在尝试编写一个简单的函数,通过获取模数并检查是否为0来检查一个整数是否除以另一个整数。我的想法就像是
divides :: (Integral a) => a -> a -> Bool
divides = (==0) . (flip mod)
除非b除以b,否则b将为真。但是,此代码给出了错误
Couldn't match expected type `a -> Bool' with actual type `Bool'
Expected type: b0 -> a -> Bool
Actual type: b0 -> Bool
In the first argument of `(.)', namely `(== 0)'
In the expression: (== 0) . mod
我真的不明白为什么这段代码不起作用。请赐教!
答案 0 :(得分:6)
要点是,.
只会为每个函数1提供参数,但flip mod
需要两个参数。一个简单的解决方案是
(.:) = (.) . (.) -- the owl or boobs operator
divides = (0==) .: flip mod
其中.:
是
(c -> d) -> (a -> b -> c) -> a -> b -> d
答案 1 :(得分:4)
要查看代码有什么问题,请手动eta-expand:
divides :: (Integral a) => a -> a -> Bool
divides = (==0) . (flip mod)
-- divides x = ((==0) . (flip mod)) x
-- = ((==0) $ flip mod x)
-- = flip mod x == 0
最后一行没有进行类型检查,因为(==) :: a -> a -> Bool
,但divides x
应该是a -> Bool
类型,而不是Bool
!
更正代码的最简单方法是以更扩展的形式编写代码,例如:
divides :: (Integral a) => a -> a -> Bool
divides x = (==0) . (`mod` x)
如果你真的想把它写成eta-reduced,这就是它的样子:
divides :: (Integral a) => a -> a -> Bool
divides = ((==0).) . (flip mod)
-- divides x = ((==0).) $ (`mod` x)
-- = (==0) . (`mod x`)
答案 2 :(得分:2)
看起来你期望.
运算符延续flip mod
对其结果采用两个参数的事实,即。你期待这个:
f :: a -> b -> c
g :: c -> d
g . f :: a -> b -> d
不幸的是,它不会那样工作。函数总是采用单个参数,并且通过具有返回函数的函数来“模拟”多个参数函数。例如,a -> b -> c
可以读为a -> (b -> c)
。因此会发生什么如下:
f :: a -> (b -> c)
g :: (b -> c) -> d
g . f :: a -> d
这就是为什么g
(在你的情况下是(== 0)
)期望一个函数,而你的函数类型实际上是a -> Bool
而不是a -> a -> Bool
。