<turbo-stream action="append" target="posts_list"><template>    <div class="postbit" id="376530" data-post-id="376530">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="Lucassifoni" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/Lucassifoni/120/39057_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  Lucassifoni
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Hi Bart,</p>
<p>We just worked on something vaguely similar with <a class="mention" href="/u/venkatd" rel="nofollow">@venkatd</a>, for spreadsheet-like DAG computation definitions. We chose to eliminate the macro DSL we had prototyped because it went in the way of explicitness and prevented using function captures without adding a lot of workaround code. Some cells need to call the world, some cells need to be cached, some do not, and this is up to the user.</p>
<p>Now the API looks like :</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule MyDummySheet do
  use ExSheet.FunctionDsl

  def build_sheet() do
     new()
      |&gt; input(:first_name, description: "A person's first name")
      |&gt; input(:last_name, description: "...")
      |&gt; input(:tenant_id)
      |&gt; input(:age)
      |&gt; cell(:full_name, inputs: [:first_name, :last_name], compute: fn (f, l) -&gt; "#{f} #{l}" end, cache: true)
      |&gt; cell(:tenant_age_rule, inputs: [:tenant_id], compute: &amp;MyApp.AgeRule.get_for_tenant/1)
      |&gt; cell(:passes_age_check, inputs: [:age, :tenant_age_rule], compute: fn age, rule -&gt; rule.(age) end)
  end
end
</code></pre>
<p>There is a bit more, but the core is a DAG of computation which you can selectively memoize or not. Usage of the given sheet is simple :</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">sheet = MyDummySheet.build_sheet
  |&gt; set(:first_name, "foo")
  |&gt; set(:first_name, "bar")
  |&gt; set(:tenant_id, 1)
  |&gt; set(:age, 21)

{passes, sheet} = get(sheet, :passes_age_check)
</code></pre>
<p>We use it for very different purposes. I use it to model UI state. We chose to keep the core API very small, but building on top of it is quite simple. For example, I’ve added a few helpers for sheet composition (nested computed state cleanly defined in separate modules) in my app that will not be in the core library.</p>
<p>The core of it is reactive calculations, but not linked to any framework or library. We have a few convenience helpers for collecting inputs to a map, and do not force a solution on how to hold a sheet in a process.</p>
<p>I see it as a way to organize an otherwise ad-hoc big “with” statement with laziness, dependency tracking,  partial querying, and optional caching ?</p>
<p>My point is that maybe computed properties are interesting to be consumed by an UI layer or anything else, and might benefit of not being directly linked to Hologram ? Despite being super useful if you provide them.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="376530" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/signals-computed-properties-and-other-reactivity-patterns-in-hologram/72968/22">Post #21</a>
	                </div>
	            </div>
              <div id="likers-container-376530" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="376530"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #21"></div>
  </section>
</div>
    <div class="postbit" id="376568" data-post-id="376568">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="bartblast" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/bartblast/120/17647_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  bartblast
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Creator of Hologram</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Hi Lucas,</p>
<p>Thank you for sharing! The ExSheet API looks nice and clean. I can see how the explicit pipeline approach with selective memoization works well for spreadsheet-like computation graphs.</p>
<p>I think these are kind of two different use cases that probably have different optimal solutions, and your approach makes sense for what you’re building.</p>
<p><strong>Different problems, different constraints</strong></p>
<p>Component-scoped computed properties vs. general computation DAGs have fundamentally different needs. Spreadsheet DAG computations often need external I/O, side effects, and selective execution - exactly what your library handles. UI component derived state, on the other hand, should be pure functions, synchronous, always safe to recompute, deterministic, and locally scoped. Your solution is more flexible and general-purpose, which fits your use case.</p>
<p><strong>Why tight framework integration makes sense for UI components</strong></p>
<p>The architecture of modern web frameworks has kind of converged to the same patterns: component-based, composition pattern, and reactive/signal patterns. Users currently expect these reactive/signal patterns to be provided by the framework, and that makes sense because the reactivity system needs to be tightly coupled with the rendering engine. Frameworks can optimize the entire update cycle when they control reactivity.</p>
<p>When reactivity is built-in you get one consistent mental model throughout the app and possibly better tooling/debugging/documentation because the framework can understand the reactive state.</p>
<p><strong>Hologram-specific considerations</strong></p>
<p>If Hologram used a separate library for component computations:</p>
<ol>
<li>
<p><strong>Transpilation overhead</strong>: The computation machinery would have to be transpiled from Elixir to JS. There’s always some overhead (boxed types, proxies for function calls, data cloning due to immutability, etc.). For critical performance paths that run on each render, it’s better to implement things by hand in JS. This is where compile-time analysis comes in - it can extract dependency information, build the computation graph, determine topological sort order, and generate optimized metadata at compile-time, then feed all of this to hand-written JavaScript code that executes efficiently at runtime without the transpilation overhead.</p>
</li>
<li>
<p><strong>Framework integration</strong>: The macro pattern helps take business logic out of templates. With a separate library, templates would need something like <code>{MyComputedState.get(@computed_state, :full_name)}</code> rather than just <code>{@full_name}</code>.</p>
</li>
<li>
<p><strong>Orchestration</strong>: Each action would need to imperatively update the computation state whenever dependencies change. When using macros, that orchestration happens automatically. This becomes even more valuable when Hologram introduces reactive queries from local-first data sources.</p>
</li>
<li>
<p><strong>Performance optimizations</strong>: When the framework understands the reactivity model, it can apply sophisticated optimizations like offloading computations to micro-tasks, batching updates, or scheduling work based on priority. These kinds of optimizations are much harder when reactivity is external to the framework and the computation is just an opaque function call.</p>
</li>
</ol>
<p><strong>On automatic vs. explicit dependencies</strong></p>
<p>Both approaches are DSLs with different trade-offs:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir"># automatic dependency tracking
derived :full_name do
  "#{first_name} #{last_name}"
end

# explicit version
|&gt; cell(:full_name, 
    inputs: [:first_name, :last_name], 
    compute: fn (first_name, last_name) -&gt; "#{first_name} #{last_name}" end, 
    cache: true)
</code></pre>
<p>The explicit version gives you fine-grained control over caching and execution (great for general computation graphs where some cells may need I/O or shouldn’t be cached). The automatic version reduces boilerplate and eliminates the “missing dependency” bug class. Since Elixir provides compile-time analysis through macros, and Hologram has the call graph available, automatic tracking leverages those strengths.</p>
<p>For UI components where you typically want everything memoized by default, opt-out memoization creates a “pit of success” - good performance without profiling every decision.</p>
<p><strong>Question for you</strong></p>
<p>Can you explain a little bit more about this problem? “It went in the way of explicitness and prevented using function captures without adding a lot of workaround code.” I’d love to understand what specific challenges you encountered - it would be helpful to think through those edge cases.</p>
<hr>
<p>So as you noticed yourself, these are different use cases. Your ExSheet library solves a more general problem with appropriate flexibility, while component-derived state has narrower constraints that benefit from tighter framework integration. Both approaches make sense in their respective contexts!</p>
<p>Thanks again for sharing your work!</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="376568" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/signals-computed-properties-and-other-reactivity-patterns-in-hologram/72968/23">Post #22</a>
	                </div>
	            </div>
              <div id="likers-container-376568" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="376568"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #22"></div>
  </section>
</div>
    <div class="postbit" id="376570" data-post-id="376570">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="bartblast" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/bartblast/120/17647_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  bartblast
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Creator of Hologram</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>One more thing I forgot to mention: the explicit dependency approach has challenges with nested state structures.</p>
<p>If you have state like <code>%{user: %{profile: %{name: "...", avatar: "..."}, settings: %{theme: "..."}}}</code> and want to derive a value from just <code>state.user.profile.name</code>, you’d have to list <code>:user</code> as the input dependency. This means the computation would re-run even when unrelated nested fields change (like <code>state.user.settings.theme</code>).</p>
<p>With automatic compile-time dependency tracking, the framework can analyze the code (thanks to Hologram’s call graph) and determine the exact nested path dependencies, avoiding unnecessary recomputations. This becomes increasingly important as component state grows more complex.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="376570" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/signals-computed-properties-and-other-reactivity-patterns-in-hologram/72968/24">Post #23</a>
	                </div>
	            </div>
              <div id="likers-container-376570" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="376570"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #23"></div>
  </section>
</div>
    <div class="postbit" id="376609" data-post-id="376609">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="garrison" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  garrison
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>This is an excellent and thorough response. I want to say again that I really appreciate the effort you are putting in here! It will probably take me a few replies just to respond to this.</p>
<p>There are parts of this I agree with and parts I don’t. I’m going to first just get a couple things out of the way to clear some room for the interesting parts <img src="https://forum.elixirforum.com/images/emoji/apple/slight_smile.png?v=15" title=":slight_smile:" class="emoji" alt=":slight_smile:" loading="lazy" width="20" height="20"></p>
<aside class="quote no-group" data-username="bartblast" data-post="18" data-topic="72968">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/bartblast/48/17647_2.png" class="avatar"> bartblast:</div>
<blockquote>
<p>Hologram leverages Elixir’s purely functional foundation with immutable data and compile-time metaprogramming</p>
</blockquote>
</aside>
<p>You can write immutable and functional code in a language like JS. It’s just not enforced.</p>
<p>That is really what React apps are <em>doing</em> if implemented properly. Even the React engine, which abuses global state for hooks, <em>could</em> be implemented with algebraic effects in a functional language that actually had them. Unfortunately we don’t have them either, but we have global state (process dictionary) so these lines are blurry anyway.</p>
<p>The point being: you could write perfectly idiomatic React-style components in Elixir, and actually they are probably more idiomatic in Elixir than in JS.</p>
<aside class="quote no-group" data-username="bartblast" data-post="18" data-topic="72968">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/bartblast/48/17647_2.png" class="avatar"> bartblast:</div>
<blockquote>
<p>You cannot conditionally execute a hook based on runtime logic</p>
</blockquote>
</aside>
<p>This is a limitation of React but not of React’s model. Actually Steven Wittens, whose writing I linked above, is the author of <code>use.gpu</code> and its <code>Live</code> React clone has hooks and no-hooks which <em>can</em> be executed conditionally (but only in pairs). Definitely check that out if you’re not familiar; I think they’re quite clever.</p>
<aside class="quote no-group" data-username="bartblast" data-post="18" data-topic="72968">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/bartblast/48/17647_2.png" class="avatar"> bartblast:</div>
<blockquote>
<p>Not having to manually specify dependencies is actually more declarative</p>
</blockquote>
</aside>
<p>I don’t think it’s more or less declarative, but I agree it’s a good idea that would be nice to have in React’s model. There is no reason it couldn’t be added! There <em>are</em> times when you want to manually specify dependencies, but this is rare and automatic would be a great default.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="376609" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/signals-computed-properties-and-other-reactivity-patterns-in-hologram/72968/25">Post #24</a>
	                </div>
	            </div>
              <div id="likers-container-376609" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="376609"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #24"></div>
  </section>
</div>
    <div class="postbit" id="376611" data-post-id="376611">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="garrison" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  garrison
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="bartblast" data-post="18" data-topic="72968">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/bartblast/48/17647_2.png" class="avatar"> bartblast:</div>
<blockquote>
<p>I’d argue that a <code>derived</code> macro would be <strong>essentially equivalent to useMemo</strong> in terms of expressiveness</p>
</blockquote>
</aside>
<p>This is a very good counter to what I wrote. Honestly the few sentences of feedback I tacked on there were probably not deserving of such a clear response, so I will try to make up for it.</p>
<p>The problem is not so much with what you have right now, but what it will <em>become</em>. You are going down the path of forcing the user to build up an explicit computation graph in <code>init()</code> in order to write their application logic. Try to picture what this is going to look like in a complex app once you’ve filled in all the blanks.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">component
|&gt; state(:user, fn %{id: id} -&gt; load_user(id) end)
|&gt; derive(:name, fn %{user: user} -&gt; "#{user.first} #{user.last}" end)
|&gt; derive(:theme, fn %{user: user} -&gt; user.settings.theme || "light" end)
|&gt; derive(:expensive_thing, fn %{user: user} -&gt; user.cached_thing || compute_thing(user) end)
#... 500 more lines of code like this
</code></pre>
<p>This will become, essentially, the <code>Ecto.Multi</code> problem taken to the extreme (sorry in advance to Multi enjoyers). This is not Elixir code anymore. This is a dataflow graph modeled in Elixir. You are not getting the “benefits of Elixir” by mangling your code in this way. With <code>transact()</code> at least we seem to have finally learned better.</p>
<p>And your response cannot be “it won’t get that bad, it’s just a few computed properties here and there”: yes, <em>it will</em> get this bad once you have to actually build real apps.</p>
<p>There is another problem with this approach, by the way: it does not compose. The React team evaluated a number of solutions before <a href="https://overreacted.io/why-do-hooks-rely-on-call-order/" rel="noopener nofollow ugc">settling on call order because it can compose</a>. If you are relying on the property names (<code>:user</code>, <code>:name</code>) to key off your rewindable state then they will collide if you try to factor them out into functions. Note that LiveView actually has this problem in <em>several</em> places, which is probably the biggest problem with LiveView.</p>
<p>I will note, also, that I actually <em>proposed</em> this sort of API for LiveView earlier this year, and others told me they thought it would end badly (and I was annoyed by this). I am arguing in their favor, now, because it <em>is</em> a bad idea. I just had no concept of how deep this problem actually runs, or what it takes to arrive at a proper solution.</p>
<p>(On the other hand, this trick could actually be retrofitted to LV, whereas a proper solution probably requires throwing out LV’s entire engine <em>and</em> API and starting over (like React did with Fiber and Hooks). But that’s what you’re doing!)</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="376611" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/signals-computed-properties-and-other-reactivity-patterns-in-hologram/72968/26">Post #25</a>
	                </div>
	            </div>
              <div id="likers-container-376611" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="376611"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #25"></div>
  </section>
</div>
    <div class="postbit" id="376615" data-post-id="376615">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="garrison" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  garrison
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="bartblast" data-post="18" data-topic="72968">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/bartblast/48/17647_2.png" class="avatar"> bartblast:</div>
<blockquote>
<p>Hooks don’t actually give you complete control and freedom</p>
</blockquote>
</aside>
<p>This is true, and I want to discuss this specifically.</p>
<p>Hooks give you about as much freedom as is realistically possible. Each component is like a “stack frame” in a program, with the unique property that they <em>survive</em> the function call and can be used again. This gives you a place to do the caching and memoization needed to make declarative programming performant.</p>
<p>The idea here is that you want to define a program which is written as though all of the components are rendered from top to bottom whenever anything changes. That’s the declarative part.</p>
<p>What is so beautiful about React’s model is they managed to find a way to produce a uniform interface for this with few concessions. The “rules of hooks” are the concessions, and they exist because there has to be <em>some</em> restriction to the program’s execution or the cache will not “line up”. React performs this “lining up” at two different levels: at the component level, where they are lined up by the <code>key</code> path, and at the hook level where execution order is used. Hooks exist specifically as a tool for factoring out reusable code <em>within</em> a component, to avoid using “components for everything”. But they don’t have the <code>key</code> trick so they can’t be conditional; there you <em>would</em> use components.</p>
<p><em>In practice</em> that approach has worked well. It was <em>evolved</em> from a long sequence of mistakes and failed attempts over many years. Nobody gets this right the first time. React certainly did not!</p>
<p>The problem that I have with your approach here is not really about “compile time” (I should not have mentioned this at all). It’s really that you have split <code>init</code> and <code>render</code>, and everything else is just downstream of that.</p>
<p>The goal of programming in a declarative style is to write code that rebuilds itself each time. A React engine is a tool for making this style performant, but it preserves the style of the underlying programming language (be it JS or Elixir or whatever). The component is called <em>each time</em>, not once at the beginning (init).</p>
<p>What you are doing is effectively inventing a new programming language, similar to what I showed above, and defining an <code>init</code> function written in that language to build up an execution graph to produce intermediate state for the template. The graph is taking care of the “lining up” (you call this automatic memoization), and actually it probably could be as expressive as React/Hooks if done right. But it’s not <em>Elixir</em> anymore.</p>
<p>In React, the <em>component</em> is the execution trace. The <em>component</em> is what you call on render. Not an execution graph. This is a substantial stylistic difference.</p>
<p>If you were to attempt something closer to React’s model you would end up with <em>more</em> idiomatic Elixir. The example of <code>Ecto.Multi</code> vs <code>Repo.transact()</code> is a helpful analogy, I think, except that it will be much worse here because frontend is generally a lot more intricate than backend.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="376615" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/signals-computed-properties-and-other-reactivity-patterns-in-hologram/72968/27">Post #26</a>
	                </div>
	            </div>
              <div id="likers-container-376615" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="376615"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #26"></div>
  </section>
</div>
    <div class="postbit" id="376618" data-post-id="376618">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="bartblast" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/bartblast/120/17647_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  bartblast
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Creator of Hologram</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Thanks for the detailed response! I think we might be talking past each other a bit here - let me clarify what I’m actually proposing…</p>
<p><strong>You’re not building up computation graphs manually in init()</strong></p>
<p>The scenario you described - piping through hundreds of lines of DSL in <code>init()</code> - is not what I’m advocating at all. You’re right that a long pipeline of DSL calls in init() would be unwieldy - similar to the concerns you raised about Ecto.Multi.</p>
<p>What I’m proposing is defining derived values through <strong>macros</strong> in your component module, not building graphs imperatively. The macros extract dependency information at compile-time, and Hologram automatically constructs the computation graph and handles recalculation. Here’s the difference:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir"># NOT this (what you're worried about):
def init() do
  component
  |&gt; state(:user, fn %{id: id} -&gt; load_user(id) end)
  |&gt; derive(:name, fn %{user: user} -&gt; "#{user.first} #{user.last}" end)
  # ... 500 more lines
end

# But THIS (declarative macros):
defmodule MyComponent do
  use Hologram.Component
  
  derived :full_name do
    "#{@first_name} #{@last_name}"
  end

  # Component logic continues normally...
end
</code></pre>
<p>This is <strong>implicit</strong> computation graph construction through compile-time automatic dependency resolution, not forcing users to explicitly build graphs. The rest happens at compile-time.</p>
<p><strong>This is not some novel dataflow framework</strong></p>
<p>I’m not trying to create a dataflow framework where everything is computed this way. I want something similar to Svelte’s <code>$derived</code> - a feature that Svelte has had much success with and which has been generally praised. You don’t put everything in it, but only things you want to memoize and/or where it makes sense to extract business logic from templates.</p>
<p><strong>On the complexity concern</strong></p>
<p>I hear your concern about real-world complexity and the “500 lines” scenario. The question is whether the complexity comes from the pattern itself or from trying to do everything in one place. I believe proper component boundaries would keep each component’s derived state manageable.</p>
<p><strong>On composability</strong></p>
<p>You raise an important point about composition and naming collisions. However, remember that Hologram has <strong>compile-time macros</strong> (unlike React) and a <strong>call graph</strong> (unlike LiveView). The composability problem could be solved through these capabilities.</p>
<p>For example, here are some directions that could be explored:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule UserDerived do
  def full_name(first_name, last_name) do
    "#{first_name} #{last_name}"
  end
end

defmodule MyComponent do
  use Hologram.Component
  
  # Most explicit - manually specify the call
  derived :full_name do
    UserDerived.full_name(nested.user.name, nested.user.surname)
  end
  
  # Or using function capture
  derived :full_name, &amp;UserDerived.full_name/2
  
  # Or import with automatic integration
  import_derived UserDerived
  
  # or with options, here import only the full_name/2
  import_derived UserDerived, only: [full_name: 2]
  
  # or map to nested state
  import_derived UserDerived, map_to: [:my, :nested, :user]
  
  # or with prefix (use @user_full_name in template)
  import_derived UserDerived, prefix: :user
end
</code></pre>
<p>Since Hologram has compile-time analysis and the call graph available, something even cleaner might be possible. React doesn’t have these tools at its disposal.</p>
<p><strong>React’s solution and its trade-offs</strong></p>
<p>React wanted to solve composability and key collision issues, so it went with explicit call ordering. But that created its own set of problems and constraints (the Rules of Hooks). Every solution has trade-offs.</p>
<p>The key difference: Hologram’s compile-time capabilities open up different solution spaces that weren’t available to the React team working within JavaScript’s runtime constraints.</p>
<p>I’m not dismissing React’s lessons - they’re invaluable. But I think there’s room to explore how compile-time metaprogramming and static analysis might address the same problems differently.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="376618" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/signals-computed-properties-and-other-reactivity-patterns-in-hologram/72968/28">Post #27</a>
	                </div>
	            </div>
              <div id="likers-container-376618" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="376618"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #27"></div>
  </section>
</div>
    <div class="postbit" id="376622" data-post-id="376622">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="garrison" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  garrison
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="bartblast" data-post="28" data-topic="72968">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/bartblast/48/17647_2.png" class="avatar"> bartblast:</div>
<blockquote>
<p>The scenario you described - piping through hundreds of lines of DSL in <code>init()</code> - is not what I’m advocating at all.</p>
</blockquote>
</aside>
<p>Perhaps this isn’t <em>literally</em> the API you are proposing, but they are equivalent. Whether you define them in <code>init()</code> or via a macro in the body of the module is not relevant (this is why I regret mentioning compilation).</p>
<p>What matters is that you are deviating from the component <em>as code</em> and turning it into a DSL for building up graphs, and my prediction is that <em>at scale</em> this is going to get ugly.</p>
<p>Let me try something different: why <em>is</em> there an <code>init()</code> function? What does it do? Do you see how it might relate to React’s class component initialization, which they went to <em>enormous</em> lengths to remove?</p>
<aside class="quote no-group" data-username="bartblast" data-post="28" data-topic="72968">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/bartblast/48/17647_2.png" class="avatar"> bartblast:</div>
<blockquote>
<p>Hologram has <strong>compile-time macros</strong> (unlike React) and a <strong>call graph</strong> (unlike LiveView). The composability problem could be solved through these capabilities.</p>
</blockquote>
</aside>
<p>It could, yes, and this is an opportunity to “do better” than React. This is actually a direction mentioned in the article I had linked, and I think it’s quite promising.</p>
<aside class="quote no-group" data-username="bartblast" data-post="28" data-topic="72968">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/bartblast/48/17647_2.png" class="avatar"> bartblast:</div>
<blockquote>
<p>But I think there’s room to explore how compile-time metaprogramming and static analysis might address the same problems differently.</p>
</blockquote>
</aside>
<p>You can do all sorts of cool things at compile time! Just be sure to <a href="https://svelte.dev/blog/runes#Runtime-reactivity" rel="noopener nofollow ugc">mind the graveyard</a>.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="376622" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/signals-computed-properties-and-other-reactivity-patterns-in-hologram/72968/29">Post #28</a>
	                </div>
	            </div>
              <div id="likers-container-376622" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="376622"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #28"></div>
  </section>
</div>
    <div class="postbit" id="376648" data-post-id="376648">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="Lucassifoni" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/Lucassifoni/120/39057_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  Lucassifoni
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Hi Bart, thanks for your thorough response. I guess the best way to describe my use case would be to standardize how I build up state for discrete parts of my backend, or before it reaches an UI framework. I see that I have started to use it like I use Pinia in the Vue world, or maybe like I would use Ash calculations if I was using Ash. This explains why I do not replace automatic dependency tracking up to sub-fields or list elements : if this backs an UI, there still is a reactivity layer afterwards which is the UI framework. My problem is just composable state DAGs.</p>
<p>Showing this example, I was mostly thinking about the compile-time graph definition via macros : I did not manage to build something satisfying there - as it was meant to be an optional alternative way of writing the graph declarations, I needed to duplicate the runtime checks at compile time (which was feasible) but did not find a satisfying way to allow inline function expressions, &amp;Mod.fun/arity references, and referencing local functions.</p>
<p>But this is a smaller footprint and only looks similar at a glance to the DSL options you were discussing. You must have greater expressive freedom with macros than I do, both from writing Hologram itself and having an explicit compiler !</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="376648" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/signals-computed-properties-and-other-reactivity-patterns-in-hologram/72968/30">Post #29</a>
	                </div>
	            </div>
              <div id="likers-container-376648" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="376648"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #29"></div>
  </section>
</div>
    <div class="postbit" id="376659" data-post-id="376659">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="venkatd" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/venkatd/120/9419_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  venkatd
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p><a class="mention" href="/u/bartblast" rel="nofollow">@bartblast</a></p>
<p>To avoid talking past each other, maybe you can clarify the following for me.</p>
<p>Is main purpose of computed properties to be performance optimization?</p>
<p>Meaning if things were rendering instantly, there would be no need for computed properties. We would prefer plain functions and re-render after each change?</p>
<p>It would be good to discuss the “what” (public API) and the “how” (implementation/optimizations) separately or discussions could get mixed up.</p>
<p>Maybe I can phrase the question this way: if performance was not an issue, what would your preferred API look like?</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="376659" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/signals-computed-properties-and-other-reactivity-patterns-in-hologram/72968/31">Post #30</a>
	                </div>
	            </div>
              <div id="likers-container-376659" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="376659"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #30"></div>
  </section>
</div>
</template></turbo-stream><turbo-stream action="replace" target="load-more-container"><template><div id="load-more-container" class="load-more-container">
    <a class="load-more-button" data-turbo-stream="true" href="/topics/72968/load_more?page=4">Load more posts (11 remaining)</a>
</div></template></turbo-stream>