我正在关注真实世界的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中的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
定义是否有可能进行类型检查?
答案 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
的实例。