Result

A Result is the result of a computation that may fail. This is a great way to manage errors in Gren.

type Result error value
= Ok value
| Err error

A Result is either Ok meaning the computation succeeded, or it is an Err meaning that there was some failure.

Query

hasValue : a -> Result x a -> Bool

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
checkValue : (a -> Bool) -> Result x a -> Bool

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
firstOk : Array (Result x a) -> Maybe a

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
allOk : Array (Result err ok) -> Result (Array err) (Array ok)

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

map : (a -> value) -> Result x a -> Result x value

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"
map2 :
(a -> b -> value)
-> Result x a
-> Result x b
-> Result x value

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.

map3 :
(a -> b -> c -> value)
-> Result x a
-> Result x b
-> Result x c
-> Result x value
map4 :
(a -> b -> c -> d -> value)
-> Result x a
-> Result x b
-> Result x c
-> Result x d
-> Result x value
map5 :
(a -> b -> c -> d -> e -> value)
-> Result x a
-> Result x b
-> Result x c
-> Result x d
-> Result x e
-> Result x value

Chaining

andThen : (a -> Result x b) -> Result x a -> Result x b

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.

onError : (a -> Result b x) -> Result a x -> Result b x

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

withDefault : a -> Result x a -> a

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
withDefaultLazy : ({} -> a) -> Result x a -> a

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.

toMaybe : Result x a -> Maybe a

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)
fromMaybe : x -> Maybe a -> Result x a

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)
mapError : (x -> y) -> Result x a -> Result y a

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"