Reputation: 458
I've been studying Haskell in my spare time and have recently crossed into the area of monadic functions. I've distilled the code from an excercise I've been working on into this very contrived example to isolate the exact problem I'm having:
import System.Random
rndPermu :: [a] -> IO (a, [a])
rndPermu xs = (front, back)
where (front, back) = hurf xs
hurf :: [a] -> IO (a, [a])
hurf xs = randomRIO (0, (length xs) - 1) >>= \r -> return $ removeAt r xs
removeAt :: Int -> [a] -> (a, [a])
removeAt n xs = (e, rest)
where e = xs !! n
rest = take n xs ++ (tail $ drop n xs)
rndPermu produces a type error when loaded into GHCi stating a type (t, t1) was expected in the 'where' clause but IO (a, [a]) was received. I can use things like (liftM fst) to pull individual items from the tuple and just assign a single value but that's obviously a sloppy and roundabout way of going about things. I feel I'm probably stumbling over some minor nuance of syntax that's staring me in the face. How does one resolve this type error? It should be possible to directly match against a tuple wrapped in a monad, shouldn't it?
Upvotes: 3
Views: 4946
Reputation: 11069
To answer the question from your comment, GHCi does infer that rndPermu
should have an IO type. That's not the problem. The problem is this line:
where (front, back) = hurf xs
Type inference just means (loosely) that you don't have to specify the types of the expressions you're dealing with. Type inference does not mean that Haskell will simply convert values of one type to another silently; quite the opposite in fact. As others mentioned, you don't have to write a do
block if you don't want to, but you do have to deal with the fact that you have an IO value.
Upvotes: 0
Reputation: 170723
As an alternative to a do
block, you can pattern match in the function to which you bind the monadic value:
rndPermu xs = hurf xs >>= \(front, back) -> return (front, back)
rndPermu xs = hurf xs >>= \res -> case res of (front, back) -> return (front, back)
Upvotes: 1
Reputation: 229593
If I understand correctly what you are trying to do, rndPermu
tries to take the value in IO
returned by hurf
and remove the IO
from it, like rndPermu :: IO a -> a
. This is not possible. A return value in the IO
monad signals that the hurf
function uses IO and all functions that use the results of a call to hurf
will therefore indirectly also use IO: Their return values should also be in the IO
monad. This is enforced by the type system.
If you just want to use pattern matching in a monad the most direct way is to use the do-notation:
rndPermu :: [a] -> IO (a, [a])
rndPermu xs =
do (front, back) <- hurf xs
return (front, back)
A common pattern is to use different, pure functions to do further processing of values.
These functions are just called from IO
or a different monad, but they don't need to know about that:
-- pure function to do something with the result of |hurf|
modify :: (a, [a]) -> (a, [a])
modify (a, as) = (a, reverse as)
rndPermu :: [a] -> IO (a, [a])
rndPermu xs =
do r <- hurf xs
return (modify r)
-- or, with >>= operator:
-- hurf xs >>= return . modify
Upvotes: 2
Reputation: 47052
I don't know why you don't have
rndPermu xs = hurf xs
but to answer the question you asked, try this
rndPermu xs = do (front, back) <- hurf xs
return (front, back)
My understanding is that you cannot directly match something within IO
. You have to extract it first using the <-
syntax.
Upvotes: 2