# To Dream of Magick

## Remembering Amelia

Savanni D'Gerinel 7 Feb, 2018

Tonight, I want to tell you about a dear friend. Someone very important to myself and to my partner, Daria.

Hir name was Amelia. Ze took hir own life late in the night last Tuesday. I found out as soon as I woke up Wednesday morning. A week ago today.

Amelia was gorgeous. Ze was brilliant. Ze sparkled, flirted, brightened spaces, tied innuendo into unexpected sentences, and could talk about advanced mathematics that I cannot begin to understand.

But, honestly, we never talked about advanced math or technology. Amelia was passionate about social justice. Ze came into our community during a chance run-in at a march in solidarity for immigrant’s rights. Late in hir life, ze started campaigning for patient’s rights in mental health. This hyper focus came about from continuous failures in her stays in various psych wards here in Boston. Some of these failures caused significant harm.

Make no mistake. While Amelia may have died at hir own hand, ze was driven to it. Multiple assaults from random men on the street who continue to feel liberty in how they treat women, followed by hospitalizations that dehumanized hir, ignored hir, and in some cases, did significantly more harm.

I miss my friend.

I miss a friendship lost.

I regret the tensions between us, the things we did not have time to heal.

The day we all found out, the morning after Amelia died, seventeen people gathered for an impromptu wake.

Let that sink in. Seventeen people dropped everything to gather, weep, and reminisce, with no advance notice. Queer community is not large, but it is tightly connected and our members are loved. To quote my friend Aria:

Queer culture is grieving as a family no outsider would recognize, a haphazard landslide of lovers & friends. The ones who bring the tequila & gin, the ones who bring the casseroles, the ones who bring the cookies and apologize about the butter, the ones who make a big thai curry

While we can never change what outsiders would see, I want very much to change the definition of “outsider”. While I always live visibly, I have not in the past put much effort into broadcasting that image, and it thus became possible for people ostensibly in my life to completely ignore that which they did not see. But that means that, should I die, there are many people who would have to learn about the full me only through pictures and stories. If they learn at all. Perhaps they would actively ignore.

For now, however, I want to actively try to show what a happy, healthy life outside of the norm of cisgender, heterosexual, monogamy looks like.

## Memoru Amelia

Savanni D'Gerinel 7 Feb, 2018

Memoru Amelia

## Haskell Exceptions and Abstractions, or Part 2 of Abstracting the Monad Stack

Savanni D'Gerinel 11 Nov, 2017

Error handling is a big deal for me. I have probably studied this more than anything else in Haskell because I find it so important to carefully handle unexpected errors. I have presented on this before, but those older presentations lacked a nuance of understanding that I have developed in the last few years.

This article carries on from my previous one, Abstracting the Monad Stack. In that article, I was unable to get all the way to exception abstraction. That this article took me so long to write demonstrates that I was correct in my assessment that this was too much for me to roll into my previous article.

# Abstracting out the Exception

Code for this section

I still want to throw exceptions from my library, and my library has the HealthException type:

data HealthException = TimeSeriesExc SeriesExc
| UnknownException String
deriving (Eq, Show)



However, the higher-level context of my application frequently has a different exception type, which may well encompass exceptions from other modules:

data WebExc = WebExcHealth HealthException
| WebExcHttp HttpException
| AppForbidden
| AppUnauthorized


Typically, I would have to unwrap the monad stack for my library in order to rewrap the exception class into the WebExc exception. Again, this works, but it creates tedious boilerplate that we would like to remove:

handleGetHistory :: Interval UTCTime -> WebM ([Sample Weight], [Sample TimeDistance], [Sample Steps])
handleGetHistory interval = do
weightRes <- getWeights interval
timeDistanceRes <- getTimeDistance interval
stepRes <- getSteps interval

case (weightRes, timeDistanceRes, stepRes) of
(Left err, _, _) -> throwError $WebExcHealth err (_, Left err, _) -> throwError$ WebExcHealth err
(_, _, Left err) -> throwError $WebExcHealth err (Right weights, Right timeDistances, Right steps) -> pure (weights, timeDistances, steps)  In order to make this goal, we can do something similar to HasHealthContext, but this time for writing. Start out by building a class that describes the process of “wrapping” this exception. We must also declare such an exception in our type constraints. class AsHealthException exc where _HealthException :: HealthException -> exc _TimeSeriesExc :: SeriesExc -> exc _TimeSeriesExc = _HealthException . TimeSeriesExc _UnknownException :: String -> exc _UnknownException = _HealthException . UnknownException type Health r exc m = (MonadIO m, MonadReader r m, HasHealthContext r, MonadError exc m, AsHealthException exc)  This class declares a generic function that wraps all health exceptions, and then two dedicated functions for simply creating and wrapping a TimeSeriesExc and an UnknownException. The new components to the type constraint both declare that the calling monad must implement MonadReader, and that the exceptions being raised in the monad must support AsHealthException. In the library code, you use these exceptions like so: getWeights :: Health r exc m => Interval UTCTime -> m a getWeights = throwError$ _HealthException $UnknownException "demonstrating with _HealthException" getTimeDistance :: Health r exc m => Interval UTCTime -> m a getTimeDistance = throwError$ _UnknownException "demonstrating with _UnknownException"


Note how the exception is no longer explicitely called out in the type signatures for getWeights or getTimeDistance. However, that an exception is thrown is still strictly type checked and even documented in the constrant on the exc parameter in Health r exc m.

The caller must implement this class to make the wrapping transparent.

instance AsHealthException WebExc where
_HealthException = WebExc . WebExcHealth


With this one change, we almost magically see our calling code collapse into something quite reasonable.

handleGetHistory :: Interval UTCTime -> WebM ([Sample Weight], [Sample TimeDistance], [Sample Steps])
handleGetHistory interval = do
weights <- getWeights interval
timeDistances <- getTimeDistance interval
steps <- getSteps interval
pure (weights, timeDistances, steps)


# Handle an exception inside the monad

Code for this section

Let’s say that there are certain exceptions that you need to handle in place. For instance, assume that when I save a TimeDistance workout, I want to update some summary data. This is slightly contrived since in this case I would usually generate the summaries based on queries, but it serves to illustrate a point.

handleSaveTimeDistance got quite a few updates alongside handleGetHistory, so it has changed from our original example and looks like this:

handleSaveTimeDistance :: Maybe SampleID -> SetTimeDistanceParameters -> WebM (Sample TimeDistance)
handleSaveTimeDistance sampleId params =
let workoutFromParams = undefined
workout = workoutFromParams params
in saveTimeDistance sampleId workout


Now I add the update function, keeping it within the Health monad for simplicity. I also add a fictitious rollback and commit functions. If written, they would assume that, like in any database setup, they are safely written to disk but in a way that does not take effect until a single monotonic commit function happens. Just for the fun of it, I’ll also add a checkAuthorization function, which would be run before actually saving any data to disk.

checkAuthorization :: WebM ()
checkAuthorization = undefined

updateTimeDistanceSummary :: Health r exc m => TimeDistance -> m a
updateTimeDistanceSummary _ = undefined

commit :: Health r exc m => m ()
commit = undefined

rollback :: Health r exc m => m ()
rollback = undefined


However, this turns out to be super simple. Remember that I am working in MonadError, and so I have access to the already-familiar catchError. I have no need for anything complicated.

handleSaveTimeDistance :: Maybe SampleID -> SetTimeDistanceParameters -> WebM (Sample TimeDistance)
handleSaveTimeDistance sampleId params =
let workoutFromParams = undefined
workout = workoutFromParams params
in
catchError (do checkAuthorization
res <- saveTimeDistance sampleId workout
updateTimeDistanceSummary workout
commit
pure res)
handler
where
handler err@(WebExcHealth _) = do
rollback
throwError err
handler exc = throwError exc


# Sprinkling in a bit of Template Haskell

Code for this section

The class declaration for AsHealthException above still looks like boilerplate, but I present it as explanation. The Lens library actually provides a function that does exactly this. Note that introducing TemplateHaskell also requires introducing additional files. Code generation from the lenses (or any other TemplateHaskell code generation) does not become available in the file in which it is declared.

{-# LANGUAGE TemplateHaskell #-}

import Control.Lens (makeClassyPrisms, prism)

data HealthException = ...

makeClassyPrisms ''HealthException


The library code must be changed just a bit. The functions on AsHealthException actually create Prisms now, a fairly complex data structure that I only barely grasp. In order to throw the exception, it must first be injected appropriately into a prism, and so the code becomes this:

getWeights :: Health r m => Interval UTCTime -> m (Either HealthException a)
getWeights = throwError $review _HealthException$ UnknownException "demonstrating with _HealthException"

getTimeDistance :: Health r m => Interval UTCTime -> m (Either HealthException a)
getTimeDistance = throwError $review _UnknownException "demonstrating with _UnknownException"  Client code is a little different, also, but only in the AsHealthException instance: instance AsHealthException WebExc where _HealthException = prism WebExcHealth unwrap where unwrap (WebExcHealth exc) = Right exc unwrap exc = Left exc  Again, _HeathException is now a prism, as are _TimeSeriesExc and _UnknownException. This adds some extra options for unwrapping the exception, but I do not currently use, or have a good example of, such a handler. # End of a journey This has already been a long journey in the making. At the end of it, however, you have learned how to use type constraints to effectively abstract an entire monad stack, making a significantly more reusable and simultaneously much easier to read and use. This is probably not the only way to set up reusable monad stacks, but it is the one that I find the easiest to understand, the easiest to build, and the easiest to use. I ask you to try this kind of setup for your own code to see where it works and where it breaks. I also would like feedback on how well this worked for you and whether you have a different means of building the same kind of flexibility. While we have covered a lot of ground, there is much more to do. In my next article I will provide a summary template of everything we have built here. After that, it is time to start designing architectures that can handle dependency injection for mocking out resources in test, or even allowing run-time configuration of resource backends. Haskell Exceptions and Abstractions, or Part 2 of Abstracting the Monad Stack by Savanni D’Gerinel is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. ## Abstracting the Monad Stack, Part 1 Savanni D'Gerinel 21 Sep, 2017 As a sidenote, my previous articles were all in terms of a fictitious image processing program. I am actually very interested in image processing, but that is not the code I have on hand, so for my examples, I am switching over to a health-tracking application that I’ve been working on for a while. I’ll probably change the previous articles to reflect it. I am making this change, though, primarily because there are so many real examples for me to draw from. Here is the application in question, and it is under active development. I have some layer refactoring to do, but the code is stable and I am mostly focusing my efforts on additional features. # Building a library monad Code for this section I previously talked about application monads, but today I will talk about a library monad. Fortunately for everyone, they are almost identical to application monads, but with one twist that I want to lead you to. In my health application, I have a library that forms the “application” and that library is meant to be wrapped around interface layers, such as the web API or a GUI. The application library is basically a set of functions that make a complete API that I can execute in the REPL. So, assume a monad like this one: data AppContext = App { weightSeries :: TimeSeries Weight , timeDistanceSeries :: TimeSeries TimeDistance , stepSeries :: TimeSeries Steps } -- TODO: handle time series exceptions. Make this less absurd. data HealthException = TimeSeriesExc SeriesExc | UnknownException String deriving (Eq, Show) newtype HealthM a = HealthM (ReaderT AppContext (ExceptT HealthException IO) a) deriving (Functor, Applicative, Monad, MonadIO, MonadError HealthException, MonadReader AppContext) runHealthM :: AppContext -> HealthM a -> IO (Either HealthException a) runHealthM ctx (HealthM act) = runExceptT (runReaderT act ctx)  (yes, that is a real TODO item in the code) On it’s own, this isn’t bad. But the pain lies in that this is a library, and thus will likely end up in a different monad stack. As it is written, I would need to unroll this stack into IO and then re-roll it into my web stack. This is not horrible, but it is annoying. In the health application, I would do the re-rolling to glue these functions into my Web application monad, and it would look like this: data Config = Config data WebContext = WebContext { config :: Config, app :: AppContext } newtype WebM a = WebM (ReaderT WebContext (ExceptT WebExc IO) a) deriving (Functor, Applicative, Monad, MonadIO, MonadError WebExc, MonadReader WebContext) handleSaveTimeDistance :: Maybe SampleID -> SetTimeDistanceParameters -> WebM (Sample TimeDistance) handleSaveTimeDistance sampleId params = let workoutFromParams = undefined workout = workoutFromParams params in do WebContext{..} <- ask res <- liftIO$ runHealthM app $saveTimeDistance sampleId workout case res of Left err -> throwError$ AppExc err
Right val -> return val

saveTimeDistance :: Maybe SampleID -> TimeDistance -> HealthM (Sample TimeDistance)


Again, this is not awful, but it is tedious. It can also become awful if I want to perform multiple operations from the library interleaved with operations from my webapp. For example, what if I want to query every series that I am storing?

handleGetHistory :: Interval UTCTime -> WebM ([Sample Weight], [Sample TimeDistance], [Sample Steps])
handleGetHistory interval = do
weightRes <- liftIO $runHealthM app$ getWeights interval
timeDistanceRes <- liftIO $runHealthM app$ getTimeDistance interval
stepRes <- liftIO $runHealthM app$ getSteps interval

case (weightRes, timeDistanceRes, stepRes) of
(Left err, _, _) -> throwError $AppExc err (_, Left err, _) -> throwError$ AppExc err
(_, _, Left err) -> throwError $AppExc err (Right weights, Right timeDistances, Right steps) -> pure (weights, timeDistances, steps) getWeights :: Interval UTCTime -> HealthM [Sample Weight] getTimeDistance :: Interval UTCTime -> HealthM [Sample TimeDistance] getSteps :: Interval UTCTime -> HealthM [Sample Steps]  handleGetHistory already becomes tedious. # Rewrapping the context Code for this section The first, most obvious solution, is a helper function to re-wrap: wrapEitherIO :: (exc -> WebExc) -> IO (Either exc a) -> WebM a wrapEitherIO excTr act = liftIO act >>= either (throwError . excTr) pure handleGetHistory :: Interval UTCTime -> WebM ([Sample Weight], [Sample TimeDistance], [Sample Steps]) handleGetHistory interval = do WebContext{..} <- ask weights <- wrapEitherIO AppExc$ runHealthM app $getWeights interval timeDistances <- wrapEitherIO AppExc$ runHealthM app $getTimeDistance interval steps <- wrapEitherIO AppExc$ runHealthM app $getSteps interval pure (weights, timeDistances, steps)  And then, probably even one step further with a utility function to do the re-wrapping. wrapEitherIO :: (exc -> WebExc) -> IO (Either exc a) -> WebM a wrapEitherIO excTr act = liftIO act >>= either (throwError . excTr) pure runHealthMInWebM :: (HealthException -> WebExc) -> AppContext -> HealthM a -> WebM a runHealthMInWebM handler app = wrapEitherIO handler . runHealthM app handleGetHistory :: Interval UTCTime -> WebM ([Sample Weight], [Sample TimeDistance], [Sample Steps]) handleGetHistory interval = do WebContext{..} <- ask weights <- runHealthMInWebM AppExc app$ getWeights interval
timeDistances <- runHealthMInWebM AppExc app $getTimeDistance interval steps <- runHealthMInWebM AppExc app$ getSteps interval
pure (weights, timeDistances, steps)


This alone makes life much nicer. All of the exception checking boilerplate gets encapsulated into wrapEitherIO, and so every step of handleGetHistory gets to exist on the happy path. In many instances, I could just call this done.

Servant actually provides a typeclass for natural transformations which abstracts this away. It has a challenging type signature, but it is pretty nice and I recommend taking a look at it.

# Type Constraints

Code for this section

I use type constraints as my preferred method for solving this problem. The idea behind it is that I try to have only one concrete monad stack anywhere in the application.

A “type constraint” is a mechanism by which I declare that a context must implement a particular typeclass, but that the context could be any context that implements that typeclass. A trivial example would be like this:

printSomeStuff :: (Show a, MonadIO m) => a -> m ()
printSomeStuff a = do
liftIO $putStrLn$ show a


This function will print out any value, so long as the value implements Show and so long as the function is called in any monad that implements MonadIO. For instance, all three of these calls to printSomeStuff are valid:

run1 :: IO ()
run1 = printSomeStuff "abcd"

run2 :: ExceptT String IO ()
run2 = printSomeStuff "abcd"

run3 :: MonadIO m => m ()
run3 = printSomeStuff "abcd"


Now, we build up on this concept, and to do so I’m going to repack all three of my get functions, this time starting from the simplest possible implementation.

saveTimeDistance :: Maybe SampleID -> TimeDistance -> AppContext -> IO (Either HealthException a)

handleSaveTimeDistance :: Maybe SampleID -> SetTimeDistanceParameters -> WebM (Sample TimeDistance)
handleSaveTimeDistance sampleId params =
let workoutFromParams = undefined
workout = workoutFromParams params
in do
res <- liftIO $saveTimeDistance sampleId workout app case res of Left err -> throwError$ AppExc err
Right val -> pure val


saveTimeDistance can function in any monad that implements MonadIO, so the first thing I will do is to abstract that away:

saveTimeDistance :: (MonadIO m) => Maybe SampleId -> TimeDistance -> AppContext -> m (Either HealthException a)

handleSaveTimeDistance :: Maybe SampleID -> SetTimeDistanceParameters -> WebM (Sample TimeDistance)
handleSaveTimeDistance sampleId params =
let workoutFromParams = undefined
workout = workoutFromParams params
in do
res <- saveTimeDistance sampleId workout app
case res of
Left err -> throwError $AppExc err Right val -> pure val  This detaches me from a particular monad stack. This function can now be called as-is from any context that implements, hence in the above code, I no longer need to apply liftIO to saveTimeDistance. For bookkeeping, and because I am going to build upon this abstraction, I will give that type constraint a name: type HealthM m = MonadIO m saveTimeDistance :: HealthM m => Maybe SampleID -> TimeDistance -> AppContext -> m (Either HealthException a)  The next step requires a fairly large jump. I want to eliminate that AppContext parameter. It is required for every function in the health application, so it would be nice if I could pass it as part of a MonadReader. The naive solution would be to just do this: type HealthM m = (MonadIO m, MonadReader AppContext m) saveTimeDistance :: HealthM m => Maybe SampleID -> TimeDistance -> m (Either HealthException a)  Unfortunately, this actually is of detriment in the caller. If the caller has its own context in a MonadReader, that context is not likely to be the same as this one. The result is code that looks like this: handleSaveTimeDistance :: Maybe SampleID -> SetTimeDistanceParameters -> WebM (Sample TimeDistance) handleSaveTimeDistance sampleId params = let workoutFromParams = undefined workout = workoutFromParams params in do WebContext{..} <- ask res <- runReaderT (saveTimeDistance sampleId workout) app case res of Left err -> throwError$ AppExc err
Right val -> pure val


I definitely do not want to be going in the direction of having to re-add a run function stack, but that is how this goes. The caller has to explicitely pull out the context for this call.

In order to get around this, I have to think a bit differently. I still want an implicit context of AppContext. But, really, the context could be larger so long as AppContext is present in it. So an alternate solution looks like this:

type HealthM r m = (MonadIO m, MonadReader WebContext m)

saveTimeDistance :: Health r m => Maybe SampleID -> TimeDistance -> m (Either HealthException a)
saveTimeDistance = undefined

handleSaveTimeDistance :: Maybe SampleID -> SetTimeDistanceParameters -> WebM (Sample TimeDistance)
handleSaveTimeDistance sampleId params =
let workoutFromParams = undefined
workout = workoutFromParams params
in do
res <- saveTimeDistance sampleId workout
case res of
Left err -> throwError $AppExc err Right val -> pure val  In some ways, this looks better. The caller now can simply treat saveTimeDistance as part of WebM. But now saveTimeDistance becomes aware of WebContext, and so is beholden to a single caller. This is better, but not good enough. What I want is a way to specify that saveTimeDistance can take any context, so long as that context provides me with a way to extract the AppContext. So, this is a constraint upon a constraint, and it ends up looking like this: type HealthM = (MonadIO m, MonadReader r m, HasHealthContext r)  Basically, a HealthM function can take any MonadReader that provides r, so long as r “has a health context”. My library gets to declare the HasHealthContext interface. The caller needs to implement that interface for its own context. type Health r m = (MonadIO m, MonadReader r m, HasHealthContext r) class HasHealthContext ctx where hasAppContext :: ctx -> AppContext WebContext = WebContext { config :: Config, app :: AppContext } instance HasHealthContext WebContext where hasAppContext WebContext{..} = app saveTimeDistance :: Health r m => Maybe SampleID -> TimeDistance -> m (Either HealthException a) saveTimeDistance _ _ = do appCtx <- hasAppContext <$> ask
...


With similar improvements made to getWeights, getTimeDistance, and getSteps, handleGetHistory also gets much nicer, and that demonstrates exactly what we wanted to begin with:

handleGetHistory :: Interval UTCTime -> WebM ([Sample Weight], [Sample TimeDistance], [Sample Steps])
handleGetHistory interval = do
weightRes <- getWeights interval
timeDistanceRes <- getTimeDistance interval
stepRes <- getSteps interval

case (weightRes, timeDistanceRes, stepRes) of
(Left err, _, _) -> throwError $AppExc err (_, Left err, _) -> throwError$ AppExc err
(_, _, Left err) -> throwError $AppExc err (Right weights, Right timeDistances, Right steps) -> pure (weights, timeDistances, steps) getWeights :: Health r m => Interval UTCTime -> m (Either HealthException a) getWeights = undefined getTimeDistance :: Health r m => Interval UTCTime -> m (Either HealthException a) getTimeDistance = undefined getSteps :: Health r m => Interval UTCTime -> m (Either HealthException a) getSteps = undefined  # Looking forward Not quite there yet. We still have some tedium with exception handling to do. In this system, any thrown SeriesExc must be caught and then re-wrapped in the HealthException in order for the application to typecheck and for the exception to propogate upwards. This sort of tedium likely drove the creation of extensible IO exceptions, which I view as unchecked and undocumented parts of the type signature. So, the next step will be to abstract the exception throwing mechanism. Abstracting the Monad Stack, Part 1 by Savanni D’Gerinel is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. ## Haskell Application Monad Savanni D'Gerinel 14 Jul, 2017 We want to get productive in Haskell very quickly. Most non-trivial applications will have configuration, connections to the outside world, can hit exceptional conditions, and benefit from having their operations logged. If your application has sensible logs at both high an low levels of detail, your devops team will thank you and your life of debugging a production application will be a happier one. I want to get all of these things at once, and so it would be nice to provide a nearly boilerplate application stack that provides them all. I define the “application stack” as a group of attributes that contain the context and all of the common behaviors for an application. In Haskell, you do that with a monad stack, though work on extensible effects shows a great deal of promise and has been used to great effect in Purescript. That said, I use monads and monad transformers, and I’ll not explain either of them today. I feel that the best explanation is a non-trivial example implementation, which I will do in a future article, or refer you to a better tutorial. While most of this article explains the process, the final result is this application stack, which may be all you need if you are already familiar with building monad transformer stacks. data Context = Context { contextRoot :: FilePath } deriving Show data AppError = AppError deriving Show newtype AppM a = AppM (LoggingT (ReaderT Context (ExceptT AppError IO)) a) deriving ( Functor, Applicative, Monad, MonadIO , MonadError AppError, MonadReader Context, MonadLogger) runAppM :: Context -> AppM a -> IO (Either AppError a) runAppM ctx (AppM act) = runExceptT (runReaderT (runStderrLoggingT act) ctx)  # The most basic stack Almost every application needs IO. In Haskell it is difficult to do IO on top of anything (see MonadBaseControl for way), so I always put it at the bottom of the monad stack. A trivial application stack would look like this: newtype AppM a = AppM (IO a) deriving (Functor, Applicative, Monad, MonadIO)  This is so trivial you will likely never do it, though it can be helpful in that it prevents confusion between your functions and system IO functions. Still, let’s build out what you need to make this work. First of all, you do want AppM to be a monad, and you will need MonadIO in order to actually run IO operations. The primary use that I have for Monads in an application is to eliminate the boilerplate involved with a lot of threading context through a series of function calls. More to the point, though, you cannot get MonadExcept, MonadReader, or MonadLogger into this stack without having Monad to begin with. newtype AppM a = AppM (IO a) deriving (Functor, Applicative, Monad, MonadIO) runAppM :: AppM a -> IO a runAppM (AppM act) = act  runAppM is the function that connects your application stack to the Haskell IO stack. This is everything you need in order to create a stack: the stack itself and the runner. Now let’s see it in action: data Image = Image deriving Show loadImage :: FilePath -> AppM Image loadImage path = do liftIO$ putStrLn $"loadImage: " <> path pure Image main :: IO () main = do res <- runAppM$ do
pure (img1, img2)
print res


IO a is too simple to make much sense. The whole point of having a stack is to unify a lot of effects within a common framework of behavior and with a common context. So, next we load and add a context.

In almost every circumstance, your context is read-only. This points us directly to ReaderT, since you will want to be able to ask for the context but never write back to it. Application state would seem like a thing that you would want to include, if your application stores state. I have generally found that it is easier to keep application state in something that is strictly IO, such as an IORef or a TVar. For now, we shall skip that.

So, change your stack to look like this:

data Context = Context { root :: FilePath } deriving Show

newtype AppM a = AppM (ReaderT Context IO a)

runAppM :: Context -> AppM a -> IO a
runAppM ctx (AppM act) = runReaderT act ctx


The addition of MonadReader means that now you can call ask within your function to get back the context, and you don’t have to explicitely pass the context in. The remaining functions get updated like so:

loadImage :: FilePath -> AppM Image
liftIO $putStrLn$ "loadImage: " <> (contextRoot </> path)
pure Image

loadContext = pure $Context { contextRoot = "/home/savanni/Pictures/" } main :: IO () main = do ctx <- loadContext res <- runAppM ctx$ do
pure (img1, img2)
print res


Suddenly, everything in Context is available to every function that runs in AppM. You get the local effect of global parameters while still getting to isolate them, potentially calling the same functions with different contexts within the same application.

# Add exception handling and logging

Exceptions happen. The Haskell community is split between what I call explicit vs. implicit exceptions. In short, implicit exceptions are not declared in the type signature, can happen from any function, and can only be caught in IO code. Explicit exceptions are explicitely stated in the type signature and can be caught just about anywhere. I prefer them for all of my application errors. I’ll give exception handling further treatment in a future article, and will show the use of explicit exceptions here.

Logging is almost always helpful for any application that is not of trivial size. And, once present, it can replace print for debugging, allowing debugging lines to remain present in the code for those cases when something starts going wrong in production.

First, the new application stack:

data AppError = AppError deriving Show

newtype AppM a = AppM (LoggingT (ReaderT Context (ExceptT AppError IO)) a)

runAppM :: Context -> AppM a -> IO (Either AppError a)
runAppM ctx (AppM act) = runExceptT (runReaderT (runStderrLoggingT act) ctx)


This gets quite a bit more complicated with both the Logging and Exceptions being added. Remember that I use the term “stack” here, and each monad transformer involved represents another layer in the stack. When running the stack, you must peel off each layer in reverse order. I will illustrate with some types:

*Json> :t loadImage "img.png"

*Json> :t unAppM $loadImage "img.png" unAppM$ loadImage "img.png"
:: LoggingT (ReaderT Context (ExceptT AppError IO)) Image

*Json> :t runStderrLoggingT $unAppM$ loadImage "img.png"
runStderrLoggingT $unAppM$ loadImage "img.png"
:: ReaderT Context (ExceptT AppError IO) Image

*Json> :t runReaderT (runStderrLoggingT $unAppM$ loadImage "img.png") ctx
runReaderT (runStderrLoggingT $unAppM$ loadImage "img.png") ctx
:: ExceptT AppError IO Image

*Json> :t runExceptT $runReaderT (runStderrLoggingT$ unAppM $loadImage "img.png") ctx runExceptT$ runReaderT (runStderrLoggingT $unAppM$ loadImage "img.png") ctx
:: IO (Either AppError Image)


The point of this is that in runAppM, the type of act is the entire stack, and the first thing to be called to begin unwrapping is runStderrLoggingT, then runReaderT, and finally runExceptT.

Notice, also, that the final type of runAppM has changed to IO (Either AppError a). runAppM will now return whatever exception gets thrown from within the context it is running, no matter where that exception is thrown, if that exception is thrown with throwException. Exceptions thrown with throw end up being the implicit exceptions I referred to, and those require some extra handling.

So, here is the rest of the code. In the places where I used to print output, I am now logging output. Note that the loggers require TemplateHaskell and have slightly odd syntax, but are otherwise nearly identical to print.

data Image = Image deriving Show

loadImage :: FilePath -> AppM Image
$(logInfo) (T.pack$ "loadImage: " <> (contextRoot </> path))
pure Image

loadContext = pure $Context { contextRoot = "/home/savanni/Pictures/" } main :: IO () main = do ctx <- loadContext do res <- runAppM ctx$ do
pure (img1, img2)
print res

do  res <- runAppM ctx \$ do
throwError AppError
pure (img1, img2)
print res


This is the output from running main:

*Json> main
Right (Image,Image)
Left AppError
*Json>


So, the first block starting with do res <- runAppM runs to completion, returnin two images. The second block, runs loadImage for the first image, but then hits throwError and returns Left AppError, discarding the first image and not loading the second image at all.

This is nearly a application stack that I have used for more applications than I can count. Even if you need only one feature, such as exceptions, starting with a small stack hidden behind an application monad makes it very easy to add additional features as you need them, without needing to change the rest of your code. This pattern is trivial to extend, or contract, as needed, and so I think it starts every application on a good path.