The Writer monad allows functions to accumulate information as functions execute. According to the Hackage page:
A writer monad parameterized by the type w of output to accumulate.
Perhaps not the most verbose of descriptions, however this is rather simple to explain with a well known example. In previous programming disciplines you would have needed to log information out of your code as your program “did things”. The Writer monad allows you to write out information in a log form. This doesn’t necessarily have to be in textual log format; an example I have seen recently is to keep track of calculations used to come to a final result. The calculations put into that log sped up calculations on other figures.
The idea here is to not clutter your code having to support things like logging/tracing, etc. Employing this monad gives your code the ability to produce this output on the side without getting in the way.
Key Pieces
Functions in the Writer monad are decorated with Writer l r. l in this case is the type that you’ll be logging out where r is the result being returned from your function.
The function tell is what’s used to push another value into the log/trace/writer.
Operations in the Writer monad can be chained together using >>=
runWriter is what you’ll use to run something in the Writer monad to get your result back.
An Example
importControl.Monad.Writer-- | Starts a value off.-- This function doesn't perform any calculation at all, it just prepares an-- initial value to start in the calculation pipeline--start::Int->Writer[String]Intstartx=dotell(["Starting with "++showx])returnx-- | Halve a value-- Any value passed into this function gets halved--half::Int->Writer[String]Inthalfx=dotell(["Halving "++showx])return(x`div`2)-- | Squares a value-- Any value passed into this function gets squared--sqr::Int->Writer[String]Intsqrx=dotell(["Squaring "++showx])return(x*x)main::IO()main=doletwork=runWriter$start10>>=half>>=sqr>>=halfletans=fstworkletlog=sndworkputStrLn$"Answer: "++showansputStrLn""putStrLn" ==== Log ==== "mapM_putStrLnlog
The State monad gives functionality of both the Reader monad and Writer monad in one. When using the State monad you’re able to read the state at any time and then set it back again, providing read/write access.
Key Points
The function get is used to read the current state
The function put is used to set the state
runState is used to manage execution of functions that run in the State monad
Operations in the State monad can use >>= to be chained together
Functions in the State monad are decorated with State s v. Where s is the type of the state and v is the return type from the function<
An Example
importControl.Monad.State-- | Starts a value off.-- This function doesn't perform any calculation at all, it just prepares an-- initial value to start in the calculation pipeline--start::Int->State[String]Intstartx=doput["Starting with "++showx]returnx-- | Halve a value-- Any value passed into this function gets halved--half::Int->State[String]Inthalfx=dos<-getletns=s++["Halving "++showx]putnsreturn(x`div`2)-- | Squares a value-- Any value passed into this function gets squared--sqr::Int->State[String]Intsqrx=dos<-getletns=s++["Squaring "++showx]putnsreturn(x*x)main::IO()main=doletc=runState$start10>>=half>>=sqr>>=halfletwork=c[""]letans=fst$workletlog=snd$workputStrLn$"Answer: "++showansputStrLn""putStrLn" ==== Log ==== "mapM_putStrLnlog
The Reader monad allows functions to use shared state (or a shared environment) to operate with. According to the Hackage page:
The Reader monad (also called the Environment monad). Represents a computation, which can read values from a shared environment, pass values from function to function, and execute sub-computations in a modified environment.
If many of your functions require the same shared values (think like a config file, application settings or just shared state), rather than adding a new parameter to all of your functions that require this information you can put your functions into the Reader monad which will give you access to this state.
Key Pieces
The Reader constructor takes the form of Reader s v where s is your state type and v is your function return type.
The ask function is what you’ll use to retrieve the state value for use in your own functions.
To run the Reader monad you use the runReader function.
An Example
importControl.Monad.Reader-- | Shared configuration for this application.-- Rather trivial (and useless), it just configures how our application will-- address the user --dataSalutationConfig=SalutationConfig{formal::Bool}-- | Returns a greeting-- Takes in someone's name and returns a greeting string--greeter::String->ReaderSalutationConfigStringgreetername=do-- grab the configuration outcfg<-ask-- grab the "formal" setting from the configletf=formalcfg-- send out the valuereturn(makeSalutationf++name)-- | Makes a salutation for a "formal" settingmakeSalutation::Bool->StringmakeSalutationTrue="Good day, "makeSalutationFalse="Wasaaaaaaaap, "main::IO()main=do-- create the configurationletcfg=SalutationConfig{formal=False}-- run the reader with the configuration for a guy named "Michael"letmsg=runReader(greeter"Michael")$cfg-- "Wasaaaaaaaaaap, Michael"putStrLnmsg
Install the operating system implementations of the OCR programs. In order to do this, you my need to enable the non-free repositories within your apt settings.
Some applications that you write from time to time may require you to study changes that occur on the file system. These changes could be that a file arrives, is modified, is closed, etc. Your program can then respond accordingly to these interesting events.
In today’s post, I’ll show you how to monitor the file system for changes using the hinotify package.
What is inotify?
inotify is short for inode notify. It’s a piece of the Linux Kernel that adds notifications at the filesystem level so that userspace programs can take advantage of these events. The Wikipedia page on inotify has a good explanation for further reading.
The Code
There are 3 main jobs that we need to take care of here:
Create awareness with INotify
Register your interest in changes
Respond to the changes
moduleMainwhereimportControl.Concurrent(threadDelay)importSystem.INotifymain::IO()main=do-- the paths that we'll monitorletpaths=["/tmp","/home/user"]-- setup INotifywithINotify$\n->do-- monitor each predefined path, and respond using printEventmapM_(\f->addWatchn[Modify,CloseWrite]f(printEventf))paths-- this gives "addWatch" some time to collect some datathreadDelay10000000where-- print the file and event to the consoleprintEvent::FilePath->Event->IO()printEventfe=putStrLn(f++": "++showe)
I’ve tried to comment this code as best I can to show you what’s going on. It’s all pretty straight forward. Delaying the main thread may seem unintuitive, however without this call being made the program will finish execution without collecting data (because INotify doesn’t block!).