PatternMetonyms.pattern

You're seeing just the macro pattern, go back to PatternMetonyms module for more information.
Link to this macro

pattern(metonym)

View Source (macro)

Macro used to define pattern metonyms.

There are three types of pattern, listed below with examples:

  1. Implicitly Bidirectional

The simplest type of pattern, because of its symmetry requirement, it can only be defined using concrete data (or adapted macro). Therefore no computation is allowed, and they are thus compatible with case and function heads. They take the form:

pattern <name>(<[variables]>) = <pattern>

where pattern reuses the variables

iex> defmodule DoctestTPX do
...>   import PatternMetonyms
...>
...>   pattern ok(x)    = {:ok, x}
...>   pattern error(x) = {:error, x}
...>
...>   pattern cons(x, xs) = [x | xs]
...>
...>   def foo(x) do
...>     view x do
...>       ok(a)    -> a
...>       error(b) -> b
...>     end
...>   end
...>
...>   def bar(x) do
...>     case x do
...>       ok(a)    -> a
...>       error(b) -> b
...>     end
...>   end
...>
...>   def baz(ok(a)   ), do: a
...>   def baz(error(b)), do: b
...>
...>   def mk_ok(x), do: ok(x)
...>
...>   def blorg(xs) do
...>     view xs do
...>       cons(x, xs) -> cons(x, Enum.map(xs, fn x -> -x end))
...>     end
...>   end
...> end
iex> DoctestTPX.foo({:ok, :banana})
:banana
iex> DoctestTPX.foo({:error, :split})
:split
iex> DoctestTPX.bar({:ok, :peach})
:peach
iex> DoctestTPX.baz({:error, :melba})
:melba
iex> DoctestTPX.mk_ok(:melba)
{:ok, :melba}
iex> DoctestTPX.blorg([1, 2, 3])
[1, -2, -3]
  1. Unidirectional

This type of pattern is read only, it may be used as abstraction over pattern matching on concrete data type that can not be reused to construct data, or as abstraction over views, as explained in view/2. They take the form:

pattern <name>(<[variables]>) <- <pattern>
pattern <name>(<[variables]>) <- (<function> -> <pattern>)

where pattern reuses the variables. (function -> pattern) is called a view

iex> defmodule DoctestTPY do
...>   import PatternMetonyms
...>
...>   pattern head(x) <- [x | _]
...>
...>   pattern rev_head(x) <- (reverse() -> head(x))
...>
...>   def reverse(xs), do: Enum.reverse(xs)
...>
...>   def foo(xs) do
...>     view xs do
...>       head(x) -> x
...>       []      -> []
...>     end
...>   end
...>
...>   def bar(x) do
...>     case x do
...>       head(a) -> a
...>       []      -> []
...>     end
...>   end
...>
...>   def baz(head(a)), do: a
...>
...>   def blorg(xs) do
...>     view xs do
...>       rev_head(x) -> x
...>     end
...>   end
...> end
iex> DoctestTPY.foo([1, 2, 3])
1
iex> DoctestTPY.bar([1, 2, 3])
1
iex> DoctestTPY.baz([1, 2, 3])
1
iex> DoctestTPY.blorg([1, 2, 3])
3
  1. Explicitly bidirectional

This type of pattern allows the same kind of abstraction as unidirectional one, but also permit defining how to construct data from computation (if necessary). They take the form:

pattern (<name>(<[variables]>) <- (<function> -> <pattern>)) when <name>(<[variables]>) = <builder>

where pattern and builder reuse the variables. (function -> pattern) is called a view

iex> defmodule DoctestTPZ do
...>   import PatternMetonyms
...>
...>   pattern (snoc(x, xs) <- (unsnoc() -> {x, xs}))
...>     when snoc(x, xs) = Enum.reverse([x | Enum.reverse(xs)])
...>
...>   defp unsnoc([]), do: :error
...>   defp unsnoc(xs) do
...>     [x | rev_tail] = Enum.reverse(xs)
...>     {x, Enum.reverse(rev_tail)}
...>   end
...>
...>   def foo(xs) do
...>     view xs do
...>       snoc(x, _) -> x
...>       []      -> []
...>     end
...>   end
...>
...>   def bar(xs) do
...>     view xs do
...>       snoc(x, xs) -> snoc(-x, xs)
...>       []      -> []
...>     end
...>   end
...> end
iex> DoctestTPZ.foo([1, 2, 3])
3
iex> DoctestTPZ.bar([1, 2, 3])
[1, 2, -3]

Patterns using a view can not be used with case.

Remote function can be used within a view, but the __MODULE__ alias won't work because the expansion is not done at the usage site. It is not yet determined which behavior is desired.

iex> defmodule DoctestTPA do
...>   import PatternMetonyms
...>
...>   pattern rev_head(x) <- (Enum.reverse -> [x | _])
...>
...>   def blorg(xs) do
...>     view xs do
...>       rev_head(x) -> x
...>     end
...>   end
...> end
iex> DoctestTPA.blorg([1, 2, 3])
3

Unknown yet if anonymous functions can be supported.

Guards within a pattern definition is considered undefined behavior, it may work, but it depends on the context. Consider that if the behavior gets a specification, it would be the removal of the possibility of using them. Patterns using a view pattern are the recommend approach. For example:

pattern heart(n) <- (less_than_3 -> {:ok, n})

Patterns can be documented:

@doc """
heart matches when the number is heartfelt <3
"""
pattern heart(n) <- (less_than_3() -> {:ok, n})

You can then access the doc as usual: h heart, or h Module.heart.