rschooley
What is the pattern for the same function implemented in different modules (strategy pattern)
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.
Most Liked
rvirding
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.
hugolnx
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
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #hex










