{"id":13508553,"url":"https://github.com/batate/elixir-pipes","last_synced_at":"2026-02-19T07:03:07.470Z","repository":{"id":57533635,"uuid":"15050539","full_name":"batate/elixir-pipes","owner":"batate","description":"Macros for more flexible composition with the Elixir Pipe operator","archived":false,"fork":false,"pushed_at":"2018-09-09T15:53:01.000Z","size":26,"stargazers_count":324,"open_issues_count":5,"forks_count":22,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-10-21T19:02:50.647Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/batate.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-12-09T15:11:35.000Z","updated_at":"2025-08-26T21:33:01.000Z","dependencies_parsed_at":"2022-09-26T18:21:12.151Z","dependency_job_id":null,"html_url":"https://github.com/batate/elixir-pipes","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/batate/elixir-pipes","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/batate%2Felixir-pipes","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/batate%2Felixir-pipes/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/batate%2Felixir-pipes/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/batate%2Felixir-pipes/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/batate","download_url":"https://codeload.github.com/batate/elixir-pipes/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/batate%2Felixir-pipes/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29605808,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-19T06:47:36.664Z","status":"ssl_error","status_checked_at":"2026-02-19T06:45:47.551Z","response_time":117,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T02:00:54.705Z","updated_at":"2026-02-19T07:03:07.453Z","avatar_url":"https://github.com/batate.png","language":"Elixir","funding_links":[],"categories":["Macros","Elixir"],"sub_categories":[],"readme":"# elixir-pipes\n\nElixir Pipes is an [Elixir](https://github.com/elixir-lang/elixir/) extension that extends the pipe (|\u003e) operator through macros. \n\n## The Pipe: Elixir Flavor Packet\n\nSome of the [best](http://joearms.github.io/2013/05/31/a-week-with-elixir.html) [programmers](http://pragprog.com/book/elixir/programming-elixir) 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: \n\n```elixir\n  def inc(x), do: x + 1\n  def double(x), do: x * 2\n  \n  1 |\u003e inc |\u003e double\n```\n\nThe 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. \n\n### Trouble in the Kitchen\n\nSometimes, 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. \n\nElixir-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:\n\n- compose a pipe that uses some variation of a function call like  `[1, 2, 3] |\u003e add(1) |\u003e times(2)` ?\n- halt the execution of a pipe on error?\n- tease nils to empty strings, without changing your original functions?\n- transform exceptions to Erlang-style `{:error, x}` tuples?\n\nThe recipes are all there waiting for you. \n\n## Getting Started\n\nAll 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:\n\n```Elixir\nuse Pipe\n\n...\n```\n\nThat's it. After that, you can continue to use unadorned pipes, or use one of the prepackaged compositions. Initially, we have three:\n\n### pipe_matching\n\nThis function will compose as long as the computed value matches the value so far. For example, consider this Russian Roulette application:\n\n```elixir\ndefmodule RussianRoulette do\n  use Pipe\n\n  def click(params) do\n    IO.puts \"params: #{inspect params} | click...\"\n    {:ok, \"click\"}\n  end\n\n  def bang(params) do\n    IO.puts \"params: #{inspect params} | BANG.\"\n    {:error, \"bang\"}\n  end\n\n  def roll do\n    pipe_matching {:ok, _},\n      {:ok, \"\"} |\u003e click |\u003e click |\u003e bang |\u003e click\n  end\n\n  def rhs_roll do\n    pipe_matching x, {:ok, x},\n      {:ok, \"\"} |\u003e click |\u003e click |\u003e bang |\u003e click\n  end\n\nend\n```\n\n...would produce...\n\n```elixir\niex(1)\u003e RussianRoulette.roll\nparams: {:ok, \"\"} | click...\nparams: {:ok, \"click\"} | click...\nparams: {:ok, \"click\"} | BANG.\n{:error, \"bang\"}\n```\n\nIt 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.\n\nIf we want to pass just the right hand side down the pipeline, we can use pipe_matching/3:\n\n```elixir\niex(1)\u003e RussianRoulette.rhs_roll\nparams: \"\" | click...\nparams: \"click\" | click...\nparams: \"click\" | BANG.\n{:error, \"bang\"}\n```\n\n### pipe_while\n\nSometimes, 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:\n\n```elixir\n    def while_test({:ok, _}), do: true\n    def while_test(_), do: false\n    def inc({code, x}), do: {code, x + 1}\n    def double({code, x}), do: {code, x * 2}\n\n    pipe_while(\u0026while_test/1, {:ok, \"\"} |\u003e click |\u003e click |\u003e bang |\u003e click )\n```\n\nYou 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. \n\n### pipe_with\n\nSometimes, 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. \n\nSay 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:\n\n```elixir\n  def inc(x), do: x + 1\n  def double(x), do: x * 2\n\n  pipe_with fn(acc, f) -\u003e Enum.map(acc, f) end,\n        [ 1, 2, 3] |\u003e inc |\u003e double\n\n```\nThis returns \n\n```\n[(1 + 1) * 2, (2 + 1) * 2, (3 + 1) * 2]\n```\nor \n```\n[4, 6, 8]\n```\n\nYou could also wrap exceptions, and translate them to the form `{:error, acc}`, or change nils to blank strings or empty arrays. \n\nContributions are welcome. Just send a pull request (you must have tests). \n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbatate%2Felixir-pipes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbatate%2Felixir-pipes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbatate%2Felixir-pipes/lists"}