
Leon Grynszpan wrote:
What I want, instead, is to run a whole bunch of computations that may throw errors. If there are any errors, I want to collect them all into one big master error. If not, I want a list of results. Here's an example of usage:
couldThrowError :: (Error e, MonadError e m) => t1 -> m t2 getParams :: (Error e, MonadError e m) => [t1] --> m [t2] getParams = groupErrors . map couldThrowError
I found it pretty easy to implement groupErrors for Either String: ... The problem, though, is that running this function now causes type inference to provide "Either String" as my MonadError implementation... It would be nice if I could stay generic.
You might find the following two functions useful:
reify :: (Error e, MonadError e m) => m a -> m (Either e a) reify m = (m >>= return . Right) `catchError` (return . Left)
-- Not needed here, but very nice to have anyway reflect :: (Error e, MonadError e m) => m (Either e a) -> m a reflect m = m >>= either throwError return
Then groupErrors can be written almost the same way you wrote for the Either String monad -- but now generically
groupErrors :: (Monoid e, Error e, MonadError e m) => [m a] -> m [a] groupErrors lst = mapM reify lst >>= \lst -> case partitionEithers lst of ([],xs) -> return xs (errs,_) -> throwError (mconcat errs)
If you don't like the Monoid constraint, this is fine. But you have to provide then some other way to group errors. For example, you could use the new extensible exceptions. Here is the rest of the code:
couldThrowError :: (MonadError String m) => String -> m Int couldThrowError s = case reads s of [(n,"")] -> return n _ -> throwError $ "parse error: " ++ s ++ "\n"
getParams :: (Monoid e, Error e, MonadError e m) => (t1 -> m t2) -> [t1] -> m [t2] getParams f = groupErrors . map f
test :: Either String [Int] test = getParams couldThrowError ["1","2","a","b"]
If you wish to know more about reify and reflect, a good paper to start is the following. Section 1.2 talks directly about exception monads. @InProceedings{ filinski-representing, author = "Andrzej Filinski", title = "Representing Monads", pages = "446--457", crossref = "popl1994", url = "http://www.diku.dk/~andrzej/papers/RM-abstract.html http://www.diku.dk/~andrzej/papers/RM.dvi http://www.diku.dk/~andrzej/papers/RM.ps.gz", abstract = "We show that any monad whose unit and extension operations are expressible as purely functional terms can be embedded in a call-by-value language with ``composable continuations''. As part of the development, we extend Meyer and Wand's characterization of the relationship between continuation-passing and direct style to one for continuation-passing vs.~general ``monadic'' style. We further show that the composable-continuations construct can itself be represented using ordinary, non-composable first-class continuations and a single piece of state. Thus, in the presence of two specific computational effects---storage and escapes---\emph{any} expressible monadic structure (e.g., nondeterminism as represented by the list monad) can be added as a purely definitional extension, without requiring a reinterpretation of the whole language. The paper includes an implementation of the construction (in Standard ML with some New Jersey extensions) and several examples." } There is a follow-up: @InProceedings{ filinski-layered, author = "Andrzej Filinski", title = "Representing Layered Monads", pages = "175--188", crossref = "popl1999", url = "http://www.diku.dk/~andrzej/papers/RLM-abstract.html http://www.diku.dk/~andrzej/papers/RLM.dvi http://www.diku.dk/~andrzej/papers/RLM.ps.gz", } The term reification has been introduced 26 years ago: @InProceedings{ friedman-reification, author = {Daniel P. Friedman and Mitchell Wand}, title = {Reification: Reflection without Metaphysics}, pages = {348--355}, crossref = "lfp1984", abstract = {We consider how the data structures of an interpreter may be made available to the program it is running, and how the program may alter its interpreter's structures. We refer to these processes as reification and reflection. We show how these processes may be considered as an extension of the fexpr concept in which not only the form and the environment, but also the continuation, are made available to the body of the procedure. We show how such a construct can be used to effectively add an unlimited variety of ``special forms'' to a very small base language. We consider some trade-offs in how interpreter objects are reified. Our version of this construct is similar to one in 3-Lisp [Smith 82, 84], but is independent of the rest of 3-Lisp. In particular, it does not rely on the notion of a tower of interpreters.} }