Loading Different Strings
In the last couple of articles we've explored the Text
and ByteString
types. These provide alternative string representations that are more efficient when compared to the basic "list of characters" definition. But we've also seen that it can be a minor pain to remember all the different ways we can convert back and forth between them. And in fact, not all conversions are actually guaranteed to work the way we would like.
Now let's imagine our program deals with input and output. Now as a beginner, it's easy to think that there are only a couple primary functions for dealing with input and output, and that these only operate on String
values:
putStrLn :: String -> IO ()
getLine :: IO String
If your program actually makes use of Text
, you might end up (as I have in many of my programs) constantly doing something like T.pack <$> getLine
to read the input as a Text
, or putStrLn (unpack text)
to print it out.
When you're dealing with files, this makes it more likely that you'll mess up lazy vs. strict semantics. You'd have to know that readFile
is lazy, and readFile'
, an alternative, is strict.
readFile :: FilePath -> IO String
readFile' :: FilePath -> IO String
But you don't need to resort to always using the String
type! All of our alternative types have their own IO functions. You just have to know which modules to use, and import them in a qualified way!
With Text
, there are separate IO
modules that offer these options for us:
import qualified Data.Text as T
import qualified Data.Text.IO as TI
import qualified Data.Text.Lazy as TL
Import qualified Data.Text.Lazy.IO as TLI
TI.putStrLn :: T.Text -> IO ()
TI.getLine :: IO T.Text
TLI.putStrLn :: TL.Text -> IO ()
TLI.getLine :: IO TL.Text
The modules also contain functions for reading and writing files as well, all with the appropriate Text
types.
With ByteString
types, these functions live in the primary module, and they aren't quite as exhaustive. There is putStr
, but not putStrLn
(I'm not sure why).
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as BL
B.putStr :: B.ByteString -> IO ()
B.getLine :: IO B.ByteString
BL.putStr :: BL.ByteString -> IO ()
BL.getLine :: IO BL.ByteString
So, when you're program is using the more sophisticated string types, use the appropriate input and output mechanisms to spare yourself the inefficiencies (both performance-wise and in terms of code clarity) that come with using String
as a go-between!
If you've never really written a Haskell program with input and output before, you'll need to have some idea of how Stack works so you can get all the pieces working. Take our free Stack mini-course to learn how everything works, and stay tuned for more tips on Strings and other useful areas of Haskell!