Disclaimer: I hate React and I wish people would stop trying to force it into every shaped hole they can find. ![]()
I don’t think trying to create an abstraction over LiveView, HTMX, React, etc is a good idea. I was thinking more along the lines of the communication semantics.
If we could merge the apis for handling a route for HTTP request-response cycle and a stateful persistent connection (e.g. websockets) into a single uniform api, then the programmer wouldn’t have to choose a framework that’s good at realtime stuff to have stateful interactivity or a framework that’s optimized for incremental stateless updates for component-based DOM-patching, etc.
I’m not sure how feasible it is ofc since if it was a simple problem, it would have been solved a long time ago, but now that we have our choice of well-supported protocols for use in browser-based and service-oriented applications, it would be worth investigating a possible abstraction over all of them.
Not just a library that makes working with each different protocol easier we’ve got those already.
My initial thoughts were that separating out sending and receiving data into separate functions (either declarative or imperative doesn’t matter) would be a good first step.
For example, we can model a bi-directional websocket connection by implementing two handlers:
def push(%{updated: val} = state), do: {val, state}
def pull(%{updated: val} = msg, state), do: {:reply, Map.put(state, :updated, val)}
def pull(msg, state), do: {:noreply, state}
This same api would work for stateless HTTP request-response communication as well.
The implementation could raise or return a 404 or something else if returning {:noreply, state} from a pull/2 running over an HTTP1 connection.
I actually want this kind of api in a web framework regardless of its broader usefulness because I think it makes it complex connections more composable. In Python I use the Quart web framework for websocket stuff, and it has a single @websocket decorator for creating a websocket “route”, and the code inside that handler runs both sending and receiving messages, so I have to manually separate sending and receiving to make them run concurrently. Separating them out ends up making the code a lot simpler and easier to test as well though, so I think it’s a good pattern to enforce if possible.
P.S. I really think send/1 and recv/2 are better names for the above functions, but that would clash with Elixir’s process utilities, so ![]()






















