Open utterances-bot opened 3 years ago
Hi, what will be the solution for the first Weather module problem. How can we use protocol to get the same effect?. Since Protocols can only be used for data types right?.
@madclaws That's a really good question! Thank you for raising that up! (I should've explained that in the article, but my ignorance made me forgot about that.)
The trick to solve this with Protocol is that you can define empty Structs:
defprotocol WeatherSource do
@spec temperature(WeatherSource.t(), Location.t()) :: {:ok, Temperature.t()}
def temperature(source, location)
end
defmodule WeatherSource.A do
# The benefit of using a struct is that we can add more fields in the future,
# to store things like configurations
# So we don't need to fetch them from Application.get_env or some global data sources
defstruct []
def new(), do: %__MODULE__{}
defimpl WeatherSource do
def temperature(location), do: ...
end
end
Promox.defmock(for: WeatherSource)
Then when we use this API
source = WeatherSource.A.new()
location = Location.new("Shanghai")
WeatherSource.temperature(source, location)
(A sidenote: the code above looks very much like Ruby code 😂 )
BTW, you may find my previous blog post useful: https://yiming.dev/blog/2021/06/13/what-i-learned-from-implementing-combinators-in-3-elixir-patterns/
@dsdshcym
Thanks for the explanation, although i felt it like very Object oriented
. To get a behaviour like experience, we had to add extra.
@madclaws Exactly! I think that's the good part of OO and can still benefit Elixir projects
And I also feel the pain of these extra boilerplate, so I made https://github.com/dsdshcym/objext, in hope to provide a better API
Here is how to solve the above Weather problem with Objext:
defmodule Weather do
use Objext.Interface
definterfaces do
def temperature(source, location)
end
end
defmodule Weather.SourceA do
use Objext, implements: [Weather]
def new(), do: buildo()
def temperature(_source, location), do: #...
end
source = Weather.SourceA.new()
location = Location.new("Shanghai")
Weather.temperature(source, location)
I think the complexity of this code is in between Behaviour and Protocol
(the biggest huddle would actually be the weird buildo
API which would build an encapsulated Struct under the hood)
But hopefully with Objext, we can deal with pure, impure interfaces, and everything in between smoothly :pray:
Stop using Behaviour to define interfaces, use Protocol! - Yiming Chen
I think Elixir developers (me included) often misuse Behaviour to define common interfaces. In this post, I'd propose to use Protocol for defining common interfaces and use Behaviour for sharing common logic. And you may see why I made Promox and Objext to fill the gaps I find in the current Elixir community.
https://yiming.dev/blog/2021/07/18/stop-using-behaviour-to-define-interfaces-use-protocol/