0

I have the following function that iterates over a list [(Map String SqlValue)]

extractPatternStrings ∷ IO [(Map String SqlValue)] → IO [String] 
extractPatternStrings [] = do
    return []
extractPatternStrings lst = do
    (m:ms) ←  lst
    return $ (toString m) : (extractPatternStrings ms)
    where
        toString ∷  Map String SqlValue → String 
        toString m = (fromSql . fromJust . (Map.lookup "word"))∷ String

Execpt the empty list case is telling me that it couldn't match the expected IO [Map String SqlValue] with actual [t0].

I thought the do = return would have taken care of this. How should I correct this?

Edit: To answer why I am using IO:

The function is being called from selectAll ↠ extractPatternStrings where selectAll reads from a database.

2
  • 5
    Why are you using IO here? This looks like it could have been a pure functions instead. Commented Oct 18, 2011 at 19:38
  • I am using IO because I read from a database. If I don't use IO the database reading code gives me an error wanting to 'return' an IO. Commented Oct 18, 2011 at 20:01

2 Answers 2

7
extractPatternStrings ∷ IO [(Map String SqlValue)] → IO [String]

An IO [String] is an IO action that produces a [String] result. Your use of do notation ensures extractPatternStrings produces an IO [String], not a [String].

An IO [(Map String SqlValue)] is an IO action that produces a [Map String SqlValue] result. But you cannot pattern match against an IO action. The syntax you use is for matching directly against a list, not against an IO action that produces a list.

You should use this type signature instead:

extractPatternStrings ∷ [Map String SqlValue] → IO [String] 

Except that, as @missingno points out, this doesn't need to be an IO action:

extractPatternStrings ∷ [Map String SqlValue] → [String] 
extractPatternStrings []     = []
extractPatternStrings (m:ms) = toString m : extractPatternStrings ms
    where
        toString ∷  Map String SqlValue → String 
        toString m = (fromSql . fromJust . (Map.lookup "word"))∷ String

Or, better (and fixing an error in toString):

extractPatternStrings ∷ [Map String SqlValue] → [String] 
extractPatternStrings = map toString
    where
        toString ∷  Map String SqlValue → String 
        toString = fromSql . fromJust . Map.lookup "word"

More succinctly:

extractPatternStrings ∷ [Map String SqlValue] → [String] 
extractPatternStrings = map (fromSql . fromJust . Map.lookup "word")

If you really must have the original signature, then use liftM, either by changing your calling code to selectAll ↠ liftM extractPatternStrings (and I must confess I don't recognise the operator you use there), or by defining extractPatternStrings as

extractPatternStrings ∷ IO [Map String SqlValue] → IO [String] 
extractPatternStrings = liftM $ map (fromSql . fromJust . Map.lookup "word")

But I recommend the former.

Sign up to request clarification or add additional context in comments.

1 Comment

I managed to get it working with the former. Thanks for your help! Oh and that operator was the >>=. My editor just converts that to the unicode symbol.
2

The return wraps your return value in IO, so that takes care of the return type IO [String]. However it doesn't help you with your argument type, which is [(Map String SqlValue)], but which you're trying to pattern match against the empty list. Basically you can't pattern match against IO values.

So you should either get rid of the IOs (which do seem wholly unnecessary in your code) or if you really do want the function to take an IO (though I can't imagine why you would), you have to unwrap your argument before you can pattern match against it, which would look like this:

extractPatternStrings lstIO = do
  lst <- lstIO
  case lst of
    [] -> ...
    (m:ms) -> ...

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.