Result
A Result
is the result of a computation that may fail. This is a great
way to manage errors in Gren.
Type and Constructors
A Result
is either Ok
meaning the computation succeeded, or it is an
Err
meaning that there was some failure.
Mapping
Apply a function to a result. If the result is Ok
, it will be converted.
If the result is an Err
, the same error value will propagate through.
map sqrt (Ok 4.0) == Ok 2.0
map sqrt (Err "bad input") == Err "bad input"
Apply a function if both results are Ok
. If not, the first Err
will
propagate through.
map2 max (Ok 42) (Ok 13) == Ok 42
map2 max (Err "x") (Ok 13) == Err "x"
map2 max (Ok 42) (Err "y") == Err "y"
map2 max (Err "x") (Err "y") == Err "x"
This can be useful if you have two computations that may fail, and you want to put them together quickly.
Chaining
Chain together a sequence of computations that may fail. It is helpful to see its definition:
andThen : (a -> Result e b) -> Result e a -> Result e b
andThen callback result =
case result of
Ok value ->
callback value
Err msg ->
Err msg
This means we only continue with the callback if things are going well. For
example, say you need to use (toInt : String -> Result String Int
) to parse
a month and make sure it is between 1 and 12:
toValidMonth : Int -> Result String Int
toValidMonth month =
if month >= 1 && month <= 12 then
Ok month
else
Err "months must be between 1 and 12"
toMonth : String -> Result String Int
toMonth rawString =
toInt rawString
|> andThen toValidMonth
-- toMonth "4" == Ok 4
-- toMonth "9" == Ok 9
-- toMonth "a" == Err "cannot parse to an Int"
-- toMonth "0" == Err "months must be between 1 and 12"
This allows us to come out of a chain of operations with quite a specific error message. It is often best to create a custom type that explicitly represents the exact ways your computation may fail. This way it is easy to handle in your code.
Handling Errors
If the result is Ok
return the value, but if the result is an Err
then
return a given default value. The following examples try to parse integers.
Result.withDefault 0 (Ok 123) == 123
Result.withDefault 0 (Err "no") == 0
Convert to a simpler Maybe
if the actual error message is not needed or
you need to interact with some code that primarily uses maybes.
parseInt : String -> Result ParseError Int
maybeParseInt : String -> Maybe Int
maybeParseInt string =
toMaybe (parseInt string)
Convert from a simple Maybe
to interact with some code that primarily
uses Results
.
parseInt : String -> Maybe Int
resultParseInt : String -> Result String Int
resultParseInt string =
fromMaybe ("error parsing string: " ++ toString string) (parseInt string)
Transform an Err
value. For example, say the errors we get have too much
information:
parseInt : String -> Result ParseError Int
type alias ParseError =
{ message : String
, code : Int
, position : (Int,Int)
}
mapError .message (parseInt "123") == Ok 123
mapError .message (parseInt "abc") == Err "char 'a' is not a number"