Haskell let-expression中的奇怪类型错误 - 问题是什么?

Mat*_*ick 10 haskell types type-inference let

我今天在Haskell 遇到了令人沮丧的事情.

这是发生的事情:

  1. 我在ghci中编写了一个函数并给它一个类型签名
  2. ghci抱怨这种类型
  3. 我删除了类型签名
  4. ghci接受了这个功能
  5. 我检查了推断类型
  6. 推断类型与我尝试提供的类型完全相同
  7. 我很苦恼
  8. 我发现我可以在任何let-expression中重现问题
  9. 咬牙切齿; 决定咨询SO的专家

尝试使用类型签名定义函数:

Prelude Control.Monad> let myFilterM f m = do {x <- m; guard (f x); return x} :: (MonadPlus m) => (b -> Bool) -> m b -> m b

<interactive>:1:20:
    Inferred type is less polymorphic than expected
      Quantified type variable `b' is mentioned in the environment:
        m :: (b -> Bool) -> m b -> m b (bound at <interactive>:1:16)
        f :: (m b -> m b) -> Bool (bound at <interactive>:1:14)
      Quantified type variable `m' is mentioned in the environment:
        m :: (b -> Bool) -> m b -> m b (bound at <interactive>:1:16)
        f :: (m b -> m b) -> Bool (bound at <interactive>:1:14)
    In the expression:
          do { x <- m;
               guard (f x);
               return x } ::
            (MonadPlus m) => (b -> Bool) -> m b -> m b
    In the definition of `myFilterM':
        myFilterM f m
                    = do { x <- m;
                           guard (f x);
                           return x } ::
                        (MonadPlus m) => (b -> Bool) -> m b -> m b
Run Code Online (Sandbox Code Playgroud)

定义没有类型签名的函数,检查推断类型:

Prelude Control.Monad> let myFilterM f m = do {x <- m; guard (f x); return x}
Prelude Control.Monad> :t myFilterM 
myFilterM :: (MonadPlus m) => (b -> Bool) -> m b -> m b
Run Code Online (Sandbox Code Playgroud)

使用该功能非常好 - 它工作正常:

Prelude Control.Monad> myFilterM (>3) (Just 4)
Just 4
Prelude Control.Monad> myFilterM (>3) (Just 3)
Nothing
Run Code Online (Sandbox Code Playgroud)

我最好的猜测是什么:
当有一个do-block时,类型注释以某种方式与let-expressions不兼容.

对于奖励积分:
标准Haskell发行版中是否有一个功能可以做到这一点?我很惊讶,filterM做了一些非常不同的事情.

ram*_*ion 9

问题是类型operator(::)的优先级.你试图描述的类型,myFilterM但你实际在做的是:

ghci> let myFilterM f m = (\
        do {x <- m; guard (f x); return x} \
        :: \
        (MonadPlus m) => (b -> Bool) -> m b -> m b)\
      )
Run Code Online (Sandbox Code Playgroud)

(插入反斜杠仅为了可读性,不是合法的ghci语法)

你看到了这个问题吗?我对同样的问题也有同样的问题

ghci> let f x = x + 1 :: (Int -> Int)
<interactive>:1:15:
    No instance for (Num (Int -> Int))
      arising from the literal `1'
    Possible fix: add an instance declaration for (Num (Int -> Int))
    In the second argument of `(+)', namely `1'
    In the expression: x + 1 :: Int -> Int
    In an equation for `f': f x = x + 1 :: Int -> Int
Run Code Online (Sandbox Code Playgroud)

解决方案是将类型签名附加到适当的元素:

ghci> let f :: Int -> Int ; f x = x + 1
ghci> let myFilterM :: (MonadPlus m) => (b -> Bool) -> m b -> m b; myFilterM f m = do {x <- m; guard (f x); return x}
Run Code Online (Sandbox Code Playgroud)

而对于奖励积分,你想要mfilter(hoogle是你的朋友).