I am close to using a behaviour as a pure interface and I feel dirty...

Stanislav Ledenev s.ledenev@REDACTED
Thu Aug 26 09:27:43 CEST 2021


You are welcome!

>> I am still not all the way there yet, but at a much better place than a
few days ago.
>> My current impression at this point is that the topic is nuanced and
>> perhaps I should be thinking more along the lines of
>> "quit THINKING of behaviours in terms of interfaces" or more generally,
>> "quit THINIKING in terms of interfaces at all"?

I don't think we can put the idea of "interface" away at all. We are
surrounded
by "interfaces" in real life. For instance, the power outlet and electrical
plug
is the interface. I'd rather say that it is better to go away from OOP's
definition
of interface to more practical one. I'd say that the set of exported
functions in the module
is the interface too.

And of course to get rid of unnecessary layers of abstractions.
As Joe Armstrong said: "The problem with object-oriented languages
is they’ve got all this implicit environment that they carry around with
them.
You wanted a banana but what you got was a gorilla holding the banana and
the entire jungle."

>> As I type this I think most of my issues stem from the fear of changing
>> something in the future and not having compile-time checks to help me
find
>> all the references (and I saw the behaviour as a nice compile-time check
>> (crutch?) that looked attractive).  This is probably a bigger and
different
>> issue all together that I need to learn more about within the Erlang
ecosystem.
>> I am assuming it is some combination of supervision, Dialyzer, and
really good tests but
>> I am sure I will learn this quite quickly after deploying some real
stuff in the wild...
>> I just would rather learn as much as I can before that time comes.

I highly recommend you to go deeper into the Erlang ecosystem. The process
of transition
from other languages to the Erlang is much steeper than with other
languages but definitely
more rewarding.
And I would recommend spending more time with tools but be careful though.
For example, due to dynamic nature of the language you write in your code
something
like this:
    foo() ->
        Result = hjsdfkjsdhfskjfhskhskhfskl:yutyrutyriuyiw(),
        ...

And it will compile. Because you can bring the module
"hjsdfkjsdhfskjfhskhskhfskl" later
and the compiler doesn't know that. And the Dialyzer won't tell you
anything...
But the "xref" tool may tell you that you are probably using an unknown
function in an unknown module.

ср, 25 авг. 2021 г. в 18:33, Brett Hemes <brhemes@REDACTED>:

> Thank you Stanislav for taking the time to iterate.  This is nice.
>
>
>
> > Though I am on the side of Alan Kay's definition of the OOP - objects
> communicating with messages.
>
>
>
> > Anyway, how you "slice" the world and how you make your abstractions -
> it is influenced by the "classic OOP".
>
>
>
> > Of course it is very tempting to call behaviour an interface, because it
> looks like an interface.
>
> > But it is not because the interface in Erlang are the functions, not a
> separate entity.
>
>
>
> Indeed, but all of the teaching texts are very quick to advocate wrapping
> your implementations with API calls that delegate to the underlying
> implementation.  This in my mind adds a layer of abstraction on top of pure
> message passing... abstractions that play to the weaknesses of "poisoned"
> practitioners ;)
>
>
>
> That said, your answer does help; thank you again very much!
>
>
>
> I am still not all the way there yet, but at a much better place than a
> few days ago.  My current impression at this point is that the topic is
> nuanced and perhaps I should be thinking more along the lines of "quit
> THINKING of behaviours in terms of interfaces" or more generally, "quit
> THINIKING in terms of interfaces at all"?
>
>
>
> I found it interesting in your example you call `create/1` in main_app at
> runtime using `DBmod` where my misguided thinking would have tried to use
> `callbacks_db` instead (interface thinking).  ...this might be my
> disconnect.
>
>
>
> > It is an instrument to remind you to implement functions.
>
>
>
> As I type this I think most of my issues stem from the fear of changing
> something in the future and not having compile-time checks to help me find
> all the references (and I saw the behaviour as a nice compile-time check
> (crutch?) that looked attractive).  This is probably a bigger and different
> issue all together that I need to learn more about within the Erlang
> ecosystem.  I am assuming it is some combination of supervision, Dialyzer,
> and really good tests but I am sure I will learn this quite quickly after
> deploying some real stuff in the wild...  I just would rather learn as much
> as I can before that time comes.
>
>
>
>
>
> Thanks,
>
> Brett
>
>
>
>
>
> *From:* Stanislav Ledenev <s.ledenev@REDACTED>
> *Sent:* Wednesday, August 25, 2021 4:04 AM
> *To:* Brett Hemes <brhemes@REDACTED>
> *Cc:* erlang-questions@REDACTED
> *Subject:* [EXTERNAL] Re: Re: I am close to using a behaviour as a pure
> interface and I feel dirty...
>
>
>
> OK, I'll try to give you my opinion on this topic. If I understand
> your question correctly.
>
> It may look rude but please believe me it is not my intention to be rude.
>
>
>
> Your examples for me are the evidence of the "classic OOP" poisoning.
>
> Remark:
>
> I call it "classic" based on how the vast majority of the developers
> perceive the idea of objects and
>
> interaction between them.
>
> Though I am on the side of Alan Kay's definition of the OOP - objects
> communicating with messages.
>
>
>
> Anyway, how you "slice" the world and how you make your abstractions - it
> is influenced by the "classic OOP".
>
> Erlang is the functional language (more or less) so abstractions are made
> the other way:
>
> 1. Basic units are Module and Function;
>
> 2. Dynamic function calls - Module:Function(Args). So you can dynamically
> (in runtime) change the module;
>
> 3. Behaviour is just a hint for a compiler to check a Module for a
> presence of the required functions.
>
>
>
> Based on these 3 points - when you write -behaviour(....) in a module you
> are just saying that
>
> this module must have a bunch of functions implemented to be successfully
> compiled. It is not for
>
> supporting your own abstractions. It is an instrument to remind you to
> implement functions.
>
>
>
> What you call "general" functionality in Erlang is simply a library
> implemented as module(s).
>
>
>
> Of course it is very tempting to call behaviour an interface, because it
> looks like an interface.
>
> But it is not because the interface in Erlang are the functions, not a
> separate entity.
>
>
>
> Example of using behaviour from my own experience.
>
> As in almost any application I must have something where I store the data.
>
> And requirements are very vague. So I don't know if it would be Mnesia,
> PostgreSQL, file, space station or a toaster.
>
> So I've made the behaviour which defines functions for my business logic
> associated with storage.
>
> To make it simple it is like the CRUD operations on my entities.
>
> This behaviour is implemented as an independent rebar3 library, so other
> parties (sometimes remote) can include it as a dependency.
>
> But their implementations are included in the final application and what
> implementation will be used
>
> is defined in the configuration file.
>
> (Pseudo) code is like this:
>
>
>
> callbacks_db.erl:
>
>     -module(callbacks_db).
>
>     -callback create(Entity::my_entity_t()) -> ok | {error, Result ::
> term()}.
>
>
>
> postgresql.erl:
>
>     -module(postgresql).
>
>     -behaviour(callbacks_db).
>
>     create(Entity) -> ....
>
>
>
> toaster_able_to_storage_data.erl:
>
>     -module(toaster_able_to_storage_data).
>
>     -behaviour(callbacks_db).
>
>     create(Entity) -> ....
>
>
>
> main_app.config:
>
>      {main_app, [
>
>          {db, #{mod => postgresql}} % If you are using a toaster use
> "toaster_able_to_storage_data"
>
>      ]}
>
>
>
> main_app.erl:
>
>     some_function(DBMod) ->
>
>         Entity = create_entity(...),
>
>         DBmod:create(Entity).
>
>
>
> That is all about behaviour.
>
>
>
> ср, 25 авг. 2021 г. в 00:46, Brett Hemes <brhemes@REDACTED>:
>
> > Erlang's behaviour is NOT an interface!
>
> > If you are looking for some analogy from other languages the closest one
> is abstract classes in C#.
>
>
>
> This isn’t very helpful...  it is the exact same response I find in the
> forums with no reasoning behind it.  I don’t need analogies either; I
> understand what behaviours are “supposed” to be from the documentation and
> comments (perhaps this wasn’t made clear by my post).  Where I fall short
> is “why” are behaviours limited to such and why aren’t more people asking
> the same questions I am stuck on (regarding polymorphism)?  My logic was:
> yes, this has been asked and discussed some in the past with no real
> resolution that I could find... therefore, users must be content/accepting
> of the tools provided.  I am not so naive to think I am the first to
> need/want such, so there must be a disconnect.
>
>
>
> I posted my example to motivate my questioning hoping for some insight
> and/or comfort.  As of now, I have proceeded with storing “meta refs” to my
> child servers that are module/reference tuples (along with some dangerous
> and future-maintenance-issue-causing assumptions regarding their
> “interface”)... and it’s works... it just smells, and I am always eager to
> learn and find the right/better/best way.
>
>
>
> Aside: a colleague came across this repo (
> https://github.com/eldarko/epolymorph) while digging and the readme seems
> to capture my use case almost exactly...
>
>
>
> Brett
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20210826/225406cf/attachment.htm>


More information about the erlang-questions mailing list