Expat - composable, reusable pattern matching

Yeah, this is definitely my favorite new library. The primary gain from it for me is making things DRYer. Where before I would have map structures repeated in multiple function clauses, e.g.

def handle_cmd(%{"dest_ib" => dest_ib,
                 "context_ib_gib" => context_ib_gib,
                 "src_ib_gib" => src_ib_gib} = data, ..) when guard1 do
def handle_cmd(%{"dest_ib" => dest_ib,
                 "context_ib_gib" => context_ib_gib,
                 "src_ib_gib" => src_ib_gib} = data, ..) when guard2 do

which has redundancy in the "var_name" => var_name, as well in the function clause level. And dest_ib and src_ib_gib are used across multiple commands in many places. I’m now able to create a single file for the reused patterns:

defmodule WebGib.Patterns do
  @moduledoc """
  Reusable patterns using expat
  """
  
  import Expat
  
  defpat dest_ib_        %{"dest_ib" => dest_ib}
  defpat src_ib_gib_     %{"src_ib_gib" => src_ib_gib}
  defpat context_ib_gib_ %{"context_ib_gib" => context_ib_gib}
  # ..
end

(NB: I am tacitly going with a _ suffix to indicate a pattern vs the var name, but I’m not sure what other non-word characters are legal in elixir. I would rather prefix the pattern with a single character and would love any suggestions.)

And then I compose them above the function and consume them:

defpat fork_data_(
  dest_ib_() =
  context_ib_gib_() =
  src_ib_gib_()
)

def handle_cmd(fork_data_(...) = data, ..) when guard1 do
def handle_cmd(fork_data_(...) = data, ..) when guard1 do

EDIT: The ... inside the pattern is literal syntax, which helps enormously with DRY. The other .. just means other args.

This is ridiculously more DRY and readable. Definitely a powerful lib you made here! :smile:

Thank you! :thumbsup:

4 Likes