2.7. State-I/O monad from module XmlState

The module XmlState provides a monad for an internal state and I/O commands. The concept of a monad comes from category theory. Monads are used for programming in a functional language using an imperative style. A monad can encapsulate computations such as I/O, manipulation of state or exceptions. For further information about monads, the section "Using Monads" of the Haskell Bookshelf [WWW17] is a good starting place.

XmlState is used for various parts of the XML parser, e.g. when building the parse tree or substituting entities. The internal state of the monad from XmlState consists of two parts, the user state and the system state. The user state is a type parameter, the system state is a list of name-value pairs. If the user state is not needed, the type parameter can be instantiated with ().

The monad provides trace functions combined with trace levels to output marked computation steps. Error reporting functions are also located in this module. The error messages are printed to stderr and the maximum error level is stored in the system state.

Furthermore there are types for XmlFilter functions working on this monad, functions for manipulating the state components and for lifting I/O commands and XmlFilters to monad filters.

Figure 2-3. Modules of XmlState

The internal state consists of a system and a user state.


data XmlState state       = XmlState { sysState  :: SysState
                                     , userState :: state
                                     }
		

The system state consists of a list of name-value pairs of type String.


type SysState             = TagAttrl
		

The monad type for commands. It is an instance of StateIO from the general module MonadStateIO.


type XState state res     = MonadStateIO.StateIO (XmlState state) res
		

The XmlFilter type for filters working on a state.


type XmlStateFilter state = XmlTree -> XState state XmlTrees
		

Functions for executing XState commands

run0 :: XmlState state -> XState state res -> IO (res, XmlState state)

Executes an XState command with an initial state.

run :: state -> XState state res -> IO res

Executes an XState command with an initial user state.

run' :: XState () res -> IO res

Executes an XState command in the I/O monad.

The following example shows the use of the State-I/O monad. All computations of processXmlN take place in the State-I/O monad. The functions getXmlContents, parseXmlDoc and putXmlTree are XmlStateFilters. The first two filters do some computations where errors might occur. These XmlStateFilters can output the errors and set an error level in the State-I/O monad. The value of the error level is tested before the constructed parse tree is returned. The XmlStateFilters putXmlTree just outputs the constructed parse tree.

Example 2-5. Using the monad from XmlState


processXmlN	:: XmlTree -> IO [XmlTree]
processXmlN t0
    = run' $ do
             setSysState (selXTagAttrl . getNode $ t0)
             setTraceLevel 0
             t1 <- getXmlContents $ t0
             t2 <- parseXmlDoc    $$< t1
             putXmlTree t2
             el <- getErrorLevel
             return ( if el == 0
                      then t2
                      else [] )
			

The functions setSysState, setTraceLevel and getErrorLevel are functions for accessing and manipulating the state of the monad. The monad comes with lots of more access functions.