为什么`守卫False =失败"跳过"`类型检查?

man*_*anu 3 monads haskell typechecking

我正在关注真实世界的Haskell书.在关于Monads的章节中,他们给出了一个简单的例子,使用list monad来计算所有数字对(x,y)x * y == n.

他们的解决方案是

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 _ = []
Run Code Online (Sandbox Code Playgroud)

但我想知道我是否可以guarded为任何monad 重申.

因为fail在monad列表中fail _ = [],我可以做到:

guarded :: (Monad m) => Bool -> m a -> m a
guarded True = id
guarded False = fail "skipped"
Run Code Online (Sandbox Code Playgroud)

但是,这实际上在ghci中失败了:

*Main> multiplyTo 24
*** Exception: skipped
Run Code Online (Sandbox Code Playgroud)

我有一种预感,我无法完全解释.这两个版本的工作:

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"
Run Code Online (Sandbox Code Playgroud)

类型fail "skipped"Monad m => m a,而类型guarded FalseMonad m => m a -> m a.那我的第一个guarded类型检查定义怎么可能呢?

lef*_*out 7

有争议的函数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`
Run Code Online (Sandbox Code Playgroud)

也就是说,你打电话fail(->) (m a)单子,并没有定义一个定制fail的实现,所以它默认为一个错误

  fail        :: String -> ((->) r) a
  fail s      = errorWithoutStackTrace s
Run Code Online (Sandbox Code Playgroud)

请注意,如果你删除了怎么这甚至typechecks Monad m从功能限制,因为fail不使用单子.

你的功能的正确概括是

guarded :: Alternative f => Bool -> f a -> f a
guarded True = id
guarded False = const empty
Run Code Online (Sandbox Code Playgroud)

这并没有进行类型检查,如果我erronously忘记了const,因为功能不是的一个实例Alternative.