C# is an object oriented language - I don’t blame it for how you have to do stuff, just as I don’t blame functional languages about how they do things. They both have their merits and shortcomings (usually the strength of one is the shortcoming of the other.)
But I allow myself to have preferences - and being a Ruby programmer (coming from Java) as well as an Elixir programmer, I see more and more functional patterns slip into my Ruby code.
I sometimes miss Ruby objects in elixir, because functional is usually more verbose to write, and I sometimes miss better functional support in Ruby (like immutable data, a more natural approach to name-spacing, no confusion about object instance and class and other stuff your language might throw at you, better modularity, no discussions about inheritance… etc.)
But I don’t think Functional or OO (as still practiced by some languages as C#) has the silver bullet - and I agree with Joe Armstrong’s sentiment that Erlang (and by extension Elixir) might be more OO in the sense that OO was supposed to be about instances with states passing messages to each other.
A little out of context (read the whole thing, it’s really interesting) Joe Armstrong: Erlang might be the only object oriented language because the 3 tenets of object oriented programming are that it’s based on message passing, that you have isolation between objects and have polymorphism.
Very off topic - but I want to talk about it - Phoenix LiveView is pulling the web back into the original idea of instances passing messages. We have been leveraging LV for a while, pretty much started using it when it was in Alpha state in a major project, and boy… It was a game-changer!
Our go-to pattern right now is to have one route LV controlling the (URL) parameters and multiple child LVs leveraging LiveComponents to optimize preloading associations, and child LVs broadcast certain events to everyone who is subscribed to the same parent - it made everything a lot more snappy and gave us developers a sense of control of what is happening in each one of those “LV components” (as in part of the page, but with different states and focus.)
If this pattern is not what Alan Kay was thinking about, then idk what he was even thinking!
EDIT how we are using LV, this is a depiction of a website in ASCII
________________________________________
| 1. PARENT (ROUTER) |
| |
| ____________________________________ |
| | 2. INDEX | 3. SHOW | |
| | | _______________ | |
| | | | 4. ENTITY | | |
| | | | INFO | | |
| | | |______________| | |
| | | | |
| | | _______________ | |
| | | | 5. ACTION | | |
| | | | TABS | | |
| | | |______________| | |
| | | | |
| | | | |
| | | | |
| | | | |
| |________________|__________________| |
|_______________________________________|
Router (Parent)
In control of the params, and informs everyone who is subscribed to the router PID about changes.
Index is a filtered list of stuff, show displays information about whatever you clicked on the index LV (we use a livelink to “redirect”, so the router informs all the children about the changes.)
All of these are subscribed to the Router (Parent):
Show
Controls the specific thing selected on index, sends updates to all children if Router changes
Info
Subscribes to Show
Is just a static card of information about the entity selected on the left side. It is also controlling edits on that entity, and then sending a global “reload_X(entity)” message to all subscribed LVs.
Tabs
Subscribes to Show
Is a collection of other, more specialized LVs, like “show all assocs for XX in context”, or a more specific example “show and edit/create all comments on these in this context” etc.
Everything that is Edit/Create is a component, and the action will trigger a global update on whatever was updated - I am having trouble to come up with a good example without giving away too much, but basically what we learned over time is that you want to keep the updates as generic as possible, let LVs reload everything and let take LiveView take care of only sending the diff (which more often than not is nothing) - whenever possible you should rely on the generic update to update your LV to avoid race conditions, so you would usually return {:noreply, socket}
without changing the assings in your handle_event
- if an update should be global, (like someone added a comment) then everyone currently listening to that update should update their view.
I would say the only downside of this pattern is that you have 4-5 lines of boilerplate code in every LV (maybe 2-3 after we optimized it) and having to implement the same handle_info
with different logic in multiple LVs/Components.