ljs.dev
ljs.dev

Reputation: 4483

Convert function which putStrLn'd to return string

In a standalone working script, I have a function returning IO () as such:

main :: IO ()
main = do
    response <- simpleHttp "https://leonstafford.github.io"

    let body = decodeUtf8 ( response )
    let bodyAsString = unpack ( body )

    putStrLn $ htmlToPlainText bodyAsString

I'm now trying to utilise same function in this existing script, but struggling with the common Couldn't match type ‘[Char]’ with ‘Char’:

renderPage :: String -> String
renderPage url = do
    response <- simpleHttp url

    let body = decodeUtf8 ( response )
        bodyAsString = unpack ( body )
        articleAsPlainText = htmlToPlainText bodyAsString
    return articleAsPlainText

I have been fiddling with it off and on for about a week and hope I've been able to define the issue enough in the title, so as it may not be yet another can't match expected types post, though am pretty sure it is just that.

Upvotes: 1

Views: 119

Answers (1)

Zeta
Zeta

Reputation: 105876

The nice thing about Haskell is that you can do that step by step, mostly without fearing that you get completely different behaviour. First of all, we move the content of main into a new function:

renderPage :: IO ()
renderPage = do
    response <- simpleHttp "https://leonstafford.github.io"

    let body = decodeUtf8 ( response )
    let bodyAsString = unpack ( body )

    putStrLn $ htmlToPlainText bodyAsString


main :: IO ()
main = renderPage

Next, we add the url as argument:

renderPage :: String -> IO ()
renderPage url = do
    response <- simpleHttp url

    let body = decodeUtf8 ( response )
    let bodyAsString = unpack ( body )

    putStrLn $ htmlToPlainText bodyAsString

main :: IO ()
main = renderPage "https://leonstafford.github.io"

And last, but not least, we move putStrLn into main and use return in renderPage:

renderPage :: String -> IO String
renderPage url = do
    response <- simpleHttp url

    let body = decodeUtf8 ( response )
    let bodyAsString = unpack ( body )

    return $ htmlToPlainText bodyAsString

main :: IO ()
main = renderPage "https://leonstafford.github.io" >>= putStrLn

Those steps (move into new function, add arguments, move functionality out) were pretty small. So when you encounter a problem like this, don't be afraid to do some inbetween steps.

Note that IO always stays with renderPage, since simpleHttp uses IO. There is no way to get rid of IO once you have it somewhere in a function. But the functionality of renderPage can be re-used, since

renderPage :: String -> IO String
renderPage = fmap (htmlToPlainText . unpack . decodeUtf8) . simpleHttp

and therefore:

utf8ToPlainText :: ByteString -> String
utf8ToPlainText = htmlToPlainText . unpack . decodeUtf8

renderPage :: String -> IO String
renderPage = fmap utf8ToPlainText . simpleHttp

Upvotes: 3

Related Questions