Hike.MayFail (hike v0.0.2)

Hike.MayFail represents a value that may either succeed with a value or fail with an error. It combines the functionality of Hike.Option and Hike.Either, making it suitable for scenarios where a value can be optional and can also potentially fail.

Creating a MayFail

To create a MayFail instance, you can use the Hike.MayFail.success/1 function to wrap a success value:

iex> may_fail = Hike.MayFail.success(42)
%Hike.MayFail{failure: nil, success: 42, is_success?: true}
iex> may_fail = Hike.MayFail.failure(:error)
%Hike.MayFail{failure: :error, success: nil, is_success?: false}
# same example can be rewritten with `MayFail`
# Define a User struct
defmodule User do
@derive Jason.Encoder
defstruct [:id, :age, :name]
end

defmodule TestHike do
# Import the Hike.MayFail module
import Hike.MayFail

# Simulating a database fetch function
@spec fetch_user(number) :: Hike.MayFail.mayfail(String.t()) | Hike.MayFail.mayfail_success(%User{})
def fetch_user(id) do
  # Simulating a database query to fetch a user by ID
  # Returns an MayFail<string, User> with success(user) if the user is found
  # Returns an MayFail<string, User> with failure("User not found") if the user is not found
  case id do
    1 -> success(%User{id: 1, age: 30, name: "Vineet Sharma"})
    2 -> success(%User{id: 2, age: 20, name: nil})
    _ -> failure("User not found")
  end
end

# Function to update the user's name to uppercase if possible
# This function takes a User struct and returns an MayFail<string, User>
def update_name_to_uppercase(user) do
case user.name do
  nil -> failure("User name is missing")
  name -> success(%User{user | name: String.upcase(name)})
end
end

# Function to increase the user's age by one
# This function takes a User struct and returns a real data type User with updated values.
def increase_age_by_1(user) do
  %User{user | age: user.age + 1}
end

# Function to print a user struct as a JSON-represented string
def print_user_as_json(user) do
  user
  |> Jason.encode!()
  |> IO.puts()
end

@spec test_user() :: :ok
def test_user() do
  fetch_user(1)
  |> bind_success(&update_name_to_uppercase/1)
  |> map_success(&increase_age_by_1/1)
  |> IO.inspect()
  |> match(&IO.puts/1, &print_user_as_json/1)

  fetch_user(2)
  |> bind_success(&update_name_to_uppercase/1)
  |> map_success(&increase_age_by_1/1)
  |> IO.inspect()
  |> match(&IO.puts/1, &print_user_as_json/1)

  fetch_user(3)
  |> bind_success(&update_name_to_uppercase/1)
  |> map_success(&increase_age_by_1/1)
  |> IO.inspect()
  |> match(&IO.puts/1, &print_user_as_json/1)

  :ok
end
end
iex> TestHike.test_user
# user id =1
%Hike.MayFail{
failure: nil,
success: %User{id: 1, age: 31, name: "VINEET SHARMA"},
is_success?: true
}
{"age":31,"id":1,"name":"VINEET SHARMA"}

# user id = 2

%Hike.MayFail{failure: "User name is missing", success: nil, is_success?: false}
User name is missing

#user id = 3

%Hike.MayFail{failure: "User not found", success: nil, is_success?: false}
User not found
:ok

Link to this section Summary

Types

binder() represent a binding(mapping) function which take no argument and return a Mayfail of type <TR>.

binder(t) represent a binding(mapping) function which take an argument of type <T> and return a Mayfail of type <TR>.

func() represent a function which take no argument and return value of type <TR>.

func(t) represent a function which take an argument of type <T> and return a value of type <TR>.

mapper() represent a mapping function which take no argument and return a value of type <TR>.

mapper(t) represent a mapping function which take an argument of type <T> and return a value of type <TR>.

represent a type of Mayfail that could be in either Failure or Success state

represent a type of Mayfail that could be in either failure or success state with a value of given type.

represent a type of Mayfail in Failure state.

represent a type of Mayfail in Failure state.

Elevated data type of Mayfail struct that represents Success state.

Elevated data type of Mayfail struct that represents Success state and have a value of type <T>.

t()

generic input type <T>.

generic input type <T_Failure> represent a type of value on Failurestate.

generic input type <T_Success>represent a type of value on Successstate.

generic return type <TR>.

Functions

%Hike.Mayfail{failure: t_failure(), success: t_success(), is_success?:boolean()} is a struct that represents an "either/or" value. It can contain either a Failure value or a Success value, but not both.

Example

iex> apply_func = fn x -> x + 2 end
iex> MayFail.failure(1) |> MayFail.apply_failure(apply_func)
%Hike.MayFail{failure: 3, success: nil, is_success?: false}

iex> MayFail.success(1) |> MayFail.apply_failure(apply_func)
%Hike.MayFail{failure: nil, success: 1, is_success?: true}

Example

iex> apply_func = fn x -> (x + 2) end
iex> MayFail.failure(1) |> MayFail.apply_success(apply_func)
%Hike.MayFail{failure: 1, success: nil, is_success?: false}

iex> MayFail.success(1) |> MayFail.apply_success(apply_func)
%Hike.MayFail{failure: nil, success: 3, is_success?: true}

Binds a function that returns a MayFail value for an MayFail in the Failure state. If the input is in the Success state, the function is not executed and the input is returned as it is.

Binds a function that returns a MayFail value for an MayFail in the Failure state. If the input is in the Success state, the function is not executed and the input is returned as it is.

Check whether MayFail is in Failure state or not.

Check whether MayFail is in Success state or not.

Example

iex> mapper = fn x -> x + 2 end
iex> MayFail.failure(1) |> MayFail.map_failure(mapper)
%Hike.MayFail{failure: 3, success: nil, is_success?: false}

iex> MayFail.success(1) |> MayFail.map_failure(mapper)
%Hike.MayFail{failure: nil, success: 1, is_success?: true}

Example

iex> mapper = fn x -> (x + 2) end
iex> MayFail.failure(1) |> MayFail.map_success(mapper)
%Hike.MayFail{failure: 1, success: nil, is_success?: false}

iex> MayFail.success(1) |> MayFail.map_success(mapper)
%Hike.MayFail{failure: nil, success: 3, is_success?: true}

Matches an Mayfail value and applies the corresponding function.

Link to this section Types

@type binder() :: (() -> mayfail_success(tr()) | mayfail_failure(tr()))

binder() represent a binding(mapping) function which take no argument and return a Mayfail of type <TR>.

example

Example

iex> success_bind_func = fn () -> Hike.MayFail.success(:ok) end
iex> failure_bind_func = fn () -> Hike.MayFail.failure(:error) end
@type binder(t) :: (t -> mayfail_success(tr()) | mayfail_failure(tr()))

binder(t) represent a binding(mapping) function which take an argument of type <T> and return a Mayfail of type <TR>.

example

Example

iex> success_bind_func = fn (x) -> Mayfail.success(x) end
iex> failure_bind_func = fn (y) -> Mayfail.failure(y) end
@type func() :: (() -> tr())

func() represent a function which take no argument and return value of type <TR>.

@type func(t) :: (t -> tr())

func(t) represent a function which take an argument of type <T> and return a value of type <TR>.

@type mapper() :: (() -> tr())

mapper() represent a mapping function which take no argument and return a value of type <TR>.

@type mapper(t) :: (t -> tr())

mapper(t) represent a mapping function which take an argument of type <T> and return a value of type <TR>.

Link to this opaque

mayfail()

(opaque)
@opaque mayfail()

represent a type of Mayfail that could be in either Failure or Success state

Link to this type

mayfail(t_failure, t_success)

@type mayfail(t_failure, t_success) :: %Hike.MayFail{
  failure: t_failure,
  is_success?: boolean(),
  success: t_success
}

represent a type of Mayfail that could be in either failure or success state with a value of given type.

Link to this type

mayfail_failure()

@type mayfail_failure() :: %Hike.MayFail{
  failure: t_failure(),
  is_success?: false,
  success: nil
}

represent a type of Mayfail in Failure state.

Link to this type

mayfail_failure(t)

@type mayfail_failure(t) :: %Hike.MayFail{
  failure: t,
  is_success?: false,
  success: nil
}

represent a type of Mayfail in Failure state.

Link to this type

mayfail_success()

@type mayfail_success() :: %Hike.MayFail{
  failure: nil,
  is_success?: true,
  success: t_success()
}

Elevated data type of Mayfail struct that represents Success state.

Link to this type

mayfail_success(t)

@type mayfail_success(t) :: %Hike.MayFail{failure: nil, is_success?: true, success: t}

Elevated data type of Mayfail struct that represents Success state and have a value of type <T>.

@type t() :: any()

generic input type <T>.

@type t_failure() :: any()

generic input type <T_Failure> represent a type of value on Failurestate.

@type t_success() :: any()

generic input type <T_Success>represent a type of value on Successstate.

@type tr() :: any()

generic return type <TR>.

Link to this section Functions

Link to this function

%Hike.MayFail{}

(struct)

%Hike.Mayfail{failure: t_failure(), success: t_success(), is_success?:boolean()} is a struct that represents an "either/or" value. It can contain either a Failure value or a Success value, but not both.

  • failure: the failure value (if is_success? is false)
  • success: the success value (if is_success? is true)
  • is_success?: a boolean flag indicating whether the value is a success value (true) or a failure value (false)
Link to this function

apply_failure(mayfail, func)

@spec apply_failure(mayfail_failure(t_failure()), func(t_failure())) ::
  mayfail_failure(tr())
@spec apply_failure(mayfail_success(t_success()), func(t_failure())) ::
  mayfail_success(t_success())

example

Example

iex> apply_func = fn x -> x + 2 end
iex> MayFail.failure(1) |> MayFail.apply_failure(apply_func)
%Hike.MayFail{failure: 3, success: nil, is_success?: false}

iex> MayFail.success(1) |> MayFail.apply_failure(apply_func)
%Hike.MayFail{failure: nil, success: 1, is_success?: true}
Link to this function

apply_success(mayfail, func)

@spec apply_success(mayfail_success(t_success()), func(t_success())) ::
  mayfail_success(tr())
@spec apply_success(mayfail_failure(t_failure()), func(t_success())) ::
  mayfail_failure(t_failure())

example

Example

iex> apply_func = fn x -> (x + 2) end
iex> MayFail.failure(1) |> MayFail.apply_success(apply_func)
%Hike.MayFail{failure: 1, success: nil, is_success?: false}

iex> MayFail.success(1) |> MayFail.apply_success(apply_func)
%Hike.MayFail{failure: nil, success: 3, is_success?: true}
Link to this function

bind_failure(mayfail, binder)

Binds a function that returns a MayFail value for an MayFail in the Failure state. If the input is in the Success state, the function is not executed and the input is returned as it is.

example

Example

iex> binder = fn x -> MayFail.success(x + 2) end
iex> MayFail.failure(1) |> MayFail.bind_failure(binder)
%Hike.MayFail{failure: nil, success: 3, is_success?: true}

iex> binder = fn x -> MayFail.failure(x + 2) end
iex>  MayFail.failure(1) |> MayFail.bind_failure(binder)
%Hike.MayFail{failure: 3, success: nil, is_success?: false}

iex> MayFail.success(1) |> MayFail.bind_failure(binder)
%Hike.MayFail{failure: nil, success: 1, is_success?: true}
Link to this function

bind_success(mayfail, binder)

Binds a function that returns a MayFail value for an MayFail in the Failure state. If the input is in the Success state, the function is not executed and the input is returned as it is.

example

Example

iex> binder = fn x -> MayFail.success(x + 2) end
iex> MayFail.failure(1) |> MayFail.bind_success(binder)
%Hike.MayFail{failure: 1, success: nil, is_success?: false}

iex> MayFail.success(1) |> MayFail.bind_success(binder)
%Hike.MayFail{failure: nil, success: 3, is_success?: true}

iex> binder = fn x -> MayFail.failure(x + 2) end
iex>  MayFail.success(1) |> MayFail.bind_success(binder)
%Hike.MayFail{failure: 3, success: nil, is_success?: false}
@spec failure(t_failure()) :: mayfail_failure(t_failure())
Link to this function

is_failure?(mayfail)

@spec is_failure?(mayfail_failure()) :: true
@spec is_failure?(mayfail_success()) :: false

Check whether MayFail is in Failure state or not.

example

Example

iex> Hike.MayFail.success(4) |> Hike.MayFail.is_failure?
false
iex> Hike.MayFail.failure("fail") |> Hike.MayFail.is_failure?
true
Link to this function

is_success?(mayfail)

@spec is_success?(mayfail_success()) :: true
@spec is_success?(mayfail_failure()) :: false

Check whether MayFail is in Success state or not.

example

Example

iex> Hike.MayFail.success(4) |> Hike.MayFail.is_success?
true
iex> Hike.MayFail.failure("fail") |> Hike.MayFail.is_success?
false
Link to this function

map_failure(mayfail, mapper)

example

Example

iex> mapper = fn x -> x + 2 end
iex> MayFail.failure(1) |> MayFail.map_failure(mapper)
%Hike.MayFail{failure: 3, success: nil, is_success?: false}

iex> MayFail.success(1) |> MayFail.map_failure(mapper)
%Hike.MayFail{failure: nil, success: 1, is_success?: true}
Link to this function

map_success(mayfail, mapper)

@spec map_success(mayfail_success(t_success()), mapper()) :: mayfail_success(tr())
@spec map_success(mayfail_failure(t_failure()), mapper(t_success())) ::
  mayfail_failure(t_failure())

example

Example

iex> mapper = fn x -> (x + 2) end
iex> MayFail.failure(1) |> MayFail.map_success(mapper)
%Hike.MayFail{failure: 1, success: nil, is_success?: false}

iex> MayFail.success(1) |> MayFail.map_success(mapper)
%Hike.MayFail{failure: nil, success: 3, is_success?: true}
Link to this function

match(mayfail, failure_fn, success_fn)

@spec match(
  mayfail(t_failure(), t_success()),
  (t_failure() -> tr()),
  (t_success() -> tr())
) :: tr()

Matches an Mayfail value and applies the corresponding function.

example

Example

iex> Hike.MayFail.success(4) |> Hike.MayFail.match(fn x -> x + 3 end, fn y -> y + 2 end)
6
@spec success(t_success()) :: mayfail_success(t_success())