Also see past discussion (or lack there-of really, only comments about it 
was someone saying how they would love such functionality) at:  
https://elixirforum.com/t/defguard/4052

On Wednesday, September 27, 2017 at 10:09:31 AM UTC-6, OvermindDL1 wrote:
>
> 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/eec36778-b463-4b67-830f-7f572fb59732%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to