假设我们有一个过滤函数。
filterM::Monad m => (a -> m Bool) -> [a] -> m [a]
假设我们的 a -> m Bool
函数如下:
p :: Integer -> Maybe Bool
p n | n > 0 = Just True
| n < 0 = Just False
| otherwise = Nothing
在示例中,m
是 Maybe。
我想创建 filterM 函数,以便:
filterM p [2,−4,1] = Just [2,1]
filterM p [2,0,−4,1] = Nothing
本质上这个filterM是过滤器,但是针对monad。
这是我的实现。它不起作用,我有几个问题。
filterM p [] = pure []
filterM p (x : xs) | p x == Just True = x : (filterM p xs)
| p x == Just False = filterM p xs
| otherwise = Nothing
首先,为什么它不起作用。它说无法将类型 m 与 Maybe m 是刚性类型变量
匹配。
Expected type: m Bool
Actual type: Maybe Bool
在我的函数中,我将“也许”硬编码为 m
,但如何使其更通用?
我会使用 do 表示法,但它似乎不是最好的选择,因为存在递归。
最佳答案
有两个问题。首先是您已经给出了类型签名:
filterM :: Monad m => (a -> m Bool) -> [a] -> m [a]
然后定义 filterM
,其中 m
专门用于 Maybe
(因为您使用 Just
和 Nothing
在你的定义中)。当 Haskell 推断出 m
必须是 Maybe
时,这会导致错误。类型 m
是“刚性”的,因为它是由您在类型签名中提供的,并且刚性类型必须保持通用;如果它们的多态性低于指定值,则会发生冲突。您可以通过编写以下内容来生成类似的错误消息:
badId :: a -> Int
badId x = x
显然,a
是 Int
,因此刚性(程序员指定的)类型 a
被确定为匹配 Int
在类型检查期间。
但是,即使您修复了类型签名:
filterM :: (a -> Maybe Bool) -> [a] -> Maybe [a]
filterM p [] = pure []
filterM p (x : xs) | p x == Just True = x : (filterM p xs)
| p x == Just False = filterM p xs
| otherwise = Nothing
您仍然会收到错误消息。您在代码中混淆了单元操作和值。表达式中:
x : filterM p xs
您正在将运算符 (:)::b -> [b] -> [b]
应用于类型 a
和 Maybe [a ]
,因此它不会进行类型检查(“无法将预期类型 也许 [a]
与实际类型 [a]
匹配。”)
您需要将 x : filterM p xs
替换为以下内容:
case filterM p xs of
Nothing -> Nothing
Just xs' -> Just (x : xs')
关于haskell - 在 haskell 中为 monad 创建过滤函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55562988/