I'm experimenting with the http-conduit library and have this simple example:
#!/usr/bin/env stack
{- stack
--resolver lts-7.12
--install-ghc
runghc
--package http-conduit
-}
{-# LANGUAGE OverloadedStrings #-}
import Network.HTTP.Conduit
import Data.ByteString.Lazy.Internal
getUrl :: IO (Data.ByteString.Lazy.Internal.ByteString) ---eeew!
getUrl = do
resp <- simpleHttp "http://www.stackoverflow.com"
return resp
I understand from this post that I should prefer a response as a ByteString to a [Char] or String. I assumed the OverloadedStrings pragma might make this less of an issue, but doesn't appear to change with respect to my out-type.
The function works fine and dutifully prints out a simple http response for the homepage of SO, but that type signature looks really ugly:
getUrl :: IO (Data.ByteString.Lazy.Internal.ByteString)
And I have to say I see very few things like that from internet examples (we've got more dots than an elliptical Java import, man). Is that right? If I want to return a reponse and then start parsing on it, say with HXT or tagsoup or attoparsec, is this the right approach or type signature?
I notice, for example, this gets even uglier when I start to add the ability to take arguments, such as supplying a different URL:
import Network.HTTP.Conduit
import Data.ByteString.Lazy.Internal
-- alright, first arg is now string for my url...
getUrl :: String -> IO (Data.ByteString.Lazy.Internal.ByteString)
getUrl url = do
resp <- simpleHttp url
return resp
main :: IO (ByteString) -- what?! inside the () ?
main = do
getUrl "https://www.stackoverflow.com"
This seems unwholesome. How should I understand how to structure this correctly?
You can always write types with their qualified path. However, as you've already imported Data.ByteString.Lazy.Internal
and unless you have some other ByteString
type in scope (like the strict one) you can simply omit the Data.ByteString.Lazy.Internal
:
getUrl :: IO ByteString
Also, unless you have some particular need to import Internal
, I recommend you just import Data.ByteString.Lazy
(which also exports the lazy ByteString
type you are using). If you have both strict and lazy ByteString
in scope, I'd import them qualified:
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BL
Then BS.ByteString
is the strict type and BL.ByteString
the lazy one - no need to write out the full Data.ByteString.ByteString
or Data.ByteString.Lazy.ByteString
.
It is also worth mentioning (thanks @duplode) that either of the bytestring modules is usually best imported qualified since they defines a whole bunch of functions that clash with the Prelude (and with each other).
Finally, note that parens around a type by itself does nothing. (ByteString)
and ByteString
are identical. As such, I would not include them.
Unrelated to your question, but some remarks on your monadic code:
Every time you write something like
do x <- e
return x
it can be replace with just e
.
Every time you write something like
do e
it can be replaced with e
.