Is pattern matching posible in custom guard expressions?

Hey guys,
I have a question about custom guard expression.I am trying to write multiple guard expressions with pattern matching however it doesn’t work . not sure it is possible :slight_smile:
Briefly I wan to use guards like functions
this is a case which ı wan to write

defguard is_position({x,y}) when is_integer(x) and is_integer(y)
defguard is_position(_) when false

defp create_robot(_,  p) when not is_position(p) ,do: {:error, "invalid position"}

Hey @altunasfatih it is not possible to do pattern matching in guards. However you can achieve what you want by combining guards in this specific case:

defguard is_position(term) when tuple_size(term) == 2
  and is_integer(elem(term, 0))
  and is_integer(elem(term, 1))
1 Like

Another way to do this would of course be to use structs…

defmodule Position do
  defstruct [:x, :y]

  def new(x, y) when is_integer(x) and is_integer(y) do
    %Position{x: x, y: y}
  end
end

defp create_robot(%Position{x: x, y: y}) do
  #... 
end

It’s a matter of preference, and how well it fits the use case of course. In this scenario you’re trusting those who work with the code to always use Position.new/2 to create the struct, if you want to be absolutely sure it always contains integers.

You could also refine the code so that the struct enforces the keys to be defined, and has a typespec; then you will get nice documentation of the expectations, and make it possible to check the constraints using Dialyzer:

defmodule Position do
  @enforce_keys [:x, :y]
  defstruct [:x, :y]
  @type t :: %__MODULE__{x: integer(), y: integer()}

  @spec new(integer(), integer()) :: Position.t()
  def new(x, y) when is_integer(x) and is_integer(y) do
    %Position{x: x, y: y}
  end
end

defp create_robot(%Position{x: x, y: y}) do
  #... 
end
2 Likes