Feature request: guarded is_struct/1 and is_struct/2

Guard statements are useful, in that they let me identify proper inputs for my functions. These requirements are inlined by the compiler, so the runtime stays very fast.

Only certain expressions are allowed in the guard clauses, so I have a limited number of tools to use when guarding my inputs.

Sometimes my inputs are Structs (which are maps), or they may be MapSets (also maps). Both structs and MapSets have a __struct__ property that identifies what kind of structure they are.

It would be advantageous to have a is_struct/1 and is_struct/2 guard macros to check that

  • input is a map
  • input has a__struct__ key
  • the __struct__ key equals my expected key

eg

def new_struct(data) when is_struct(data) do
  Map.get(data, :__struct__).new
end

def has_foo?(set) when is_struct(set, MapSet) do
  MapSet.member?(set, :foo)
end

If this is not practical, is there a better way I should be writing my elixir functions to ensure safety from improper struct inputs?

Even better, you can even specify the struct name using something like this:

defmodule A do
  defstruct [:a, :b]

  def test(%A{a: this_is_a}) do
    IO.inspect(this_is_a)
  end
end

Have you try this one?

You can just pattern match the struct such as:

def do_something(%MyStruct{} = data) do
  # do some thing 
end
1 Like

As others have mentioned, you can already use pattern matching to match for particular structs. Additionally, you can match for something that is ANY struct by doing:

def blah(%_{} = struct) do
  # do something with struct
end
def blah(not_a_struct) do
  # do something with not a struct
end
9 Likes

I fully agree that an is_struct guard would be useful. Unfortunately, it’s not currently possible to realise technically. There is no way to “get into” a map from a guard. It might be possible in the future, though, with some (major) changes to the Elixir compiler, or if the ability to access maps from guards is added directly to Erlang.

3 Likes

Ehh, there are ways, but it would require a slight change to how the def macro compiles, kind of like how I do in my def_guard project on github where I do implement an is_struct() guard that works properly (via a hack, it really should be built in to elixir…).

Okay, so it sounds like this a feature that is desirable, but Is currently technically challenging for the elixir compiler to successfully inline. It might be worth putting on the back burner for now. It’s cool to know there’s a def_guard project that can provide some solutions. I’ll check that out. :+1:

@muhifauzan, @jeromedoyle, @benwilson512 have all provided great examples of pattern matching on struct function inputs. I feel confident using these techniques for validating my inputs.

Thanks everyone for your advice and suggestions! :grinning:

It is not complete, though it would not be hard to finish out (PR’s welcome!), but it was made just to show that it can be done. It really should be in the compiler, but if that would never be done I guess it should be finished… ^.^;

Anytime :slight_smile: