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


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="grych" data-post="83" 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>How would you implement long running processes in Texas? For example, <a href="https://tg.pl/drab#long_running" rel="noopener nofollow ugc">like this one here</a>. The similar stuff was my first usecase to create Drab.</p>
</blockquote>
</aside>
<p>with texas, nothing is a response (unless your websocket disconnects, then you fall back to http) - you have to broadcast a message whenever you want a view to be updated, for a long running process this could be as simple as broadcasting out “progress_bar” once every second or so and all the clients subscribed to that topic would update themselves with a function <code>def progress_bar(conn), do: ...</code> inside that function you would just call on some state somewhere that knows what percentile is complete, so you’d just broadcast that message out over and over until you’re done processing</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="97294" 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/86">Post #85</a>
	                </div>
	            </div>
              <div id="likers-container-97294" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="97294"
                     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 #85"></div>
  </section>
</div>
    <div class="postbit" id="98052" data-post-id="98052">
  <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">
								<p>Finally some progress! I haven’t tested the code as thoroughly as I should, but I’m pretty happy with these preliminary results (code here: <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>The way it works is very simple:</p>
<ul>
<li>
<p>Instead of nesting blocks (like the default implementation of phoenix templates), we generate a proper list (not even a normal iolist).</p>
</li>
<li>
<p>We merge the binaries in the list so that it’s always: <code>[static_binary, dynamic_expression, static_binary, dynamic_expression, ..., dynamic_expression, static_binary]</code></p>
</li>
<li>
<p>Variables defined in one of the elements of the list aren’t available in the following elements, which is a problem. For example, this raises an error: <code>[x = 1, y = x + 1]</code>.</p>
</li>
<li>
<p>To go around this limitation, we assign the elements of the list to named variables and put them all in a block. This way, all variables are injected into the current scope as the user expects from a normal EEx template.</p>
</li>
<li>
<p>Finally, in the last expression of the block, we return the list of variables we care about:</p>
<ol>
<li>If we want to render the full template, return both the <code>static__</code> and the <code>dynamic__</code> variables.</li>
<li>If we want to render the static part of the template, return only the <code>satic__</code> variables (which we can statically replace by their value, as I show above)</li>
<li>If we want to render the dynamic part of the template, return only the <code>dynamic__</code> variables</li>
</ol>
</li>
</ul>
<p>I can finally separate the dynamic from the static parts of the template, and I didn’t even have to do significant AST rewriting! Actually, there is a place where I pattern match on the AST but that’s just a shortcut I’ll replace later. I can now take this template:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">&lt;% a = 1 %&gt;
Blah blah blah
&lt;%= a %&gt;
Blah blah
&lt;%= if a &gt; 1 do %&gt;
  &lt;%= a + 1 %&gt;
&lt;% else %&gt;
  &lt;%= a - 1 %&gt;
&lt;% end %&gt;
</code></pre>
<p>Compile it into the following (full template):</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">{:safe,
 (
   static__1 = ""

   dynamic__1 =
     (
       a = 1
       nil
     )

   static__2 = "\nBlah blah blah\n"

   dynamic__2 =
     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

   static__3 = "\nBlah blah\n"

   dynamic__3 =
     case(
       if(a &gt; 1) do
         [
           "\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"
         ]
       else
         [
           "\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"
         ]
       end
     ) 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

   static__4 = ""
   [static__1, dynamic__1, static__2, dynamic__2, static__3, dynamic__3, static__4]
 )}
</code></pre>
<p>And separate the static parts…</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">{:safe, ["", "\nBlah blah blah\n", "\nBlah blah\n", ""]}
</code></pre>
<p>From the dynamic parts:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">{:safe,
 (
   dynamic__1 =
     (
       a = 1
       nil
     )

   dynamic__2 =
     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

   dynamic__3 =
     case(
       if(a &gt; 1) do
         [
           "\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"
         ]
       else
         [
           "\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"
         ]
       end
     ) 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

   [dynamic__1, dynamic__2, dynamic__3]
 )}
</code></pre>
<p>The static template could be optimized further (by removing the empty binaries), but the goal is to send these things over the network and interact with Javascript, so it’s useful to make sure that the static parts start and end with a binary. The static parts will only be transmitted once, which means they optimizing them is not as rewarding as optimizing the dynamic parts.</p>
<p>The main limitation is that I can’t yet expand macros and optimize the macro-expanded result (it might raise some delicate questions regarding scope, but it’s probably not such a big deal. Without the ability to optimize macroexpanded results this code is rather useless for dynamic forms, as we’ve discussed above.</p>
<p>Code here: <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>. The code is not yet optimized, and it uses a quadratic algorithm in some places to build quoted expressions, but it’s good enough for a proof of concept, and it does prove that you don’t need hardcore AST rewriting to separate 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="98052" 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/87">Post #86</a>
	                </div>
	            </div>
              <div id="likers-container-98052" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="98052"
                     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 #86"></div>
  </section>
</div>
    <div class="postbit" id="98053" data-post-id="98053">
  <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>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>
</blockquote>
</aside>
<p>A (slightly) better possibility might be:</p>
<pre><code class="lang-plaintext">&lt;%= f = open_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;%= close_form %&gt;
</code></pre>
<p>which at least doesn’t leak as many implementation details of what <code>form_for</code> does… I still don’t like it that much, 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="98053" 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/88">Post #87</a>
	                </div>
	            </div>
              <div id="likers-container-98053" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="98053"
                     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 #87"></div>
  </section>
</div>
    <div class="postbit" id="98121" data-post-id="98121">
  <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">
								<p>NOTE: the form above won’t work for more complex templates because the inner lists must be recursively converted into blocks as well (because of th scoping issue mentioned above).</p>
<p>I’d like to treat top-level expressions differently in the EEx engine itself, which would avoid the recursive step. Unfortuntely, I still don’t understand the EEx callbacks as well as I should. The key is probably in the <code>handle_init()</code> and the <code>handle_begin()</code> callbacks.</p>
<p>None of this helps me with the task of expanding macros, and optimize their expansion, of course. Expanding macros will be trickier.</p>
<p>If I expand the macros “inline” as I compile the template (this might be possible if I carry the expansion env around), then things should work. I’ll probably have to perform some AST rewriting in that case, but I’ll be rewriting an AST that I’ve built myself (not an arbitrary AST), so it seems simple enough.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="98121" 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/89">Post #88</a>
	                </div>
	            </div>
              <div id="likers-container-98121" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="98121"
                     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 #88"></div>
  </section>
</div>
    <div class="postbit" id="98127" data-post-id="98127">
  <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="89" 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’d like to treat top-level expressions differently in the EEx engine itself, which would avoid the recursive step. Unfortuntely, I still don’t understand the EEx callbacks as well as I should. The key is probably in the <code>handle_init()</code> and the <code>handle_begin()</code> callbacks.</p>
</blockquote>
</aside>
<p>Yes, that’s where I would start exploring.</p>
<aside class="quote no-group" data-username="tmbb" data-post="89" 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>None of this helps me with the task of expanding macros, and optimize their expansion, of course. Expanding macros will be trickier.</p>
</blockquote>
</aside>
<p>Yeah, I am also concerned with the slippery slope this may open. For example, should we convert <code>content_tag</code> to macros and try to expand something such as <code>content_tag :a, dynamic</code>? For things like <code>form_for</code>, the only way a macro would work is by also changing the semantics of the code (i.e. <code>f</code> would leak to outside of the anonymous function). So we are looking at a growing complexity here.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="98127" 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/90">Post #89</a>
	                </div>
	            </div>
              <div id="likers-container-98127" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="98127"
                     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 #89"></div>
  </section>
</div>
    <div class="postbit" id="98136" data-post-id="98136">
  <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="90" 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.e. <code>f</code> would leak to outside of the anonymous function)</p>
</blockquote>
</aside>
<p>I bet you can compile</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">&lt;%= form_for changeset, ..., fn f -&gt; %&gt;
  # code that uses f
&lt;% end %&gt;
</code></pre>
<p>into</p>
<pre data-code-wrap="elixir"><code class="lang-elixir"> &lt;form ...&gt;
  # static stuff
  &lt;% very_higyenic_var = FormData.to_form(changeset) %&gt;
  # code is transformed so that f becomes very_hygienic_var
  # the variable leaks but because it's hygienic it will have no effect on the global scope
  # (it's never used anywhere)
&lt;/form&gt;
</code></pre>
<p>In the case of a form, it would be useful to reuse the csrf token and mark it as static, but that’s really easy to handle. We just need to tag it with something like <code>{:static, expr}</code> and move the <code>expr</code> into the static part of the template. That way it will only be rendered once and can be reused.</p>
<p>EDIT: just because we can it doesn’t mean we should, but it’s an avenue worth pursuing, and even if we fail it’s worth finding out why and documenting it, so that others won’t have to discover it by</p>
<p>EDIT2: for even more safety, the “hygienic variable” could have the name of the function argument (in this case <code>f</code>) with a different context so that if anything depends on the variable name things will still work. Of course it’s never hoing to be 100% referentiay transparent but it’s pretty close.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="98136" 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/91">Post #90</a>
	                </div>
	            </div>
              <div id="likers-container-98136" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="98136"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-last-post cat-last-post" title="Last post!"></div>
  </section>
</div>
</template></turbo-stream><turbo-stream action="replace" target="load-more-container"><template><div id="load-more-container" class="load-more-container">
    <span class="all-loaded">— All posts loaded —</span>
</div></template></turbo-stream>