I'm learning Haskell and it feeds me some new experiences, but I'm scary against its difficulties sometimes. (Actually so many times.)
At a glance, Haskell's I/O handler looks difficult but not actually.
I figured out that Haskell's I/O handler is so simple and there is no difference with other language's ones like python's file like object.
Let's go with a concrete example.
I wrote logging function for my interest like below.(Please ignore detail and structures, naming.)
module Main where
import Data.Time (UTCTime, getCurrentTime)
import Prelude hiding (log)
import System.IO (stdout, Handle, openFile, IOMode (WriteMode), hFlush, hClose, hPutStrLn)
data Severity
= Debug
| Info
| Warning
| Error
deriving (Ord, Eq)
severityLabel :: Severity -> Char
severityLabel Debug = 'D'
severityLabel Info = 'I'
severityLabel Warning = 'W'
severityLabel Error = 'E'
newtype LogFormatter = LogFormatter {format :: UTCTime -> Severity -> String -> String}
data Logger = Logger
{ loggerSeverity :: Severity,
loggerHandler :: Handle,
loggerFormatter :: LogFormatter
}
log :: String -> Logger -> IO ()
log msg logger = do
timestamp <- getCurrentTime
let formatter = loggerFormatter logger
severity = loggerSeverity logger
handler = loggerHandler logger
hPutStrLn handler (format formatter timestamp severity msg)
hFlush handler
basicFormatter :: LogFormatter
basicFormatter = LogFormatter formatter
where
formatter t s msg = show t <> " - " <> [severityLabel s] <> ": " <> msg
stdInLogger :: Severity -> Logger
stdInLogger sev = Logger sev stdout basicFormatter
main :: IO ()
main = do
let debugLogger = stdInLogger Debug
log "this is logger" debugLogger
let logfilePath = "out.log"
hdl <- openFile logfilePath WriteMode
let fileLogger = Logger Debug hdl basicFormatter
log "this is on file" fileLogger
hClose hdl
This code generates like 2023-05-07 02:27:25.997054484 UTC - D: <arbitrary message>
to stdout and a logfile.
When I am going to display some messages to stdout, I use print
and putStrLn
usually.
As well as I am goint to write out some messages to any files, I use openFile
and writeFile
usually.
In above example case, I have to make the something to write out to real world via I/O handler abstraction, so I guess my function log
is the one of core concept of Haskell's I/O handleing.
log msg logger = do
timestamp <- getCurrentTime
let formatter = loggerFormatter logger
severity = loggerSeverity logger
handler = loggerHandler logger
hPutStrLn handler (format formatter timestamp severity msg)
hFlush handler
log
uses h
prefixed function for System.IO.Handle
. So this is
all things about Haskell's I/O handling.
I just do passing System.IO.Handle
type something to the I/O handle wrapper and this wrapper just writes some messages to handler's buffer and finally flushes, NO DIFFICULTIES! YAY!
Haskell is difficult to me. This truth is maybe never changed forever to me.
But I learned those difficurlties are from some impressions.
Forgetting type system, Monad, etc and treating as a just function makes me more easy to understanding what is it.
Now Haskell's I/O handling is no fear, it is my friend (maybe).
Top comments (1)
That's a good approach – to focus on functions and not worry about the rest. Thank you for sharing