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


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="Qqwy" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/Qqwy/120/1349_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  Qqwy
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>TypeCheck Core Team</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="aseigo" data-post="11" data-topic="14999">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/aseigo/48/10674_2.png" class="avatar"> aseigo:</div>
<blockquote>
<p>We don’t want them all created up front as it would mean holding all the jobs for every request in memory at the same time and means we can’t start any jobs until they are all created in the first place. That ends up being non-trivial, especially once we start adding parallel requests into the mix.</p>
</blockquote>
</aside>
<p>Almost everywhere in my code snippet it is possible to use <code>Stream</code> instead of <code>Enum</code>, which means you end up with a stream of to-be-created jobs, meaning that the next one is only constructed once there is a worker that wants to consume it.<br>
If you’d like to do this, you probably want to change the <code>Enum.map(&amp;create_jobs/1)</code> to a <code>Stream.flat_map</code>, as well as changing the <code>Enum.map(&amp;create_job(pivot, &amp;1))</code> to a <code>Stream.flat_map</code>.</p>
<p>It is only difficult to reverse a Stream since we don’t yet know how many elements there will be, but this is something that the code you showed in the first post also does not address.<br>
(And for that note, it is definitely possible to create a version that works with streams that starts from the beginning, but memory usage will ever increase since the first elements of old batches will all have to be retained to compare to newer oners; this is why I presumed that your set of input elements have some well-defined maximum size after which old ones do not have to be compared to newer ones anymore).</p>
<p>This is a fun exercise <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="87360" 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/stupid-pet-tricks-functions-as-state/14999/12">Post #11</a>
	                </div>
	            </div>
              <div id="likers-container-87360" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="87360"
                     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 #11"></div>
  </section>
</div>
    <div class="postbit" id="87362" data-post-id="87362">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="aseigo" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/aseigo/120/10674_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  aseigo
                    <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>Yes, we do not know how many elements there will be before being called (though it obviously does not change once started) … but no, no particular maximums other than our own sanity <img src="https://forum.elixirforum.com/images/emoji/apple/wink.png?v=15" title=":wink:" class="emoji" alt=":wink:" loading="lazy" width="20" height="20"></p>
<p>The calculation is commutative so we do not care about ordering of the pairs. So once a given element is processed with respect to all other elements we can safely forget about that element.</p>
<p>Is also so happens we also do not care about order of results, just that they all get processed eventually, so order preservation is also thankfully not a requirement.</p>
<p>But the real annoyance here is that Stream by itself does not provide a nice way to start moving through a stream, stop at a given point, and return. The best way that I know of is to use <code>Stream.transform/4</code> and then save the accumulator returned from the after function. Which means we get precisely zero benefit from the abstraction of Stream here. AFAIK, Stream is not intended to be used asyncronously, and that is apparent from its API. (Someone please correct me on that point if I am wrong!)</p>
<p>This is the sort of thing that GenServer does enable, but in this case we’d only really need/want one stage and it would internally implement exactly such an iterator and so .. we’re back to square one <img src="https://forum.elixirforum.com/images/emoji/apple/wink.png?v=15" title=":wink:" class="emoji" alt=":wink:" loading="lazy" width="20" height="20"></p>
<p>It is the need for an asynchronous stepping through a single set of data, or a resumable stream, that led to then “hand-rolled” iteration function.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="87362" 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/stupid-pet-tricks-functions-as-state/14999/13">Post #12</a>
	                </div>
	            </div>
              <div id="likers-container-87362" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="87362"
                     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 #12"></div>
  </section>
</div>
    <div class="postbit" id="87398" data-post-id="87398">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="Qqwy" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/Qqwy/120/1349_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  Qqwy
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>TypeCheck Core Team</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="aseigo" data-post="13" data-topic="14999">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/aseigo/48/10674_2.png" class="avatar"> aseigo:</div>
<blockquote>
<p>But the real annoyance here is that Stream by itself does not provide a nice way to start moving through a stream, stop at a given point, and return</p>
</blockquote>
</aside>
<p>Indeed; it is not possible with the built-in Enum and Stream to only consume a couple of elements at a time: You can either consume an Enumerable fully, or not at all.<br>
The reason for this has been performance, IIRC from conversations I had about this with <a class="mention" href="/u/josevalim" rel="nofollow">@josevalim</a> in IRC about a year ago. (And on top of this, some things we like to be streams like files, require the closing of file handles when we’re done with them; keeping open a file handle during a suspended iteration for a long time is a bad idea)</p>
<p>At some point I came across a similar problem where this limitation made Enum and Stream a bad abstraction for me, and therefore I wrote the <a href="https://hex.pm/packages/extractable" rel="nofollow">Extractable</a> library (as well as the related <a href="https://hex.pm/packages/insertable" rel="nofollow">Insertable</a>).</p>
<hr>
<p>That said, using streams in this way is definitely possible; however, you end up calling <code>Stream.take</code> and <code>Stream.drop</code> more frequent than you’d like.</p>
<p>Here’s an example using Streams:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">
```defmodule PairwiseComputation do

  def create_all_jobs(data) do

    data
    |&gt; Stream.chunk_every(3)
    |&gt; tails
    |&gt; Stream.map(&amp;create_jobs/1)
    |&gt; Stream.concat

  end

  defp tails(stream) do
    [{}]
    |&gt; Stream.cycle()
    |&gt; Stream.transform(stream, fn
      _, elems -&gt;
        case Enum.take(elems, 1) do
          [] -&gt; {:halt, []}
          _ -&gt;
            {[elems], Stream.drop(elems, 1)}
        end
    end)
  end

  defp create_jobs(stream) do
    first_batch = Stream.take(stream, 1)
    case Enum.take(first_batch, 1) do
      [] -&gt; []
      [pivot] -&gt;
        Stream.concat(Stream.drop(first_batch, 1), Stream.drop(stream, 1))
        |&gt; Stream.map(&amp;create_job(pivot, &amp;1))
      other -&gt; IO.inspect(other)
    end
  end

  defp create_job(pivot, elem), do: IO.puts "creating job for #{inspect pivot} &lt;=&gt; #{inspect elem}"
end

# Example: 
PairwiseComputation.create_all_jobs(1000..1100) |&gt; Enum.take(20)</code></pre> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="87398" 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/stupid-pet-tricks-functions-as-state/14999/14">Post #13</a>
	                </div>
	            </div>
              <div id="likers-container-87398" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="87398"
                     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 #13"></div>
  </section>
</div>
    <div class="postbit" id="87570" data-post-id="87570">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="aseigo" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/aseigo/120/10674_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  aseigo
                    <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="Qqwy" data-post="14" data-topic="14999">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/qqwy/48/1349_2.png" class="avatar"> Qqwy:</div>
<blockquote>
<p>you end up calling <code>Stream.take</code> and <code>Stream.drop</code> more frequent than you’d like</p>
</blockquote>
</aside>
<p>Yeah, but then you aren’t <em>really</em> using streams IMO.. you’re just using them as an odd abstraction over normal collections and you may as well just write the recursion by hand (again, imo)</p>
<aside class="quote no-group" data-username="Qqwy" data-post="14" data-topic="14999">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/qqwy/48/1349_2.png" class="avatar"> Qqwy:</div>
<blockquote>
<p>therefore I wrote the <a href="https://hex.pm/packages/extractable" rel="nofollow">Extractable </a> library (as well as the related <a href="https://hex.pm/packages/insertable" rel="nofollow">Insertable </a>)</p>
</blockquote>
</aside>
<p>Oh, neat. I will have to check these out. Thanks!</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="87570" 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/stupid-pet-tricks-functions-as-state/14999/15">Post #14</a>
	                </div>
	            </div>
              <div id="likers-container-87570" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="87570"
                     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 #14"></div>
  </section>
</div>
    <div class="postbit" id="87719" data-post-id="87719">
  <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="michalmuskala" data-post="4" data-topic="14999">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/michalmuskala/48/20288_2.png" class="avatar"> michalmuskala:</div>
<blockquote>
<p>There’s one disadvantage of the described pattern - the state is opaque. This will become problematic in some debugging situations - the function is basically opaque and you can’t “look into” it with default logging to figure out what’s going on (unless you use some tricks like <code>:erlang.fun_info(fun, :env)</code> to get the data bound in the closure).</p>
</blockquote>
</aside>
<p><strong>This</strong> right here is why I like tuple calls.  They were basically function closures but with the ‘internal state’ entirely visible and accessible!</p>
<aside class="quote no-group" data-username="peerreynders" data-post="6" data-topic="14999">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/peerreynders/48/5826_2.png" class="avatar"> peerreynders:</div>
<blockquote>
<p>In Elixir you can’t mutate any of the data structures that serve as object surrogates either.</p>
</blockquote>
</aside>
<p>Processes, those are the Elixir version of OOP objects, and they do ‘mutate’ themselves by updating their stack state.  ^.^</p>
<aside class="quote no-group" data-username="Qqwy" data-post="8" data-topic="14999">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/qqwy/48/1349_2.png" class="avatar"> Qqwy:</div>
<blockquote>
<p>I actually think that we can do some Y-combinator-like trickery here to create a closure that, when called, returns new versions of itself that include updated data.</p>
</blockquote>
</aside>
<p>That’s what chainable tuple-calls did, it just returns the updated state.  Monadic tuple calls were awesome, now you can’t do that anymore without introducing new operators or so (of which there are plenty of libraries of).</p>
<aside class="quote no-group" data-username="Qqwy" data-post="8" data-topic="14999">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/qqwy/48/1349_2.png" class="avatar"> Qqwy:</div>
<blockquote>
<p><a class="mention" href="/u/aseigo" rel="nofollow">@aseigo</a> Thanks for sharing this technique though! I am wondering currently if it is possible to use some more higher-level stream combinators instead of writing out the recursion of `next_batch manually; this might improve readability. I’ll give it a try.</p>
</blockquote>
</aside>
<p>That’s just monads with mini-effects, make a monad/effect library for that purpose.  <img src="https://forum.elixirforum.com/images/emoji/apple/wink.png?v=15" title=":wink:" class="emoji" alt=":wink:" 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="87719" 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/stupid-pet-tricks-functions-as-state/14999/16">Post #15</a>
	                </div>
	            </div>
              <div id="likers-container-87719" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="87719"
                     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>