Proposal: implements?/2

52 views
Skip to first unread message

Thomas J.

unread,
May 17, 2020, 4:41:41 AM5/17/20
to elixir-lang-core
Hello,

What do you think about adding `implements?/2`?

It accepts two module names as parameters, and it checks if the first parameter implements the behaviour passed as the second parameter.

It would execute something like:

`Enum.member?(Foo.module_info[:attributes][:behaviour], Bar)`

Only problem I see is that a module can somehow implement a behaviour without wriring the @behaviour statement. But that could be a documented limitation.


Andrea Leopardi

unread,
May 17, 2020, 11:15:08 AM5/17/20
to elixir-lang-core
Hey Thomas,

do you have a specific use case in mind to check that a module implements a behaviour? The fact that you can do this today by getting the module attributes makes me lean towards not adding this to the standard library, but I'm curious as to that's the use case you're thinking of :)

Andrea

--
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 elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/12b5fbff-7460-44c1-81c1-b6710949738e%40googlegroups.com.

Thomas J.

unread,
May 17, 2020, 8:26:10 PM5/17/20
to elixir-lang-core
Hello Andrea!

Imagine, as a library author, that a library module can somehow receive a module from your application to call functions on.

E.g. `use LibraryModule, yourModule: Foo`

`LibraryModule` expect to receive in its `:yourModule` option a module implementing a certain behaviour. `Foo` is given and the library code wants to raise an error if the given module doesn't implement the expected behaviour.

Actually I thought, that should be pretty common. So I checked in the dependencies containing large amount of code and configs such as Phoenix, Ecto, Absinthe, and so on, and guess what, this pattern is almost not used...

I still found this:


On Sunday, May 17, 2020 at 5:15:08 PM UTC+2, Andrea Leopardi wrote:
Hey Thomas,

do you have a specific use case in mind to check that a module implements a behaviour? The fact that you can do this today by getting the module attributes makes me lean towards not adding this to the standard library, but I'm curious as to that's the use case you're thinking of :)

Andrea

On Sun, May 17, 2020 at 10:41 AM Thomas J. <thojan...@gmail.com> wrote:
Hello,

What do you think about adding `implements?/2`?

It accepts two module names as parameters, and it checks if the first parameter implements the behaviour passed as the second parameter.

It would execute something like:

`Enum.member?(Foo.module_info[:attributes][:behaviour], Bar)`

Only problem I see is that a module can somehow implement a behaviour without wriring the @behaviour statement. But that could be a documented limitation.


--
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 elixir-l...@googlegroups.com.

Thomas J.

unread,
May 17, 2020, 9:40:32 PM5/17/20
to elixir-lang-core
I wish I could edit my messages here :) But passing a user's module to a library is really common (I had not had my first coffee yet...).
Just looking at the config file should be enough.

For example:
`config :elixir, :time_zone_database, Tz.TimeZoneDatabase`

Don't we want to make sure that the module I pass (`Tz.TimeZoneDatabase`) implements the `Calendar.TimeZoneDatabase` behaviour? And raise if that's not the case.
Or that the Repo I pass in the config is a valid Repo? Et cetera.

For example I changed in my config file module `Tz.TimeZoneDatabase` by `Foo`. And there is no error. Only when I call something like:
`DateTime.shift_zone(~U[2018-07-16 10:00:00Z], "America/Los_Angeles")`

I will get the error:
`** (UndefinedFunctionError) function Foo.time_zone_period_from_utc_iso_days/2 is undefined (module Foo is not available)`

In my library I would add a function such as `raise_unless_implements/2` to make sure that the custom module behaves as expected.

I'm now surprised that this isn't a common practice.

Adam Lancaster

unread,
May 18, 2020, 5:04:10 AM5/18/20
to elixir-l...@googlegroups.com
Part of me questions whether you do really want to raise in such a case, as it feels like defensive programming.

Having said that right now you can do these things:

1. Check that a specific function is exported from a specific module with funtion_exported/3 : https://hexdocs.pm/elixir/Kernel.html#function_exported?/3

If you really wanted to do something like alert the user of the library, you could use that for each function in the behaviour. In fact this might be the best way as there would be no way to tell if a user has (incorrectly) only partly implemented a behaviour. Imagine if they haven’t used `@impl true`  for example.

2. Use YourModule.__info__(:functions) and check that way which functions are exported.
3. Use Module.has_attribute?(YourModule, :value)    https://hexdocs.pm/elixir/Module.html#has_attribute?/2 and see if the behaviour is listed there.

Another option though is for the library to use a protocol instead and define a default implementation for that protocol which raises.


Best

Adam

To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/bd8b2a59-a5e2-4e94-a4f3-bcf22fa8e3d3%40googlegroups.com.

Reply all
Reply to author
Forward
0 new messages