bartblast

bartblast

Creator of Hologram

Composability Patterns for Hologram - Looking for Your Ideas

I recently started a discussion about reactive patterns in Hologram, but I realized I need to step back and design the composability architecture first - the reactive patterns should fit within that broader framework.

Quick Context on Hologram

Hologram is a framework that compiles Elixir to JavaScript, letting you write isomorphic web applications entirely in Elixir. Components can run on both client and server.

Currently, a Hologram component is represented by a Component struct containing:

  • State - the component’s data
  • Emitted context - data passed down to child components
  • Next operations - instructions for what should happen next (e.g., “run action X”)

Components can define:

  • Actions - functions that run client-side and update local state
  • Commands - functions that execute on the server

However, don’t feel constrained by this - I’m open to completely different approaches.

The Challenge

We’ll likely need some reactive patterns - things like:

  • Memoization/derived state
  • Effects (à la React’s useEffect)
  • Watchers (à la Vue)

Or maybe something completely different! These are just examples from other frameworks.

In React, hooks provide elegant composability:

// Extract reusable logic
function useToggle(initialValue = false) {
  const [value, setValue] = useState(initialValue);
  const toggle = () => setValue(v => !v);
  return [value, toggle];
}

// Compose it:
function Panel() {
  const [isOpen, toggleOpen] = useToggle(false);
  
  return (
    <div>
      <button onClick={toggleOpen}>Toggle</button>
      <div>{isOpen ? 'Open' : 'Closed'}</div>
    </div>
  );
}

What I’m Looking For

How could actions, state operations, reactive patterns, and other patterns compose in Hologram?

Due to Elixir’s functional/immutable nature, the patterns will likely manifest differently than in React/Vue. That’s the interesting part!

Show me your ingenuity and creativity - every idea matters, no matter how unconventional or silly you think it is. Everything is on the table:

  • Function composition
  • Macros
  • Explicit or implicit namespacing
  • Flat keys
  • Mixins
  • Something else entirely?

Also worth noting: Hologram has compile-time access to the full call graph, which could potentially help solve this problem (though it doesn’t have to).

What patterns would you explore? Feel free to share anything from high-level ideas to concrete code examples or even full system designs.

First 10 of 40 Posts! Switch mode

derek-zhou

derek-zhou

I think you are thinking too much. Honestly, nobody else matters, it is your project, so do whatever your heart lead you to. If I were you, I will just pick the path that require the least amount of work, as long as it does not prevent you from picking a more adventurous path later. You can have breaking changes! you can have multiple APIs!

nikfp

nikfp

I always liked the idea of the reducer patterns introduced in Redux ans RTK myself. I didnt like all the plumbing that went into it sometimes, but the base premise of “starting state →input → new state” was simpler to reason about, and would also align with Elixir’s functional nature well IMO.

I also really liked Svelte’s writable and derived stores, as they were easy to compose inside a state module and expose only what I wanted exposed, so I could have a stateful reactive core that the presentation layer could bind to as needed.

In the end though, your leadership, preferences, and vision will have to be the deciding factor. For all the opinions you get on this question, you will naturally have a far better view of how it all fits together.

garrison

garrison

I am inclined to agree with this. Unless you’re going to copy an existing solution you have to accept that it’s going to take years of real-world use to nail down these APIs.

I find these discussions fun and helpful for nailing down my own beliefs (and finding out which of them hold up to scrutiny), but at the end of the day you have to iterate on something if you want to make progress.

I will see if I can come up with some thoughts, though, as I do think composition is important. Also remember that in a lot of these frameworks the components are the main unit of composition and features like Hooks or Signals are intended as a means to share logic across components (but they should still compose too, of course).

AstonJ

AstonJ

Bit off-topic for the thread but I’ll quickly respond.

While I would agree that following your heart/what feels right is generally a good way to go I also really like how Bart has been seeking feedback on things he feels might benefit from it :icon_biggrin:

It helps people feel like what they want matters and often leads to comments like this:

It’s something which plays a role in project confidence and helps create a deep and personal affiliation.

I would go as far as to say Hologram, and the way Bart has been involving everyone (/his meticulous attention to these kinds of details) has all the hallmarks of it potentially obtaining that all-elusive cult status :icon_cool: (This won’t just be good for Bart or Hologram, but for the entire Elixir community).


Back on topic, I don’t have any specific thoughts myself other than saying I hope whatever Bart decides echoes his goal of making Hologram as easy (natural and intuitive) and enjoyable to use as possible.

14
Post #4
nikfp

nikfp

This is why I was drawn to Redux and Svelte Stores tbh. They allowed me to centralize state. So the components could be the method of composing UI, but the state could be centralized and decoupled which seemed a lot cleaner to me somehow.

Edit: Let me add an elaboration from an end user

Say you have some semi-complex state, and some of it is derived to either allow or disallow some action.

If the components are responsible for composing the data and deriving that, you have to pass the entire state to the component and then verify if the action is allowed, and then present accordingly.

OTOH, if the state can be decoupled, you can have the component be “dumb” which makes it easier to test in isolation. Then you have the state representation on it’s own which also makes it easier to test in isolation.

If the state is wrong, you have 1 place to check / debug. If the presentation is wrong, it’s dirt simple to sort out.

jam

jam

I think it depends on the overarching goal and vision for Hologram. Zooming out for a second, it seems like these design decisions are mainly influenced by these opposing forces:

  1. Offering complete control and explicitness
  2. Abstracting complexity and optimizing for ergonomics

#1 seems to generally lead to verbosity but you as the engineer have full control over how things work. #2 generally results in having to write less code but more trust in the framework. If we’re keeping in line with Hologram’s goal to make web dev simple, then I think it should lean more heavily into #2 even if it means sacrificing some sense of control.

With that said, I think composition can be achieved mostly with components. I like the way Hologram is currently designed in this regard. It feels Svelte-like which I prefer. I think the main area that wouldn’t fall directly inside of a component would be some sort of shared store. I typically reach for stores seldomly though, most of my composition is done with components.

Currently, for a store in Hologram, it looks like I’d use Context. If I wanted a global store, I suppose I’d have a Context at the app’s root (if I have this wrong, let me know). It might be nice to be decouple a store completely from a component and be able to use them in any component (maybe this is already possible?).

Beyond that, I think what is missing are:

  1. Derived / memoized values
  2. Side effects

I think it might be a nice DX if both of these were macros, maybe derived and effect (though I’ve never liked effect as a name, I’m not sure there’s a better alternative at this point).

Whatever you choose, I suggest keeping the api and macro surface area small. :slightly_smiling_face: IMO, Svelte has done well here whereas Vue has not for example.

To be fair, I’m a bit Svelte-brained at this point. I imagine others that are used to other frameworks will be biased in that framework’s way of thinking too.

One other thing worth considering is: will / should Hologram support fine-grained reactivity?

I agree with Derek though: Ultimately, Hologram should be a reflection of how you think it should work. All of the other frameworks succeeded based on the opinions and taste of the primary author. In Bart we trust. :slightly_smiling_face:

Lucassifoni

Lucassifoni

I share your view on composable state/store modules fully decoupled from UIs. Even with LiveView this is really powerful.

As an example, I have a few liveviews that are used by users but also used by programs.

Users act on state via LiveView’s handle_events, that call a public API exposed by the pure state modules to update the sockets, then LiveView re-renders.

But programs can build up state outside of components by creating and calling the pure state module’s public API in isolation, then just ask LiveView for a single render of the built up state.

There’s still coupling since those pure state modules represent UI state, not application state, ( UI_state = fn(app_state, user_or_robot_intents) if you wish) but being able to handle them inside or outside components is very valuable to me. That and the ability to compose smaller state into bigger state.

As Garrison showed it in the other thread, that makes for a bit verbose code for the simpler cases.

I also think Bart is leading the community in a new direction with Hologram and has all the rights of trying a few things and changing his mind :slight_smile: at least before 1.0.0 ! (And even after)

garrison

garrison

Just to clarify, my concern is mainly with complex (real-world) cases; simpler cases tend to come up only in examples. Whether you agree with me is another matter (and I won’t drag that debate here), but that’s my intent.

bartblast

bartblast

Creator of Hologram

While I do make the final decisions based on what feels right, I’ve learned that designing in isolation would likely create a project perfect for some past version of me, but not necessarily for the broader community. Hologram has grown beyond just my personal project - there’s already a growing number of projects using it, including in production. The broader success of Hologram means more Elixir use cases and potential new jobs for the ecosystem.

I hear you both on this. The challenge is that after 5+ years, nearly 10k commits and 1M lines of code, the project has real inertia. Changing core APIs at this stage isn’t a quick iteration - it’s measured in months of work, especially with plans for component libraries and other tooling that depend on these decisions. I don’t have infinite time to experiment, which is exactly why I’m being deliberate about these crucial architectural choices now.


To be clear, I’m not trying to please everyone - I’m trying to understand the solution space and how different patterns might serve real use cases. The feedback here helps with that, even if the final direction is my call.

bartblast

bartblast

Creator of Hologram

Great point about the reducer pattern - Hologram actions already work this way (current state → action → new state), so this validates that approach. I’d also love to explore how Svelte stores would translate to Hologram, and how these concepts could enable composability patterns. Would you be interested in showing a simple code example of how you’d imagine this in Hologram?

Last Post!

jam

jam

Honestly if it wasn’t already overloaded react would have probably been best. Maybe watch or track but feels a little passive. Maybe trigger,
spark, fire.

Where Next?

Trending in Discussions Top

AstonJ
As the title says, please share what you’ve been up to with Elixir. Whether that’s been learning it, looking into it, making stuff with i...
2976 91332 914
New
f0rest8
Hi everyone :waving_hand: Posting here to showcase and announce that Metamorphic is now officially live on a public-facing domain at htt...
New
byu
@chrismccord : I just saw the Extract AGENTS.md from Phoenix.new into phx.new generator commit to the phoenix project. My initial shotgu...
New
arcanemachine
I was working on an Ecto migration and I needed a timestamp. So, for the nth time, I looked up the different data types for timestamps, a...
New
AstonJ
Just a general thread to post chat/news/info relating to AI/ML stuff that may be relevant for Nx now or in the future. Got anything to sh...
New
type1fool
I just stumbled on a newly redesigned elixir-lang.org. :tada: It looks like @Software_Mansion did the work, and I think it is generally a...
New
juhalehtonen
There has been a thread to discuss the Stack Overflow Developer Survey on this forum every year since 2018, so here’s yet another one for...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New
akoutmos
@hugobarauna and I (Alex Koutmos) have been hard at work on writing a book on Nerves that takes you from simply blinking LEDs to building...
New
zachdaniel
Introducing AshStorage! Attachment and file management that slots directly into your resources :smiling_face_with_sunglasses: I had hope...
New

We're in Beta

About us Mission Statement