<turbo-stream action="append" target="posts_list"><template>    <div class="postbit" id="303016" data-post-id="303016">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="zachallaun" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/zachallaun/120/29208_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  zachallaun
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>This is really cool!</p>
<p>The first thing that comes first to mind is composition possibilities using techniques similar to Ecto’s query DSL.</p>
<p>Playing with the APIs a bit:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">power_filter = Matcha.filter(%{power: power} when power &gt; 9000)
age_filter = Matcha.filter(%{age: age} when age &gt; 20)

and_query =
  Matcha.filter(
    {_id, ^power_filter and ^age_filter}
  )

or_query =
  Matcha.filter(
    {_id, ^power_filter or ^age_filter}
  )

goku_query =
  Matcha.filter(
    {_id, ^and_query and %{name: "Son Goku"}}
  )

Matcha.select(table, ^and_query) |&gt; Enum.to_list()
#=&gt; [{id, goku}]

Matcha.select(table, ^or_query) |&gt; Enum.to_list()
#=&gt; [{id, krillin}, {id, goku}]

Matcha.select(table, ^goku_query) |&gt; Enum.to_list()
#=&gt; [{id, goku}]

# or operate on an enumerable directly
Matcha.select(saiyans, ^power_filter) |&gt; Enum.to_list()
#=&gt; [goku]
</code></pre>
<p>Takeaways/questions from above:</p>
<ul>
<li>Can the API be simplified, with more living in <code>Matcha</code> for discoverability and composability? Is <code>Matcha.Table.Query</code> needed at all?</li>
<li>Can a <code>Matcha.select</code> be intelligent enough to operate on enumerables as well as tables? (It first argument is an atom, assume table name?)</li>
</ul>
<p>Far-out-there-idea: Could all of this be used to implement a far larger subset of the Ecto query DSL for ETS tables so that it can be used as an Ecto adapter with far fewer caveats than currently exist for Etso? (Perhaps <a class="mention" href="/u/evadne" rel="nofollow">@evadne</a> may be able to shed more light on this if she has time.)</p>
<p>Edit to add: this strikes me as more doable than previous suggestions of merging match specs because you sidestep the introduction of new/named bindings. You’re only matching/filtering on the structure of data, and named bindings are only available within a single filter clause.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="303016" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/matcha-first-class-match-specifications-for-elixir/52182/22">Post #21</a>
	                </div>
	            </div>
              <div id="likers-container-303016" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="303016"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #21"></div>
  </section>
</div>
    <div class="postbit" id="303017" data-post-id="303017">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="evadne" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/evadne/120/13949_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  evadne
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I don’t know, but I would be happy to review any attempt at this. Etso was built to my own requirements and has been deployed in production 24/7/365 for several years as-is.</p>
<p>The main problem solved within Etso was to map Ecto’s Query, which is currently an undocumented structure, to ETS operations</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="303017" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/matcha-first-class-match-specifications-for-elixir/52182/23">Post #22</a>
	                </div>
	            </div>
              <div id="likers-container-303017" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="303017"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #22"></div>
  </section>
</div>
    <div class="postbit" id="303058" data-post-id="303058">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="christhekeele" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/christhekeele/120/1039_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  christhekeele
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p><a class="mention" href="/u/zachallaun" rel="nofollow">@zachallaun</a> thanks for your thoughts! Some notes:</p>
<hr>
<aside class="quote no-group" data-username="zachallaun" data-post="22" data-topic="52182">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/zachallaun/48/29208_2.png" class="avatar"> zachallaun:</div>
<blockquote>
<pre data-code-wrap="elixir"><code class="lang-elixir">power_filter = Matcha.filter(%{power: power} when power &gt; 9000)
age_filter = Matcha.filter(%{age: age} when age &gt; 20)

and_query =
  Matcha.filter(
    {_id, ^power_filter and ^age_filter}
  )

or_query =
  Matcha.filter(
    {_id, ^power_filter or ^age_filter}
  )
</code></pre>
</blockquote>
</aside>
<p>This syntax is pretty intriguing, and doable with a bit of work to get <code>Matcha</code> macros to recognize <code>Matcha</code> structs. It’s the semantics that are difficult: your example works mainly by virtue of how uniquely map pattern matching is handled. In any other context it’d be trivial to combine non-sensical filters:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">filter(power when power &gt; 9000) and filter(age when age &lt; 30)
</code></pre>
<p>Ultimately, pattern matching syntax is not readily composable. To do it justice you need to know more about the structure you are querying upfront, have a mechanism for navigating the composition of data bindings, and a query planner to coalesce predicates. That’s exactly what SQL is for, so I think Etso is probably always going to be a more-correct solution for this sort of thing. I intend to keep exploring though as filters come together!</p>
<hr>
<aside class="quote no-group" data-username="zachallaun" data-post="22" data-topic="52182">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/zachallaun/48/29208_2.png" class="avatar"> zachallaun:</div>
<blockquote>
<ul>
<li>Can the API be simplified, with more living in <code>Matcha</code> for discoverability and composability? Is <code>Matcha.Table.Query</code> needed at all?</li>
<li>Can a <code>Matcha.select</code> be intelligent enough to operate on enumerables as well as tables? (It first argument is an atom, assume table name?)</li>
</ul>
</blockquote>
</aside>
<p>Good questions, and related ones. It mostly depends on what this featureset ends up looking like.</p>
<p>Right now, there’s an intentional divide: <code>Matcha.{Pattern,Filter,Spec}</code> APIs let you apply them to in-memory enumerables, and <code>Matcha.{Table,Mnesia}</code> APIs mediate querying those respective data stores. They are separate today because match specs can be compiled differently for different contexts.</p>
<p>Today, Matcha effectively re-writes matchspecs intended for in-memory data so that they can be executed via <code>:erlang.match_spec_test</code>/<code>:ets.match_spec_run</code> but still behave more like the results of an <code>:ets.select</code> call, since the two modes of invocation have different semantics. Ironically this prevents the same MS from being used in either context. However, I can see a higher-level API JIT-converting an unadulterated MS into the right form for the target it is being called against.</p>
<p>I don’t love the current solution and have been meaning to look into a refactor for a while, this sort of functionality definitely motivates me to focus on reworking it to get a cleaner API!</p>
<hr>
<aside class="quote no-group" data-username="zachallaun" data-post="22" data-topic="52182">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/zachallaun/48/29208_2.png" class="avatar"> zachallaun:</div>
<blockquote>
<p>Far-out-there-idea: Could all of this be used to implement a far larger subset of the Ecto query DSL for ETS tables so that it can be used as an Ecto adapter with far fewer caveats than currently exist for Etso?</p>
</blockquote>
</aside>
<p>I’m not sure there’s much overlap. Fundamentally <code>Matcha</code> converts Elixir AST into MS AST; and <code>Etso</code> converts <code>Ecto</code>’s SQL-esque AST into MS AST and executes it for you. SQL, and <code>Ecto</code>’s AST, is more expressive and composable than what pure Elixir syntax can describe in match heads.</p>
<p>I am starting to emit some metadata about the compiled MSs that might help with certain kinds of composition, currently by exposing variable binding names post-compilation, but that’s more likely to lead to a <code>Matcha</code>-powered <a href="https://www.erlang.org/doc/man/qlc" rel="nofollow"><code>:qlc</code></a>-type thing with it’s own semantics rather than something that can sensibly insert itself into SQL semantics.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="303058" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/matcha-first-class-match-specifications-for-elixir/52182/24">Post #23</a>
	                </div>
	            </div>
              <div id="likers-container-303058" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="303058"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #23"></div>
  </section>
</div>
    <div class="postbit" id="305318" data-post-id="305318">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="christhekeele" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/christhekeele/120/1039_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  christhekeele
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<h1><a name="p-305318-devlogmd-2023-10-20-1" class="anchor" href="#p-305318-devlogmd-2023-10-20-1" aria-label="Heading link" rel="nofollow"></a>DEVLOG.md 2023-10-20</h1>
<p>This update discusses new syntactic support for <em><strong>nested matches</strong></em> I want to experiment with for <a href="https://forum.elixirforum.com/t/matcha-first-class-match-specifications-for-elixir/52182" rel="nofollow"><code>Matcha</code></a>!</p>
<hr>
<p>I started <a href="https://forum.elixirforum.com/t/matcha-first-class-match-specifications-for-elixir/52182/21" rel="nofollow">developing <code>Matcha.filter/1</code></a> because I needed it for something I wanted to build for <a href="https://forum.elixirforum.com/t/2023-10-28-spawnfest-is-back-worldwide-online/57221" rel="nofollow">SpawnFest</a>. Sadly, while developing the unannounced library I wanted to release first and use in the competition, I’ve ran into a limitation of matchspec’s expressivity I’ve long aspired to overcome. I’ve suspected for a while that it’s solvable, but solving it properly in <code>Matcha</code> will take too much time away from the development of the depending library I’d planned on using in the competition—it’s a hard blocker. I may submit something else, though!</p>
<p>My consolation prize is that my remorse has fueled me to think on the problem of <em>nested matches</em> more, and recent changes to the <code>Matcha</code> compiler to support filters should give me what I need to implement it. Let’s dive into nested matches in matchspecs!</p>
<hr>
<h2><a name="p-305318-the-power-of-patterns-2" class="anchor" href="#p-305318-the-power-of-patterns-2" aria-label="Heading link" rel="nofollow"></a>The power of patterns</h2>
<p>Here at <a href="https://forum.elixirforum.com/u/christhekeele/summary" rel="nofollow"><img src="https://forum.elixirforum.com/images/emoji/apple/tea.png?v=15" title=":tea:" class="emoji" alt=":tea:" loading="lazy" width="20" height="20"> <img src="https://forum.elixirforum.com/images/emoji/apple/tm.png?v=15" title=":tm:" class="emoji" alt=":tm:" loading="lazy" width="20" height="20"> <strong>Matcha Incorporated</strong> <img src="https://forum.elixirforum.com/images/emoji/apple/copyright.png?v=15" title=":copyright:" class="emoji" alt=":copyright:" loading="lazy" width="20" height="20"></a>, we’re big fans of <a href="https://hexdocs.pm/elixir/Kernel.SpecialForms.html#=/2" rel="noopener nofollow ugc"><strong>the match operator,</strong> <code>=</code></a>, which performs a pattern match.</p>
<p>When you’re first learning a BEAM VM language, it’s easy to think of it as just <em><strong>the variable binding operator</strong></em>. After all, these are the semantics of <code>=</code> most of us are familiar with coming from other languages, and the pattern <code>variable</code> on the left side will always match the right side, and bind the entirety of the right side to <code>variable</code>:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">variable = {:some, %{complicated: [:data, "structure"]}}
variable
#=&gt; {:some, %{complicated: [:data, "structure"]}}
</code></pre>
<p>Over time, we learn to appreciate that pattern matching can also perform <em><strong>destructuring</strong></em> as well as <em><strong>variable binding</strong></em>:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">{:some, data} = variable
data
#=&gt; %{complicated: [:data, "structure"]}
</code></pre>
<p>It provides a natural syntax for <em><strong>multiple assignment</strong></em>, even from deeply nested values:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">{:some, %{complicated: [type, specifics]}} = variable
{type, specifics}
#=&gt; {:data, "structure"}
</code></pre>
<p>But what’s really wild is that you can <em><strong>nest matches inside matches</strong></em>, to both bind variables at a shallower level of nesting, and match on data at a deeper level of nesting:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">{:some, data = %{complicated: [:data, specifics]}} = variable
{data, specifics}
#=&gt; {%{complicated: [:data, "structure"]}, "structure"}
</code></pre>
<p>This is often used in combination with guard expressions, so we can extract a set of <code>data</code> when its <code>specifics</code> satisfy a guard:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">case variable do
  {:some, data = %{complicated: [:data, specifics]}} when is_binary(specifics)
    -&gt; data
end
#=&gt; %{complicated: [:data, "structure"]}
</code></pre>
<h2><a name="p-305318-matches-inside-matches-inside-matchspecs-3" class="anchor" href="#p-305318-matches-inside-matches-inside-matchspecs-3" aria-label="Heading link" rel="nofollow"></a>Matches inside matches inside matchspecs</h2>
<p>Obviously, if you can do this in Elixir, I want to support it in <code>Matcha</code>. Extracting general <code>data</code> from an object in an <code>:ets</code> table where its <code>specifics</code> satisfy a certain guard is a common use-case. However, if you’ve ever spent any time studying <a href="https://www.erlang.org/doc/apps/erts/match_spec#grammar" rel="nofollow">the matchspec grammar</a>, first off: I’m sorry.</p>
<p>Secondly, you might have realized that there is no direct analog of <strong>nested matching</strong> in them! Specifically, the anatomy of a <code>MatchHeadPart</code> does not allow you to <em>both</em> describe binding a term to a variable, <em>and</em> performing a destructuring operation on that term (that may permit a binding with a deeper nested term).</p>
<p>Put another way, you cannot both <em>bind to</em> <em><strong>and</strong></em> <em>destructure</em> a term in a matchspec!</p>
<p>To clarify, for some:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">Matcha.spec do
  pattern -&gt; ...
end
</code></pre>
<p>This pattern <strong>is</strong> representable:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">{:some, data} -&gt; ...
</code></pre>
<p>And this pattern <strong>is</strong> representable:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">{:some, %{complicated: [:data, specifics]}} -&gt; ...
</code></pre>
<p>But <em>both at once</em> <em><strong>are not</strong></em>:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">{:some, data = %{complicated: [:data, specifics]}} -&gt; ...
</code></pre>
<p>Today, <code>Matcha</code> reflects this reality:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">Matcha.spec do
  {:some, data = %{complicated: [:data, specifics]}} when is_binary(specifics) -&gt; data
end
#!&gt; ** (Matcha.Rewrite.Error) found problems rewriting code into a match spec: when binding variables
#!&gt; 
#!&gt;  ({:some, data = %{complicated: [:data, specifics]}} when is_binary(specifics) -&gt; data)
#!&gt;    error: cannot match `data` to `%{complicated: [:data, specifics]}`: cannot use the match operator in match spec heads, except to re-assign variables to each other
#!&gt;     (matcha 0.1.10) lib/matcha/rewrite.ex:472: Matcha.Rewrite.raise_match_in_match_error!/3
#!&gt;     (elixir 1.15.6) lib/macro.ex:667: Macro.do_traverse/4
#!&gt;     (matcha 0.1.10) lib/matcha/rewrite.ex:445: Matcha.Rewrite.do_rewrite_bindings/2
#!&gt;     (matcha 0.1.10) lib/matcha/rewrite.ex:323: Matcha.Rewrite.rewrite_clause/2
#!&gt;     (elixir 1.15.6) lib/enum.ex:1693: Enum."-map/2-lists^map/1-1-"/2
#!&gt;     (matcha 0.1.10) lib/matcha/rewrite.ex:210: Matcha.Rewrite.spec/2
#!&gt;     (matcha 0.1.10) lib/matcha/rewrite.ex:199: Matcha.Rewrite.build_spec/3
#!&gt;     (matcha 0.1.10) expanding macro: Matcha.spec/1
</code></pre>
<h2><a name="p-305318-look-to-the-guards-4" class="anchor" href="#p-305318-look-to-the-guards-4" aria-label="Heading link" rel="nofollow"></a>Look to the Guards</h2>
<p>Is there a way forward from this? Nope, not really.</p>
<p>At least, not until recently.</p>
<ul>
<li>Since before I started working on <code>Matcha</code>, matchspec guards supported most, but not all, <a href="https://www.erlang.org/doc/reference_manual/expressions#guard-expressions" rel="nofollow">BIFs</a> that are <a href="https://hexdocs.pm/elixir/patterns-and-guards.html#guards" rel="noopener nofollow ugc">allowed in guards</a>.</li>
<li>OTP 25 introduced support for using the BIFs <a href="https://www.erlang.org/doc/man/erlang#is_map_key-2" rel="nofollow"><code>:erlang.is_map_key/2</code></a> and <a href="https://www.erlang.org/doc/man/erlang#map_get-2" rel="nofollow"><code>:erlang.map_get/2</code></a> in matchspecs.</li>
<li>At <a href="https://github.com/erlang/otp/issues/7045" rel="noopener nofollow ugc">my behest</a>, in OTP 26 <a class="mention" href="/u/jhogberg" rel="nofollow">@jhogberg</a> graciously added support for all the other missing guard BIFs to matchspecs, mostly critically including <a href="https://www.erlang.org/doc/man/erlang#tuple_size-1" rel="nofollow"><code>:erlang.tuple_size/1</code></a>, and added tests to ensure that all guard-safe BIFs are also allowed in matchspec guards going forwards!</li>
</ul>
<h3><a name="p-305318-fake-it-til-you-match-it-5" class="anchor" href="#p-305318-fake-it-til-you-match-it-5" aria-label="Heading link" rel="nofollow"></a>Fake it 'til you match it</h3>
<p>With the added support for the guard <a href="https://www.erlang.org/doc/man/erlang#tuple_size-1" rel="nofollow"><code>:erlang.tuple_size/1</code></a> in matchspecs, we finally have all the tools we need to support both binding and destructuring on the same nested term. All it would take is re-implementing destructuring of composite terms into guard checks in the <code>Matcha</code> compiler!</p>
<p>Take, for example, the matchspec:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">Matcha.spec do
  {:some, data = %{complicated: [:data, specifics]}}
    when is_binary(specifics) 
      -&gt; data
end
</code></pre>
<p>We can’t literally describe the nested match <code>data = %{complicated: [:data, specifics]}</code> in today’s match specification grammar.</p>
<p>But what we <em><strong>can</strong></em> do is… grisly, but semantically equivalent:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">Matcha.spec do
  {:some, data}
    when :erlang.is_map(data) and :erlang.is_map_key(:complicated, data)
     and :erlang.is_list(:erlang.map_get(:complicated, data))
     and :erlang.length(:erlang.map_get(:complicated, data)) == 2
     and :erlang.hd(:erlang.map_get(:complicated, data)) == :data
     and is_binary(:erlang.hd(:erlang.tail(:erlang.map_get(:complicated, data)))) 
      -&gt; data
end
</code></pre>
<p>Rather than ask you to type all that out, it should be possible to develop a destructuring-to-guards transpiler in the <code>Matcha</code> compiler and do it for you! We finally have enough guards available in matchspecs that I think we can convert any arbitrary destructuring of composite terms in an Elixir match pattern into a mess of matchspec-supported <code>:erlang</code> guards:</p>
<ul>
<li>Tuple destructuring can be implemented via:
<ul>
<li><a href="https://www.erlang.org/doc/man/erlang#is_tuple-1" rel="nofollow">:erlang.is_tuple/1</a></li>
<li><a href="https://www.erlang.org/doc/man/erlang#tuple_size-1" rel="nofollow">:erlang.tuple_size/1</a></li>
<li><a href="https://www.erlang.org/doc/man/erlang#element-2" rel="nofollow">:erlang.element/2</a></li>
</ul>
</li>
<li>Map destructuring can be implemented via:
<ul>
<li><a href="https://www.erlang.org/doc/man/erlang#is_map-1" rel="nofollow">:erlang.is_map/1</a></li>
<li><a href="https://www.erlang.org/doc/man/erlang#is_map_key-2" rel="nofollow">:erlang.is_map_key/2</a></li>
<li><a href="https://www.erlang.org/doc/man/erlang#map_get-2" rel="nofollow">:erlang.map_get/2</a></li>
</ul>
</li>
<li>List destructuring can be implemented via:
<ul>
<li><a href="https://www.erlang.org/doc/man/erlang#is_list-1" rel="nofollow">:erlang.is_list/1</a></li>
<li><a href="https://www.erlang.org/doc/man/erlang#length-1" rel="nofollow">:erlang.length/1</a></li>
<li><a href="https://www.erlang.org/doc/man/erlang#hd-1" rel="nofollow">:erlang.hd/1</a></li>
<li><a href="https://www.erlang.org/doc/man/erlang#tl-1" rel="nofollow">:erlang.tl/1</a></li>
</ul>
</li>
<li>Binary destructuring is surely possible, but not something I’ve ever spent much time on in <code>Matcha</code> to date.</li>
</ul>
<p>On paper, this is very cool. Off paper, this is very cool but requires a whole heck of a lot of work to the compiler. So I’ll be playing with this premise more over the next few months, when I find time!</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="305318" data-batch-url="/posts/batch_likers">
                        7
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/matcha-first-class-match-specifications-for-elixir/52182/25">Post #24</a>
	                </div>
	            </div>
              <div id="likers-container-305318" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="305318"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #24"></div>
  </section>
</div>
    <div class="postbit" id="308590" data-post-id="308590">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="christhekeele" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/christhekeele/120/1039_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  christhekeele
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="christhekeele" data-post="25" data-topic="52182">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/christhekeele/48/1039_2.png" class="avatar"> christhekeele:</div>
<blockquote>
<p>This update discusses new syntactic support for <em><strong>nested matches</strong></em> I want to experiment with for <a href="https://forum.elixirforum.com/t/matcha-first-class-match-specifications-for-elixir/52182" rel="nofollow"><code>Matcha</code></a>!</p>
</blockquote>
</aside>
<p>A fairly stable version of this is now on the <a href="https://github.com/christhekeele/matcha/tree/latest" rel="noopener nofollow ugc"><code>latest</code></a> branch of Matcha! Tests are passing, only a few had to be changed. I will continue to dogfood it and the new <a href="https://forum.elixirforum.com/t/matcha-first-class-match-specifications-for-elixir/52182/21#tea-matcha-filters-2" rel="nofollow"><code>Matcha.Filter</code></a> feature in my own projects before releasing as a proper version.</p>
<p>As discussed, I’ve written a custom destructuring engine that can translate Elixir’s <code>=</code> pattern matching to a sequence of matchspec guards. This is non-trivial, so I wouldn’t be surprised if there are some bugs and poor error messages around edge cases.</p>
<hr>
<h2><a name="p-308590-nesting-1" class="anchor" href="#p-308590-nesting-1" aria-label="Heading link" rel="nofollow"></a>Nesting</h2>
<p>Remember that</p>
<aside class="quote no-group quote-modified" data-username="christhekeele" data-post="25" data-topic="52182">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/christhekeele/48/1039_2.png" class="avatar"> christhekeele:</div>
<blockquote>
<p>You cannot both <em>bind to</em> <em><strong>and</strong></em> <em>destructure</em> a term in a matchspec.</p>
</blockquote>
</aside>
<p>This means that before, you could bind to and extract some high-level data from a nested data structure:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">spec = Matcha.spec do
  {:some, data} -&gt; data
end
</code></pre>
<p>And you could also do some deep pattern matching on nested datastructures:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">spec = Matcha.spec do
  {:some, %{complicated: [:data, {:specifics, specifics}]}}
    when is_binary(specifics) 
      -&gt; specifics
end
</code></pre>
<p>However, you could not do both at once with standard Elixir <code>=</code> syntax, since matchspecs don’t have real support for destructuring.</p>
<h3><a name="p-308590-destructuring-2" class="anchor" href="#p-308590-destructuring-2" aria-label="Heading link" rel="nofollow"></a>Destructuring</h3>
<p>For example, perhaps you want to extract some high-level value by guarding on a nested one:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">spec = Matcha.spec do
  {:some, data = %{complicated: [:data, {:specifics, specifics}]}}
    when is_binary(specifics) 
      -&gt; data
end
</code></pre>
<p>This limitation has been hopefully thoroughly overcome! Where it used to raise an exception, the spec above works as expected: we can extract the higher-level subset of <code>data</code> when the <code>specifics</code> are binaries:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">table = :ets.new(:table, [:bag])
:ets.insert(table, [
  {:some, %{complicated: [:data, {:specifics, :NOT_BINARY}]}},
  {:some, %{complicated: [:data, {:specifics, "BINARY"}]}},
])
Matcha.Table.ETS.Select.all(table, spec)
#=&gt; [%{complicated: [:data, {:specifics, "BINARY"}]}]
</code></pre>
<h3><a name="p-308590-assignment-3" class="anchor" href="#p-308590-assignment-3" aria-label="Heading link" rel="nofollow"></a>Assignment</h3>
<p>This works with anywhere we could normally bind variables, and “inner” bound variables are available to be used in your matchspec bodies, event though erlang matchspecs technically do not support them:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">spec =
  Matcha.spec :table do
    {:some, map = %{complicated: list = [:data, tuple = {:specifics, specifics}]}}
    when is_binary(specifics) -&gt;
      %{map: map, list: list, tuple: tuple, specifics: specifics}
  end
</code></pre>
<p>We’ll find this spec discovers the <code>"BINARY"</code> record we’re looking for, as well as letting us reference any nested destructuring assignment to bindings we performed.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">Matcha.Table.ETS.Select.all(table, spec)
[
  %{
    map: %{complicated: [:data, {:specifics, "BINARY"}]},
    list: [:data, {:specifics, "BINARY"}],
    tuple: {:specifics, "BINARY"},
    specifics: "BINARY"
  }
]
</code></pre>
<p>And if we don’t use all of them—we still get our classic Elixir warnings about unused variables, as we should:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">spec =
  Matcha.spec :table do
    {:some, map = %{complicated: list = [:data, tuple = {:specifics, specifics}]}}
    when is_binary(specifics) -&gt;
      map
  end

#=&gt; warning: variable "list" is unused (if the variable is not meant to be used, prefix it with an underscore)
#=&gt; warning: variable "tuple" is unused (if the variable is not meant to be used, prefix it with an underscore)
</code></pre>
<hr>
<p>Let me know if you get a chance to play around with these new capabilities, or discover any hiccups! This crosses a pretty major awkward bridge between Just Writing Elixir <img src="https://forum.elixirforum.com/images/emoji/apple/tm.png?v=15" title=":tm:" class="emoji" alt=":tm:" loading="lazy" width="20" height="20"> code, and actual desired matchspec behaviour; making even the most complicated matchspecs easy to build with Elixir’s pleasant syntax.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="308590" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/matcha-first-class-match-specifications-for-elixir/52182/26">Post #25</a>
	                </div>
	            </div>
              <div id="likers-container-308590" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="308590"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #25"></div>
  </section>
</div>
    <div class="postbit" id="308592" data-post-id="308592">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="zoedsoupe" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/zoedsoupe/120/25602_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  zoedsoupe
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>The matcha ETS “adapter” can be also used to query mnesia tables? it also uses match specification (:</p>
<p>EDIT: I just take a more deeper look into the docs and found the ETS/DETS/Mnesia uses the same logic <img src="https://forum.elixirforum.com/images/emoji/apple/sweat_smile.png?v=15" title=":sweat_smile:" class="emoji" alt=":sweat_smile:" loading="lazy" width="20" height="20"></p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="308592" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/matcha-first-class-match-specifications-for-elixir/52182/27">Post #26</a>
	                </div>
	            </div>
              <div id="likers-container-308592" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="308592"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #26"></div>
  </section>
</div>
    <div class="postbit" id="308593" data-post-id="308593">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="christhekeele" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/christhekeele/120/1039_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  christhekeele
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group quote-modified" data-username="zoedsoupe" data-post="27" data-topic="52182">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/zoedsoupe/48/25602_2.png" class="avatar"> zoedsoupe:</div>
<blockquote>
<p>Can the matcha ETS “adapter" also be used to query mnesia tables?</p>
</blockquote>
</aside>
<p>Yep!</p>
<aside class="quote no-group quote-modified" data-username="zoedsoupe" data-post="27" data-topic="52182">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/zoedsoupe/48/25602_2.png" class="avatar"> zoedsoupe:</div>
<blockquote>
<p>I just found the ETS/DETS/Mnesia docs</p>
</blockquote>
</aside>
<p>LMK if you try it out or the docs need work, I haven’t really spent a lot of time with mnesia!</p>
<aside class="quote no-group quote-modified" data-username="zoedsoupe" data-post="27" data-topic="52182">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/zoedsoupe/48/25602_2.png" class="avatar"> zoedsoupe:</div>
<blockquote>
<p>Mnesia also uses match specifications</p>
</blockquote>
</aside>
<p>It does. And, if you just want to use <code>Matcha</code> as an elixir-to-ms compiler, you don’t need to use things like its <code>Mnesia</code> APIs—just call <code>.source</code> on any <code>Matcha.Spec</code> to get a raw ms you can pass straight to <code>:mnesia</code> and friends:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">spec = Matcha.spec(:table) do
  { x, y } = z when x &gt; 10 -&gt; z
end

spec.source
#=&gt; [{{:"$1", :"$2"}, [{:&gt;, :"$1", 10}], [:"$_"]}]

:mnesia.select(table, spec.source, :read)
</code></pre> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="308593" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/matcha-first-class-match-specifications-for-elixir/52182/28">Post #27</a>
	                </div>
	            </div>
              <div id="likers-container-308593" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="308593"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #27"></div>
  </section>
</div>
    <div class="postbit" id="308597" data-post-id="308597">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="zoedsoupe" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/zoedsoupe/120/25602_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  zoedsoupe
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Yeah, i was just thinking to match (pun intended) matcha with the memento library! it will give an experience ala ecto (:</p>
<p>Zoey de Souza Pessanha<br>
<em>Desenvolvedora de Software</em><br>
<em><a href="https://github.com/zoedsoupe" class="inline-onebox" rel="noopener nofollow ugc">zoedsoupe (zoey) · GitHub</a></em></p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="308597" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/matcha-first-class-match-specifications-for-elixir/52182/29">Post #28</a>
	                </div>
	            </div>
              <div id="likers-container-308597" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="308597"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #28"></div>
  </section>
</div>
    <div class="postbit" id="308599" data-post-id="308599">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="christhekeele" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/christhekeele/120/1039_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  christhekeele
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Interesting thought!</p>
<p>It looks like memento uses a custom format for its <a href="https://hexdocs.pm/memento/Memento.Query.html#select/3" rel="noopener nofollow ugc">select/3</a>, so you can’t give it a <code>matchspec</code> directly. It seems to be doing something similar to but incompatible with a <code>Matcha.filter()</code>.</p>
<p>However, it seems like <a href="https://hexdocs.pm/memento/Memento.Query.html#match/3" rel="noopener nofollow ugc"><code>match/3</code></a> might support a <code>Matcha.pattern(...).source</code> if it’s not also a DSL on top of them, worth trying!</p>
<p>And of course, <a href="https://hexdocs.pm/memento/Memento.Query.html#select_raw/3" rel="noopener nofollow ugc"><code>select_raw/3</code></a> happily takes a full <code>Matcha.spec(...).source</code>, so you’re good to go there! The example they give:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">match_head = {Movie, :"$1", :"$2", :"$3", :"$4"}
result = [:"$_"]
guards = [
  {:andalso,
    {:&gt;, :"$3", 2010},
    {:orelse,
      {:==, :"$4", "Quentin Tarantino"},
      {:==, :"$4", "Steven Spielberg"},
    }
  }
]

Memento.Query.select_raw(Movie, [{match_head, guards, result}], coerce: true)
# =&gt; [%Movie{...}, ...]
</code></pre>
<p>should be able to be written something like</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">require Matcha

spec = Matcha.spec(:table) do
  {Movie, _id, _title, year, director} = record
    when year &gt; 2010 and director in [ "Quentin Tarantino", "Steven Spielberg"]
      -&gt; record 
end

Memento.Query.select_raw(Movie, spec.source, coerce: true)
</code></pre>
<p>The resulting <code>spec.source</code> is indeed identical to the example given.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="308599" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/matcha-first-class-match-specifications-for-elixir/52182/30">Post #29</a>
	                </div>
	            </div>
              <div id="likers-container-308599" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="308599"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #29"></div>
  </section>
</div>
    <div class="postbit" id="308644" data-post-id="308644">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="zoedsoupe" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/zoedsoupe/120/25602_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  zoedsoupe
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>That’s a great fit! thanks for the upfront tests, I liked the way matcha can be used with memento! I surely will give it a try in my next project!</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="308644" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/matcha-first-class-match-specifications-for-elixir/52182/31">Post #30</a>
	                </div>
	            </div>
              <div id="likers-container-308644" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="308644"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #30"></div>
  </section>
</div>
</template></turbo-stream><turbo-stream action="replace" target="load-more-container"><template><div id="load-more-container" class="load-more-container">
    <a class="load-more-button" data-turbo-stream="true" href="/topics/52182/load_more?page=4">Load more posts (1 remaining)</a>
</div></template></turbo-stream>