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.
