Reputation: 697
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
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