我无法理解为什么Haskell不能推断出这种类型

时间:2014-09-24 15:14:45

标签: haskell

我试图使用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

,因此我无法理解输入无法输入的原因

提前致谢, 亚历

1 个答案:

答案 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。这是一个微妙的差异,一开始很难发现。它也适用于这两个签名中ab之间的差异。您可以做的只是删除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)

现在两个类型签名中的ma都引用相同的类型变量。我还让hlint告诉我可以对您的代码进行一些改进,例如使用{{1}将return移出最后一行的if语句do的表示法,以及eta-reduced foldFn隐含filterM参数,以及删除一些不必要的括号。