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


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="tmbb" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  tmbb
                    <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>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<h3><a name="p-99219-after-the-silence-some-news-1" class="anchor" href="#p-99219-after-the-silence-some-news-1" aria-label="Heading link" rel="nofollow"></a>After the silence, some news!</h3>
<p>(<a class="mention" href="/u/josevalim" rel="nofollow">@josevalim</a>, there are almost the news you’ve been waiting for regarding expansion of macros)</p>
<p>I can finally expand Elixir macros inline inside the templates and optimize the resulting template by merging the static binaries together. I still can’t compile my new templates into executable Elixir code but it doesn’t pose any hard problems.</p>
<p>I’ve <em>yet again</em> changed the template format and the nomenclature. Templates are now composed of <em>segments</em>. A segment is either static text or a quoted expression represented by <code>&lt;%= ... %&gt;</code> (dynamic segment), <code>&lt;% ... %&gt;</code> (dynamic segment with no output) or <code>&lt;%/ ... &gt;</code> (fixed segment).</p>
<p>Segments are now represented as:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">{segment_tag, {contents, metadata}}
</code></pre>
<p>As you can see, templates and parts of templates are represented by a 2-tuple nested inside another 2-tuple. The slight change from the previous post is because it’s useful to be able to store metadata in the segments for error reporting. Why nested 2-tuples instead of a 3-tuple? It’s because 2-tuples are represented as themselves in Elixir’s AST, unlike 3-tuples which have a special meaning. For example, the form <code>{atom, metadata, arg} when not is_list(arg)</code> is not valid Elixir AST!  so it’s better to avoid 3-tuples and more complex expressions and working with nested 2-tuples. <a class="mention" href="/u/overminddl1" rel="nofollow">@OvermindDL1</a> has refereed to this as “escaping Elixir’s AST” and I really like the expression.</p>
<p>So, back to the point. Working with the nested 2-tuples is hard and the format is quite artificial, so I have combinators that make it easier to build them (and in the future maybe even pattern match on them). I’d love to use records (from the excellent <a href="https://hexdocs.pm/elixir/Record.html" rel="noopener nofollow ugc">Record</a> module, which BTW should be more well known) instead, but they havd the complication that they wouldn’t compile to nested 2-tuples…</p>
<p>So, what’s so great about using 2-tuples exactly? It’s the fact that <em>the intermediate representation of the compiled templates is a valid Elixir quoted expression!</em> It’s not executable Elixir code, of course (it requires a couple transformation steps to become executable Elixir code), but is something which I can feed into <a href="https://hexdocs.pm/elixir/Macro.html#prewalk/2" rel="noopener nofollow ugc">Macro.prewalk/2</a>, which makes it trivial to traverse the expression without having to manually implement a tree traversal that respects my templates’ semantics. My templates’ semantics are now the semantics of normal Elixir code.</p>
<h4><a name="p-99219-template-widgets-as-macros-2" class="anchor" href="#p-99219-template-widgets-as-macros-2" aria-label="Heading link" rel="nofollow"></a>Template widgets as macros</h4>
<p>Last post I’ve talked about the possibility to implement reusable widgets as macros which expand into undead templates. An undead template is a value of the form:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">{UndeadEngine.Segment.UndeadTemplate, {segments, meta}}
</code></pre>
<p>(Remember that <code>UndeadEngine.Segment.UndeadTemplate</code> is just an atom name like <code>:undead_template</code>. The advantagte of using a more verbose name like the one above is that it reduces the chance of accidental name collisions. This should be made more hygienic in the future anyway, but for now it’s good enough)</p>
<p>Any macro that expands into nested 2-tuples like the above can be optimized by flattening the segments and merging the static parts. As a (quite functional) proof of concept, I’ve implemented the <code>tag/2</code> macro (you can find the implementation <a href="https://github.com/tmbb/phoenix_undead_view/blob/master/lib/phoenix_undead_view/template/widgets/tag.ex" rel="noopener nofollow ugc">here</a>). It works like this:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">iex(2)&gt; tag(:input, [name: "user[name]", name: "user_name", value: ""])
{UndeadEngine.Segment.UndeadTemplate,
 {[
    {UndeadEngine.Segment.Static, {"&lt;", []}},
    {UndeadEngine.Segment.Static, {"input", []}},
    {UndeadEngine.Segment.Static, {" ", []}},
    {UndeadEngine.Segment.Static, {"name=\"user[name]\"", []}},
    {UndeadEngine.Segment.Static, {" ", []}},
    {UndeadEngine.Segment.Static, {"name=\"user_name\"", []}},
    {UndeadEngine.Segment.Static, {" ", []}},
    {UndeadEngine.Segment.Static, {"value=\"\"", []}},
    {UndeadEngine.Segment.Static, {"&gt;", []}}
  ], []}}
</code></pre>
<p>The argument list given to <code>tag/2</code> is static, so it will generate a series of <em>static</em> segments. On itself, this is not very impressive.</p>
<p>On the other hand, <em>this</em> is very impressive:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">iex(3)&gt; tag(:input, [name: "user[name]", name: "user_name", value: ""]) |&gt; Optimizer.optimize(__ENV__)
{UndeadEngine.Segment.UndeadTemplate,
 {[
    {UndeadEngine.Segment.Static,
     {"&lt;input name=\"user[name]\" name=\"user_name\" value=\"\"&gt;", []}}
  ], []}}
</code></pre>
<p>The optimizer has just recognized the template as purely static, and has just merged the static parts together. Our reusable widget has been compiled into the most efficient format possible. Now let’s make it harder. Usually, an HTML widget won’t be purely static. It will contain dynamic parts. For example:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">iex(4)&gt; Macro.expand(quote(do: tag(:input, [name: "user[name]", name: "user_name", value: value])), __ENV__) |&gt; Optimizer.optimize(__ENV__)
{UndeadEngine.Segment.UndeadTemplate,
 {[
    {UndeadEngine.Segment.Static,
     {"&lt;input name=\"user[name]\" name=\"user_name\" value=\"", []}},
    {UndeadEngine.Segment.Dynamic,
     {{{:., [], [PhoenixUndeadView.Template.HTML, :html_escape]}, [],
       [{:value, [], Elixir}]}, []}},
    {UndeadEngine.Segment.Static, {"\"&gt;", []}}
  ], []}}
</code></pre>
<p>The system has detected that parts of the template are static and other parts are dynamic, and the optimizer has compiled the template into three segments: a static segment, a dynamic segment and a static segment. Again we see that the widget has been as optimized as possible.</p>
<p>Now let’s try it with a real template:</p>
<pre data-code-wrap="plaintext"><code class="lang-plaintext">&lt;% a = 2 %&gt;
Blah blah blah

&lt;%= tag(:input, [name: "user[name]", id: "user_name", value: @user.name]) %&gt;

&lt;%= a %&gt;

Blah blah
</code></pre>
<p>It’s intuitively obvious that the template contains some dynamic parts and some static parts. It also contains the <code>tag/2</code> macro which has an output that’s mostly static. Only the <code>value</code> attribute is dynamic. When we compile it, we get the following raw output (converted into Elixir code - not a quoted expression! Remember, we can do it using <code>Macro.to_string()</code> because the templates are valid AST):</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">{UndeadEngine.Segment.UndeadTemplate,
 {[
    {UndeadEngine.Segment.DynamicNoOutput, {a = 2, [line: 1]}},
    {UndeadEngine.Segment.Static, {"\nBlah blah blah\n\n", []}},
    {UndeadEngine.Segment.Dynamic,
     {tag(:input,
        name: "user[name]",
        id: "user_name",
        value: __MODULE__.fetch_assign(var!(assigns), :user).name()
      ), [line: 4]}},
    {UndeadEngine.Segment.Static, {"\n\n", []}},
    {UndeadEngine.Segment.Dynamic, {a, [line: 6]}},
    {UndeadEngine.Segment.Static, {"\n\nBlah blah\n", []}}
  ], []}}
</code></pre>
<p>Although the expression is quite complex, if you look carefully you can recognize the static and dynamic parts in the template above. You can also see that the <code>tag/2</code> macro hasn’t been expanded yet. If we expand the macro and optimize it, we get the following:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">{UndeadEngine.Segment.UndeadTemplate,
 {[
    {UndeadEngine.Segment.DynamicNoOutput, {a = 2, [line: 1]}},
    {UndeadEngine.Segment.Static,
     {"\nBlah blah blah\n\n&lt;input name=\"user[name]\" id=\"user_name\" value=\"", []}},
    {UndeadEngine.Segment.Dynamic,
     {PhoenixUndeadView.Template.HTML.html_escape(Fixtures.fetch_assign(assigns, :user).name()),
      []}},
    {UndeadEngine.Segment.Static, {"\"&gt;\n\n", []}},
    {UndeadEngine.Segment.Dynamic, {a, [line: 6]}},
    {UndeadEngine.Segment.Static, {"\n\nBlah blah\n", []}}
  ], []}}
</code></pre>
<p>As you can see, the static parts of the <code>tag</code> at the beginning and at the end have been merged into the static parts before and after the tag, so minimize the number of segments.</p>
<p>As long as the macros expand into the appropriate format, thee optimizations are always possible.</p>
<h4><a name="p-99219-whats-missing-3" class="anchor" href="#p-99219-whats-missing-3" aria-label="Heading link" rel="nofollow"></a>What’s missing</h4>
<p>I’ve taken a big detour with the goal of being able to expand macros inside the templates and optimize their result. I now have a very general framework to create optimized widgets, and it’s easy for other users to create their own libraries of optimized widgets using some basic combinators.</p>
<p>What is now missing is a way of compiling these templates into Elixir code that actually generates the iolist with the rendered template. The implementation doesn’t pose any particular challenges, and I’ll get to it soon. As you probably remember from prior posts, older versions of this project already had the capability to compile into quite efficient iolists, it’s just that the internal format of the templates has changed so much that I’ve had to scrap the old compiler and must reimplement a new one.</p>
<p>The basic idea is simple: you just need to compile the segments tagged as <code>UndeadEngine.Segment.UndeaedTemplate</code> into blocks that run their expressions in order, assign them to variables and return them as a list. It will be even simpler than what I was doing in previous versions because before I was actually compiling the templates into Elixir AST and then <em>parsing that AST again</em>, just to finally compile it into AST… I can be much more efficient now.</p>
<h4><a name="p-99219-source-code-4" class="anchor" href="#p-99219-source-code-4" aria-label="Heading link" rel="nofollow"></a>Source code</h4>
<p>The code is, as always, on Github: <a href="https://github.com/tmbb/phoenix_undead_view" class="inline-onebox" rel="noopener nofollow ugc">GitHub - tmbb/phoenix_undead_view: EEx engines that compile Phoenix templates into static and dynamic parts for better diffing over the network · GitHub</a></p>
<p>It’s still a little rough and some parts need to be refactored. The meat of the project is in the <a href="https://github.com/tmbb/phoenix_undead_view/blob/master/lib/phoenix_undead_view/template/widgets/tag.ex" rel="noopener nofollow ugc">Tag module</a>, which implements the “self-optimizing” <code>tag/2</code> macro and the <a href="https://github.com/tmbb/phoenix_undead_view/blob/master/lib/phoenix_undead_view/template/optimizer.ex" rel="noopener nofollow ugc">Optimizer module</a>, which expands the macros in the template and optimizes it as much as possible by flattening it and merging the static segments together.</p>
<p>EDIT: Even if it turns out this architecture is not a good fit for something like LiveView, I have shown that with the thelp of macros such as <code>tag/2</code> and with the optimization steps I can produce much better code than the default Phoenix templates and their functions-based widgets which must rerender everything (even some parts tat are actually static) each time the template is called. As I’ve said before, I believe the Phoenix engine could take some ideas from this implementation.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="99219" data-batch-url="/posts/batch_likers">
                        4
                      </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/phoenixundeadview-lets-discuss-optimization-possibilities-for-something-like-phoenix-liveview/16533/42">Post #41</a>
	                </div>
	            </div>
              <div id="likers-container-99219" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="99219"
                     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 #41"></div>
  </section>
</div>
    <div class="postbit" id="99338" data-post-id="99338">
  <section>
    <div class="post-wrap">


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="tmbb" data-post="42" data-topic="16533">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/t/77aa72/48.png" class="avatar"> tmbb:</div>
<blockquote>
<p>{atom, metadata, arg} when not is_list(arg)</p>
</blockquote>
</aside>
<p>Sure you can, a binding like <code>{:blah, [], Elixir}</code> matches that pattern.  <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>
<p>Looking good though!</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="99338" 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/phoenixundeadview-lets-discuss-optimization-possibilities-for-something-like-phoenix-liveview/16533/43">Post #42</a>
	                </div>
	            </div>
              <div id="likers-container-99338" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="99338"
                     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 #42"></div>
  </section>
</div>
    <div class="postbit" id="99377" data-post-id="99377">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="tmbb" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  tmbb
                    <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>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="OvermindDL1" data-post="43" data-topic="16533">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/overminddl1/48/2677_2.png" class="avatar"> OvermindDL1:</div>
<blockquote>
<p>Sure you can, a binding like <code>{:blah, [], Elixir}</code> matches that pattern. <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>
</blockquote>
</aside>
<p>Ok, <code>arg</code> can be an atom too, but it can’t be a general Elixir value, which I need for this to 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="99377" data-batch-url="/posts/batch_likers">
                        0
                      </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/phoenixundeadview-lets-discuss-optimization-possibilities-for-something-like-phoenix-liveview/16533/44">Post #43</a>
	                </div>
	            </div>
              <div id="likers-container-99377" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="99377"
                     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 #43"></div>
  </section>
</div>
    <div class="postbit" id="99455" data-post-id="99455">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="tmbb" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  tmbb
                    <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>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<h3><a name="p-99455-compiling-templates-into-quoted-expressions-1" class="anchor" href="#p-99455-compiling-templates-into-quoted-expressions-1" aria-label="Heading link" rel="nofollow"></a>Compiling templates into quoted expressions</h3>
<p>(again, all examples are pretty simple, but this should work on arbitrarily complex templates)</p>
<p>As always, you can lookup the example (yes, singular, it’s getting late here!) on the github repo, but for those who are short on time, I reproduce them here. I’ve converted the quoted expressions into Elixir code because they are easier to read that way. When we write the quoted expressions into text, variable hygiene is lost, but rest assured that name collisions won’t happen.</p>
<p>Example template:</p>
<pre data-code-wrap="plaintext"><code class="lang-plaintext">&lt;% a = 2 %&gt;
Blah blah blah

&lt;%= tag(:input, [name: "user[name]", id: "user_name", value: @user.name]) %&gt;

&lt;%= a %&gt;

Blah blah
</code></pre>
<p>Full template:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">a = 2

tmp_3 =
  case(PhoenixUndeadView.Template.HTML.html_escape(fetch_assign(assigns, :user).name())) do
    {:safe, data} -&gt;
      data

    bin when is_binary(bin) -&gt;
      Plug.HTML.html_escape_to_iodata(bin)

    other -&gt;
      Phoenix.HTML.Safe.to_iodata(other)
  end

tmp_5 =
  case(a) do
    {:safe, data} -&gt;
      data

    bin when is_binary(bin) -&gt;
      Plug.HTML.html_escape_to_iodata(bin)

    other -&gt;
      Phoenix.HTML.Safe.to_iodata(other)
  end

{:safe,
 [
   "\nBlah blah blah\n\n&lt;input name=\"user[name]\" id=\"user_name\" value=\"",
   tmp_3,
   "\"&gt;\n\n",
   tmp_5,
   "\n\nBlah blah\n"
 ]}
</code></pre>
<p>Static part:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">{:safe,
 [
   "\nBlah blah blah\n\n&lt;input name=\"user[name]\" id=\"user_name\" value=\"",
   "\"&gt;\n\n",
   "\n\nBlah blah\n"
 ]}
</code></pre>
<p>Dynamic part:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">a = 2

tmp_3 =
  case(PhoenixUndeadView.Template.HTML.html_escape(fetch_assign(assigns, :user).name())) do
    {:safe, data} -&gt;
      data

    bin when is_binary(bin) -&gt;
      Plug.HTML.html_escape_to_iodata(bin)

    other -&gt;
      Phoenix.HTML.Safe.to_iodata(other)
  end

tmp_5 =
  case(a) do
    {:safe, data} -&gt;
      data

    bin when is_binary(bin) -&gt;
      Plug.HTML.html_escape_to_iodata(bin)

    other -&gt;
      Phoenix.HTML.Safe.to_iodata(other)
  end

{:safe, [tmp_3, tmp_5]}
</code></pre>
<h3><a name="p-99455-plans-for-the-future-2" class="anchor" href="#p-99455-plans-for-the-future-2" aria-label="Heading link" rel="nofollow"></a>Plans for the future</h3>
<h4><a name="p-99455-documentation-3" class="anchor" href="#p-99455-documentation-3" aria-label="Heading link" rel="nofollow"></a>Documentation</h4>
<p>I need to write a design document to explain the basic ideas behind the undead compiler and to discuss the tradeoffs of certain implementation choices</p>
<h4><a name="p-99455-features-4" class="anchor" href="#p-99455-features-4" aria-label="Heading link" rel="nofollow"></a>Features</h4>
<p>The engine should now have feature parity with the default Phoenix engine. I don’t know if I’m going to add more features than what it already has.</p>
<h4><a name="p-99455-widgets-5" class="anchor" href="#p-99455-widgets-5" aria-label="Heading link" rel="nofollow"></a>Widgets</h4>
<p>For this to be useful, I need to write a library of reusable macro widgets. The <code>tag/2</code> macro is a good combinator, on top of which I can probably implement almost everything in the <code>phoenix_html</code> package.</p>
<h4><a name="p-99455-performance-6" class="anchor" href="#p-99455-performance-6" aria-label="Heading link" rel="nofollow"></a>Performance</h4>
<p>This engine should be even more efficient than the default phoenix engine as long as reusable widgets are implemented as macros instead of functions (like my <code>tag/2</code> macro above).</p>
<h4><a name="p-99455-transport-protocol-7" class="anchor" href="#p-99455-transport-protocol-7" aria-label="Heading link" rel="nofollow"></a>Transport protocol</h4>
<p>I have to implement a transport protocol so that the dynamic parts can be set to the browser (and write the necessary Elixir and Javascript code for it to work, of course). I think I’ll need help on this one.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="99455" 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/phoenixundeadview-lets-discuss-optimization-possibilities-for-something-like-phoenix-liveview/16533/45">Post #44</a>
	                </div>
	            </div>
              <div id="likers-container-99455" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="99455"
                     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 #44"></div>
  </section>
</div>
    <div class="postbit" id="99478" data-post-id="99478">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="tmbb" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  tmbb
                    <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>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>PS: I need help with the frontend parts not because I can’t do it (I can, it’s not that hard), bur because I’m not sure I can’t make it as efficient as possible andI’m not sure of what the best API would be.</p>
<p>The basic idea is to render something like:</p>
<pre><code class="lang-plaintext">&lt;span data-undead-id="undead-id" data-undead-channel&gt;
  ... (this is the initial html)
&lt;/span&gt;

&lt;script type="application/json" undead-widget-id="undead-id"&gt;
  [
     "Static#1",
     "Static#2",
     ... // literal JSON
  ]
&lt;/script&gt;
</code></pre>
<p>Then, the javascript at the end of the page can join the appropriate channels. Upon receiving a message from the server, the javascript will decode the message into a list of strings and intersperse them into the static parts above. This can be done for many widgets, not only a single widget.</p>
<p>Then it can use morphdom or a similar library to merge the new DOM into the old one.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="99478" data-batch-url="/posts/batch_likers">
                        0
                      </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/phoenixundeadview-lets-discuss-optimization-possibilities-for-something-like-phoenix-liveview/16533/46">Post #45</a>
	                </div>
	            </div>
              <div id="likers-container-99478" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="99478"
                     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 #45"></div>
  </section>
</div>
    <div class="postbit" id="99527" data-post-id="99527">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="tmbb" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  tmbb
                    <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>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<h3><a name="p-99527-where-to-go-from-now-1" class="anchor" href="#p-99527-where-to-go-from-now-1" aria-label="Heading link" rel="nofollow"></a>Where to go from now</h3>
<p>There are two main avenues to explore now:</p>
<ol>
<li>
<p>Incorporate the template optimizations into “normal” Phoenix templates and develop a library of macro widgets so that more optimizations are possible</p>
</li>
<li>
<p>Push forward into implementing my “full” copy of PhoenixLiveView, only with the unholy power of human sacrifice and the blood of virgins</p>
</li>
</ol>
<h4><a name="p-99527-h-1-incorporating-the-improvements-into-normal-static-phoenix-templates-2" class="anchor" href="#p-99527-h-1-incorporating-the-improvements-into-normal-static-phoenix-templates-2" aria-label="Heading link" rel="nofollow"></a>1. Incorporating the improvements into “normal” (static) Phoenix templates</h4>
<p>These improvements only make sense if I reimplement most widgets in Phoenix.HTML as macros. Otherwise, it’s probably not worth it… While I generate “cleaner” code (from the perspective of a human reader) than the default Phoenix templates, it’s not any faster, and the code complexity is much higher. The EEx engine used by Phoenix is dead-simple. My approach requires a custom EEx engine (actually simpler than the Phoenix one) and running a whole compiler on the output of said engine.</p>
<p>So if I decide to go this way, I will start by reimplementing the widgets in Phoenix.HTML as undead macros (I’ve just made up this term right now to mean "macros that compiler to an optimizable undead container). I think I can make it work in a backwards-compatible way (for some rather generous meaning of “backwar-compatible”).</p>
<p>The main problem with converting functions to macros is that macros don’t play well with higher order functions, unlike functions themselves, which are more composable. The only advantage of macros is that they can inspect the AST of their arguments and optimize their result at compile-time.</p>
<p>For normal static templates it’s not even clear that using my macros instead of functions makes that much of a difference. Theoretically it should, though, as it reduces the overhead of function calls and avoids calling functions for things that are actually static. The only way to be sure is to implement it and measuring performance.</p>
<h4><a name="p-99527-h-2-implement-an-end-to-end-system-ready-to-drop-into-a-phoenix-app-3" class="anchor" href="#p-99527-h-2-implement-an-end-to-end-system-ready-to-drop-into-a-phoenix-app-3" aria-label="Heading link" rel="nofollow"></a>2. Implement an end-to-end system ready to drop into a Phoenix app</h4>
<p>If I decide to go this way, the best place to steal ideas from is probably Drab. Pinging <a class="mention" href="/u/grych" rel="nofollow">@grych</a> in case he wants to contribute with his experience.</p>
<p>Since I’m stealing from Drab, I’ll have something like <code>Commander</code>s (should I rename them to <code>Necromancer</code> or something to keep with the Undead theme?), which communicate bidirectionally with the browser. A <code>Commander</code> can only do two things:</p>
<ol>
<li>Receive events from the browser.</li>
<li>Send deltas into the browser. A delta is a list of the strings that correspond to the parts that have changed. A client-side library like morphdom will make sense of the changes.</li>
</ol>
<p>A <code>Commander</code> <em>does not</em> send arbitrary Javascript to the browser to be executed. This is just to keep the scope limited, it’s probably easy to make the server send Javascript to the browser.</p>
<p>In this example I’ll work with a commander named <code>MyUndeadCommander</code>. I’d like to render the initial HTML (and accompanying JSON) as:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">&lt;%= MyUndeadCommander.render() %&gt;
</code></pre>
<p>It would generate the following parameters as random UUIDs:</p>
<ol>
<li>channel topic (for communication)</li>
<li>widget id</li>
</ol>
<p>The javascript at the end of the page would then join the appropriate channels. I’m still not sure how I want to match channels to commanders, but I’ll probably just copy what Drab does.</p>
<p>I’ll look at option number 1 first, because it seems simpler (and the way forward is clear). My second option requires lots of complex design decisions, and requires establishing conventions to communicate between the Javascript on the page and the Elixir code on the server.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="99527" 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/phoenixundeadview-lets-discuss-optimization-possibilities-for-something-like-phoenix-liveview/16533/47">Post #46</a>
	                </div>
	            </div>
              <div id="likers-container-99527" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="99527"
                     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 #46"></div>
  </section>
</div>
    <div class="postbit" id="99545" data-post-id="99545">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="tmbb" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  tmbb
                    <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>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I’ve found a way of compiling <code>form(form_data, action, options, fun)</code> into something sensible that optimizes well. I assume the <code>fun</code> is of the form <code>fn arg -&gt; body end</code>. Because my <code>form/4</code> is a macro, it receives a quoted expression from which I can extract both <code>arg</code> and <code>body</code>. The expression <code>arg</code> should be a variable (i.e. <code>{name, meta, context} when is_atom(name) and is_list(meta) and is_atom(context)</code>). Otherwise I raise an error (I could fallback to the normal <code>form_for/4</code> instead, but an error seems better).</p>
<p>Then, I do something like:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">{name, meta, _context} = arg
# create a safe hygienic variable
new_arg = {name, meta, __MODULE__}
# replace the old variable by the new hygienic variable
new_body = substitute(body, arg, old_arg)
# Do something like this, only more complex because I'm working with undead segments
# instead of literal Elixir AST:
quote do
  unquote(new_arg) = FormData.to_form(new_arg)
  unquote(new_body)
end
</code></pre>
<p>The new variable will leak outside the quoted expression, of course, but it’s now in a different context, so it shouldn’t be accessible to the rest of the template. The only way for a variable of the same name to be created if it is created by the <code>PhoenixUndeadView.Template.Widgets.Form</code>module, which doesn’t create any other variables.</p>
<p>Maybe it could become safer by adding a <code>:counter</code> to the metadata? I could then make sure the counter would be unique to the function that generates the AST above. That way, even if the <code>PhoenixUndeadView.Template.Widgets.Form</code> generates variables somewhere else, there will be no problems.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="99545" 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/phoenixundeadview-lets-discuss-optimization-possibilities-for-something-like-phoenix-liveview/16533/48">Post #47</a>
	                </div>
	            </div>
              <div id="likers-container-99545" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="99545"
                     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 #47"></div>
  </section>
</div>
    <div class="postbit" id="99559" data-post-id="99559">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="tmbb" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  tmbb
                    <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>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<h3><a name="p-99559-showtime-compiling-forms-1" class="anchor" href="#p-99559-showtime-compiling-forms-1" aria-label="Heading link" rel="nofollow"></a>Showtime: compiling forms</h3>
<p>The template:</p>
<pre><code class="lang-plaintext">&lt;% f = 2 %&gt;
Blah blah blah

&lt;%= form @changeset, action, [], fn f -&gt; %&gt;
  &lt;%= text_input(f, :name) %&gt;
  &lt;%= text_input(f, :surname) %&gt;
  &lt;%= number_input(f, :age) %&gt;
&lt;% end %&gt;

&lt;%= f %&gt;

Blah blah
</code></pre>
<p>The full template:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">f = 2

tmp_3_dynamic =
  case(PhoenixUndeadView.Template.HTML.html_escape(action)) do
    {:safe, data} -&gt;
      data

    bin when is_binary(bin) -&gt;
      Plug.HTML.html_escape_to_iodata(bin)

    other -&gt;
      Phoenix.HTML.Safe.to_iodata(other)
  end

tmp_5_fixed =
  case(Plug.CSRFProtection.get_csrf_token_for(action)) do
    {:safe, data} -&gt;
      data

    bin when is_binary(bin) -&gt;
      Plug.HTML.html_escape_to_iodata(bin)

    other -&gt;
      Phoenix.HTML.Safe.to_iodata(other)
  end

f = Phoenix.HTML.FormData.to_form(fetch_assign(assigns, :changeset), [])

tmp_8_fixed =
  case(PhoenixUndeadView.Template.Widgets.Form.FormInputs.input_name_to_string(f)) do
    {:safe, data} -&gt;
      data

    bin when is_binary(bin) -&gt;
      Plug.HTML.html_escape_to_iodata(bin)

    other -&gt;
      Phoenix.HTML.Safe.to_iodata(other)
  end

tmp_10_fixed =
  case(PhoenixUndeadView.Template.Widgets.Form.FormInputs.input_name_to_string(f)) do
    {:safe, data} -&gt;
      data

    bin when is_binary(bin) -&gt;
      Plug.HTML.html_escape_to_iodata(bin)

    other -&gt;
      Phoenix.HTML.Safe.to_iodata(other)
  end

tmp_12_fixed =
  case(PhoenixUndeadView.Template.Widgets.Form.FormInputs.input_name_to_string(f)) do
    {:safe, data} -&gt;
      data

    bin when is_binary(bin) -&gt;
      Plug.HTML.html_escape_to_iodata(bin)

    other -&gt;
      Phoenix.HTML.Safe.to_iodata(other)
  end

tmp_14_fixed =
  case(PhoenixUndeadView.Template.Widgets.Form.FormInputs.input_name_to_string(f)) do
    {:safe, data} -&gt;
      data

    bin when is_binary(bin) -&gt;
      Plug.HTML.html_escape_to_iodata(bin)

    other -&gt;
      Phoenix.HTML.Safe.to_iodata(other)
  end

tmp_16_fixed =
  case(PhoenixUndeadView.Template.Widgets.Form.FormInputs.input_name_to_string(f)) do
    {:safe, data} -&gt;
      data

    bin when is_binary(bin) -&gt;
      Plug.HTML.html_escape_to_iodata(bin)

    other -&gt;
      Phoenix.HTML.Safe.to_iodata(other)
  end

tmp_18_fixed =
  case(PhoenixUndeadView.Template.Widgets.Form.FormInputs.input_name_to_string(f)) do
    {:safe, data} -&gt;
      data

    bin when is_binary(bin) -&gt;
      Plug.HTML.html_escape_to_iodata(bin)

    other -&gt;
      Phoenix.HTML.Safe.to_iodata(other)
  end

tmp_20_dynamic =
  case(f) do
    {:safe, data} -&gt;
      data

    bin when is_binary(bin) -&gt;
      Plug.HTML.html_escape_to_iodata(bin)

    other -&gt;
      Phoenix.HTML.Safe.to_iodata(other)
  end

{:safe,
 [
   "\nBlah blah blah\n\n&lt;form action=\"",
   tmp_3_dynamic,
   "\" accept_charset=\"UTF-8\"&gt;&lt;input name=\"_csrf_token\" type=\"hidden\" value=\"",
   tmp_5_fixed,
   "\"&gt;&lt;input name=\"_utf8\" hidden=\"hidden\" value=\"✓\"&gt;\n  &lt;input name=\"",
   tmp_8_fixed,
   "[name]\" id=\"",
   tmp_10_fixed,
   "_name\" type=\"text\"&gt;\n  &lt;input name=\"",
   tmp_12_fixed,
   "[surname]\" id=\"",
   tmp_14_fixed,
   "_surname\" type=\"text\"&gt;\n  &lt;input name=\"",
   tmp_16_fixed,
   "[age]\" id=\"",
   tmp_18_fixed,
   "_age\" type=\"number\"&gt;\n&lt;/form&gt;\n\n",
   tmp_20_dynamic,
   "\n\nBlah blah\n"
 ]}
</code></pre>
<p>The code above is long, but the most important part is the result:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">{:safe,
 [
   "\nBlah blah blah\n\n&lt;form action=\"",
   tmp_3_dynamic,
   "\" accept_charset=\"UTF-8\"&gt;&lt;input name=\"_csrf_token\" type=\"hidden\" value=\"",
   tmp_5_fixed,
   "\"&gt;&lt;input name=\"_utf8\" hidden=\"hidden\" value=\"✓\"&gt;\n  &lt;input name=\"",
   tmp_8_fixed,
   "[name]\" id=\"",
   tmp_10_fixed,
   "_name\" type=\"text\"&gt;\n  &lt;input name=\"",
   tmp_12_fixed,
   "[surname]\" id=\"",
   tmp_14_fixed,
   "_surname\" type=\"text\"&gt;\n  &lt;input name=\"",
   tmp_16_fixed,
   "[age]\" id=\"",
   tmp_18_fixed,
   "_age\" type=\"number\"&gt;\n&lt;/form&gt;\n\n",
   tmp_20_dynamic,
   "\n\nBlah blah\n"
 ]}
</code></pre>
<p>Most of the variables that appear in the final list are actually fixed and not dynamic (fixed variables end in <code>_fixed</code> and dynamic variables end in <code>_dynamic</code>), that is, they only need to be re-rendered when the data changes. This is a dangerous optimization, because it assumes the for name doesn’t change. This is true for sane templates, but not for “insane” ones. The problem is that there’s a tradeoff between how much you tag as dynamic and as much as you can optimize.</p>
<p>The line <code>f = Phoenix.HTML.FormData.to_form(fetch_assign(assigns, :changeset), [])</code> seems problematic, but it’s not as unsafe as it looks. The corresponding quoted expression is:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">{:=, [],
    [
      {:f, [line: 4, counter: -576_460_752_303_422_203], PhoenixUndeadView.Template.Widgets.Form},
      {{:., [], [Phoenix.HTML.FormData, :to_form]}, [],
       [{:fetch_assign, [line: 4], [{:assigns, [line: 4, var: true], nil}, :changeset]}, []]}
    ]},
</code></pre>
<p>which means the variable is not accessible by the code written by the user (the user-defined variable <code>f</code> is <code>{:f, [line: 5], nil}</code>).</p>
<p>NOTES: The numbers in the variables are unique but not sequential because making them sequential would complicate the implementation too much and those variables are not user-facing anyway.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="99559" 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/phoenixundeadview-lets-discuss-optimization-possibilities-for-something-like-phoenix-liveview/16533/49">Post #48</a>
	                </div>
	            </div>
              <div id="likers-container-99559" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="99559"
                     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 #48"></div>
  </section>
</div>
    <div class="postbit" id="99690" data-post-id="99690">
  <section>
    <div class="post-wrap">


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="tmbb" data-post="45" data-topic="16533">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/t/77aa72/48.png" class="avatar"> tmbb:</div>
<blockquote>
<p>For this to be useful, I need to write a library of reusable macro widgets. The <code>tag/2</code> macro is a good combinator, on top of which I can probably implement almost everything in the <code>phoenix_html</code> package.</p>
</blockquote>
</aside>
<p>Considering you can have it only support HTML and not something else like EMail or so (javascript DOM updating, etc…), then you can easily make a lot of assumptions there too.  <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="tmbb" data-post="46" data-topic="16533">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/t/77aa72/48.png" class="avatar"> tmbb:</div>
<blockquote>
<p>PS: I need help with the frontend parts not because I can’t do it (I can, it’s not that hard), bur because I’m not sure I can’t make it as efficient as possible andI’m not sure of what the best API would be.</p>
</blockquote>
</aside>
<p>I’ve learned a lot the past few years, not certain if what I know is always the best but I did a lot of benchmarking when making bucklescript-tea too so I think my methods are at least the common and efficient ones if whatever is needed to help?</p>
<aside class="quote no-group" data-username="tmbb" data-post="47" data-topic="16533">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/t/77aa72/48.png" class="avatar"> tmbb:</div>
<blockquote>
<p>Push forward into implementing my “full” copy of PhoenixLiveView, only with the unholy power of human sacrifice and the blood of virgins</p>
</blockquote>
</aside>
<p>+1 Lol</p>
<p>Although merging it into Phoenix as it’s own EEx replacement engine for specific html work could be quite interesting…</p>
<aside class="quote no-group" data-username="tmbb" data-post="47" data-topic="16533">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/t/77aa72/48.png" class="avatar"> tmbb:</div>
<blockquote>
<p>(should I rename them to <code>Necromancer</code> or something to keep with the Undead theme?)</p>
</blockquote>
</aside>
<p>That conceptually works rather well actually…  ^.^</p>
<aside class="quote no-group" data-username="tmbb" data-post="47" data-topic="16533">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/t/77aa72/48.png" class="avatar"> tmbb:</div>
<blockquote>
<p>A <code>Commander</code> <em>does not</em> send arbitrary Javascript to the browser to be executed. This is just to keep the scope limited, it’s probably easy to make the server send Javascript to the browser.</p>
</blockquote>
</aside>
<p>You will <em>need</em> to handle some javascript at the very least from the server, else interpolation in all ways becomes extremely difficult to handle well and the interface loses performance to the end-user.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="99690" 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/phoenixundeadview-lets-discuss-optimization-possibilities-for-something-like-phoenix-liveview/16533/50">Post #49</a>
	                </div>
	            </div>
              <div id="likers-container-99690" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="99690"
                     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 #49"></div>
  </section>
</div>
    <div class="postbit" id="99692" data-post-id="99692">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="tmbb" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  tmbb
                    <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>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="OvermindDL1" data-post="50" data-topic="16533">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/overminddl1/48/2677_2.png" class="avatar"> OvermindDL1:</div>
<blockquote>
<p>Considering you can have it only support HTML and not something else like EMail or so</p>
</blockquote>
</aside>
<p>I can generate any binary. This engine doesn’t care about HTML, and I don’t intend to make it HTML specific.</p>
<p>BTW, if you look at the repo (and the example above) you can see how I’ve managed to implement <code>form_for/4</code> in a sane way.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="99692" 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/phoenixundeadview-lets-discuss-optimization-possibilities-for-something-like-phoenix-liveview/16533/51">Post #50</a>
	                </div>
	            </div>
              <div id="likers-container-99692" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="99692"
                     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 #50"></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/16533/load_more?page=6">Load more posts (91 remaining)</a>
</div></template></turbo-stream>