<turbo-stream action="append" target="posts_list"><template>    <div class="postbit" id="96876" data-post-id="96876">
  <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
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote group-livebook_core_team" data-username="josevalim" data-post="52" data-topic="16445">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/josevalim/48/1787_2.png" class="avatar"> josevalim:</div>
<blockquote>
<p>Oh, totally. I meant to say that we can’t do smart diffs with .eex (or a subset thereof) unless we introduce parsing + some restrictions which I assume would put us somewhere close to .tex.</p>
</blockquote>
</aside>
<p>You can’t do smart HTML diffs, of course, but you can do some smart textual diffs given two restrictions:</p>
<ol>
<li>
<p>Change the EEx engine semantics so that each <code>&lt;%= ... %&gt;</code> block runs in its own scope. This means you can’t have <code>&lt;% a = 1 %&gt;&lt;%= a %&gt;</code>.</p>
</li>
<li>
<p>Add a new block type, say, <code>&lt;%| ... %&gt;</code>, which runs its contents at compile time and inserts the result</p>
</li>
<li>
<p>Write a bunch of “compile-time” combinators (for example, a compile-time <code>form_for()</code>, which returns another template with these restrictions. These combinators <em>could</em> be macros, but it’s better to make them functions that return quoted expressions (like in <code>nimble_parsec</code>).</p>
</li>
</ol>
<p>This way, you can tease out the static and dynamic parts of a template at compile time, as long as the level of nesting is bounded and you’ve written suitable widgets. That allows you to separate the dynamic parts of the template at compile-time.</p>
<p>I’m working on a prototype, and it’s pretty cool.</p>
<p>If you then use morphdom on the client, you can just reconstruct the string on the client, and diff it into the existing DOM.</p>
<p>EDIT: This can even be used to optimize the “normal” string-based templates (by compiling into static binaries as much of the template as possible).</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="96876" 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/54-elixirconf-us-2018-closing-keynote-chris-mccord/16445/55">Post #54</a>
	                </div>
	            </div>
              <div id="likers-container-96876" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="96876"
                     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 #54"></div>
  </section>
</div>
    <div class="postbit" id="96952" data-post-id="96952">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="josevalim" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/josevalim/120/1787_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  josevalim
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Creator of Elixir</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="tmbb" data-post="55" data-topic="16445">
<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>
<ul>
<li>Change the EEx engine semantics so that each <code>&lt;%= ... %&gt;</code> block runs in its own scope. This means you can’t have <code>&lt;% a = 1 %&gt;&lt;%= a %&gt;</code> .</li>
</ul>
</blockquote>
</aside>
<p>Then it is no longer EEx. <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="55" data-topic="16445">
<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>Add a new block type, say, <code>&lt;%| ... %&gt;</code> , which runs its contents at compile time and inserts the result</p>
</blockquote>
</aside>
<p>I don’t think we need this? If we make <code>form_for</code> a macro AND we make the “smart diff” mechanism work on any safe IO data, then we don’t need precompilation, as long as <code>form_for</code> emits data in the safe io shape.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="96952" 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/54-elixirconf-us-2018-closing-keynote-chris-mccord/16445/56">Post #55</a>
	                </div>
	            </div>
              <div id="likers-container-96952" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="96952"
                     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 #55"></div>
  </section>
</div>
    <div class="postbit" id="96964" data-post-id="96964">
  <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
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote group-livebook_core_team" data-username="josevalim" data-post="56" data-topic="16445">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/josevalim/48/1787_2.png" class="avatar"> josevalim:</div>
<blockquote>
<p>Then it is no longer EEx. <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>If you’re being that strict, then yeah. I still think that one could use smart diff on “normal” eex templates by transforming the resulting quoted expression into “erlang-like” elixir (with no variable rebindings and stuff like that). But that requires reimplementing most of the Elixir compiler, which although fun doesn’t seem easy.</p>
<aside class="quote group-livebook_core_team" data-username="josevalim" data-post="56" data-topic="16445">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/josevalim/48/1787_2.png" class="avatar"> josevalim:</div>
<blockquote>
<p>I don’t think we need this? If we make <code>form_for</code> a macro AND we make the “smart diff” mechanism work on any safe IO data, then we don’t need precompilation, as long as <code>form_for</code> emits data in the safe io shape.</p>
</blockquote>
</aside>
<p>Yeah, you’re right… I don’t know if <code>Plug.HTML.Engine</code> can make use of this oprimization right now, but if it can’t it’d be a cool pull request. It would make “normal” templates more eficient.</p>
<p>Besides <code>form_for</code>, most of the widgets in <code>Phoenix.HTML</code> could be optimized by reimplementing them as macros. I should benchmark it.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="96964" 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/54-elixirconf-us-2018-closing-keynote-chris-mccord/16445/57">Post #56</a>
	                </div>
	            </div>
              <div id="likers-container-96964" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="96964"
                     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 #56"></div>
  </section>
</div>
    <div class="postbit" id="96987" data-post-id="96987">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="josevalim" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/josevalim/120/1787_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  josevalim
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Creator of Elixir</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="tmbb" data-post="57" data-topic="16445">
<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>If you’re being that strict, then yeah.</p>
</blockquote>
</aside>
<p>Yeha, I think the goal is not break the model we have today, and that would definitely break it.</p>
<aside class="quote no-group" data-username="tmbb" data-post="57" data-topic="16445">
<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>I don’t know if <code>Plug.HTML.Engine</code> can make use of this oprimization right now, but if it can’t it’d be a cool pull request. It would make “normal” templates more eficient.</p>
</blockquote>
</aside>
<p>Exactly. There are a lot of changes here we can just push to the default template in general that will make it more efficient too and better for the diffing.</p>
<p>Regarding <code>form_for</code>, one approach that Chris and I thought was to render it like this:</p>
<pre><code class="lang-plaintext">&lt;%= f = form_for @changeset, Routes.some_path(...) %&gt;
  &lt;label&gt;...&lt;/label&gt;
  &lt;%= input_for f, :name %&gt;
  &lt;%= error_tag f, :name %&gt;
&lt;/form&gt;
</code></pre>
<p>In this approach we remove the nesting and it will work as long as we implement <code>Phoenix.HTML.Safe</code> for <code>Phoenix.HTML.FormData</code>. But it goes against your first rule. <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> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="96987" 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/54-elixirconf-us-2018-closing-keynote-chris-mccord/16445/58">Post #57</a>
	                </div>
	            </div>
              <div id="likers-container-96987" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="96987"
                     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 #57"></div>
  </section>
</div>
    <div class="postbit" id="97011" data-post-id="97011">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="grych" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/grych/120/5610_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  grych
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Creator of Drab</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote group-livebook_core_team" data-username="josevalim" data-post="52" data-topic="16445">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/josevalim/48/1787_2.png" class="avatar"> josevalim:</div>
<blockquote>
<p>Oh, totally. I meant to say that we can’t do smart diffs with .eex (or a subset thereof) unless we introduce parsing + some restrictions which I assume would put us somewhere close to .tex.</p>
</blockquote>
</aside>
<p>I don’t know if it is helpful, but this is how Drab does it. In compile time:</p>
<ul>
<li>it has its own EEx engine</li>
<li>during compilation the Drab EEx, while <code>handle_expr</code>, it is checking if there are any assigns inside the expression</li>
<li>if there are assigns, it is injecting the special attribute (drab-ampere), to the parent tag; if there is no parent tag, it is surrounding the expression with the  <code>&lt;span&gt;</code></li>
<li>injecting the attribute also checks if the assign is in the tag body, or in the attribute</li>
<li>there is a special mark, <code>&lt;%/ %&gt;</code> which marks expressions which should not be ‘drabbed’</li>
<li>at the end, it collects all <code>amperes</code> with the corresponding assigns; this is how it knows where to change if you update the assign in a runtime</li>
<li>assigns are stored in the frontend, Drab injects javascripts like <code>window.__drab.assigns.myassigns = &lt;%= @myassign%&gt;</code> to the template</li>
</ul>
<p>In a runtime:</p>
<ul>
<li>you render the template as usual, with the controller</li>
<li>on connect, Drab gets the current assign values from the frontend and cache it in the genserver</li>
<li>on any assigns change (called <code>poke</code>), Drab re-renders the template on the backend; because it know exactly where in the template the assign you are changing is begin used, it gets the value of the tag body (or the attribute value) with Floki</li>
<li>then it sends the JS back to the browser - it is a simple update of the specific tags only</li>
<li>it also sends back the new assign value to the browser - this is important, because in case of disconnect the Drab genserver is killed</li>
</ul>
<p>There are also corner cases, like a special treatment for <span class="mention">@conn</span>, or keeping the csrf token in case you update the whole form, or detecting which <code>ampere</code> to update in case they are nested.</p>
<p>All the above looks complicated, but this was the only what I could invent with the assumptions that:</p>
<ul>
<li>you do not require many changes to the existing phoenix project: in this case the only change is the extension from *.eex to *drab</li>
<li>it must be beginner friendly (to convince beginners who are scared of Ajax and JS)</li>
<li>it must work as expected, be intuitive (this is why assign values must survive disconnection)</li>
<li>all must be archived with external library only, without any changes to Phoenix (<a href="https://github.com/phoenixframework/phoenix/issues/2967" rel="noopener nofollow ugc">as they are not very welcomed</a>)</li>
</ul> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="97011" data-batch-url="/posts/batch_likers">
                        8
                      </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/54-elixirconf-us-2018-closing-keynote-chris-mccord/16445/60">Post #59</a>
	                </div>
	            </div>
              <div id="likers-container-97011" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="97011"
                     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 #59"></div>
  </section>
</div>
    <div class="postbit" id="97024" data-post-id="97024">
  <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
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote group-livebook_core_team" data-username="josevalim" data-post="58" data-topic="16445">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/josevalim/48/1787_2.png" class="avatar"> josevalim:</div>
<blockquote>
<p> In this approach we remove the nesting and it will work as long as we implement <code>Phoenix.HTML.Safe</code> for <code>Phoenix.HTML.FormData</code> . But it goes against your first rule. <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>Besides going against my first rule, this approach is conceptually very weird, at least to me. You have a widget for the opening tag but you must close it manually, in a way that’s completely opaque to someone who looks at the code. I think I prefer turning <code>form_for(form, action, options, fun)</code> into a macro which calls <code>fun.(form)</code> at compile time.</p>
<p>The main problem is that <code>form</code> can be anything that implements a protocol, so it would have to be converted into <code>%FormData{}</code> by all inner widgets. That’s very inefficient (although still the right choice if we want to optimize for bandwidth at all costs!).</p>
<p>The problem is that with my model (respecting the first rule) you can’t precompute the conversion to <code>%FormData{}</code> at runtime! So you’d have to perform that conversion outside the template (this is a limitation of my apparoach I didn’t think of earlier…)</p>
<p>There <em>might</em> be a way out that avoids my rule, though (I’ll get very technical here). The semantics of variable rebinding and its scope is what got us into this mess. In an EEx template, we must respect rebinding semantics, which means an EEx template is compiled to a series of nested lists with blocks inside the lists, which is kinda messy to refactor automatically. For example, if you compile this template:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">&lt;%= a = 1 %&gt;
&lt;%= a %&gt;
&lt;%= a = 2 %&gt;
&lt;%= a %&gt;

</code></pre>
<p>with the default phoenix engine, you get this:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">{:safe,
 [
   (
     tmp1 = [
       (
         tmp1 = [
           (
             tmp1 = [
               (
                 tmp1 = ""

                 [
                   tmp1
                   | case(a = 1) 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
                 ]
               )
               | "\n"
             ]

             [
               tmp1
               | 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
             ]
           )
           | "\n"
         ]

         [
           tmp1
           | case(a = 2) 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
         ]
       )
       | "\n"
     ]

     [
       tmp1
       | 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
     ]
   )
   | "\n"
 ]}
</code></pre>
<p>(those not experienced in looking at printed macro output, remember that parentheses on their own lines are block delimiters in Elixir)</p>
<p>With my simplified semantics, without variable rebinding, I can compile the template into something like this (I’m cheating a little by removing the <code>{:safe, ...}</code> tuple, but you get the idea.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">[
  "",
  [
    case(a = 1) 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
  ],
  "\n",
  [
    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
  ],
  "\n",
  [
    case(a = 2) 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
  ],
  "\n",
  [
    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
  ],
  "\n"
]
</code></pre>
<p>You can see that I was able to optimize the template into a list where alternate elements are either static or dynamic. That is very hard to do with the default HTML template…</p>
<p>I’ve thought about trying to build lists as I walk the EEx file. The output of a EEx engine doesn’t need to be a quoted expression representing the template, which means I can thread a context as I parse the source and then try to output something sensible from the context, but so far I’ve been having troubles. No matter how you look at it, the output of an EEx template seems to require a deeply nested tree of blocks with many instances of variable rebinding.</p>
<p>This is a problem Elixir already has to solve when compiling to Erlang (there are no variable rebindings in Erlang). To compile to Erlang, Elixir uses something called Static Single Assignment, in which something like this:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">a = 1
a = a + 1
a = a + 2
</code></pre>
<p>becomes:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">_a@0 = 1
_a@1 = _a@0 + 2
_a@2 = _a@1 + 3

</code></pre>
<p>If we could replace the reassigned variables using Static Single Assignment, we could probably turn the AST inside out, and move all variable calculations outside the tree and collapse the tree into a simple flat list. I’ve been avoiding going that way, because that requires an intimate knowledge of the core elixir semantics (namely, it requires the programmer to know all places where variables can be bound). But besides that, I can’t see any way of simplifying the AST returned by the EEx engine and separating the static from the dynamic parts.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="97024" 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/54-elixirconf-us-2018-closing-keynote-chris-mccord/16445/61">Post #60</a>
	                </div>
	            </div>
              <div id="likers-container-97024" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="97024"
                     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 #60"></div>
  </section>
</div>
    <div class="postbit" id="97025" data-post-id="97025">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="josevalim" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/josevalim/120/1787_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  josevalim
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Creator of Elixir</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="grych" data-post="60" data-topic="16445">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/grych/48/5610_2.png" class="avatar"> grych:</div>
<blockquote>
<p>I don’t know if it is helpful, but this is how Drab does it.</p>
</blockquote>
</aside>
<p>Thanks, this is very interesting! How do you handle an injected <code>&lt;span&gt;</code> that may appear multiple times? Imagine on the first render you have 3 comments, then there is a new comment, how would you track this on Drab’s side and send it to the browser? For a reference, the approach I have in mind would render <strong>all of the comments</strong> again and send it to the browser for diffing.</p>
<p>Also, how do you sync assigns in case of disconnect+reconnect? Because the data on the server may be more recent, so you can’t just trust the assigns the client sends?</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="97025" 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/54-elixirconf-us-2018-closing-keynote-chris-mccord/16445/62">Post #61</a>
	                </div>
	            </div>
              <div id="likers-container-97025" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="97025"
                     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 #61"></div>
  </section>
</div>
    <div class="postbit" id="97027" data-post-id="97027">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="josevalim" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/josevalim/120/1787_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  josevalim
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Creator of Elixir</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="tmbb" data-post="61" data-topic="16445">
<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>Besides going against my first rule, this approach is conceptually very weird, at least to me. You have a widget for the opening tag but you must close it manually, in a way that’s completely opaque to someone who looks at the code. I think I prefer turning <code>form_for(form, action, options, fun)</code> into a macro which calls <code>fun.(form)</code> at compile time.</p>
</blockquote>
</aside>
<p>Yes, I agree. <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"> Just bringing options to discuss!</p>
<aside class="quote no-group" data-username="tmbb" data-post="61" data-topic="16445">
<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>If we could replace the reassigned variables using Static Single Assignment, we could probably turn the AST inside out, and move all variable calculations outside the tree and collapse the tree into a simple flat list. I’ve been avoiding going that way, because that requires an intimate knowledge of the core elixir semantics (namely, it requires the programmer to know all places where variables can be bound). But besides that, I can’t see any way of simplifying the AST returned by the EEx engine and separating the static from the dynamic parts.</p>
</blockquote>
</aside>
<p>Well, since we have deprecated <code>var</code> as a function call, a variable in EEx is either a variable being invoked (which means it has to be previously defined) OR it is being defined. If a variable is being invoked, then we can’t do the transformation you propose. If the variable is being defined, then we may be able to do it but how frequently do we define variables in templates?</p>
<p>So can’t we flatten it as long as there are no variables? It should be safe for Phoenix because the only variable we have is <code>assigns</code> and most of the assigns accesses are hidden behind <code>@access</code>, which means it should just bypass the optimization you propose.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="97027" 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/54-elixirconf-us-2018-closing-keynote-chris-mccord/16445/63">Post #62</a>
	                </div>
	            </div>
              <div id="likers-container-97027" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="97027"
                     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 #62"></div>
  </section>
</div>
    <div class="postbit" id="97036" data-post-id="97036">
  <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
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote group-livebook_core_team" data-username="josevalim" data-post="63" data-topic="16445">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/josevalim/48/1787_2.png" class="avatar"> josevalim:</div>
<blockquote>
<p>Well, since we have deprecated <code>var</code> as a function call, a variable in EEx is either a variable being invoked (which means it has to be previously defined) OR it is being defined. If a variable is being invoked, then we can’t do the transformation you propose. If the variable is being defined, then we may be able to do it but how frequently do we define variables in templates?</p>
</blockquote>
</aside>
<p>This is one of those things in which we can’t ask “how often” <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"> I think we either support it or not. And yes, I think we can perform many transformation even with variable rebinding. Just look at this example I produced painstakingly by hand (I didn’t even have the courage to see if it compiles, but it gives you the idea of what one can do):</p>
<h3><a name="p-97036-h-1-template-1" class="anchor" href="#p-97036-h-1-template-1" aria-label="Heading link" rel="nofollow"></a>1. Template:</h3>
<pre data-code-wrap="elixir"><code class="lang-elixir">&lt;%= a = 1 %&gt;
&lt;%= a %&gt;
&lt;%= a = 2 %&gt;
&lt;%= a %&gt;
</code></pre>
<h3><a name="p-97036-h-2-compiled-template-2" class="anchor" href="#p-97036-h-2-compiled-template-2" aria-label="Heading link" rel="nofollow"></a>2. Compiled template</h3>
<p>The variable <code>tmp1</code> repeats a bunch of times. The variable <code>a</code> also repeats.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">{:safe,
 [
   (
     tmp1 = [
       (
         tmp1 = [
           (
             tmp1 = [
               (
                 tmp1 = ""

                 [
                   tmp1
                   | case(a = 1) 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
                 ]
               )
               | "\n"
             ]

             [
               tmp1
               | 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
             ]
           )
           | "\n"
         ]

         [
           tmp1
           | case(a = 2) 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
         ]
       )
       | "\n"
     ]

     [
       tmp1
       | 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
     ]
   )
   | "\n"
 ]}
</code></pre>
<h3><a name="p-97036-h-3-replace-variable-names-so-that-they-become-unique-3" class="anchor" href="#p-97036-h-3-replace-variable-names-so-that-they-become-unique-3" aria-label="Heading link" rel="nofollow"></a>3. Replace variable names so that they become unique</h3>
<pre data-code-wrap="elixir"><code class="lang-elixir">{:safe,
 [
   (
     tmp1__0 = [
       (
         tmp1__1 = [
           (
             tmp1__2 = [
               (
                 tmp1__3 = ""

                 [
                   tmp1__3
                   | case(a__0 = 1) 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
                 ]
               )
               | "\n"
             ]

             [
               tmp1__2
               | case(a__0) 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
             ]
           )
           | "\n"
         ]

         [
           tmp1__1
           | case(a__1 = 2) 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
         ]
       )
       | "\n"
     ]

     [
       tmp1__0
       | case(a__2) 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
     ]
   )
   | "\n"
 ]}
</code></pre>
<h3><a name="p-97036-h-4-move-variables-out-of-the-list-into-the-top-level-4" class="anchor" href="#p-97036-h-4-move-variables-out-of-the-list-into-the-top-level-4" aria-label="Heading link" rel="nofollow"></a>4. Move variables out of the list into the top level</h3>
<pre data-code-wrap="elixir"><code class="lang-elixir"># Assign from inside out
# Turn the lists into proper lists;
# It's always safe to flatten iolists() and turn them into proper lists
tmp1__3 = ""
a__0 = 1
tmp1__2 = [
  ""
  , case(a__0) 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
  , "\n"
]
tmp1__1 = [
  tmp1__2
  , case(a__0) 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
  , "\n"
]
a__1 = 2
tmp1__0 = [
  tmp1__1
  , case(a__1) 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
  , "\n"
]

result = [
  tmp1__0
  , case(a__1) 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
  , "\n"
]
</code></pre>
<h3><a name="p-97036-h-5-replace-the-ast-of-the-tmp1__x-variables-in-the-list-and-flatten-5" class="anchor" href="#p-97036-h-5-replace-the-ast-of-the-tmp1__x-variables-in-the-list-and-flatten-5" aria-label="Heading link" rel="nofollow"></a>5. Replace the AST of the tmp1__X variables in the list and flatten</h3>
<pre data-code-wrap="elixir"><code class="lang-elixir"># Move all assignments out of the dynamic parts into the top level in the order they appear.
# Replace the AST of the tmp1__X variables into the lists and flatten them
# (we won't need the tmp1__X variables anymore)
a__0 = 1
a__1 = 2
# We now have a flat list
result = [
  ""
  , case(a__0) 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
  , "\n"
  , case(a__1) 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
  , "\n"
  , case(a__1) 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
  , "\n"
  , case(a__1) 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
  , "\n"
]
</code></pre>
<p>Compilers do this kind of thing all the time, and Elixir is not that hard to analyze statically.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="97036" 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/54-elixirconf-us-2018-closing-keynote-chris-mccord/16445/64">Post #63</a>
	                </div>
	            </div>
              <div id="likers-container-97036" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="97036"
                     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 #63"></div>
  </section>
</div>
    <div class="postbit" id="97046" data-post-id="97046">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="josevalim" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/josevalim/120/1787_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  josevalim
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Creator of Elixir</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="tmbb" data-post="64" data-topic="16445">
<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>Move variables out of the list into the top level</p>
</blockquote>
</aside>
<p>I am OK with this restriction, assuming it only applies at the top level. Is this straight-forward to detect though?</p>
<p>EDIT: This is actually a <strong>great</strong> idea! Good job! <img src="https://forum.elixirforum.com/images/emoji/apple/smiley.png?v=15" title=":smiley:" class="emoji" alt=":smiley:" loading="lazy" width="20" height="20"></p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="97046" data-batch-url="/posts/batch_likers">
                        3
                      </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/54-elixirconf-us-2018-closing-keynote-chris-mccord/16445/65">Post #64</a>
	                </div>
	            </div>
              <div id="likers-container-97046" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="97046"
                     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 #64"></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/16445/load_more?page=7">Load more posts (26 remaining)</a>
</div></template></turbo-stream>