kazu-yamamoto/ghc-mod — pull request #301 by DanielG (2014-08-06 19:11:45+00:00)
Title: Rework GhcModT Monad stack, begin integrating ErrorT and some cleanups

As I mentioned in #292 I'd like to improve the way we do error handling in ghc-mod. The way I imagine it is the following:

We should add more descriptive, user facing errors everywhere. As an example look at [`getSymbolCachePath`](https://github.com/DanielG/ghc-mod/commit/459106a6d54b4a13d0994e3e8a0c8bf0f4ef1250#diff-7a52db770876e3ec83078c1e68378487R121) from this changeset. Here I am catching two pattern match failures using `catchError` and re-throwing a more descriptive error message.

As a general rule of thumb anything that can `fail` (as in the `Monad` method) should get a surrounding `catchError` with a description of what the function was trying to do. If a function is trying to do more than one thing it should probably be split up into multiple seperate functions anyways. All uses of the major partial functions (i.e fromJust, fromLeft, head etc.) should probably be replaced by pattern matching in the do block (since pattern match failures are very easy to handle with `catchError`). We also have to be careful with anything that uses liftIO since that might introduce unexpected exceptions. For example [`parseCabalFile`](https://github.com/kazu-yamamoto/ghc-mod/blob/master/Language/Haskell/GhcMod/CabalApi.hs#L74) still uses IOError's for error reporting which would circumvent `ErrorT` and make handling that error with MonadError impossible. Stuff like that should probably be converted to `GhcModT` or return `IO (Either String SomeThing)` instead. It should be pretty easy to write a function to lift `IO (Either e a)` into a `GhcModT a` (where e could be `String`, ´GhcModError` or something like that).

Pure code that uses fromJust etc. should probably use the `Maybe` or `Either e` monad instances instead. For an example look at [Language.Haskell.GhcMod.CabalConfig](https://github.com/kazu-yamamoto/ghc-mod/blob/master/Language/Haskell/GhcMod/CabalConfig.hs#L91) where I use Either, Maybe monadically quite a lot.

The nice thing about using MonadError and `catchError` rather than the normal exception handling stuff is that you don't need to disambiguate the exception type (which always results in ugly code) as that's fixed to a concrete type in the instances of MonadError. For example the error type for `IO` is `IOException` and `GhcModT`'s error type is `GhcModError`.

This changeset also adds support for logging to the monad stack using monad-journal, which is basically a newtype wrapper around StateT that behaves kind of like a WriterT but is as fast as StateT.