views:

129

answers:

2

I want to apply a function f to a list of values, however function f might randomly fail (it is in effect making a call out to a service in the cloud).

I thought I'd want to use something like map, but I want to apply the function to all elements in the list and afterwards, I want to know which ones failed and which were successful.

Currently I am wrapping the response objects of the function f with an error pair which I could then effectively unzip afterwards

i.e. something like

g : (a->b) -> a -> [ b, errorBoolean]

f : a-> b

and then to run the code ... map g (xs)

Is there a better way to do this? The other alternative approach was to iterate over the values in the array and then return a pair of arrays, one which listed the successful values and one which listed the failures. To me, this seems to be something that ought to be fairly common. Alternatively I could return some special value. What's the best practice in dealing with this??

+2  A: 

You could write your g to return a Maybe monad:

f: a -> b
g: (a -> b) -> a -> Maybe b

If f fails, g returns Nothing, otherwise it returns Just (f x).

outis
+15  A: 

If f is making a call out to the cloud, than f is undoubtedly using some monad, probably the IO monad or a monad derived from the IO monad. There are monadic versions of map. Here is what you would typically do, as a first attempt:

f :: A -> IO B -- defined elsewhere
g :: [A] -> IO [B]
g xs = mapM f xs
-- or, in points-free style:
g = mapM f

This has the (possibly) undesirable property that g will fail, returning no values, if any call to f fails. We fix that by making it so f returns either an answer or an error message.

type Error = String
f :: A -> IO (Either Error B)
g :: [A] -> IO [Either Error B]
g = mapM f

If you want all of the errors to be returned together, and all of the successes clumped together, you can use the lefts and rights functions from Data.Either.

h :: [A] -> IO ([B], [Error])
h xs = do ys <- g xs
          return (rights ys, lefts ys)

If you don't need the error messages, just use Maybe B instead of Either Error B.

The Either data type is the most common way to represent a value which can either result in an error or a correct value. Errors use the Left constructor, correct values use the Right constructor. As a bonus, "right" also means "correct" in English, but the reason that the correct value uses the Right constructor is actually deeper (because this means we can create a functor out of the Either type which modifies correct results, which is not possible over the Left constructor).

Dietrich Epp