As requested at
https://github.com/elixir-lang/elixir/pull/5857#issuecomment-332563628 this is created.
Currently there is a `defguard` being created for elixir 1.6.0 that is nothing more than a macro and does no extra functionality there-of. It (as far as my reading shows) just allows:
```elixir
defguard is_even(value) when is_integer(value) and rem(value, 2) == 0
```
Which can be used like:
```elixir
def steps(n) when n > 0, do: steps(n, 0)
defp step(1, step_count), do: step_count
defp step(n, step_count) when is_even(n), do: step(div(n, 2), step_count + 1)
defp step(n, step_count), do: step(3*n + 1, step_count + 1)
```
Which is really no gain over just using a macro like:
```elixir
defmacro is_even(value), do: is_integer(value) and rem(value, 2) == 0
```
Other than just verify that only proper guards are used (which could just be another macro that verifies that too).
Disclaimer: It is just an example of something that should be, in my opinion, built in to Elixir's `def`/`defp`/`defmacro`/`defmacrop`, right now when it is used it just replaces the built-in `def` calls with its own macro version that just delegates back down after performing expansion. It does not make a good standalone library and that is why I have very purposefully not finished it to an extent to be generically useful (replacing `def` and so forth is not good form in my opinion).
Now what it does is you can do:
```elixir
defguard is_struct(%{__struct__: struct_name}) when is_atom(struct_name)
defguard is_struct(%{__struct__: struct_name}, struct_name)
defguard is_exception(%{__exception__: true} = exc) when is_struct(exc)
```
Which can then be used like:
```elixir
def blah(any_exc) when is_exception(any_exc), do: any_exc
def blah(specific_struct) when is_struct(specific_struct, Specific), do: specific_struct
def blah(any_struct) when is_struct(any_struct), do: any_struct
```
Note that this is something that you *cannot* do with the current `defguard` proposal in Elixir slated for 1.6.0, that is structural matching, I.E. testing the structure of the values and being able to pull out and test the data inside, which is what you cannot do with the current proposal, notable with maps and structures and other deep constructs become significantly easier.
However, unlike just creating a `defguard` macro that then creates other macro's, that should still be done as my example library does (although it should generate two versions of it I'd say, see below), however the difference is that the definitions of `def` and the others needs to have an extra expansion phase. In essence they would need to be changed so that if any guard is called that is not one of the valid guard types then it should be expanded (the macro is called) but instead of it called, say for a given `is_struct` for a macro it would normally expand the function named :"MACRO-is_struct" it should instead expand a function named `:"GUARD-is_struct"` or something of that style if it exists, then it takes the return information and mixes in both the structural part into the correct location in the argument and mixes in the guards into the `when` guards section of the head. If `is_struct` were called in any other place than the function head then it would expand via the normal macro call (perhaps even function? I'd opt for a macro return though) that does the structural test and guard tests and returns true/false as appropriate.
Thus, `defguard` would need to generate at least 2 functions for each guard so it is useful in every possible location, and `def`/`defp`/`defmacro`/`defmacrop`/`case` and maybe `cond` should have their ast updated to handle that expansion into the heads/cases. This then makes it ubiquitous through-out elixir-the-language and let's people define new guards that are significantly more powerful than what a `defmacro` version of the `defguard` should be capable of otherwise. I.E. `defguard` should only be added if it actually adds functionality over an equivalent `defmacro` or there is still no purpose to its existence other than just being a `defmacro` that shuffles it's guards into its body, which really gains extremely little (even the readability gain is minor, see the top example and comparison).