0

I get an IO String via:

import Data.Char
import Network.HTTP
import Text.HTML.TagSoup

openURL :: String -> IO String
openURL x = getResponseBody =<< simpleHTTP (getRequest x)

crawlType :: String -> IO String
crawlType pkm = do
  src <- openURL url
  return . fromBody $ parseTags src
  where
    fromBody = unwords . drop 6 . take 7 . words . innerText . dropWhile (~/= "<p>")
    url = "http://pokemon.wikia.com/wiki/" ++ pkm

and I want to parse its data via:

getType :: String -> (String, String)
getType pkmType = (dropWhile (== '/') $ fst b, dropWhile (== '/') $ snd b)
                  where b = break (== '/') pkmType

But like you see, getType doesn't support the IO String yet.

I'm new to IO, so how to make it working? I also tryed to understand the error when giving the IO String to that function, but it's too complicated for me up to now :/

1 Answer 1

7

First, to emphasize: an IO String is not a string. It's an IO action which, when you bind it somewhere within the main action, will yield a result of type String, but you should not think of it as some sort of “variation on the string type”. Rather, it's a special instantiation of the IO a type.

For this reason, you almost certainly do not want to “change a function to support IO String instead of String”. Instead, you want to apply this string-accepting function, as it is, to an outcome of the crawlType action. Such an outcome, as I said, has type String, so you're fine there. For instance,

main :: IO ()
main = do
   pkm = "blablabla"
   typeString <- crawlType pkm
   let typeSpec = getType typeString
   print typeSpec -- or whatever you wish to do with it.

You can omit the typeString variable by writing

   typeSpec <- getType <$> crawlType pkm

if you prefer; this corresponds to what in a procedural language might look like

   var typeSpec = getType(crawlType(pkm));

Alternatively, you can of course include the parsing right in crawlType:

crawlType' :: String -> IO (String, String)
crawlType' pkm = do
  src <- openURL url
  return . getType . fromBody $ parseTags src
  where
    fromBody = unwords . drop 6 . take 7 . words . innerText . dropWhile (~/= "<p>")
    url = "http://pokemon.wikia.com/wiki/" ++ pkm

If you're curious what the <$> operator does: this is not built-in syntax like do/<- notation. Instead, it's just an infix version of fmap, which you may better know in its list-specialised version map. Both list [] and IO are functors, which means you can pull them through ordinary functions, changing only the element/outcome values but not the structure of the IO action / list spine.

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

1 Comment

This answer is fantastic. You helped me a lot. Thank you!

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.