This works, but I’d like to clean it up a bit (extra functions in caller):
defmodule whatever do
defp do_thing(%{source: "foo"}) do
IO.puts("calling foo")
end
defp do_thing(%{source: "bar"}) do
IO.puts("calling bar")
end
def handle_info(:dont_care, state) do
SomeContext.some_query
|> Enum.each(fun record ->
{:ok, yeah} = do_thing(record)
end)
end
end
But moving the extra private functions into their actual modules results in this error:
function do_thing/1 imported from both Bar and Foo, call is ambiguous
defmodule Foo do
def do_thing(%{source: "foo"}) do
IO.puts("calling foo")
end
end
defmodule Bar do
def do_thing(%{source: "bar"}) do
IO.puts("calling bar")
end
end
defmodule whatever do
import Foo, only: [do_thing: 1]
import Bar, only: [do_thing: 1]
....
end
I looked at protocols but didn’t see how they could clean this up. I’d like to just call a modules’ do_thing
method without having a wrapper method for each one. Is there a way to do this? Thanks.
I think you can simply include the module when calling the function with Foo.do_thing()
, and that importing or aliasing a module is “just” a way to avoid having to use the module name for every call.
Edit: see here and here for fuller and more accurate information.
1 Like
Hi @BrightEyesDavid,
Explicitly calling Foo.
is what I am trying to avoid.
To call Foo.
I’d have to match on source
in a case statement, or pattern match in the do_thing
function args (1 for each source type).
I am doing the latter above, but am wondering if there is a way to just call either modules function directly based on the pattern in the struct.
To summarize, it looks like two functions can have the same arity and arg types
- if they match on a different value in the struct arg
- and are defined in the same module
but
- that doesn’t work if the function are defined in different modules and then imported
Edit: to give an example of what I have now, and it’s ok and works, but might be improved is this:
defp do_thing(%{source: "foo"} = record), do: Foo.do_thing(record)
Ah, sorry.
I did wonder if I was missing something.
Hi, @rschooley! That’s a pretty abstract question/sample hehe
But, if your do_thing
is a call that make a thing so different with each kind of record, I would guess that it structure changes a lot depending on its source. If is that the case, I think it make sense that each type of record is a struct and that exist a ThingDoer
protocol, so you could easily extend this thing to be done to other record types in the future. It would be something like that:
defmodule FooRecord do
defstruct [:content, source: "foo"]
end
defmodule BarRecord do
defstruct [:content, source: "bar"]
end
defprotocol ThingDoer do
def do_thing(record)
end
defimpl ThingDoer, for: FooRecord do
def do_thing(record), do: IO.puts("calling foo")
end
defimpl ThingDoer, for: BarRecord do
def do_thing(record), do: IO.puts("calling bar")
end
defmodule ThingDoerBoss do
def handle_info(:get_thing_doers_to_work, state) do
[
%FooRecord{content: "lol"},
%BarRecord{content: "xpto"},
]
|> Enum.each(&ThingDoer.do_thing/1)
end
end
With that solution you can easily extend the do_thing
to other source structures and also have the record structures documented.
PS.: In that case, your SomeContext.some_query
must return the records as structs.
PS2.: I would not recommend you to use this import overriding thing even if it worked, it seems that would be a hell of situation to navigate through modules trying to discover in which of them are the matching function hehe
2 Likes
To clarify things a bit. In module Whatever
you are only defining one function do_thing/1
which has two clauses. While it looks 2 separate functions they are actually parts of the same function. In the modules Foo
and Bar
you are defining 2 different functions do_thing/1
so now you have 3 functions do_thing/1
in separate modules. What uniquely defines a function is the module its in, its name, and its arity (number of arguments). Importing does not change that, it just gives you a way of not having to prefix calling a function in another module with the module name. You still have 3 different do_thing/1
in 3 modules.
3 Likes
Interesting… thanks for this!