Martin Fischer
Martin Fischer

Reputation: 697

Change a function to support IO String instead of String

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 :/

Upvotes: 0

Views: 111

Answers (1)

leftaroundabout
leftaroundabout

Reputation: 120751

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.

Upvotes: 7

Related Questions