Applying a function that may fail to all values in a list
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??
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).
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)
.
精彩评论