Elixir/Phoenix-like C#

I’ve not been able to work with Elixir for a while, instead having to jump over to Windows, C# and .NET Core.

Just caught myself writing this…

public async Task<FormModel> GetFormWithSubject(string formCode, string subjectPrimaryKey)
        {
            FormModel Form = new FormModel(formCode);

            using (var conn = new SqlConnection(_connectionString))
            {
                conn.Open();
                Form = await LoadFormAndFields(conn, Form);
                Form = await LoadSubject(conn, Form, subjectPrimaryKey);
            }

            return Form;
        }

Fair to say I’m missing piping and the way that Phoenix passes around it’s conn! :rofl: I don’t even need the assignments to Form but I’m so used to immutable data that it looks plain wrong without them.

Between working with C# and Swift lately I’ve come to appreciate a great deal about Elixir!

4 Likes

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.

I’m sorry that I long ago coined the term “objects” for this topic because it gets many people to focus on the lesser idea.

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.

5 Likes

I have had some Rust stuff to do lately and I find myself looking for the functional pipe-able patterns there as well (which they have in e.g. Iterator where you can do .map(...).fold(...) etc.). :slight_smile:

I generally agree with your entire comment minus one exception: OOP usually goes hand-in-hand with imperative programming with shared mutable state which, finally, our profession begins to understand is a cause for countless bugs and weird behaviour and many language ecosystems started actively working against those and are trying to have more immutability.

I, like you, believe that the misrepresentation – and the subsequent abuse of – the idea of OOP has done it disservice but I also think it’s too late to change that now. OOP + imperative code + shared mutable state must mostly be made irrelevant in one form or another – that’s the only way to “kill” the horrifying spaghetti monsters that I had to tackle in Java years ago.

This is not just some hate; Rust for example still gives you polymorphism (so OOP) and is imperative by nature but, unlike most other languages out there, it enforces memory ownership so at least the mutable shared state is gone there (which is the main villain in this trio).

I believe we are currently living in some kind of a programming Renaissance when people finally started wanting their machines working for them and not the other way around. :003: I am quite excited for Multlicore OCaml (it’s a very fine language!) and any possible final touches on Rust’s async/await situation – those two languages are extremely good and strong contenders for taking the crown from C++ (as for C, sadly it looks like it’ll be around seemingly forever). And even though I do dislike JS quite a bit I’m also happy seeing they are working towards immutability and multi-threading (workers / worklets) – although I fear JS will be, as usual, the last to arrive. But hey, any improvement is welcome!

Things are, in general, going in a good direction IMO.

I hope V language https://vlang.io goes somewhere in future and starts replacing C.

C# is also hopefully getting records (immutable classes and structs) with structural equality in next C# version 9.

1 Like

Since you are using .NET would it be possible for you to just use F# instead of C# if you miss immutable data and pipeline operator?.

1 Like

I’d bet more on Zig btw. You can use the Zig compiler to compile C projects now, making it seamless to have one project in which you’re gradually migrating away from C and into Zig.

1 Like

In your router configuration, are you pointing all routes to the same live view and handling routing yourself?

Extension methods and LINQ are always your friend here.


When I have to do C# I adhere to a couple of rules:

  1. Adhering to anhemic objects for record/structs.
  2. Static classes as function containers.
  3. Extension methods in said classes to “pipe” with the help of LINQ.
  4. Generics (if appropiate) to make functions as flexible as possible.

My design misteps where when I had to decide between using interfaces or just injecting functions, but I had to rarely make that decision; It made programming a couple years back really fun.