base-io-access

The IO functions included in base delimited into small, composable classes in order to safely restrict IO functions


Keywords
library, system, Propose Tags, , Index, Access.Control.Concurrent, Access.Control.Concurrent.Chan, Access.Control.Concurrent.MVar, Access.Control.Concurrent.QSem, Access.Control.Concurrent.QSemN, Access.Control.Exception, Access.Core, Access.Data.IORef, Access.Data.Unique, Access.Debug.Trace, Access.System.CPUTime, Access.System.Environment, Access.System.Exit, Access.System.IO, Access.System.IO.Error, Access.System.Mem, Access.System.Mem.StableName, Access.System.Mem.Weak, Access.System.Timeout, base-io-access-0.4.0.0.tar.gz, browse, Package description, Package maintainers, bheklilr, edit package information
License
GPL-2.0-only
Install
cabal install base-io-access-0.4.0.0

Documentation

base-io-access

An attempt to break up the monolithic IO monad into small, composable classes that can be used to restrict a function to only having access to, say, functions to work with the standard pipes, or a function that can access the environment. The motivation for this library is to allow people to make a stricter contract than simply "this function does IO", and express through the type system exactly what IO is being performed.

Implementation

The method used is to make a thinly veiled class breaking up groups of IO actions into smaller sub-groups. As an example:

class Access io => ExitAccess io where
    exitWith'       :: ExitCode -> io a
    exitFailure'    :: io a
    exitSuccess'    :: io a

instance ExitAccess IO where
    exitWith'    = exitWith
    exitFailure' = exitFailure
    exitSuccess' = exitSuccess

Here the Access class is an empty super class that combines Monad, Applicative, Functor, Typeable1, and MonadFix. It is the super class for all the others in this library. Maybe eventually I will make it more powerful, or add in special support for handling generic Access actions, but for now it's going to remain very simplistic.

In this example taken from Access.System.Exit, we have defined a small class ExitAccess that has three functions, each corresponding to their implementation in System.Exit but with an extra ' appended to the name. By itself this class isn't very interesting, but when combined with (the larger ) StdIOAccess class, we could write a function like

helpMessage :: (ExitAccess io, StdIOAcess io) => io ()
helpMessage = do
    putStrLn' "Usage:"
    putStrLn' "    mytool [options]"
    putStrLn' "Options:"
    putStrLn' "    --help       Display this help message"
    exitSuccess'

main :: IO ()
main = do
    args <- getArgs
    if "--help" `elem` args
        then helpMessage
        else mainLoop

While this is a simple and contrived example, it illustrates that a function's capabilities are greatly reduced through this method. Since we often want to use the most restrictive type we can in Haskell, this provides an easy way to achieve this when working with the all-encompassing IO monad.

Performance

Since the entire library is based on just using typeclasses, there is no runtime overhead whatsoever. The compiler will optimize away the class lookups, and every function is simply a thin wrapper around its actual implementation in base.