Ok Jose

help maintain this lib

A tiny library for piping values on a given pattern like the erlang idiom {:ok, _} and {:error, _} tuples.

You can also define your own pattern-matched pipes besides ok and error.

Besides ok_jose I've made a couple more of related macro libraries that might be related (or useful depending on your needs and taste):

Installation

  1. Add ok_jose to your list of dependencies in mix.exs:
def deps do
[{:ok_jose, "~> 2.1.0"}]
end

Motivation

A lot of erlang libraries follow the convention of returning {:ok, _} and {:error, _} tuples to denote the success or failure of a computation. It's my opinion that more people should embrace this convention on their code, and this library helps by making it easier to pipe values by unwrapping them from the tagged tuples.

This library is my try at having a beautiful syntax for a happy pipe, that is, a pipe that expects {:ok, _} tuples to be returned by each piped function. If any piped function returns a non matched value, the remaining functions expecting an {:ok, _} value wont get executed.

So, for example, the following code

filename
|> File.read()
|> case do
{:ok, content} ->
content |> Poison.Parser.parse()
{:error, _} = error -> error
end

can be written as:

{:ok, filename}
|> File.read
|> Poison.Parser.parse
|> ok

The main advantage of the macros defined by OkJose is that you dont need to learn new syntax it's just plain old Elixir piping. It's also very easy to define your own, as they are just case clauses.

Usage

use OkJose

Provides you with the following macros:

ok/2

Pipes values into functions as long as they match {:ok, _}

{:ok, v} |> f |> g |> ok
ok!/2

Pipes values into functions but if at any point a value does not match {:ok, _} raises a match error.

error/2 and error!/2

Work exactly as the previous examples, but for tuples tagged as :error.

use OkJose.Pipe

defpipe

This module lets you define your own pipes, actually the previous ok pipe is defined like the following. Any value not matching is just piped as-is down the pipe.

defpipe ok do
{:ok, value} -> value
end

However if the pipe name ends with an exclamation mark, and no clause matches, an error will be raise just as with any function not matching on its arguments.

defpipe ok! do
{:ok, value} -> value
end

With these you can define a pipe for working on common values like ecto models and changesets.

defpipe ok_or_valid do
{:ok, value} -> value
changeset = %{valid?: true} -> changeset
end

This way you can pipe ok values or valid changesets like:

{:ok, %User{}}
|> cast(params, @required)
|> put_change(:name, "John")
|> Repo.insert
|> update_jwk_token
|> ok_or_valid

You can also define pipes that for values based on a predicate instead of just a pattern match. For example, if you need to call some function to determine if the value is safe to be passed through the rest of your pipe.

The OkJose.pipe_when macro takes a block of patterns and expects each to return a form like {true, value} or {false, value} The first boolean value on the tuple tells if the payload is to be passed down the pipe or if the pipe must suspend execution.

This way you can use negative matches, for example the following pipe will only let safe pets to be given to functions:

The predicate ?-ending form of defpipe is just sugar for pipe_when

@dangerous [Lion, Wolf, Cocodrile]
defpipe pet_safe? do
pet = %{__struct__: mod} when not mod in @dangerous ->
{true, pet}
anything ->
{false, pet}
end

And you can use them by just adding to the end of the pipe

{:ok, pet}
|> take_out
|> go_to(:park)
|> play_with(:kids)
|> pet_safe?

About ok

OkJose was born a while ago, back before we had Elixir 1.0, and before Elixir had its own with form to deal with this kind of problems. There are a lot of other libraries for dealing with tagged tuples and in general for monads in elixir. I'd recommend you to checkout these in particular:

Is it any good?

Yes

Take a look at the tests for more code examples.

マクロス Makurosu

[Elixir macros,] The things I do for beautiful code ― George Martin, Game of Thrones

#myelixirstatus #FridayLiterally