为什么`guarded False =失败的“跳过”类型检查?

时间:2018-06-22 13:53:20

标签: haskell monads typechecking

我正在关注真实世界的Haskell书。在有关Monad的章节中,他们给出了一个简单的示例,该示例使用list 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 _ = []

但是我想知道是否可以为任何monad重新声明guarded

由于列表monad中的failfail _ = [],所以我可以这样做:

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定义是否有可能进行类型检查?

1 个答案:

答案 0 :(得分:7)

您正在被有争议的 function monad实例绊倒(实际上,这在Haskell社区中并不是有争议的,但是我个人认为,如果不存在,我们可能会更好一些) )以及毫无争议的fail方法。

查看类型:

guarded False
   = fail "skipped" :: m a -> m a
   ≡ (fail :: String -> (m a -> m a)) "skipped"
   ≡ (fail :: String -> F (m a)) "skipped"    -- with `type F x = m a -> x`

也就是说,您在fail单子上调用(->) (m a),然后在does not define a custom fail implementation上调用defaults to the error one

  fail        :: String -> ((->) r) a
  fail s      = errorWithoutStackTrace s

请注意,即使您从函数中删除了Monad m约束,它也会进行类型检查,因为 fail不使用该monad

函数的正确概括是

guarded :: Alternative f => Bool -> f a -> f a
guarded True = id
guarded False = const empty

如果我错误地忘记了const,这不是 not 类型检查,因为函数不是Alternative的实例。