bartblast

bartblast

Creator of Hologram

Signals, computed properties and other reactivity patterns in Hologram

This discussion started in the Does Hologram support two way data binding? thread and evolved into a deeper conversation about reactivity patterns in Hologram. We’re exploring questions like: Should Hologram support computed properties? How do signals fit with functional programming? What’s the right abstraction for derived values?

Most Liked

garrison

garrison

I haven’t replied again out of concern for this specifically. I want to make sure I’ve seen the API you have in mind before I give further thoughts. Plus it will probably be easier to explain what I have in mind if I can demonstrate it in terms of what you already have.

BTW, your dedication to responding to feedback on here is amazing. Don’t think it goes unnoticed!

bartblast

bartblast

Creator of Hologram

Signals are compelling, but they’re fundamentally a JavaScript reactivity primitive. Hologram transpiles Elixir to JS, preserving Elixir’s functional semantics - immutable data structures, explicit state updates via put_state, etc. Adopting signals would mean either reimplementing them in Elixir semantics (complex) or breaking from Elixir patterns (defeats the purpose). The current explicit approach aligns better with Elixir’s philosophy.

That said, a middle ground with similar purpose is planned - computed values/properties. This would give you automatic recalculation of derived state when dependencies change, similar to Vue’s computed properties, while maintaining the explicit, declarative, functional style that fits naturally with Elixir.

bartblast

bartblast

Creator of Hologram

When I said “middle ground” I didn’t mean “half-measure” - that was unfortunate wording on my part. I’m talking about reaching similar goals - automatic recalculation, performance optimization - but in a way that aligns with functional programming principles. I think computed properties actually fit naturally into the functional paradigm: they’re pure functions that transform immutable state into derived values. That’s fundamentally functional.

I know you’re not a fan of templates, but computed properties actually strengthen the template-as-presentation-layer approach by letting you move business logic out of templates into small, focused, testable functions.

To be clear: when I talk about computed/derived properties, I mean values like full_name, valid?, or formatted_price that derive from existing state - not template fragments. (Partials like <my_partial(param_1, @param2) /> would be a separate feature for generating template fragments.) These are tools in your toolbelt, not fundamental architectural commitments.

I think it’s worth considering the concrete benefits they provide - they solve real problems:

  • Performance: Automatic memoization of expensive calculations
  • Consistency: Derived state that can’t get out of sync when multiple values depend on each other
  • Readability: Clear separation between data and derived values
  • Testability: Small, pure functions that can be tested in isolation

Regarding “there is no forest; you are only concerned with the trees” - I understand your concern about losing the holistic view. But I think the key difference is that computed properties in Hologram would be values derived from state, not a reactive programming model that forces you to think in terms of dependencies and updates. You still write your component declaratively as a whole - the state updates explicitly through actions, and computed properties are just transformations of that state. The component’s logic remains interconnected and whole. Computed properties are simply a way to extract and name derived values rather than calculating them inline in the template.

Additionally, when components are small and properly isolated with focused responsibilities, it becomes easier to keep the entire component in your head at once - the “trees” are manageable and the “forest” emerges from how components compose together.

The key point: computed properties in Hologram would be optional and complementary to the existing explicit approach, not a replacement. They’re a tool for specific use cases where the benefits are clear. I want to be pragmatic about giving developers useful tools while maintaining a coherent architecture.

bartblast

bartblast

Creator of Hologram

Hi @venkatd, thanks for the feedback! :slight_smile:

I completely understand your wariness based on KnockoutJS/EmberJS - those frameworks created convoluted webs of interdependent computations that were hard to trace.

However, I think those issues stemmed from their mutation-based reactive model: mutable observables, dependency graphs spanning multiple files/components, scattered observers with side effects, and hard to trace execution flow when changes cascade through dependencies.

Hologram’s approach is fundamentally different:

  • Immutable state with explicit updates
  • Dependency graphs scoped to component state and props only - local and bounded
  • Pure functions, no scattered side effects
  • Clear data flow: action → state update → re-render

Yes, there would be a dependency graph for computed properties, but it’s deterministic, local to the component, and traceable by reading the code. No hidden global reactivity. This explicit nature would even enable a Time Travel Debugger to track all actions, commands, and the computed properties graph in real-time.

Computed properties would be named, memoized pure functions that transform state. They’re still part of ui = fn(state) - the function is just composed of smaller, named, cached transformations.

Why memoization matters in ui = fn(state):

The key is that some form of memoization is needed anyway. Without it, you face: repeated function calls with identical inputs, expensive recalculations on every render, composed calculations where all functions in the chain recalculate even when inputs haven’t changed. Your options are: recalculate everything (expensive), manually cache (boilerplate), or automatic memoization. Computed properties provide the latter while keeping the functional paradigm intact, plus they help move business logic out of templates into named, testable functions.

Computed properties would be completely optional - just a tool in your toolbelt for cases where they provide clear value.

Does this address your concern? I know the term “computed properties” carries baggage from various JS frameworks, but I think the fundamental paradigm difference (immutable, functional, local) makes them a different beast entirely. Would love to hear your thoughts on solving these memoization challenges, or if different terminology would help clarify the distinction.

bartblast

bartblast

Creator of Hologram

Alright, here are some ideas to show what I have in mind! @garrison @venkatd

The core idea: a simple, component-scoped DSL that defines how derived/computed/memoized values (terminology is up for discussion) are calculated. Hologram automatically updates these values when any dependencies change, and the values are injected into template vars accessible with the @ syntax, e.g. {@my_derived_value}.

The dependencies for derived values can be state, props, and other derived values, so a dependency graph is built underneath and topological sort is applied to manage the recalculation order.

The DSL can be implemented in different ways (full_name probably isn’t the best real-world use case, but it’s easy to understand):

Option 1: Macro with explicit deps

derived :full_name, [:first_name, :last_name] do
  "#{first_name} #{last_name}"
end

Option 2: Attribute annotation

@derived deps: [:first_name, :last_name]
def full_name(vars) do
  "#{vars.first_name} #{vars.last_name}"
end

Option 3: Function-like syntax

defderived full_name(first_name, last_name) do
  "#{first_name} #{last_name}"
end

…and other permutations and hybrids of these approaches.

Key characteristics:

  • Pure functions transforming immutable state (no side effects)
  • Component-scoped dependencies (local and bounded)
  • Automatically memoized (recalculated only when dependencies change)
  • Deterministic dependency graph (fully traceable)
  • Still fits the ui = fn(state) paradigm

This is fundamentally different from the mutable observer patterns in KnockoutJS/EmberJS that created convoluted webs of interdependencies. There’s no global reactivity, no scattered side effects, no mutation-based cascading updates.

Important clarification: This is purely for deriving/computing values without any component struct modifications. For reactive state updates, effects/watchers would be a separate concept – but that’s a different discussion.

Would love to hear which syntax option resonates with you, and what other ideas you might have!

Where Next?

Popular in Questions Top

freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
LegitStack
I’m hoping you guys can give me some general advice and perhaps code examples if you’re feeling up to it. I’m very interested in Elixir,...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
quazar
How to set Jason to encode all fields in ecto schema, I don’t care about security and implementing only is taking long list of attributes...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
chewm
Hi guys, nice to meet you to the whole forum, I’m new here, I’m trying to configure visual studio code for elixir, right now the intellis...
New
Exadra37
Sometimes I want to check if the input into a function is not a blank string. My first approach: defmodule Example do def do_stuff(s...
New
jc00ke
Expanding on this topic: https://forum.elixirforum.net/t/map-typespec-question/19217 Let’s say I have a map with required and optional k...
New

Other popular topics Top

lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
peerreynders
Manning 2016 Halloween weekend sale via Deal of the Day Friday, October 28 - Half off all MEAPs - code WM102816LT Saturday, October 29 ...
326 29600 154
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod -- where is this set? Thanks.
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
chrismccord
As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New

We're in Beta

About us Mission Statement