Haskell let 表达式中出现奇怪的类型错误——问题出在哪里?

发布于 2024-12-07 23:01:55 字数 2095 浏览 1 评论 0原文

今天我在 Haskell 中遇到了一件令人沮丧的事情。

发生的事情是这样的:

  1. 我在 ghci 中编写了一个函数,并给了它一个类型签名
  2. ghci 抱怨了该类型
  3. 我删除了类型签名
  4. ghci 接受了该函数
  5. 我检查了推断类型
  6. 推断类型与我尝试赋予它的类型完全相同
  7. 我很苦恼地
  8. 发现我可以在任何let表达式中重现这个问题,
  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

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

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 块时,类型注释在某种程度上不能很好地与 let 表达式配合使用。

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

I came across a frustrating something in Haskell today.

Here's what happened:

  1. I wrote a function in ghci and gave it a type signature
  2. ghci complained about the type
  3. I removed the type signature
  4. ghci accepted the function
  5. I checked the inferred type
  6. the inferred type was exactly the same as the type I tried to give it
  7. I was very distressed
  8. I discovered that I could reproduce the problem in any let-expression
  9. Gnashing of teeth; decided to consult with the experts at SO

Attempt to define the function with a type signature:

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

Defined the function without a type signature, checked the inferred type:

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

Used the function for great good -- it worked properly:

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

My best guess as to what is going on:
type annotations somehow don't work well with let-expressions, when there's a do-block.

For bonus points:
is there a function in the standard Haskell distribution that does this? I was surprised that filterM does something very different.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

梦回旧景 2024-12-14 23:01:55

问题在于类型运算符 (::) 的优先级。您试图描述 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}

对于奖励积分,您需要 mfilter (hoogle 是你的朋友)。

The problem is the precedence of the type operator (::). You're trying to describe the type of myFilterM but what you're actually doing is this:

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

(backslashes inserted for readability only, not legit ghci syntax)

Do you see the issue? I get the same problem for something simple like

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

The solution is to attach the type signature to the proper element:

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}

And for bonus points, you want mfilter (hoogle is your friend).

情深如许 2024-12-14 23:01:55

这可能只是类型注释语法和绑定优先级的问题。如果您将示例写为,

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

那么 GHCi 会给您一个高五并让您继续前进。

This is likely just an issue of type annotation syntax and binding precendence. If you write your example as,

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

then GHCi will give you a high-five and send you on your way.

奢欲 2024-12-14 23:01:55

我不知道你使用哪种编译器,但在我的平台(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 中新的类型检查器有关。

I don't know what kind of compiler you use, but on my platform (GHC 7.0.3) I get a simple type mismatch:

$ 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>

I guess the problem lies in the fact, that the :: does not reaches the argument. This small variation (note the separate type declaration)

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

runs without problems. It may be related to the new type-checker in GHC 7.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文