Giter Club home page Giter Club logo

elixir-pipes's Introduction

elixir-pipes

Elixir Pipes is an Elixir extension that extends the pipe (|>) operator through macros.

The Pipe: Elixir Flavor Packet

Some of the best programmers who have taken an early dive into Elixir have mentioned the pipe as one of the key features of the language. It allows a clear, concise expression of the programmer's intent:

  def inc(x), do: x + 1
  def double(x), do: x * 2
  
  1 |> inc |> double

The return value of each function is used as the first argument of the next function in the pipe. It's a beautiful expression that makes the intent of the programmer clear.

Trouble in the Kitchen

Sometimes, you need to compose functions with a different strategy. Say your functions use Erlang-style APIs. You might have functions that return {:ok, value} or {:error, value}. Then, the pipe operator might make things difficult. After you receive an error code, you probably want the pipe to stop.

Elixir-Pipes allows you to specify a strategy, in one concise space, that you can then apply to all segments in an Elixir pipe. This capability will help you compose many different types of functions. How many times have you wanted to:

  • compose a pipe that uses some variation of a function call like [1, 2, 3] |> add(1) |> times(2) ?
  • halt the execution of a pipe on error?
  • tease nils to empty strings, without changing your original functions?
  • transform exceptions to Erlang-style {:error, x} tuples?

The recipes are all there waiting for you.

Getting Started

All you need to do to get started is to add the project to your mix file as a dependency. Then, when you want to use the macros, you'll simply use it:

use Pipe

...

That's it. After that, you can continue to use unadorned pipes, or use one of the prepackaged compositions. Initially, we have three:

pipe_matching

This function will compose as long as the computed value matches the value so far. For example, consider this Russian Roulette application:

defmodule RussianRoulette do
  use Pipe

  def click(params) do
    IO.puts "params: #{inspect params} | click..."
    {:ok, "click"}
  end

  def bang(params) do
    IO.puts "params: #{inspect params} | BANG."
    {:error, "bang"}
  end

  def roll do
    pipe_matching {:ok, _},
      {:ok, ""} |> click |> click |> bang |> click
  end

  def rhs_roll do
    pipe_matching x, {:ok, x},
      {:ok, ""} |> click |> click |> bang |> click
  end

end

...would produce...

iex(1)> RussianRoulette.roll
params: {:ok, ""} | click...
params: {:ok, "click"} | click...
params: {:ok, "click"} | BANG.
{:error, "bang"}

It would evaluate functions as long as the accumulator matched the expression. In this case, we process statements as long as the composition yields an :ok on the left hand side.

If we want to pass just the right hand side down the pipeline, we can use pipe_matching/3:

iex(1)> RussianRoulette.rhs_roll
params: "" | click...
params: "click" | click...
params: "click" | BANG.
{:error, "bang"}

pipe_while

Sometimes, you may want to test on something other than a match. This composition strategy will continue as long as your composition satisfies the test function you provide. To implement the above, you could do this just as well:

    def while_test({:ok, _}), do: true
    def while_test(_), do: false
    def inc({code, x}), do: {code, x + 1}
    def double({code, x}), do: {code, x * 2}

    pipe_while(&while_test/1, {:ok, ""} |> click |> click |> bang |> click )

You could also write tests for testing a value, such as whether a value is even, whether a record is valid, or whether a user is authorized.

pipe_with

Sometimes, you want to write the composition rules yourself. You can do this with pipe_with function, pipe where function has a sig of f(x, pipe_segment) where pipe_segment is a function in the pipe. The macro will pass the accumulated value and a function that wraps each pipe segment to your function.

Say you have a list, and you want to do arithmetic on each element of the list. You can do so with pipe_with like this:

  def inc(x), do: x + 1
  def double(x), do: x * 2

  pipe_with fn(acc, f) -> Enum.map(acc, f) end,
        [ 1, 2, 3] |> inc |> double

This returns

[(1 + 1) * 2, (2 + 1) * 2, (3 + 1) * 2]

or

[4, 6, 8]

You could also wrap exceptions, and translate them to the form {:error, acc}, or change nils to blank strings or empty arrays.

Contributions are welcome. Just send a pull request (you must have tests).

elixir-pipes's People

Contributors

adbl avatar batate avatar d0rc avatar ericmj avatar existentialmutt avatar guzishiwo avatar kill9zombie avatar sergey-ivlev avatar ugisozols avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

elixir-pipes's Issues

Warning: this clause cannot match because a previous clause at line X always matches

Thank you for this great library. I have one question:

pipe_matching x, {:ok, x},
      {:ok, source_dir}
      |> build
      |> tag(name, version)
      |> save(dest_dir)
      |> delete_image

leads to this compiler warning:

warning: this clause cannot match because a previous clause at line 1 always matches

Am I doing something wrong or do I simply have to live with this warning?

Related to fmaps and monads

This extensions reads like it is related to fmaps, monads or function composition. Would be great if you could mention those in the README, and how they are derived.

Upgrade to 0.14.x and hexify

Hi!
This is very cool :))

Please do some maintenance: keep up w/ Elixir 0.14.x and register on hex, so it can be used in a most convenient/ less troubling way.

[there are issues with mix when part of deps are from github and other from hex]

cheers

Release `hex.pm` package 0.0.2

I was about to open a pull request to update the required version of Elixir to 1.0, but I noticed you already merged a pull request for this.

Can you publish a 0.0.2 version of the hex package so I can use it as a dependency in my library and still provide support for Elixir 1.0?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.