Reputation: 49095
I came across a frustrating something in Haskell today.
Here's what happened:
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.
Upvotes: 11
Views: 1452
Reputation: 89093
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).
Upvotes: 10
Reputation: 3791
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.
Upvotes: 3
Reputation: 93062
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.
Upvotes: 1