简体   繁体   中英

Haskell Recursive function over IO list

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.

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.

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) -> ...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM