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).

Instead, a while back, I made a library called 
`defguard`:  https://github.com/overminddl1/defguard

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).

-- 
You received this message because you are subscribed to the Google Groups 
"elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/elixir-lang-core/caf5bad3-761c-4827-8ef3-dd78cc264643%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to