{"id":13508547,"url":"https://github.com/mgwidmann/elixir-pattern_tap","last_synced_at":"2025-03-16T15:33:26.864Z","repository":{"id":24182946,"uuid":"27573785","full_name":"mgwidmann/elixir-pattern_tap","owner":"mgwidmann","description":"Macro for tapping into a pattern match while using the pipe operator","archived":false,"fork":false,"pushed_at":"2017-07-07T04:46:10.000Z","size":15,"stargazers_count":58,"open_issues_count":3,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-10-12T03:11:28.286Z","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":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mgwidmann.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-12-05T04:09:30.000Z","updated_at":"2023-09-13T17:05:58.000Z","dependencies_parsed_at":"2022-08-22T11:10:49.431Z","dependency_job_id":null,"html_url":"https://github.com/mgwidmann/elixir-pattern_tap","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mgwidmann%2Felixir-pattern_tap","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mgwidmann%2Felixir-pattern_tap/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mgwidmann%2Felixir-pattern_tap/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mgwidmann%2Felixir-pattern_tap/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mgwidmann","download_url":"https://codeload.github.com/mgwidmann/elixir-pattern_tap/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221665603,"owners_count":16860290,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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.618Z","updated_at":"2024-10-27T10:51:56.208Z","avatar_url":"https://github.com/mgwidmann.png","language":"Elixir","funding_links":[],"categories":["Macros"],"sub_categories":[],"readme":"PatternTap\n==========\n\n##### The pipe operator `|\u003e` is an awesome feature of Elixir. Keep using it.\n\nBut when your result cannot be directly input into the next function, you have to stop, pattern match out the value you want and start piping again!\n\nIt is a common pattern to return data like `{:ok, result}` or `{:error, reason}`. When you want to handle both cases, something like [elixir-pipes](https://github.com/batate/elixir-pipes) may be a better use case for you. But otherwise, for simple destructuring of data and returning it in one line (or to just **let it fail**) you can `use PatternTap`!\n\n#### Not fun way\n\n```elixir\ndefmodule Foo do\n  def get_stuff(input) do\n    {:ok, intermediate_result} = input\n      |\u003e Enum.map(\u0026(to_string(\u00261)))\n      |\u003e Foo.HardWorker.work\n    {:ok, result} = intermediate_result\n      |\u003e Enum.map(\u0026(Foo.IntermediateResult.handle(\u00261)))\n    result\n  end\nend\n```\n\nAnytime where the object you want requires pattern matching but you want to either return on one line or continue piping, you can `use PatternTap`!\n\n```elixir\ndef my_function do\n  {:ok, result} = something |\u003e something_else\n  result\nend\n```\n\n#### Pattern Tap\n\nHeres the above example using `PatternTap`\n\n```elixir\ndefmodule Foo do\n  def get_stuff(input) do\n    input\n      |\u003e Enum.map(\u0026(to_string(\u00261)))\n      |\u003e Foo.HardWorker.work\n      |\u003e tap({:ok, r1} ~\u003e r1) # tap({:ok, r1}, r1) is also a supported format\n      |\u003e Enum.map(\u0026(Foo.IntermediateResult.handle(\u00261)))\n      |\u003e tap({:ok, r2} ~\u003e r2) # tap({:ok, r2}, r2) is also a supported format\n  end\nend\n```\n\nAnd the second example\n\n```elixir\n# tap({:ok, result}, result) also supported\ndef my_function do\n  something |\u003e something_else |\u003e tap({:ok, result} ~\u003e result)\nend\n```\n\n### Variable Leakage\n\n**PatternTap** makes use of `case` in order to prevent leaking the variables you create. So after using `tap`, you won't have access to the patterns you create. This means if you bind more than one variable in your pattern, you won't have access to it.\n\nTake the following example:\n\n```elixir\nmy_data = {:data1, :data2} |\u003e tap({d1, d2} ~\u003e d1)\nd2 # =\u003e ** (CompileError) ...: function d2/0 undefined\n```\n\nInstead you can use `destruct` to destructure the data you want. This does the same thing but with the side effect of keeping the binding you created in your patterns.\n\n```elixir\n{:data1, :data2} |\u003e destruct({d1, d2} ~\u003e d1) |\u003e some_func(d2)\n```\n\nTo simply save a partial result for later use, consider using `leak/2`:\n\n```elixir\niex\u003e [:data1, :data2] |\u003e Enum.reverse |\u003e leak(reversed) |\u003e hd\n:data2\niex\u003e reversed\n[:data2, :data1]\n```\n\nNote that `|\u003e leak(variable_name)` is equivalent to `|\u003e destruct(variable_name ~\u003e variable_name)`.\n\n\n### Unmatched results\n\n#### Tap\n\nBecause `tap/3` uses `case` you will get a `CaseClauseError` with the data which did not match in the error report.\n\n```elixir\n{:error, \"reason\"} |\u003e tap({:ok, result} ~\u003e result)\n# ** (CaseClauseError) no case clause matching: {:error, \"reason\"}\n```\n\n\n#### Destruct\n\nSince `destruct/3` and `leak/2` use `=` you will instead get a `MatchError` with the data which did not match in the error report.\n\n```elixir\n{:error, \"reason\"} |\u003e destruct({:ok, result} ~\u003e result)\n# ** (MatchError) no match of right hand side value: {:error, \"reason\"}\n```\n\n#### Leak\n\n`leak(data, variable)` expands to `variable = data`, so in a simple use case,\nleak can never fail, though it may override an existing variable:\n\n```elixir\niex\u003e old_var = 5\niex\u003e [1, 2] |\u003e leak(old_var) |\u003e length\n2\niex\u003e old_var\n[1, 2]\n```\n\nBecause `leak(data, variable)` expands to `variable = data`, we can do all of our\nfavorite Elixir pattern-matching tricks here, e.g.:\n\n```elixir\niex\u003e %{a: 1, b: 2} |\u003e leak(%{b: b})\n%{a: 1, b: 2}\niex\u003e b\n2\n```\n\nThis flexibility allows `leak` to fail just like `destruct`:\n\n```elixir\niex\u003e %{a: 1, b: 2} |\u003e leak(%{c: c})\n** (MatchError) no match of right hand side value: %{a: 1, b: 2}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmgwidmann%2Felixir-pattern_tap","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmgwidmann%2Felixir-pattern_tap","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmgwidmann%2Felixir-pattern_tap/lists"}