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

时间:2011-10-05 14:30:12

标签: haskell types type-inference let

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

以下是发生的事情:

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

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

    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
    

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

    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
    

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

    Prelude Control.Monad> myFilterM (>3) (Just 4)
    Just 4
    Prelude Control.Monad> myFilterM (>3) (Just 3)
    Nothing
    

    我最好猜测发生了什么:

    ,当有一个do-block时,类型注释以某种方式与let-expression不兼容

    奖励积分:
    标准的Haskell发行版中有一个函数可以做到这一点吗?我很惊讶filterM做了一些非常不同的事情。

3 个答案:

答案 0 :(得分:9)

问题是类型运算符(::)的优先级。你试图描述myFilterM的类型,但你实际上在做的是:

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

(插入反斜杠仅为了可读性,不是合法的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

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

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}

对于奖励积分,您需要mfilterhoogle is your friend)。

答案 1 :(得分:3)

这可能只是类型注释语法和绑定优先级的问题。如果你把你的例子写成,

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

然后GHCi会给你一个高五并送你前往。

答案 2 :(得分:1)

我不知道你使用什么样的编译器,但在我的平台上(GHC 7.0.3),我得到一个简单的类型不匹配:

$ ghci
GHCi, version 7.0.3: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Loading package ffi-1.0 ... linking ... done.
Prelude> :m +Control.Monad
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:30:
    Could not deduce (t1 ~ ((b1 -> Bool) -> m1 b1 -> m1 b1))
    from the context (MonadPlus m)
      bound by the inferred type of
               myFilterM :: MonadPlus m => t -> t1 -> (b -> Bool) -> m b -> m b
      at <interactive>:1:5-100
    or from (MonadPlus m1)
      bound by an expression type signature:
                 MonadPlus m1 => (b1 -> Bool) -> m1 b1 -> m1 b1
      at <interactive>:1:21-100
      `t1' is a rigid type variable bound by
           the inferred type of
           myFilterM :: MonadPlus m => t -> t1 -> (b -> Bool) -> m b -> m b
           at <interactive>:1:5
    In a stmt of a 'do' expression: x <- m
    In the expression:
        do { x <- m;
             guard (f x);
             return x } ::
          MonadPlus m => (b -> Bool) -> m b -> m b
    In an equation for `myFilterM':
        myFilterM f m
          = do { x <- m;
                 guard (f x);
                 return x } ::
              MonadPlus m => (b -> Bool) -> m b -> m b

<interactive>:1:40:
    Could not deduce (t ~ ((m1 b1 -> m1 b1) -> Bool))
    from the context (MonadPlus m)
      bound by the inferred type of
               myFilterM :: MonadPlus m => t -> t1 -> (b -> Bool) -> m b -> m b
      at <interactive>:1:5-100
    or from (MonadPlus m1)
      bound by an expression type signature:
                 MonadPlus m1 => (b1 -> Bool) -> m1 b1 -> m1 b1
      at <interactive>:1:21-100
      `t' is a rigid type variable bound by
          the inferred type of
          myFilterM :: MonadPlus m => t -> t1 -> (b -> Bool) -> m b -> m b
          at <interactive>:1:5
    The function `f' is applied to one argument,
    but its type `t' has none
    In the first argument of `guard', namely `(f x)'
    In a stmt of a 'do' expression: guard (f x)
Prelude Control.Monad>

我想问题在于::没有达到论证的事实。这个小变化(注意单独的类型声明)

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

运行没有问题。它可能与GHC 7中的新型检查器有关。