Result
A Result is the result of a computation that may fail. This is a great
way to manage errors in Gren.
A Result is either Ok meaning the computation succeeded, or it is an
Err meaning that there was some failure.
Query
If the result is Ok check if the contained value matches the provided value.
Result.hasValue 123 (Ok 123) == True
Result.hasValue 123 (Ok 5) == False
Result.hasValue 123 (Err "failed") == False
If the result is Ok check if the contained value passes the provided test.
Result.checkValue isOdd (Ok 5) == True
Result.checkValue isOdd (Ok 12) == False
Result.checkValue isOdd (Err "failed") == False
Returns the first Ok value in an Array of Results.
Result.firstOk [ Ok 5, Err 0, Ok 10 ] == Just 5
Result.firstOk [ Err 0, Err 1 ] == Nothing
Convert an Array of Result err ok to Result (Array err) (Array ok). You'll only
receive an Ok if there are no Err values in the Array.
Result.allOk [ Ok 5, Err 0, Ok 10 ] == Err [ 0 ]
Result.allOk [ Ok 0, Ok 1 ] == Ok [ 0, 1 ]
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.
Apply a function if three results are Ok. If not, the first Err will
propagate through.
map3 (+) (Ok 1) (Ok 3) (Ok 5) == Ok 9
map3 (+) (Err "x") (Ok 3) (Ok 5) == Err "x"
map3 (+) (Ok 1) (Err "y") (Ok 5) == Err "y"
This can be useful if you have three computations that may fail, and you want to put them together quickly.
This function lets you map over multiple values. Useful when map2 and map3 aren't enough.
Ok (\one two three four -> one + two + three + four)
|> andMap (Ok 1)
|> andMap (Ok 2)
|> andMap (Ok 3)
|> andMap (Ok 4)
== Ok 10
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 =
when result is
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.
This is similar to andThen but the callback is triggered when
the Result is an Err value. This gives you the option of dealing with errors
in a chain.
toInt "a"
|> onError (\_msg -> Ok 1) -- defaulting to first month of the year
|> andThen toValidMonth
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
Same as withDefault but the default value is provided by a function. This lets you avoid computing the default value if it isn't necessary.
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"