/hayase

Simple monads for Elixir

Primary LanguageElixirMIT LicenseMIT

Hayase

Simple implementation of common ADTs (Maybe, Result) and their instances of Functor and Monad typeclasses in Elixir.

This package is based on towel package but modifies and extends it in a few ways:

  • add Hayase.Maybe.unwrap/2 to provide fallback value (just like Algae.Maybe.from_maybe/2 from algae package)
  • unwrap functions from both Maybe and Result ADTs don't unwrap passed values recursively but do it one level deep only
  • return value of Hayase.Functor.fmap/2 is always constructed with the same type constructor as input value
  • implement an instance of Functor typeclass for Elixir maps

hayase package is not meant to replace other packages providing algebraic and categorical abstractions - it aims to implement a small subset of their functionality which seems to be enough for my needs.

Examples

Using Hayase.Types.Maybe - before processing it's useful to wrap input value with Hayase.Types.Maybe.wrap/1 to build Maybe value automatically:

use Hayase

# -----------------------------------------------
# using with `Hayase.Typeclasses.Functor.fmap/2`
# -----------------------------------------------

"foo"
|> Maybe.wrap()
|> fmap(fn x -> x <> " bar" end)
|> fmap(fn x -> x <> " baz" end)
# => {:just, "foo bar baz"}

nil
|> Maybe.wrap()
|> fmap(fn x -> x <> " bar" end)
|> fmap(fn x -> x <> " baz" end)
# => :nothing

# -----------------------------------------------
# using with `Hayase.Typeclasses.Monad.bind/2`
# -----------------------------------------------

"foo"
|> Maybe.wrap()
|> bind(fn x -> Maybe.just(x <> " bar") end)
|> bind(fn x -> Maybe.just(x <> " baz") end)
# => {:just, "foo bar baz"}

"foo"
|> Maybe.wrap()
|> bind(fn x -> Maybe.just(x <> " bar") end)
|> bind(fn x -> Maybe.nothing(x <> " baz") end)
# => :nothing

nil
|> Maybe.wrap()
|> bind(fn x -> Maybe.just(x <> " bar") end)
|> bind(fn x -> Maybe.just(x <> " baz") end)
# => :nothing

# -----------------------------------------------
# unwrapping
# -----------------------------------------------

{:just, "foo bar baz"}
|> Maybe.unwrap()
# => "foo bar baz"

:nothing
|> Maybe.unwrap()
# => nil

:nothing
|> Maybe.unwrap("fallback value")
# => "fallback value"

Using Hayase.Types.Result - as a rule it's not necessary to wrap input value with Hayase.Types.Result.wrap/1 since in most cases it's already provided as a Result value (so called tagged tuple):

use Hayase

# -----------------------------------------------
# using with `Hayase.Typeclasses.Functor.fmap/2`
# -----------------------------------------------

{:ok, "foo"}
|> fmap(fn x -> x <> " bar" end)
|> fmap(fn x -> x <> " baz" end)
# => {:ok, "foo bar baz"}

{:error, "error"}
|> fmap(fn x -> x <> " bar" end)
|> fmap(fn x -> x <> " baz" end)
# => {:error, "error"}

# -----------------------------------------------
# using with `Hayase.Typeclasses.Monad.bind/2`
# -----------------------------------------------

{:ok, "foo"}
|> bind(fn x -> Result.ok(x <> " bar") end)
|> bind(fn x -> Result.ok(x <> " baz") end)
# => {:ok, "foo bar baz"}

{:ok, "foo"}
|> bind(fn x -> Result.error(x <> "error") end)
|> bind(fn x -> Result.ok(x <> " baz") end)
# => {:error, "error"}

# -----------------------------------------------
# unwrapping
# -----------------------------------------------

{:ok, "foo bar baz"}
|> Result.unwrap()
# => "foo bar baz"

{:error, "error"}
|> Result.unwrap()
# => "error"

Installation

If available in Hex, the package can be installed by adding hayase to your list of dependencies in mix.exs:

def deps do
  [
    {:hayase, "~> 0.1"}
  ]
end

Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/hayase.

TODO

  • tests
  • typespecs
  • docs