Skip to content

Commit ae23d6e

Browse files
committed
Added additional syntax to make cleaner use of tap
1 parent 302eeaa commit ae23d6e

File tree

4 files changed

+42
-8
lines changed

4 files changed

+42
-8
lines changed

README.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,19 @@ defmodule Foo do
4141
input
4242
|> Enum.map(&(to_string(&1)))
4343
|> Foo.HardWorker.work
44-
|> tap({:ok, r1}, r1)
44+
|> tap({:ok, r1} ~> r1) # tap({:ok, r1}, r1) is also a supported format
4545
|> Enum.map(&(Foo.IntermediateResult.handle(&1)))
46-
|> tap({:ok, r2}, r2)
46+
|> tap({:ok, r2} ~> r2) # tap({:ok, r2}, r2) is also a supported format
4747
end
4848
end
4949
```
5050

5151
And the second example
5252

5353
```elixir
54+
# tap({:ok, result}, result) also supported
5455
def my_function do
55-
something |> something_else |> tap({:ok, result}, result)
56+
something |> something_else |> tap({:ok, result} ~> result)
5657
end
5758
```
5859

@@ -63,14 +64,14 @@ end
6364
Take the following example:
6465

6566
```elixir
66-
my_data = {:data1, :data2} |> tap({d1, d2}, d1)
67+
my_data = {:data1, :data2} |> tap({d1, d2} ~> d1)
6768
d2 # => ** (CompileError) ...: function d2/0 undefined
6869
```
6970

7071
Instead 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.
7172

7273
```elixir
73-
{:data1, :data2} |> destruct({d1, d2}, d1) |> some_func(d2)
74+
{:data1, :data2} |> destruct({d1, d2} ~> d1) |> some_func(d2)
7475
```
7576

7677
### Unmatched results
@@ -80,7 +81,7 @@ Instead you can use `destruct` to destructure the data you want. This does the s
8081
Because `tap/3` uses `case` you will get a `CaseClauseError` with the data which did not match in the error report.
8182

8283
```elixir
83-
{:error, "reason"} |> tap({:ok, result}, result)
84+
{:error, "reason"} |> tap({:ok, result} ~> result)
8485
# ** (CaseClauseError) no case clause matching: {:error, "reason"}
8586
```
8687

@@ -90,6 +91,6 @@ Because `tap/3` uses `case` you will get a `CaseClauseError` with the data which
9091
Since `destruct/3` uses `=` you will instead get a `MatchError` with the data which did not match in the error report.
9192

9293
```elixir
93-
{:error, "reason"} |> destruct({:ok, result}, result)
94+
{:error, "reason"} |> destruct({:ok, result} ~> result)
9495
# ** (MatchError) no match of right hand side value: {:error, "reason"}
9596
```

lib/pattern_tap.ex

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,23 @@ defmodule PatternTap do
1414
end
1515
end
1616

17+
defmacro tap(data, {:~>, _, [pattern, var]}) do
18+
quote do
19+
tap(unquote(data), unquote(pattern), unquote(var))
20+
end
21+
end
22+
1723
defmacro destruct(data, pattern, var) do
1824
quote do
1925
unquote(pattern) = unquote(data)
2026
unquote(var)
2127
end
2228
end
2329

30+
defmacro destruct(data, {:~>, _, [pattern, var]}) do
31+
quote do
32+
destruct(unquote(data), unquote(pattern), unquote(var))
33+
end
34+
end
35+
2436
end

mix.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ defmodule PatternTap.Mixfile do
33

44
def project do
55
[app: :pattern_tap,
6-
version: "0.2.0",
6+
version: "0.2.1",
77
elixir: "~> 1.0",
88
description: """
99
Macro for tapping into a pattern match while using the pipe operator

test/pattern_tap_test.exs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,52 +4,73 @@ defmodule PatternTapTest do
44

55
test "can do simple pattern matching" do
66
assert tap(:ok, :ok, nil) == nil
7+
assert tap(:ok, :ok ~> nil) == nil
78
assert destruct(:ok, :ok, nil) == nil
9+
assert destruct(:ok, :ok ~> nil) == nil
810
end
911

1012
test "can do list pattern matching" do
1113
assert tap([:a], [a], a) == :a
14+
assert tap([:a], [a] ~> a) == :a
1215
assert destruct([:a], [a], a) == :a
16+
assert destruct([:a], [a] ~> a) == :a
1317
end
1418

1519
test "variables are not available after" do
1620
tap([:foo], [f], f)
1721
assert binding[:f] == nil
22+
tap([:foo], [f] ~> f)
23+
assert binding[:f] == nil
1824
end
1925

2026
test "can do tuple pattern matching" do
2127
assert tap({:b}, {b}, b) == :b
28+
assert tap({:b}, {b} ~> b) == :b
2229
assert destruct({:b}, {b}, b) == :b
30+
assert destruct({:b}, {b} ~> b) == :b
2331
end
2432

2533
@data [:a, :b, :c]
2634
test "can match with the |> operator" do
2735
assert @data |> Enum.map(&(to_string(&1))) |> tap([_, b, _], b) == "b"
36+
assert @data |> Enum.map(&(to_string(&1))) |> tap([_, b, _] ~> b) == "b"
2837
assert @data |> Enum.map(&(to_string(&1))) |> destruct([_, b, _], b) == "b"
38+
assert @data |> Enum.map(&(to_string(&1))) |> destruct([_, b, _] ~> b) == "b"
2939
end
3040

3141
@data [key: :val, key2: :val2]
3242
test "can match |> with keyword lists" do
3343
assert @data |> tap([_, {:key2, v}], v) == :val2
44+
assert @data |> tap([_, {:key2, v}] ~> v) == :val2
3445
assert @data |> destruct([_, {:key2, v}], v) == :val2
46+
assert @data |> destruct([_, {:key2, v}] ~> v) == :val2
3547
end
3648

3749
test "can match typical {:ok, result}" do
3850
assert {:ok, 1} |> tap({:ok, result}, result) == 1
51+
assert {:ok, 1} |> tap({:ok, result} ~> result) == 1
3952
assert {:ok, 1} |> destruct({:ok, result}, result) == 1
53+
assert {:ok, 1} |> destruct({:ok, result} ~> result) == 1
4054
end
4155

4256
test "failure matches result in the correct error message" do
4357
assert_raise CaseClauseError, "no case clause matching: {:error, \"reason\"}", fn ->
4458
{:error, "reason"} |> tap({:ok, result}, result)
4559
end
60+
assert_raise CaseClauseError, "no case clause matching: {:error, \"reason\"}", fn ->
61+
{:error, "reason"} |> tap({:ok, result} ~> result)
62+
end
4663
assert_raise MatchError, "no match of right hand side value: {:error, \"reason\"}", fn ->
4764
{:error, "reason"} |> destruct({:ok, result}, result)
4865
end
66+
assert_raise MatchError, "no match of right hand side value: {:error, \"reason\"}", fn ->
67+
{:error, "reason"} |> destruct({:ok, result} ~> result)
68+
end
4969
end
5070

5171
test "destruct keeps variables around" do
5272
destruct({:a, :b}, {a, b}, a)
73+
destruct({:a, :b}, {a, b} ~> a)
5374
assert a == :a
5475
assert b == :b
5576
end

0 commit comments

Comments
 (0)