This is an alternative site for discovering Elm packages. You may be looking for the official Elm package site instead.

Elchemy.XResult

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

Type and Constructors

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.

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 to two results, if both results are Ok. If not, the first argument which is an Err will propagate through.

map2 (+) (XString.toInt "1") (XString.toInt "2") == Ok 3
map2 (+) (XString.toInt "1") (XString.toInt "y") == Err "could not convert string 'y' to an Int"
map2 (+) (XString.toInt "x") (XString.toInt "y") == Err "could not convert string 'x' to an Int"
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:

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:

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

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.

withDefault 0 (XString.toInt "123") == 123
withDefault 0 (XString.toInt "abc") == 0
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.

fromMaybe : x -> Maybe a -> Result x a

Convert from a simple Maybe to interact with some code that primarily uses Results.

mapError : (x -> y) -> Result x a -> Result y a

Transform an Err value. For example, say the errors we get have too much information:

mapError XTuple.first (Ok (123, 1)) == Ok (123, 1)
mapError XTuple.second (Err ("nothing", "important")) == Err "important"
module Elchemy.XResult
    exposing
        ( Result(..)
        , withDefault
        , map
        , map2
        , map3
        , map4
        , map5
        , andThen
        , toMaybe
        , fromMaybe
        , mapError
        )

{-| A `Result` is the result of a computation that may fail. This is a great
way to manage errors in Elm.


# Type and Constructors

@docs Result


# Mapping

@docs map, map2, map3, map4, map5


# Chaining

@docs andThen


# Handling Errors

@docs withDefault, toMaybe, fromMaybe, mapError

-}

import Elchemy exposing (..)


{-| A `Result` is either `Ok` meaning the computation succeeded, or it is an
`Err` meaning that there was some failure.
-}
type Result error value
    = Ok value
    | Err error


{-| 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.

    withDefault 0 (XString.toInt "123") == 123
    withDefault 0 (XString.toInt "abc") == 0

-}
withDefault : a -> Result x a -> a
withDefault def result =
    case result of
        Ok a ->
            a

        Err _ ->
            def


{-| 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"

-}
map : (a -> value) -> Result x a -> Result x value
map func ra =
    case ra of
        Ok a ->
            Ok (func a)

        Err e ->
            Err e


{-| Apply a function to two results, if both results are `Ok`. If not,
the first argument which is an `Err` will propagate through.

    map2 (+) (XString.toInt "1") (XString.toInt "2") == Ok 3
    map2 (+) (XString.toInt "1") (XString.toInt "y") == Err "could not convert string 'y' to an Int"
    map2 (+) (XString.toInt "x") (XString.toInt "y") == Err "could not convert string 'x' to an Int"

-}
map2 : (a -> b -> value) -> Result x a -> Result x b -> Result x value
map2 func ra rb =
    case ( ra, rb ) of
        ( Ok a, Ok b ) ->
            Ok (func a b)

        ( Err x, _ ) ->
            Err x

        ( _, Err x ) ->
            Err x


{-| -}
map3 : (a -> b -> c -> value) -> Result x a -> Result x b -> Result x c -> Result x value
map3 func ra rb rc =
    case ( ra, rb, rc ) of
        ( Ok a, Ok b, Ok c ) ->
            Ok (func a b c)

        ( Err x, _, _ ) ->
            Err x

        ( _, Err x, _ ) ->
            Err x

        ( _, _, Err x ) ->
            Err x


{-| -}
map4 : (a -> b -> c -> d -> value) -> Result x a -> Result x b -> Result x c -> Result x d -> Result x value
map4 func ra rb rc rd =
    case ( ra, rb, rc, rd ) of
        ( Ok a, Ok b, Ok c, Ok d ) ->
            Ok (func a b c d)

        ( Err x, _, _, _ ) ->
            Err x

        ( _, Err x, _, _ ) ->
            Err x

        ( _, _, Err x, _ ) ->
            Err x

        ( _, _, _, Err x ) ->
            Err x


{-| -}
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
map5 func ra rb rc rd re =
    case ( ra, rb, rc, rd, re ) of
        ( Ok a, Ok b, Ok c, Ok d, Ok e ) ->
            Ok (func a b c d e)

        ( Err x, _, _, _, _ ) ->
            Err x

        ( _, Err x, _, _, _ ) ->
            Err x

        ( _, _, Err x, _, _ ) ->
            Err x

        ( _, _, _, Err x, _ ) ->
            Err x

        ( _, _, _, _, Err x ) ->
            Err x


{-| Chain together a sequence of computations that may fail. It is helpful
to see its definition:

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:

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.

-}
andThen : (a -> Result x b) -> Result x a -> Result x b
andThen callback result =
    case result of
        Ok value ->
            callback value

        Err msg ->
            Err msg


{-| Transform an `Err` value. For example, say the errors we get have too much
information:

    mapError XTuple.first (Ok (123, 1)) == Ok (123, 1)
    mapError XTuple.second (Err ("nothing", "important")) == Err "important"

-}
mapError : (x -> y) -> Result x a -> Result y a
mapError f result =
    case result of
        Ok v ->
            Ok v

        Err e ->
            Err (f e)


{-| 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.
-}
toMaybe : Result x a -> Maybe a
toMaybe result =
    case result of
        Ok v ->
            Just v

        Err _ ->
            Nothing


{-| Convert from a simple `Maybe` to interact with some code that primarily
uses `Results`.
-}
fromMaybe : x -> Maybe a -> Result x a
fromMaybe err maybe =
    case maybe of
        Just v ->
            Ok v

        Nothing ->
            Err err