【发布时间】:2018-06-22 13:53:20
【问题描述】:
我正在关注 Real World Haskell 的书。在关于 Monads 的章节中,他们给出了一个简单的例子,使用列表 monad 来计算所有满足 x * y == n 的数字对 (x, y)。
他们的解决方案是:
multiplyTo :: Int -> [(Int, Int)]
multiplyTo n = do
x <- [1..n]
y <- [x..n]
guarded (x * y == n) $
return (x, y)
guarded :: Bool -> [a] -> [a]
guarded True xs = xs
guarded False _ = []
但我想知道是否可以为任何单子重述guarded。
由于列表 monad 中的 fail 是 fail _ = [],我虽然可以这样做:
guarded :: (Monad m) => Bool -> m a -> m a
guarded True = id
guarded False = fail "skipped"
但是,这实际上在 ghci 中失败了:
*Main> multiplyTo 24
*** Exception: skipped
我有一种无法完全解释的预感。这两个版本有效:
guarded :: (Monad m) => Bool -> m a -> m a
guarded True = id
guarded False = \s -> fail "skipped"
guarded :: (Monad m) => Bool -> m a -> m a
guarded True xs = xs
guarded False _ = fail "skipped"
fail "skipped" 的类型是Monad m => m a,而guarded False 的类型是Monad m => m a -> m a。那么我对guarded 的第一个定义怎么可能进行类型检查呢?
【问题讨论】:
-
AFAIK,
fail已被弃用,因为它不适合在所有单子中使用。使用来自Control.Monad的MonadPlus类型类中的mzero。 -
@AJFarmar 确实 RWH 建议不要使用 fail,因为除非您知道它是如何在您的代码涉及的所有 monad 中实现的,否则它可能会失败。然而,我的问题更多是关于类型检查:请注意
fail "..."似乎同时使用Monad m => m a和Monad m => m a -> m a进行类型检查。 -
它使用
(->) r的 monad 实例,在本例中为r ~ m a。
标签: haskell monads typechecking