1

我正在尝试编写一个简单的函数来检查一个整数是否通过取模并检查它是否为0来除以另一个整数。我的想法是

divides :: (Integral a) => a -> a -> Bool
divides = (==0) . (flip mod)

如果 a 除 b,则除 ab 为真。但是,这段代码给了我错误

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

我真的不明白为什么这段代码不起作用。请赐教!

4

3 回答 3

6

要点是,.只会为每个函数提供 1 个参数,但flip mod需要两个。一个简单的解决方案是

(.:) = (.) . (.) -- the owl or boobs operator

divides = (0==) .: flip mod

.:在哪里

 (c -> d) -> (a -> b -> c) -> a -> b -> d
于 2013-11-09T17:47:04.527 回答
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`)  
于 2013-11-09T17:37:42.803 回答
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 -> Booland not a -> a -> Bool

于 2013-11-09T17:42:52.123 回答