Hike.Option (hike v0.1.0)
The Hike.Option
module provides an implementation of the Optional data type.
It defines a struct Option
with a single field value
which can either be @none nil
or any other value of type <T>
. This implementation provides functions to work with
Optional data including
- mapping,
- binding
- filtering,
- applying and many more functions to the value inside the Optional data.
example-usage
Example Usage
iex> option = %Hike.Option{value: 42}
%Hike.Option{value: 42}
iex> Hike.Option.map(option, &(&1 * 2))
%Hike.Option{value: 84}
iex> Hike.Option.filter(option, &(rem(&1, 2) == 0))
%Hike.Option{value: 42}
iex> Hike.Option.apply(option, &(&1 + 10))
%Hike.Option{value: 52}
# Define a User struct
defmodule User do
@derive {Jason.Encoder, only: [:id,:age, :name]}
defstruct [:id, :age, :name]
end
defmodule TestHike do
# Import the Hike.Option module
import Hike.Option
# Simulating a database fetch function
@spec fetch_user(number) :: Hike.Option.option(%User{}) | Hike.Option.option()
# Simulating a database fetch function
def fetch_user(id) do
# Simulating a database query to fetch a user by ID
# Returns an Option<User> with some(user) if the user is found
# Returns an Option<User> with none() if the user is not found
case id do
1 -> some(%User{id: 1, age: 30, name: "Vineet Sharma"})
2 -> some(%User{id: 2, age: 20, name: "Jane Smith"})
_ -> none()
end
end
# Function to update the user's name to uppercase
# This function takes a user, a real data type, and returns an elevated data type Option
# if `%User{}` could transform successfully return `Option` in `some` state or else in `none` state.
def update_name_to_uppercase(user) do
case user.name do
nil -> none()
name -> %User{user | name: String.upcase(user.name)} |> some
end
end
# for above function another version could be like this where user take real data type %User{}, and return update %User{}
# def update_name_to_uppercase(user) do
# uppercase_name = String.upcase(user.name)
# %User{user | name: uppercase_name}
# end
# for this case map function will be used like it's been used for `increase_age_by1`
# Function to increase the user's age by one
# This function takes a user, a real data type, and returns a real data type user
def increase_age_by1(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
Jason.encode!(user) |> IO.puts
end
# Example: Fetching a user from the database, updating the name, and matching the result
def test_user() do
user_id = 1
# 1. Expressiveness: Using Hike's Option type to handle optional values
fetch_user(user_id)
|> bind(&update_name_to_uppercase/1)
|> map(&increase_age_by1/1)
|> match(&print_user_as_json/1, fn -> IO.puts("User not found") end)
user_id = 3
# 2. Safer and more predictable code: Handling all possible cases explicitly
fetch_user(user_id)
|> bind(&update_name_to_uppercase/1)
|> map(&increase_age_by1/1)
|> match(&print_user_as_json/1, fn -> IO.puts("User not found") end)
end
end
output
Output
iex> TestHike.test_user
# User ID: 1
{"id":1,"age":31,"name":"JOHN DOE"}
# User ID: 3
User not found
For more information on how to use this module, please see the documentation for the individual functions.
Link to this section Summary
Types
binder()
represent a binding(mapping) function which take no parameter and return an Option
of type <TR>
.
binder(t)
represent a binding(mapping) function which take a parameter of type <T>
and return an Option
of type <TR>
.
func()
represent a function which take no parameter and return value of type <TR>
.
func(t)
represent a function which take a parameter of type <T>
and return a value of type <TR>
.
mapper()
represent a mapping function which take no parameter and return a value of type <TR>
.
mapper(t)
represent a mapping function which take a parameter of type <T>
and return a value of type <TR>
.
Elevated data type of Option struct that represents None
state.
Elevated data type of Option struct that represents Some
state and have a value of type <T>
.
generic input type <T>
.
generic return type <TR>
.
Functions
Applies a given function to the value of an Option
struct and returns the result as a new Option
.
async version on apply
applies the func
function to a value wrapped in a Task
and returns a new task with the transformed value.
Transforms an Option<T>
struct with a non-nil value, using a binder function that returns another Option
option in none state or Option<TR>
option in some state.
async version on bind
applies the binder
function to a value wrapped in a Task
and returns a new task with the transformed value.
Applies the given function to the value of the provided option, returning a new option containing the original value if the function returns a truthy value, otherwise returning an empty option. If the provided option has no value, this function simply returns the empty option.
Returns true
if the Option
is in None
state, otherwise false
.
Returns true
if the Option
is in Some
state, otherwise false
.
Applies the given mapping function to the value inside the Option
struct and returns a new Option
struct containing the transformed value. If the input Option
struct is @none
, the function
returns a new Option
struct in none state.
async version on map
Applies the mapper
function to a value wrapped in a Task
and returns a new task with the mapped value.
Pattern matches on an option
and returns the result of the respective matching function.
Calls some_fun
with the value of the Option if the Option is in some
state,
or calls none_fun
if the Option is in none
state.
async version on match
pattern matches on an Option
value wrapped in a Task
and returns
the result of the matching function.
Creates an Option
in None
state.
Creates an Option
in Some
state
convert Option
into respective Either
.
if Option
is in None
state Either
will be return in Left
state. and Option
from Some
state will convert to Either
Right
state.
convert Option
into respective MayFail
.
if Option
is in None
state MayFail
will be return in Failure
state. and Option
from Some
state will convert to MayFail
Success
state.
Link to this section Types
binder()
binder()
represent a binding(mapping) function which take no parameter and return an Option
of type <TR>
.
example
Example
@spec whatever() :: Hike.Option.option(atom)
def whatever(), do: some(:ok)
@spec whatever() :: Hike.Option.option()
def whatever(), do: none()
binder(t)
binder(t)
represent a binding(mapping) function which take a parameter of type <T>
and return an Option
of type <TR>
.
example
Example
@spec square(pos_number) :: Hike.Option.option(number)
def square(x) when x > 0, do: x * x |> some()
@spec square(number) :: Hike.Option.option()
def square(x) do
case x when x < 0 do
true -> none()
false -> square(x)
end
end
func()
@type func() :: (() -> tr())
func()
represent a function which take no parameter and return value of type <TR>
.
example
Example
@spec whatever() :: atom
def whatever(), do: :ok
func(t)
@type func(t) :: (t -> tr())
func(t)
represent a function which take a parameter of type <T>
and return a value of type <TR>
.
example
Example
@spec add_one(number) :: number
def add_one(x), do: x + 1
mapper()
@type mapper() :: (() -> tr())
mapper()
represent a mapping function which take no parameter and return a value of type <TR>
.
example
Example
@spec whatever() :: atom
def whatever(), do: :ok
mapper(t)
@type mapper(t) :: (t -> tr())
mapper(t)
represent a mapping function which take a parameter of type <T>
and return a value of type <TR>
.
example
Example
@spec square(number) :: number
def square(x), do: x * x
option()
@type option() :: %Hike.Option{value: nil}
Elevated data type of Option struct that represents None
state.
example
Example
%Hike.Option{value: nil, is_some?: false}
option(t)
@type option(t) :: %Hike.Option{value: t}
Elevated data type of Option struct that represents Some
state and have a value of type <T>
.
example
Example
%Hike.Option{value: 40, is_some?: true}
@type t() :: any()
generic input type <T>
.
tr()
@type tr() :: any()
generic return type <TR>
.
Link to this section Functions
apply(option, func)
Applies a given function to the value of an Option
struct and returns the result as a new Option
.
examples
Examples
iex> option = %Option{value: 42}
iex> add_one = fn x -> x + 1 end
iex> Option.apply(option, add_one)
%Option{value: 43}
iex> none_option = %Option{value: nil}
iex> Option.apply(none_option, add_one)
%Option{value: nil}
iex> option = %Option{value: "hello"}
iex> upcase_string = fn str -> String.upcase(str) end
iex> Option.apply(option, upcase_string)
%Option{value: "HELLO"}
apply_async(wrapped_task, func)
async version on apply
applies the func
function to a value wrapped in a Task
and returns a new task with the transformed value.
examples
Examples
iex> task = Task.async(fn -> Hike.Option.some(10) end)
...> mapped_task = apply_async(task, &(&1 * 2))
...> Task.await(mapped_task)
%Hike.Option{value: 20}
iex> task = Task.async(fn -> Hike.Option.none() end)
iex> mapped_task = apply_async(task, &(&1))
iex> Task.await(mapped_task)
%Hike.Option{value: nil}
bind(opt, func)
Transforms an Option<T>
struct with a non-nil value, using a binder function that returns another Option
option in none state or Option<TR>
option in some state.
** If the input Option
has a nil
value, returns a new Option
struct with a nil
value.
if you have a function that return Option<TR>
and you want to apply mapping then use bind function to avoid double elevation
examples
Examples
iex> option = %Option{value: 42}
iex> add_one = fn x -> Option.some(x + 1) end
iex> Option.bind(option, add_one)
%Option{value: 43}
iex> Option.bind(Option.some("hello"), fn x -> Option.some(String.upcase(x)) end)
%Option{value: "HELLO"}
iex> Option.bind(Option.none() )
%Option{value: nil}
bind_async(wrapped_task, func)
async version on bind
applies the binder
function to a value wrapped in a Task
and returns a new task with the transformed value.
examples
Examples
iex> task = Task.async(fn -> Hike.Option.some(10) end)
...> mapped_task = bind_async(task, fn x -> Hike.Option.some(x + 1) end)
...> Task.await(mapped_task)
%Hike.Option{value: 20}
iex> task = Task.async(fn -> Hike.Option.none() end)
iex> mapped_task = bind_async(task, fn x -> Hike.Option.some(x + 1) end)
iex> Task.await(mapped_task)
%Hike.Option{value: nil}
filter(opt, func)
Applies the given function to the value of the provided option, returning a new option containing the original value if the function returns a truthy value, otherwise returning an empty option. If the provided option has no value, this function simply returns the empty option.
examples
Examples
iex> Option.filter(%Option{value: 42}, fn x -> rem(x, 2) == 0 end)
%Option{value: 42}
iex> Option.filter(%Option{value: 42}, fn x -> rem(x, 2) == 1 end)
%Option{value: nil}
iex> Option.filter(%Option{value: nil}, fn x -> rem(x, 2) == 0 end)
%Option{value: nil}
iex> list_opt = Option.some([1,2,3])
iex> list_filter = fn (lst) -> Enum.count(lst) > 5 end
iex> Option.filter(list_opt, list_filter)
%Hike.Option{value: nil}
is_none?(option)
Returns true
if the Option
is in None
state, otherwise false
.
examples
Examples
iex> Option.is_none?(Option.none())
true
iex> Option.is_none?(Option.some("hello"))
false
is_some?(option)
Returns true
if the Option
is in Some
state, otherwise false
.
examples
Examples
iex> Option.is_some?(Option.some("hello"))
true
iex> Option.is_some?(Option.none())
false
map(option, func)
Applies the given mapping function to the value inside the Option
struct and returns a new Option
struct containing the transformed value. If the input Option
struct is @none
, the function
returns a new Option
struct in none state.
examples
Examples
iex> Option.map(Option.some("hello"), fn x -> String.upcase(x) end)
%Option{value: "HELLO"}
iex> Option.map(Option.none(), fn x -> String.upcase(x) end)
%Option{value: nil}
map_async(wrapped_task, mapper)
async version on map
Applies the mapper
function to a value wrapped in a Task
and returns a new task with the mapped value.
examples
Examples
iex> task = Task.async(fn -> Hike.Option.some(10) end)
...> mapped_task = map_async(task, &(&1 * 2))
...> Task.await(mapped_task)
%Hike.Option{value: 20}
iex> task = Task.async(fn -> Hike.Option.none() end)
iex> mapped_task = map_async(task, &(&1))
iex> Task.await(mapped_task)
%Hike.Option{value: nil}
match(arg1, some_fun, none_fun)
@spec match(option(t()) | option(), func(t()), func()) :: tr()
@spec match(Task.t(), func(t()), func()) :: tr()
Pattern matches on an option
and returns the result of the respective matching function.
Calls some_fun
with the value of the Option if the Option is in some
state,
or calls none_fun
if the Option is in none
state.
examples
Examples
iex> import Hike.Option
# Match on `some` value with a matching function and on `none` value with a none-matching function
iex> match(some("hello"), &(&1 <> " world"), fn -> "no value" end)
# => "hello world"
# Match on `none` value with a none-matching function even if a matching function is provided
iex> match(none(), &(&1 * 2), fn -> "no value" end)
# => "no value"
iex> Option.match(Option.some("hello"), fn x -> String.upcase(x) end, fn -> "nothing" end )
"HELLO"
iex> Option.match(Option.none(), fn x -> String.upcase(x) end, fn -> "nothing" end)
"nothing"
match_async(wrapped_task, some_fun, none_fun)
async version on match
pattern matches on an Option
value wrapped in a Task
and returns
the result of the matching function.
examples
Examples
iex> task = Task.async(fn -> Hike.Option.some(10) end)
iex> mapped_task = map_async(task, &(&1 * 2))
iex> match_async(mapped_task , fn x -> "value is : " <> x <> "." end, fn ()-> "No Result Found." end)
"value is : 20."
iex> task = Task.async(fn -> Hike.Option.none() end)
iex> mapped_task = map_async(task, &(&1))
iex> match_async(mapped_task , fn x -> "value is : " <> x <> "." end, fn ()-> "No Result Found." end)
"No Result Found."
none()
@spec none() :: option()
Creates an Option
in None
state.
examples
Examples
iex> Option.none()
%Option{value: :nil}
some(value)
Creates an Option
in Some
state
examples
Examples
iex> Option.some("hello")
%Option{value: "hello"}
to_either(option)
@spec to_either(option()) :: Hike.Either.either_left(:error)
@spec to_either(option(t())) :: Hike.Either.either_right()
convert Option
into respective Either
.
if Option
is in None
state Either
will be return in Left
state. and Option
from Some
state will convert to Either
Right
state.
example
Example
iex> Hike.option(9) |> Hike.Option.to_either
%Hike.Either{l_value: nil, r_value: 9, is_left?: false}
iex> Hike.option() |> Hike.Option.to_either
%Hike.Either{l_value: :error, r_value: nil, is_left?: true}
to_mayfail(option)
@spec to_mayfail(option()) :: Hike.MayFail.mayfail_failure(:error)
@spec to_mayfail(option(t())) :: Hike.MayFail.mayfail_success(t())
convert Option
into respective MayFail
.
if Option
is in None
state MayFail
will be return in Failure
state. and Option
from Some
state will convert to MayFail
Success
state.
example
Example
iex(13)> Hike.option() |> Hike.Option.to_mayfail
%Hike.MayFail{failure: :error, success: nil, is_success?: false}
iex(14)> Hike.option(3) |> Hike.Option.to_mayfail
%Hike.MayFail{failure: nil, success: 3, is_success?: true}