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>
.
generic input type <T>
.
generic input type <T_Failure>
represent a type of value on Failure
state.
generic input type <T_Success>
represent a type of value on Success
state.
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
binder()
@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
binder(t)
@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
func()
@type func() :: (() -> tr())
func()
represent a function which take no argument and return value of type <TR>
.
func(t)
@type func(t) :: (t -> tr())
func(t)
represent a function which take an argument of type <T>
and return a value of type <TR>
.
mapper()
@type mapper() :: (() -> tr())
mapper()
represent a mapping function which take no argument and return
a value of type <TR>
.
mapper(t)
@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>
.
@opaque mayfail()
represent a type of Mayfail
that could be in either Failure
or Success
state
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.
mayfail_failure()
@type mayfail_failure() :: %Hike.MayFail{ failure: t_failure(), is_success?: false, success: nil }
represent a type of Mayfail
in Failure
state.
mayfail_failure(t)
@type mayfail_failure(t) :: %Hike.MayFail{
failure: t,
is_success?: false,
success: nil
}
represent a type of Mayfail
in Failure
state.
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.
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>
.
t_failure()
@type t_failure() :: any()
generic input type <T_Failure>
represent a type of value on Failure
state.
t_success()
@type t_success() :: any()
generic input type <T_Success>
represent a type of value on Success
state.
tr()
@type tr() :: any()
generic return type <TR>
.
Link to this section 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.
failure
: the failure value (ifis_success?
is false)success
: the success value (ifis_success?
is true)is_success?
: a boolean flag indicating whether the value is a success value (true
) or a failure value (false
)
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}
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}
bind_failure(mayfail, binder)
@spec bind_failure(mayfail_failure(t_failure()), binder(t_failure())) :: mayfail_failure(tr())
@spec bind_failure(mayfail_failure(t_success()), binder(t_failure())) :: mayfail_failure(t_success())
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}
bind_success(mayfail, binder)
@spec bind_success(mayfail_success(t_success()), binder(t_success())) :: mayfail_success(tr())
@spec bind_success(mayfail_failure(t_failure()), binder(t_success())) :: mayfail_failure(t_failure())
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}
failure(error)
@spec failure(t_failure()) :: mayfail_failure(t_failure())
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
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
map_failure(mayfail, mapper)
@spec map_failure(mayfail_failure(t_failure()), mapper(t_failure())) :: mayfail_failure(tr())
@spec map_failure(mayfail_failure(t_success()), mapper(t_failure())) :: mayfail_failure(t_success())
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}
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}
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
success(value)
@spec success(t_success()) :: mayfail_success(t_success())