我试图使用foldr实现filterM功能,但收到错误,我无法理解原因。
我的实现(这只是为了理解,我知道我应该使用do notation ...):
filterM :: (Monad m) => (a -> (m Bool)) -> [a] -> m [a]
filterM f list = foldr foldFn (return []) list
where
foldFn :: (Monad m) => a -> m [a] -> m [a]
foldFn x acc = let
m = f x
in acc >>=
\l -> m >>=
\b -> (if b == True then return (x:l) else return l)
我收到以下错误
Could not deduce (a ~ a1)
from the context (Monad m)
bound by the type signature for
filterM :: Monad m => (a -> m Bool) -> [a] -> m [a]
at wadler.hs:124:12-55
or from (Monad m1)
bound by the type signature for
ff :: Monad m1 => a1 -> m1 [a1] -> m1 [a1]
at wadler.hs:127:23-54
`a' is a rigid type variable bound by
the type signature for
filterM :: Monad m => (a -> m Bool) -> [a] -> m [a]
at wadler.hs:124:12
`a1' is a rigid type variable bound by
the type signature for ff :: Monad m1 => a1 -> m1 [a1] -> m1 [a1]
at wadler.hs:127:23
In the first argument of `f', namely `x'
In the expression: f x
In an equation for `m': m = f x
由于foldr的类型为foldr :: (a -> b -> b) -> b -> [a] -> b
提前致谢, 亚历
答案 0 :(得分:10)
我相信你的意思
filterM f list = foldr foldFn (return []) list
而不是xs
,所以我会假设你继续前进。
您遇到的问题是foldFn
的类型签名中的类型变量与filterM
的类型签名中的类型变量完全分开。这真的相当于说
filterM :: (Monad m) => (a -> (m Bool)) -> [a] -> m [a]
filterM f list = foldr foldFn (return []) list
where
foldFn :: (Monad m1) => a1 -> m1 [a1] -> m1 [a1]
foldFn x acc = let
m = f x
in acc >>=
\l -> m >>=
\b -> (if b == True then return (x:l) else return l)
但是,您在f
的定义中使用了foldFn
,其中m1
必须与上面的m
完全相同。您不希望foldFn
的类型适用于任何Monad
,只有Monad
正在使用的f
。这是一个微妙的差异,一开始很难发现。它也适用于这两个签名中a
和b
之间的差异。您可以做的只是删除foldFn
的类型签名,GHC可以正确推断foldFn
的类型,但如果这不起作用,您可以使用ScopedTypeVariables
扩展名。我不建议在这种情况下使用它,但有时它确实有用甚至是必要的:
{-# LANGUAGE ScopedTypeVariables #-}
filterM :: forall m a. (Monad m) => (a -> m Bool) -> [a] -> m [a]
filterM f = foldr foldFn (return [])
where
foldFn :: (Monad m) => a -> m [a] -> m [a]
foldFn x acc = do
l <- acc
b <- f x
return (if b then x:l else l)
现在两个类型签名中的m
和a
都引用相同的类型变量。我还让hlint
告诉我可以对您的代码进行一些改进,例如使用{{1}将return
移出最后一行的if
语句do
的表示法,以及eta-reduced foldFn
隐含filterM
参数,以及删除一些不必要的括号。