<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
        <title>Elixir - Tag - Smaller Infinity</title>
        <link>https://blog.smaller-infinity.com/tags/elixir/</link>
        <description>Elixir - Tag - Smaller Infinity</description>
        <generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Mon, 19 May 2025 00:00:00 -0400</lastBuildDate><atom:link href="https://blog.smaller-infinity.com/tags/elixir/" rel="self" type="application/rss+xml" /><item>
    <title>Lazy Combinations in Elixir</title>
    <link>https://blog.smaller-infinity.com/posts/lazy-combinations-in-elixir/</link>
    <pubDate>Mon, 19 May 2025 00:00:00 -0400</pubDate>
    <author>Paul Ricks</author>
    <guid>https://blog.smaller-infinity.com/posts/lazy-combinations-in-elixir/</guid>
    <description><![CDATA[<p>When building <a href="https://gitlab.com/smaller-infinity/guesswork" target="_blank" rel="noopener noreffer ">Guesswork</a>, a logic programming library for <a href="https://elixir-lang.org/" target="_blank" rel="noopener noreffer ">Elixir</a>, I ran into an
interesting problem: supporting both logical <code>And</code> as well as infinite streams
of possibilities.
To give an example, the statement &lsquo;Let <code>A</code> be some positive number, and let <code>B</code> be
some factorial of <code>A</code>&rsquo;, has both an infinite stream of values (<code>A</code> could be 1, 2,
3, etc.) and a logical conjunction; it cannot be represented without both.</p>
<h2 id="background">Background</h2>
<p>At a basic level, Guesswork is pretty simple.
Each logical statement produces a stream of possible answers (which may be lazy
and could be infinite), and statements that take other statements as inputs (such
as <code>And</code>, <code>Or</code>, and <code>Not</code>) don&rsquo;t know anything about their inputs, aside from
the fact that they are <a href="https://hexdocs.pm/elixir/Enumerable.html" target="_blank" rel="noopener noreffer ">Enumerable</a>.
Furthermore, the calculations behind that <code>Enumerable</code> can be pretty expensive.
For <code>Or</code> and <code>Not</code>, this isn&rsquo;t much of an issue.
<code>Not</code> is very simple, it uses <a href="https://hexdocs.pm/elixir/Stream.html#map/2" target="_blank" rel="noopener noreffer ">Stream.map/2</a> to turn each answer into it&rsquo;s logical
negation, and <code>Or</code> uses <a href="https://hexdocs.pm/elixir/Stream.html#concat/1" target="_blank" rel="noopener noreffer ">Stream.concat/1</a><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</p>
<p><code>And</code>, however, is more complicated.
For <code>And</code> to produce a single answer it takes a single answer from each of its
inputs, and uses <a href="https://hexdocs.pm/guesswork/Guesswork.Answer.html#union/2" target="_blank" rel="noopener noreffer ">Guesswork.Answer.union/2</a> to combine them.
But to build the next answer, only a single answer is pulled from a single input,
and then combined with the already pulled answers from the other streams.
So not only must <code>And</code> hold all the previously pulled answers in some form of
state, but it must then produce the new answers in a lazy fashion.</p>
<h2 id="an-eager-attempt">An Eager Attempt</h2>
<p>Before doing the hard work of building a lazy version, we&rsquo;ll build a simpler,
eager version<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>.
The answer sets are just maps of terms<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>, and two answer sets can be combined with
<code>union/2</code>, provided that they have the same terms for all values.</p>
<div class="code-block code-line-numbers" style="counter-reset: code-block 0">
    <div class="code-header language-elixir">
        <span class="code-title"><i class="arrow fas fa-angle-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-elixir" data-lang="elixir"><span class="line"><span class="cl"><span class="kd">defmodule</span> <span class="nc">LazyCombinations.Answer</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="na">@type</span> <span class="n">t</span><span class="p">()</span> <span class="o">::</span> <span class="p">%{</span><span class="n">atom</span><span class="p">()</span> <span class="o">=&gt;</span> <span class="n">term</span><span class="p">()}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="na">@spec</span> <span class="n">new</span><span class="p">()</span> <span class="o">::</span> <span class="n">t</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="kd">def</span> <span class="n">new</span><span class="p">()</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="p">%{}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="na">@spec</span> <span class="n">union</span><span class="p">(</span><span class="n">t</span><span class="p">(),</span> <span class="n">t</span><span class="p">())</span> <span class="o">::</span> <span class="n">t</span><span class="p">()</span> <span class="o">|</span> <span class="no">nil</span>
</span></span><span class="line"><span class="cl">  <span class="kd">def</span> <span class="n">union</span><span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="n">a2</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">new</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl">      <span class="nc">Map</span><span class="o">.</span><span class="n">merge</span><span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="n">a2</span><span class="p">,</span> <span class="k">fn</span>
</span></span><span class="line"><span class="cl">        <span class="n">_</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="n">t</span> <span class="o">-&gt;</span> <span class="n">t</span>
</span></span><span class="line"><span class="cl">        <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span> <span class="o">-&gt;</span> <span class="no">nil</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nc">Enum</span><span class="o">.</span><span class="n">any?</span><span class="p">(</span><span class="n">new</span><span class="p">,</span> <span class="k">fn</span> <span class="p">{</span><span class="n">_</span><span class="p">,</span> <span class="n">t</span><span class="p">}</span> <span class="o">-&gt;</span> <span class="n">t</span> <span class="o">==</span> <span class="no">nil</span> <span class="k">end</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="no">nil</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">new</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div>
<p>Building on the answer set, anding sets together is surprisingly<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>
straightforward.
<a href="https://hexdocs.pm/elixir/Enum.html#reduce/2" target="_blank" rel="noopener noreffer ">Enum.reduce/2</a> allows us to walk through the <code>args</code>, unioning all possible pairs
together, and removing all invalid combinations.</p>
<div class="code-block code-line-numbers" style="counter-reset: code-block 0">
    <div class="code-header language-elixir">
        <span class="code-title"><i class="arrow fas fa-angle-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-elixir" data-lang="elixir"><span class="line"><span class="cl"><span class="kd">defmodule</span> <span class="nc">LazyCombinations.EagerAnd</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="kd">defstruct</span> <span class="p">[</span><span class="ss">:args</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kn">alias</span> <span class="nc">LazyCombinations.Answer</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="na">@type</span> <span class="n">t</span><span class="p">()</span> <span class="o">::</span> <span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="ss">args</span><span class="p">:</span> <span class="nc">Enumerable</span><span class="o">.</span><span class="n">t</span><span class="p">(</span><span class="nc">Answer</span><span class="o">.</span><span class="n">t</span><span class="p">())}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="na">@spec</span> <span class="n">new</span><span class="p">([</span><span class="nc">Enumerable</span><span class="o">.</span><span class="n">t</span><span class="p">(</span><span class="nc">Answer</span><span class="o">.</span><span class="n">t</span><span class="p">())])</span> <span class="o">::</span> <span class="n">t</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="kd">def</span> <span class="n">new</span><span class="p">(</span><span class="n">args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="ss">args</span><span class="p">:</span> <span class="n">args</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="na">@spec</span> <span class="n">resolve</span><span class="p">(</span><span class="n">t</span><span class="p">())</span> <span class="o">::</span> <span class="p">[</span><span class="nc">Answer</span><span class="o">.</span><span class="n">t</span><span class="p">()]</span>
</span></span><span class="line"><span class="cl">  <span class="kd">def</span> <span class="n">resolve</span><span class="p">(</span><span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="ss">args</span><span class="p">:</span> <span class="n">args</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="nc">Enum</span><span class="o">.</span><span class="n">reduce</span><span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="k">fn</span> <span class="n">val</span><span class="p">,</span> <span class="n">acc</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nc">Enum</span><span class="o">.</span><span class="n">flat_map</span><span class="p">(</span><span class="n">val</span><span class="p">,</span> <span class="k">fn</span> <span class="n">val_item</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="n">acc</span>
</span></span><span class="line"><span class="cl">        <span class="o">|&gt;</span> <span class="nc">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="nc">Answer</span><span class="o">.</span><span class="n">union</span><span class="p">(</span><span class="ni">&amp;1</span><span class="p">,</span> <span class="n">val_item</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="o">|&gt;</span> <span class="nc">Enum</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="o">&amp;</span><span class="nc">Kernel</span><span class="o">.!=</span><span class="p">(</span><span class="ni">&amp;1</span><span class="p">,</span> <span class="no">nil</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div>
<p>Finally, we can confirm that everything works.</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-elixir">
        <span class="code-title"><i class="arrow fas fa-angle-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-elixir" data-lang="elixir"><span class="line"><span class="cl"><span class="n">arg1</span> <span class="o">=</span> <span class="p">[%{</span><span class="ss">a</span><span class="p">:</span> <span class="mi">1</span><span class="p">},</span> <span class="p">%{</span><span class="ss">a</span><span class="p">:</span> <span class="mi">2</span><span class="p">}]</span>
</span></span><span class="line"><span class="cl"><span class="n">arg2</span> <span class="o">=</span> <span class="p">[%{</span><span class="ss">b</span><span class="p">:</span> <span class="mi">1</span><span class="p">},</span> <span class="p">%{</span><span class="ss">b</span><span class="p">:</span> <span class="mi">2</span><span class="p">}]</span>
</span></span><span class="line"><span class="cl"><span class="n">arg3</span> <span class="o">=</span> <span class="p">[%{</span><span class="ss">c</span><span class="p">:</span> <span class="mi">1</span><span class="p">},</span> <span class="p">%{</span><span class="ss">c</span><span class="p">:</span> <span class="mi">2</span><span class="p">}]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nc">LazyCombinations.EagerAnd</span><span class="o">.</span><span class="n">new</span><span class="p">([</span><span class="n">arg1</span><span class="p">,</span> <span class="n">arg2</span><span class="p">,</span> <span class="n">arg3</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="o">|&gt;</span> <span class="nc">LazyCombinations.EagerAnd</span><span class="o">.</span><span class="n">resolve</span><span class="p">()</span></span></span></code></pre></div></div>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-text">
        <span class="code-title"><i class="arrow fas fa-angle-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">[
</span></span><span class="line"><span class="cl">  %{c: 1, a: 1, b: 1},
</span></span><span class="line"><span class="cl">  %{c: 1, a: 2, b: 1},
</span></span><span class="line"><span class="cl">  %{c: 1, a: 1, b: 2},
</span></span><span class="line"><span class="cl">  %{c: 1, a: 2, b: 2},
</span></span><span class="line"><span class="cl">  %{c: 2, a: 1, b: 1},
</span></span><span class="line"><span class="cl">  %{c: 2, a: 2, b: 1},
</span></span><span class="line"><span class="cl">  %{c: 2, a: 1, b: 2},
</span></span><span class="line"><span class="cl">  %{c: 2, a: 2, b: 2}
</span></span><span class="line"><span class="cl">]</span></span></code></pre></div></div>
<h2 id="laziness">Laziness</h2>
<p>The above example is a good starting point, but it is obviously not exactly what
we want, because if we tried to represent &lsquo;Let <code>A</code> be some positive number&rsquo;<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>
and passed that stream of answer sets to the above <code>And</code>, the <code>resolve/1</code> function
would never return.</p>
<h3 id="iterating-over-streams">Iterating over Streams</h3>
<p>Before getting into the meat of this problem, we have to resolve the issue of
pulling items from our <code>Enumerable</code> inputs one at a time<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup>.
As there is no way to do this directly in the standard library, we&rsquo;ll have to
use <a href="https://elixirforum.com/t/best-way-to-iterate-a-stream/24382" target="_blank" rel="noopener noreffer ">Enumerable.reduce/3</a>.
This is actually how many of the functions in the standard library are
implemented, and it provides very fine-grained control over how we iterate over
the data.</p>
<p>The <code>stream</code> here is another infinite list of numbers, built with
<a href="https://hexdocs.pm/elixir/Stream.html#iterate/2" target="_blank" rel="noopener noreffer ">Stream.iterate/2</a>.
To pull items one at a time we need to use a <code>reducer/0</code> function that always
returns <code>:suspend</code>, which in turn instructs <code>reduce/3</code> to pause the stream and
return the next value and a continuation.</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-elixir">
        <span class="code-title"><i class="arrow fas fa-angle-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-elixir" data-lang="elixir"><span class="line"><span class="cl"><span class="n">stream</span> <span class="o">=</span> <span class="nc">Stream</span><span class="o">.</span><span class="n">iterate</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="o">&amp;</span><span class="nc">Kernel</span><span class="o">.+</span><span class="p">(</span><span class="ni">&amp;1</span><span class="p">,</span> <span class="mi">1</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="n">reduce_fun</span> <span class="o">=</span> <span class="k">fn</span> <span class="n">item</span><span class="p">,</span> <span class="n">_acc</span> <span class="o">-&gt;</span> <span class="p">{</span><span class="ss">:suspend</span><span class="p">,</span> <span class="n">item</span><span class="p">}</span> <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span><span class="ss">:suspended</span><span class="p">,</span> <span class="no">nil</span><span class="p">,</span> <span class="n">continuation</span><span class="p">}</span> <span class="o">=</span> <span class="nc">Enumerable</span><span class="o">.</span><span class="n">reduce</span><span class="p">(</span><span class="n">stream</span><span class="p">,</span> <span class="p">{</span><span class="ss">:suspend</span><span class="p">,</span> <span class="no">nil</span><span class="p">},</span> <span class="n">reduce_fun</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span><span class="ss">:suspended</span><span class="p">,</span> <span class="n">val</span><span class="p">,</span> <span class="n">continuation</span><span class="p">}</span> <span class="o">=</span> <span class="n">continuation</span><span class="o">.</span><span class="p">({</span><span class="ss">:cont</span><span class="p">,</span> <span class="no">nil</span><span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="n">val</span></span></span></code></pre></div></div>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-text">
        <span class="code-title"><i class="arrow fas fa-angle-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">1</span></span></code></pre></div></div>
<p>Once we have the continuation we can just keep calling it with <code>{:cont, nil}</code>,
which instructs the closure to move the stream forward once, before the <code>reducer/0</code>
function suspends it again.</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-elixir">
        <span class="code-title"><i class="arrow fas fa-angle-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-elixir" data-lang="elixir"><span class="line"><span class="cl"><span class="p">{</span><span class="ss">:suspended</span><span class="p">,</span> <span class="n">val</span><span class="p">,</span> <span class="n">continuation</span><span class="p">}</span> <span class="o">=</span> <span class="n">continuation</span><span class="o">.</span><span class="p">({</span><span class="ss">:cont</span><span class="p">,</span> <span class="no">nil</span><span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="n">val</span></span></span></code></pre></div></div>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-text">
        <span class="code-title"><i class="arrow fas fa-angle-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">2</span></span></code></pre></div></div>
<h3 id="lazy-and">Lazy And</h3>
<p>To actually build a stream of values we are going to use <a href="https://hexdocs.pm/elixir/Stream.html#unfold/2" target="_blank" rel="noopener noreffer ">Stream.unfold/2</a>.
This function works for our purposes because it allows us to store state in an
accumulator.
We can then generate our sequence from that state, updating it as we go.
The exact details of that &lsquo;state&rsquo; are abstracted away here using a behavior<sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup>
where the important callback is <code>next_unions/1</code>.
<code>next_unions/1</code> just takes a list of converted streams and either indicates that
there is nothing left to calculate (<code>:empty</code> or <code>:done</code>) or returns a list of
&lsquo;unioned&rsquo; answer sets and the next version of the streams to keep in the accumulator.</p>
<div class="code-block code-line-numbers" style="counter-reset: code-block 0">
    <div class="code-header language-elixir">
        <span class="code-title"><i class="arrow fas fa-angle-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-elixir" data-lang="elixir"><span class="line"><span class="cl"><span class="kd">defmodule</span> <span class="nc">LazyCombinations.LazyAnd</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="kd">defstruct</span> <span class="p">[</span><span class="ss">:partial_module</span><span class="p">,</span> <span class="ss">:args</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kn">alias</span> <span class="nc">LazyCombinations.Answer</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="na">@callback</span> <span class="n">new</span><span class="p">(</span><span class="nc">Enumerable</span><span class="o">.</span><span class="n">t</span><span class="p">(</span><span class="nc">Answer</span><span class="o">.</span><span class="n">t</span><span class="p">()))</span> <span class="o">::</span> <span class="n">term</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="na">@doc</span> <span class="sh">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="sh">  Takes a list of partial evaluations and attempts to calculate the next set of
</span></span></span><span class="line"><span class="cl"><span class="sh">  unioned answer sets, based on the next evaluated answer set unioned with the
</span></span></span><span class="line"><span class="cl"><span class="sh">  previously evaluated answer sets.
</span></span></span><span class="line"><span class="cl"><span class="sh">  &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="na">@callback</span> <span class="n">next_unions</span><span class="p">([</span><span class="n">term</span><span class="p">()])</span> <span class="o">::</span> <span class="ss">:empty</span> <span class="o">|</span> <span class="ss">:done</span> <span class="o">|</span> <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="p">[</span><span class="n">term</span><span class="p">()],</span> <span class="p">[</span><span class="nc">Answer</span><span class="o">.</span><span class="n">t</span><span class="p">()]}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="na">@type</span> <span class="n">t</span><span class="p">()</span> <span class="o">::</span> <span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="ss">args</span><span class="p">:</span> <span class="nc">Enumerable</span><span class="o">.</span><span class="n">t</span><span class="p">(</span><span class="nc">Answer</span><span class="o">.</span><span class="n">t</span><span class="p">())}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="na">@spec</span> <span class="n">new</span><span class="p">(</span><span class="n">module</span><span class="p">(),</span> <span class="p">[</span><span class="nc">Enumerable</span><span class="o">.</span><span class="n">t</span><span class="p">(</span><span class="nc">Answer</span><span class="o">.</span><span class="n">t</span><span class="p">())])</span> <span class="o">::</span> <span class="n">t</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="kd">def</span> <span class="n">new</span><span class="p">(</span><span class="n">partial_module</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="ss">partial_module</span><span class="p">:</span> <span class="n">partial_module</span><span class="p">,</span> <span class="ss">args</span><span class="p">:</span> <span class="n">args</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="na">@spec</span> <span class="n">resolve</span><span class="p">(</span><span class="n">t</span><span class="p">())</span> <span class="o">::</span> <span class="nc">Enumerable</span><span class="o">.</span><span class="n">t</span><span class="p">(</span><span class="nc">Answer</span><span class="o">.</span><span class="n">t</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">  <span class="kd">def</span> <span class="n">resolve</span><span class="p">(</span><span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="ss">args</span><span class="p">:</span> <span class="n">args</span><span class="p">,</span> <span class="ss">partial_module</span><span class="p">:</span> <span class="n">partial_module</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">streams</span> <span class="o">=</span> <span class="nc">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">partial_module</span><span class="o">.</span><span class="n">new</span><span class="o">/</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nc">Stream</span><span class="o">.</span><span class="n">unfold</span><span class="p">({</span><span class="n">streams</span><span class="p">,</span> <span class="p">[]},</span> <span class="o">&amp;</span><span class="n">unfold_streams</span><span class="p">(</span><span class="ni">&amp;1</span><span class="p">,</span> <span class="n">partial_module</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">def</span> <span class="n">unfold_streams</span><span class="p">({</span><span class="n">streams</span><span class="p">,</span> <span class="p">[]},</span> <span class="n">partial_module</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="n">partial_module</span><span class="o">.</span><span class="n">next_unions</span><span class="p">(</span><span class="n">streams</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="ss">:empty</span> <span class="o">-&gt;</span> <span class="no">nil</span>
</span></span><span class="line"><span class="cl">      <span class="ss">:done</span> <span class="o">-&gt;</span> <span class="no">nil</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">new_streams</span><span class="p">,</span> <span class="n">unions</span><span class="p">}</span> <span class="o">-&gt;</span> <span class="n">unfold_streams</span><span class="p">({</span><span class="n">new_streams</span><span class="p">,</span> <span class="n">unions</span><span class="p">},</span> <span class="n">partial_module</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">def</span> <span class="n">unfold_streams</span><span class="p">({</span><span class="n">streams</span><span class="p">,</span> <span class="p">[</span><span class="n">head</span> <span class="o">|</span> <span class="n">rest</span><span class="p">]},</span> <span class="n">_</span><span class="p">),</span> <span class="ss">do</span><span class="p">:</span> <span class="p">{</span><span class="n">head</span><span class="p">,</span> <span class="p">{</span><span class="n">streams</span><span class="p">,</span> <span class="n">rest</span><span class="p">}}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div>
<p>There is a lot of code in the state below, but that is mostly handling all the
edge cases.
The first bit, <code>new/1</code> and <code>head/1</code> are just modified versions of the iterated
stream we saw above.
The only noticeable difference is that a <code>defstruct</code> is used to hold the
<code>continuation</code> (stream) and the previously <code>evaluated</code> answer sets.</p>
<p>The rest of the module is spent implementing <code>next_unions/1</code>, which simply
works through the streams, pulling an answer set if nothing has been evaluated,
and &lsquo;unioning&rsquo; everything together as it works.</p>
<div class="code-block code-line-numbers" style="counter-reset: code-block 0">
    <div class="code-header language-elixir">
        <span class="code-title"><i class="arrow fas fa-angle-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-elixir" data-lang="elixir"><span class="line"><span class="cl"><span class="kd">defmodule</span> <span class="nc">LazyCombinations.LazyAnd.SimplePartiallyEvaluated</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="na">@behaviour</span> <span class="nc">LazyCombinations.LazyAnd</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">defstruct</span> <span class="p">[</span><span class="ss">:continuation</span><span class="p">,</span> <span class="ss">evaluated</span><span class="p">:</span> <span class="p">[]]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kn">alias</span> <span class="nc">LazyCombinations.Answer</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="na">@type</span> <span class="n">t</span><span class="p">()</span> <span class="o">::</span> <span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="ss">continuation</span><span class="p">:</span> <span class="nc">Enumerable</span><span class="o">.</span><span class="n">continuation</span><span class="p">()</span> <span class="o">|</span> <span class="no">nil</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="ss">evaluated</span><span class="p">:</span> <span class="p">[</span><span class="nc">Answer</span><span class="o">.</span><span class="n">t</span><span class="p">()]</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="na">@impl</span> <span class="no">true</span>
</span></span><span class="line"><span class="cl">  <span class="na">@spec</span> <span class="n">new</span><span class="p">(</span><span class="nc">Enumerable</span><span class="o">.</span><span class="n">t</span><span class="p">(</span><span class="nc">Answer</span><span class="o">.</span><span class="n">t</span><span class="p">()))</span> <span class="o">::</span> <span class="n">t</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="kd">def</span> <span class="n">new</span><span class="p">(</span><span class="n">stream</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">reduce_fun</span> <span class="o">=</span> <span class="k">fn</span> <span class="n">item</span><span class="p">,</span> <span class="n">_acc</span> <span class="o">-&gt;</span> <span class="p">{</span><span class="ss">:suspend</span><span class="p">,</span> <span class="n">item</span><span class="p">}</span> <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="ss">:suspended</span><span class="p">,</span> <span class="no">nil</span><span class="p">,</span> <span class="n">continuation</span><span class="p">}</span> <span class="o">=</span> <span class="nc">Enumerable</span><span class="o">.</span><span class="n">reduce</span><span class="p">(</span><span class="n">stream</span><span class="p">,</span> <span class="p">{</span><span class="ss">:suspend</span><span class="p">,</span> <span class="no">nil</span><span class="p">},</span> <span class="n">reduce_fun</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="ss">continuation</span><span class="p">:</span> <span class="n">continuation</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="na">@spec</span> <span class="n">head</span><span class="p">(</span><span class="n">t</span><span class="p">())</span> <span class="o">::</span> <span class="n">t</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="kd">def</span> <span class="n">head</span><span class="p">(</span><span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="ss">continuation</span><span class="p">:</span> <span class="no">nil</span><span class="p">}</span> <span class="o">=</span> <span class="n">stream</span><span class="p">),</span> <span class="ss">do</span><span class="p">:</span> <span class="n">stream</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">def</span> <span class="n">head</span><span class="p">(</span><span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="ss">continuation</span><span class="p">:</span> <span class="n">continuation</span><span class="p">,</span> <span class="ss">evaluated</span><span class="p">:</span> <span class="n">evaluated</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="n">continuation</span><span class="o">.</span><span class="p">({</span><span class="ss">:cont</span><span class="p">,</span> <span class="no">nil</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="ss">:suspended</span><span class="p">,</span> <span class="n">item</span><span class="p">,</span> <span class="n">continuation</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="ss">continuation</span><span class="p">:</span> <span class="n">continuation</span><span class="p">,</span> <span class="ss">evaluated</span><span class="p">:</span> <span class="p">[</span><span class="n">item</span> <span class="o">|</span> <span class="n">evaluated</span><span class="p">]}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="ss">:halted</span><span class="p">,</span> <span class="n">item</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="ss">continuation</span><span class="p">:</span> <span class="no">nil</span><span class="p">,</span> <span class="ss">evaluated</span><span class="p">:</span> <span class="p">[</span><span class="n">item</span> <span class="o">|</span> <span class="n">evaluated</span><span class="p">]}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="ss">:done</span><span class="p">,</span> <span class="no">nil</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="ss">continuation</span><span class="p">:</span> <span class="no">nil</span><span class="p">,</span> <span class="ss">evaluated</span><span class="p">:</span> <span class="n">evaluated</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="na">@spec</span> <span class="n">done?</span><span class="p">(</span><span class="n">t</span><span class="p">())</span> <span class="o">::</span> <span class="n">boolean</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="kd">def</span> <span class="n">done?</span><span class="p">(</span><span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="ss">continuation</span><span class="p">:</span> <span class="n">continuation</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">continuation</span> <span class="o">==</span> <span class="no">nil</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="na">@impl</span> <span class="no">true</span>
</span></span><span class="line"><span class="cl">  <span class="na">@spec</span> <span class="n">next_unions</span><span class="p">([</span><span class="n">t</span><span class="p">()])</span> <span class="o">::</span> <span class="ss">:empty</span> <span class="o">|</span> <span class="ss">:done</span> <span class="o">|</span> <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="p">[</span><span class="n">t</span><span class="p">()],</span> <span class="p">[</span><span class="nc">Answer</span><span class="o">.</span><span class="n">t</span><span class="p">()]}</span>
</span></span><span class="line"><span class="cl">  <span class="kd">def</span> <span class="n">next_unions</span><span class="p">(</span><span class="n">streams</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nc">Enum</span><span class="o">.</span><span class="n">reduce_while</span><span class="p">(</span><span class="n">streams</span><span class="p">,</span> <span class="p">{</span><span class="no">false</span><span class="p">,</span> <span class="p">[],</span> <span class="p">[</span><span class="nc">Answer</span><span class="o">.</span><span class="n">new</span><span class="p">()]},</span> <span class="o">&amp;</span><span class="n">collect_next_head</span><span class="o">/</span><span class="mi">2</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="ss">:empty</span> <span class="o">-&gt;</span> <span class="ss">:empty</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="no">false</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">}</span> <span class="o">-&gt;</span> <span class="ss">:done</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="no">true</span><span class="p">,</span> <span class="n">new_streams</span><span class="p">,</span> <span class="n">unions</span><span class="p">}</span> <span class="o">-&gt;</span> <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">new_streams</span><span class="p">,</span> <span class="n">unions</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">defp</span> <span class="n">collect_next_head</span><span class="p">(</span><span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="ss">continuation</span><span class="p">:</span> <span class="no">nil</span><span class="p">,</span> <span class="ss">evaluated</span><span class="p">:</span> <span class="p">[]},</span> <span class="n">_</span><span class="p">),</span> <span class="ss">do</span><span class="p">:</span> <span class="p">{</span><span class="ss">:halt</span><span class="p">,</span> <span class="ss">:empty</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">defp</span> <span class="n">collect_next_head</span><span class="p">(</span><span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="ss">evaluated</span><span class="p">:</span> <span class="p">[]}</span> <span class="o">=</span> <span class="n">stream</span><span class="p">,</span> <span class="p">{</span><span class="n">_</span><span class="p">,</span> <span class="n">streams</span><span class="p">,</span> <span class="n">acc</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">next</span> <span class="o">=</span> <span class="n">head</span><span class="p">(</span><span class="n">stream</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">done?</span><span class="p">(</span><span class="n">next</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="ss">:halt</span><span class="p">,</span> <span class="ss">:empty</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="ss">:cont</span><span class="p">,</span> <span class="p">{</span><span class="no">true</span><span class="p">,</span> <span class="p">[</span><span class="n">next</span> <span class="o">|</span> <span class="n">streams</span><span class="p">],</span> <span class="n">union_pulled_value</span><span class="p">(</span><span class="n">next</span><span class="p">,</span> <span class="n">acc</span><span class="p">)}}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">defp</span> <span class="n">collect_next_head</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">         <span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="ss">continuation</span><span class="p">:</span> <span class="n">continuation</span><span class="p">}</span> <span class="o">=</span> <span class="n">stream</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">         <span class="p">{</span><span class="no">false</span><span class="p">,</span> <span class="n">streams</span><span class="p">,</span> <span class="n">acc</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">       <span class="p">)</span>
</span></span><span class="line"><span class="cl">       <span class="ow">when</span> <span class="ow">not</span> <span class="n">is_nil</span><span class="p">(</span><span class="n">continuation</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">next</span> <span class="o">=</span> <span class="n">head</span><span class="p">(</span><span class="n">stream</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">done?</span><span class="p">(</span><span class="n">next</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="ss">:cont</span><span class="p">,</span> <span class="p">{</span><span class="no">false</span><span class="p">,</span> <span class="p">[</span><span class="n">next</span> <span class="o">|</span> <span class="n">streams</span><span class="p">],</span> <span class="n">union_unpulled_value</span><span class="p">(</span><span class="n">next</span><span class="p">,</span> <span class="n">acc</span><span class="p">)}}</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="ss">:cont</span><span class="p">,</span> <span class="p">{</span><span class="no">true</span><span class="p">,</span> <span class="p">[</span><span class="n">next</span> <span class="o">|</span> <span class="n">streams</span><span class="p">],</span> <span class="n">union_pulled_value</span><span class="p">(</span><span class="n">next</span><span class="p">,</span> <span class="n">acc</span><span class="p">)}}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">defp</span> <span class="n">collect_next_head</span><span class="p">(</span><span class="err">%</span><span class="n">__MODULE__</span><span class="p">{}</span> <span class="o">=</span> <span class="n">stream</span><span class="p">,</span> <span class="p">{</span><span class="n">pulled</span><span class="p">,</span> <span class="n">streams</span><span class="p">,</span> <span class="n">acc</span><span class="p">}),</span>
</span></span><span class="line"><span class="cl">    <span class="ss">do</span><span class="p">:</span> <span class="p">{</span><span class="ss">:cont</span><span class="p">,</span> <span class="p">{</span><span class="n">pulled</span><span class="p">,</span> <span class="p">[</span><span class="n">stream</span> <span class="o">|</span> <span class="n">streams</span><span class="p">],</span> <span class="n">union_unpulled_value</span><span class="p">(</span><span class="n">stream</span><span class="p">,</span> <span class="n">acc</span><span class="p">)}}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">defp</span> <span class="n">union_pulled_value</span><span class="p">(</span><span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="ss">evaluated</span><span class="p">:</span> <span class="p">[</span><span class="n">head</span> <span class="o">|</span> <span class="n">_</span><span class="p">]},</span> <span class="n">acc</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">acc</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="nc">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="nc">Answer</span><span class="o">.</span><span class="n">union</span><span class="p">(</span><span class="ni">&amp;1</span><span class="p">,</span> <span class="n">head</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="nc">Enum</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="o">&amp;</span><span class="nc">Kernel</span><span class="o">.!=</span><span class="p">(</span><span class="ni">&amp;1</span><span class="p">,</span> <span class="no">nil</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">defp</span> <span class="n">union_unpulled_value</span><span class="p">(</span><span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="ss">evaluated</span><span class="p">:</span> <span class="n">evaluated</span><span class="p">},</span> <span class="n">acc</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="nc">Enum</span><span class="o">.</span><span class="n">flat_map</span><span class="p">(</span><span class="n">evaluated</span><span class="p">,</span> <span class="k">fn</span> <span class="n">eval_item</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="n">acc</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="nc">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="nc">Answer</span><span class="o">.</span><span class="n">union</span><span class="p">(</span><span class="ni">&amp;1</span><span class="p">,</span> <span class="n">eval_item</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="nc">Enum</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="o">&amp;</span><span class="nc">Kernel</span><span class="o">.!=</span><span class="p">(</span><span class="ni">&amp;1</span><span class="p">,</span> <span class="no">nil</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div>
<p>Now we can confirm that everything works!
The only difference is that, because our new and is lazy, we need to use
<code>Enum.to_list/0</code> to force evaluation.</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-elixir">
        <span class="code-title"><i class="arrow fas fa-angle-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-elixir" data-lang="elixir"><span class="line"><span class="cl"><span class="n">arg1</span> <span class="o">=</span> <span class="p">[%{</span><span class="ss">a</span><span class="p">:</span> <span class="mi">1</span><span class="p">},</span> <span class="p">%{</span><span class="ss">a</span><span class="p">:</span> <span class="mi">2</span><span class="p">}]</span>
</span></span><span class="line"><span class="cl"><span class="n">arg2</span> <span class="o">=</span> <span class="p">[%{</span><span class="ss">b</span><span class="p">:</span> <span class="mi">1</span><span class="p">},</span> <span class="p">%{</span><span class="ss">b</span><span class="p">:</span> <span class="mi">2</span><span class="p">}]</span>
</span></span><span class="line"><span class="cl"><span class="n">arg3</span> <span class="o">=</span> <span class="p">[%{</span><span class="ss">c</span><span class="p">:</span> <span class="mi">1</span><span class="p">},</span> <span class="p">%{</span><span class="ss">c</span><span class="p">:</span> <span class="mi">2</span><span class="p">}]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nc">LazyCombinations.LazyAnd</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="nc">LazyCombinations.LazyAnd.SimplePartiallyEvaluated</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="n">arg1</span><span class="p">,</span> <span class="n">arg2</span><span class="p">,</span> <span class="n">arg3</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="o">|&gt;</span> <span class="nc">LazyCombinations.LazyAnd</span><span class="o">.</span><span class="n">resolve</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="o">|&gt;</span> <span class="nc">Enum</span><span class="o">.</span><span class="n">to_list</span><span class="p">()</span></span></span></code></pre></div></div>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-text">
        <span class="code-title"><i class="arrow fas fa-angle-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">[
</span></span><span class="line"><span class="cl">  %{c: 1, a: 1, b: 1},
</span></span><span class="line"><span class="cl">  %{c: 2, a: 1, b: 1},
</span></span><span class="line"><span class="cl">  %{c: 2, a: 2, b: 1},
</span></span><span class="line"><span class="cl">  %{c: 1, a: 2, b: 1},
</span></span><span class="line"><span class="cl">  %{c: 2, a: 2, b: 2},
</span></span><span class="line"><span class="cl">  %{c: 1, a: 2, b: 2},
</span></span><span class="line"><span class="cl">  %{c: 2, a: 1, b: 2},
</span></span><span class="line"><span class="cl">  %{c: 1, a: 1, b: 2}
</span></span><span class="line"><span class="cl">]</span></span></code></pre></div></div>
<h3 id="ensuring-equal-evaluation">Ensuring Equal Evaluation</h3>
<p>Now we&rsquo;ll test some infinite streams with a helper function that, again, builds
a stream of positive numbers.</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-elixir">
        <span class="code-title"><i class="arrow fas fa-angle-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-elixir" data-lang="elixir"><span class="line"><span class="cl"><span class="kd">defmodule</span> <span class="nc">Test</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="kd">def</span> <span class="n">build_answer_stream</span><span class="p">(</span><span class="n">var</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="nc">Stream</span><span class="o">.</span><span class="n">iterate</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="o">&amp;</span><span class="nc">Kernel</span><span class="o">.+</span><span class="p">(</span><span class="ni">&amp;1</span><span class="p">,</span> <span class="mi">1</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="nc">Stream</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="p">%{</span><span class="n">var</span> <span class="o">=&gt;</span> <span class="ni">&amp;1</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div>
<p>But, sadly, there is a subtle issue with this implementation.
If you take a look at the result below, you&rsquo;ll see that our <code>:b</code> stream never
gets past <code>1</code>.
This can be a bigger problem than it might first appear.
Consider the <a href="https://hexdocs.pm/guesswork/pythagorean_triples.html" target="_blank" rel="noopener noreffer ">Pythagorean triples example</a> and imagine our current setup.
If you request at least one answer, the function will never return because there
is no valid answer where <code>B</code> is <code>1</code>.</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-elixir">
        <span class="code-title"><i class="arrow fas fa-angle-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-elixir" data-lang="elixir"><span class="line"><span class="cl"><span class="n">arg1</span> <span class="o">=</span> <span class="nc">Test</span><span class="o">.</span><span class="n">build_answer_stream</span><span class="p">(</span><span class="ss">:a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">arg2</span> <span class="o">=</span> <span class="p">[%{</span><span class="ss">b</span><span class="p">:</span> <span class="mi">1</span><span class="p">},</span> <span class="p">%{</span><span class="ss">b</span><span class="p">:</span> <span class="mi">2</span><span class="p">}]</span>
</span></span><span class="line"><span class="cl"><span class="n">arg3</span> <span class="o">=</span> <span class="nc">Test</span><span class="o">.</span><span class="n">build_answer_stream</span><span class="p">(</span><span class="ss">:c</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nc">LazyCombinations.LazyAnd</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="nc">LazyCombinations.LazyAnd.SimplePartiallyEvaluated</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="n">arg1</span><span class="p">,</span> <span class="n">arg2</span><span class="p">,</span> <span class="n">arg3</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="o">|&gt;</span> <span class="nc">LazyCombinations.LazyAnd</span><span class="o">.</span><span class="n">resolve</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="o">|&gt;</span> <span class="nc">Enum</span><span class="o">.</span><span class="n">take</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span></span></span></code></pre></div></div>
<div class="code-block code-line-numbers" style="counter-reset: code-block 0">
    <div class="code-header language-text">
        <span class="code-title"><i class="arrow fas fa-angle-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">[
</span></span><span class="line"><span class="cl">  %{c: 1, a: 1, b: 1},
</span></span><span class="line"><span class="cl">  %{c: 2, a: 1, b: 1},
</span></span><span class="line"><span class="cl">  %{c: 2, a: 2, b: 1},
</span></span><span class="line"><span class="cl">  %{c: 1, a: 2, b: 1},
</span></span><span class="line"><span class="cl">  %{c: 3, a: 2, b: 1},
</span></span><span class="line"><span class="cl">  %{c: 3, a: 1, b: 1},
</span></span><span class="line"><span class="cl">  %{c: 3, a: 3, b: 1},
</span></span><span class="line"><span class="cl">  %{c: 2, a: 3, b: 1},
</span></span><span class="line"><span class="cl">  %{c: 1, a: 3, b: 1},
</span></span><span class="line"><span class="cl">  %{c: 4, a: 3, b: 1}
</span></span><span class="line"><span class="cl">]</span></span></code></pre></div></div>
<p>The above bug is a result of the order in which the streams are evaluated.
The first incomplete stream seen in the list will have a new value calculated.
In the above example, that first stream is <strong>always</strong> one of the infinite streams.
An answer is to ensure that the smaller streams are always seen first.</p>
<p>The module below achieves this by tracking the count (so we do not have to count
through every item of the <code>evaluated</code> list each time we order the streams) and
then sorting the streams on each call of <code>next_unions/1</code><sup id="fnref:8"><a href="#fn:8" class="footnote-ref" role="doc-noteref">8</a></sup> in lines 51-53.</p>
<div class="code-block code-line-numbers" style="counter-reset: code-block 0">
    <div class="code-header language-elixir">
        <span class="code-title"><i class="arrow fas fa-angle-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-elixir" data-lang="elixir"><span class="line"><span class="cl"><span class="kd">defmodule</span> <span class="nc">LazyCombinations.LazyAnd.OrderedPartiallyEvaluated</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="na">@behaviour</span> <span class="nc">LazyCombinations.LazyAnd</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">defstruct</span> <span class="p">[</span><span class="ss">:continuation</span><span class="p">,</span> <span class="ss">evaluated</span><span class="p">:</span> <span class="p">[],</span> <span class="ss">count</span><span class="p">:</span> <span class="mi">0</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kn">alias</span> <span class="nc">LazyCombinations.Answer</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="na">@type</span> <span class="n">t</span><span class="p">()</span> <span class="o">::</span> <span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="ss">continuation</span><span class="p">:</span> <span class="nc">Enumerable</span><span class="o">.</span><span class="n">continuation</span><span class="p">()</span> <span class="o">|</span> <span class="no">nil</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="ss">evaluated</span><span class="p">:</span> <span class="p">[</span><span class="nc">Answer</span><span class="o">.</span><span class="n">t</span><span class="p">()],</span>
</span></span><span class="line"><span class="cl">          <span class="ss">count</span><span class="p">:</span> <span class="n">integer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="na">@impl</span> <span class="no">true</span>
</span></span><span class="line"><span class="cl">  <span class="na">@spec</span> <span class="n">new</span><span class="p">(</span><span class="nc">Enumerable</span><span class="o">.</span><span class="n">t</span><span class="p">(</span><span class="nc">Answer</span><span class="o">.</span><span class="n">t</span><span class="p">()))</span> <span class="o">::</span> <span class="n">t</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="kd">def</span> <span class="n">new</span><span class="p">(</span><span class="n">stream</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">reduce_fun</span> <span class="o">=</span> <span class="k">fn</span> <span class="n">item</span><span class="p">,</span> <span class="n">_acc</span> <span class="o">-&gt;</span> <span class="p">{</span><span class="ss">:suspend</span><span class="p">,</span> <span class="n">item</span><span class="p">}</span> <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="ss">:suspended</span><span class="p">,</span> <span class="no">nil</span><span class="p">,</span> <span class="n">continuation</span><span class="p">}</span> <span class="o">=</span> <span class="nc">Enumerable</span><span class="o">.</span><span class="n">reduce</span><span class="p">(</span><span class="n">stream</span><span class="p">,</span> <span class="p">{</span><span class="ss">:suspend</span><span class="p">,</span> <span class="no">nil</span><span class="p">},</span> <span class="n">reduce_fun</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="ss">continuation</span><span class="p">:</span> <span class="n">continuation</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="na">@spec</span> <span class="n">head</span><span class="p">(</span><span class="n">t</span><span class="p">())</span> <span class="o">::</span> <span class="n">t</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="kd">def</span> <span class="n">head</span><span class="p">(</span><span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="ss">continuation</span><span class="p">:</span> <span class="no">nil</span><span class="p">}</span> <span class="o">=</span> <span class="n">stream</span><span class="p">),</span> <span class="ss">do</span><span class="p">:</span> <span class="n">stream</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">def</span> <span class="n">head</span><span class="p">(</span><span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="ss">continuation</span><span class="p">:</span> <span class="n">continuation</span><span class="p">,</span> <span class="ss">evaluated</span><span class="p">:</span> <span class="n">evaluated</span><span class="p">,</span> <span class="ss">count</span><span class="p">:</span> <span class="n">count</span><span class="p">}</span> <span class="o">=</span> <span class="n">partial</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="n">continuation</span><span class="o">.</span><span class="p">({</span><span class="ss">:cont</span><span class="p">,</span> <span class="no">nil</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="ss">:suspended</span><span class="p">,</span> <span class="n">item</span><span class="p">,</span> <span class="n">continuation</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="ss">continuation</span><span class="p">:</span> <span class="n">continuation</span><span class="p">,</span> <span class="ss">evaluated</span><span class="p">:</span> <span class="p">[</span><span class="n">item</span> <span class="o">|</span> <span class="n">evaluated</span><span class="p">],</span> <span class="ss">count</span><span class="p">:</span> <span class="n">count</span> <span class="o">+</span> <span class="mi">1</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="ss">:halted</span><span class="p">,</span> <span class="n">item</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="n">partial</span> <span class="o">|</span> <span class="ss">continuation</span><span class="p">:</span> <span class="no">nil</span><span class="p">,</span> <span class="ss">evaluated</span><span class="p">:</span> <span class="p">[</span><span class="n">item</span> <span class="o">|</span> <span class="n">evaluated</span><span class="p">]}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="ss">:done</span><span class="p">,</span> <span class="no">nil</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="n">partial</span> <span class="o">|</span> <span class="ss">continuation</span><span class="p">:</span> <span class="no">nil</span><span class="p">,</span> <span class="ss">evaluated</span><span class="p">:</span> <span class="n">evaluated</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="na">@spec</span> <span class="n">done?</span><span class="p">(</span><span class="n">t</span><span class="p">())</span> <span class="o">::</span> <span class="n">boolean</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="kd">def</span> <span class="n">done?</span><span class="p">(</span><span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="ss">continuation</span><span class="p">:</span> <span class="n">continuation</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">continuation</span> <span class="o">==</span> <span class="no">nil</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="na">@spec</span> <span class="n">count</span><span class="p">(</span><span class="n">t</span><span class="p">())</span> <span class="o">::</span> <span class="n">integer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="kd">def</span> <span class="n">count</span><span class="p">(</span><span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="ss">count</span><span class="p">:</span> <span class="n">count</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">count</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="na">@impl</span> <span class="no">true</span>
</span></span><span class="line"><span class="cl">  <span class="na">@spec</span> <span class="n">next_unions</span><span class="p">([</span><span class="n">t</span><span class="p">()])</span> <span class="o">::</span> <span class="ss">:empty</span> <span class="o">|</span> <span class="ss">:done</span> <span class="o">|</span> <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="p">[</span><span class="n">t</span><span class="p">()],</span> <span class="p">[</span><span class="nc">Answer</span><span class="o">.</span><span class="n">t</span><span class="p">()]}</span>
</span></span><span class="line"><span class="cl">  <span class="kd">def</span> <span class="n">next_unions</span><span class="p">(</span><span class="n">streams</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="n">streams</span>
</span></span><span class="line"><span class="cl">         <span class="o">|&gt;</span> <span class="nc">Enum</span><span class="o">.</span><span class="n">sort_by</span><span class="p">(</span><span class="o">&amp;</span><span class="n">count</span><span class="o">/</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">         <span class="o">|&gt;</span> <span class="nc">Enum</span><span class="o">.</span><span class="n">reduce_while</span><span class="p">({</span><span class="no">false</span><span class="p">,</span> <span class="p">[],</span> <span class="p">[</span><span class="nc">Answer</span><span class="o">.</span><span class="n">new</span><span class="p">()]},</span> <span class="o">&amp;</span><span class="n">collect_next_head</span><span class="o">/</span><span class="mi">2</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="ss">:empty</span> <span class="o">-&gt;</span> <span class="ss">:empty</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="no">false</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">}</span> <span class="o">-&gt;</span> <span class="ss">:done</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="no">true</span><span class="p">,</span> <span class="n">new_streams</span><span class="p">,</span> <span class="n">unions</span><span class="p">}</span> <span class="o">-&gt;</span> <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">new_streams</span><span class="p">,</span> <span class="n">unions</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">defp</span> <span class="n">collect_next_head</span><span class="p">(</span><span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="ss">continuation</span><span class="p">:</span> <span class="no">nil</span><span class="p">,</span> <span class="ss">evaluated</span><span class="p">:</span> <span class="p">[]},</span> <span class="n">_</span><span class="p">),</span> <span class="ss">do</span><span class="p">:</span> <span class="p">{</span><span class="ss">:halt</span><span class="p">,</span> <span class="ss">:empty</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">defp</span> <span class="n">collect_next_head</span><span class="p">(</span><span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="ss">evaluated</span><span class="p">:</span> <span class="p">[]}</span> <span class="o">=</span> <span class="n">stream</span><span class="p">,</span> <span class="p">{</span><span class="n">_</span><span class="p">,</span> <span class="n">streams</span><span class="p">,</span> <span class="n">acc</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">next</span> <span class="o">=</span> <span class="n">head</span><span class="p">(</span><span class="n">stream</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">done?</span><span class="p">(</span><span class="n">next</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="ss">:halt</span><span class="p">,</span> <span class="ss">:empty</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="ss">:cont</span><span class="p">,</span> <span class="p">{</span><span class="no">true</span><span class="p">,</span> <span class="p">[</span><span class="n">next</span> <span class="o">|</span> <span class="n">streams</span><span class="p">],</span> <span class="n">union_pulled_value</span><span class="p">(</span><span class="n">next</span><span class="p">,</span> <span class="n">acc</span><span class="p">)}}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">defp</span> <span class="n">collect_next_head</span><span class="p">(</span><span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="ss">continuation</span><span class="p">:</span> <span class="n">continuation</span><span class="p">}</span> <span class="o">=</span> <span class="n">stream</span><span class="p">,</span> <span class="p">{</span><span class="no">false</span><span class="p">,</span> <span class="n">streams</span><span class="p">,</span> <span class="n">acc</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">       <span class="ow">when</span> <span class="ow">not</span> <span class="n">is_nil</span><span class="p">(</span><span class="n">continuation</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">next</span> <span class="o">=</span> <span class="n">head</span><span class="p">(</span><span class="n">stream</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">done?</span><span class="p">(</span><span class="n">next</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="ss">:cont</span><span class="p">,</span> <span class="p">{</span><span class="no">false</span><span class="p">,</span> <span class="p">[</span><span class="n">next</span> <span class="o">|</span> <span class="n">streams</span><span class="p">],</span> <span class="n">union_unpulled_value</span><span class="p">(</span><span class="n">next</span><span class="p">,</span> <span class="n">acc</span><span class="p">)}}</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="ss">:cont</span><span class="p">,</span> <span class="p">{</span><span class="no">true</span><span class="p">,</span> <span class="p">[</span><span class="n">next</span> <span class="o">|</span> <span class="n">streams</span><span class="p">],</span> <span class="n">union_pulled_value</span><span class="p">(</span><span class="n">next</span><span class="p">,</span> <span class="n">acc</span><span class="p">)}}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">defp</span> <span class="n">collect_next_head</span><span class="p">(</span><span class="err">%</span><span class="n">__MODULE__</span><span class="p">{}</span> <span class="o">=</span> <span class="n">stream</span><span class="p">,</span> <span class="p">{</span><span class="n">pulled</span><span class="p">,</span> <span class="n">streams</span><span class="p">,</span> <span class="n">acc</span><span class="p">}),</span>
</span></span><span class="line"><span class="cl">    <span class="ss">do</span><span class="p">:</span> <span class="p">{</span><span class="ss">:cont</span><span class="p">,</span> <span class="p">{</span><span class="n">pulled</span><span class="p">,</span> <span class="p">[</span><span class="n">stream</span> <span class="o">|</span> <span class="n">streams</span><span class="p">],</span> <span class="n">union_unpulled_value</span><span class="p">(</span><span class="n">stream</span><span class="p">,</span> <span class="n">acc</span><span class="p">)}}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">defp</span> <span class="n">union_pulled_value</span><span class="p">(</span><span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="ss">evaluated</span><span class="p">:</span> <span class="p">[</span><span class="n">head</span> <span class="o">|</span> <span class="n">_</span><span class="p">]},</span> <span class="n">acc</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">acc</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="nc">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="nc">Answer</span><span class="o">.</span><span class="n">union</span><span class="p">(</span><span class="ni">&amp;1</span><span class="p">,</span> <span class="n">head</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="nc">Enum</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="o">&amp;</span><span class="nc">Kernel</span><span class="o">.!=</span><span class="p">(</span><span class="ni">&amp;1</span><span class="p">,</span> <span class="no">nil</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">defp</span> <span class="n">union_unpulled_value</span><span class="p">(</span><span class="err">%</span><span class="n">__MODULE__</span><span class="p">{</span><span class="ss">evaluated</span><span class="p">:</span> <span class="n">evaluated</span><span class="p">},</span> <span class="n">acc</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="nc">Enum</span><span class="o">.</span><span class="n">flat_map</span><span class="p">(</span><span class="n">evaluated</span><span class="p">,</span> <span class="k">fn</span> <span class="n">eval_item</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="n">acc</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="nc">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="nc">Answer</span><span class="o">.</span><span class="n">union</span><span class="p">(</span><span class="ni">&amp;1</span><span class="p">,</span> <span class="n">eval_item</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="nc">Enum</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="o">&amp;</span><span class="nc">Kernel</span><span class="o">.!=</span><span class="p">(</span><span class="ni">&amp;1</span><span class="p">,</span> <span class="no">nil</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div>
<p>Finally, we&rsquo;ll check that this change works.
And, low and behold, <code>:b</code> makes it to <code>2</code>!
Everything is being evaluated, and while we might have to check a lot of
possibilities, we won&rsquo;t ever get stuck because an answer set is never created.</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-elixir">
        <span class="code-title"><i class="arrow fas fa-angle-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-elixir" data-lang="elixir"><span class="line"><span class="cl"><span class="n">arg1</span> <span class="o">=</span> <span class="nc">Test</span><span class="o">.</span><span class="n">build_answer_stream</span><span class="p">(</span><span class="ss">:a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">arg2</span> <span class="o">=</span> <span class="p">[%{</span><span class="ss">b</span><span class="p">:</span> <span class="mi">1</span><span class="p">},</span> <span class="p">%{</span><span class="ss">b</span><span class="p">:</span> <span class="mi">2</span><span class="p">}]</span>
</span></span><span class="line"><span class="cl"><span class="n">arg3</span> <span class="o">=</span> <span class="nc">Test</span><span class="o">.</span><span class="n">build_answer_stream</span><span class="p">(</span><span class="ss">:c</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nc">LazyCombinations.LazyAnd</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="nc">LazyCombinations.LazyAnd.OrderedPartiallyEvaluated</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="n">arg1</span><span class="p">,</span> <span class="n">arg2</span><span class="p">,</span> <span class="n">arg3</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="o">|&gt;</span> <span class="nc">LazyCombinations.LazyAnd</span><span class="o">.</span><span class="n">resolve</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="o">|&gt;</span> <span class="nc">Enum</span><span class="o">.</span><span class="n">take</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span></span></span></code></pre></div></div>
<div class="code-block code-line-numbers" style="counter-reset: code-block 0">
    <div class="code-header language-text">
        <span class="code-title"><i class="arrow fas fa-angle-right fa-fw" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h fa-fw" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy fa-fw" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">[
</span></span><span class="line"><span class="cl">  %{c: 1, a: 1, b: 1},
</span></span><span class="line"><span class="cl">  %{c: 2, a: 1, b: 1},
</span></span><span class="line"><span class="cl">  %{c: 2, a: 2, b: 1},
</span></span><span class="line"><span class="cl">  %{c: 1, a: 2, b: 1},
</span></span><span class="line"><span class="cl">  %{c: 2, a: 2, b: 2},
</span></span><span class="line"><span class="cl">  %{c: 1, a: 2, b: 2},
</span></span><span class="line"><span class="cl">  %{c: 2, a: 1, b: 2},
</span></span><span class="line"><span class="cl">  %{c: 1, a: 1, b: 2},
</span></span><span class="line"><span class="cl">  %{c: 2, a: 3, b: 2},
</span></span><span class="line"><span class="cl">  %{c: 1, a: 3, b: 2}
</span></span><span class="line"><span class="cl">]</span></span></code></pre></div></div>
<h2 id="conclusion">Conclusion</h2>
<p>Elixir&rsquo;s standard library is very comprehensive (especially the <code>Enum</code> and
<code>Stream</code> modules), but not every use case is, or should be, implemented in it.
However, the abstractions that those modules are built on, like the <code>Enumerable</code>
protocol, provide a lot of leverage and let one build pretty complex behavior
with surprisingly little work.</p>
<h2 id="edits">Edits</h2>
<h3 id="2025-07-03">2025-07-03</h3>
<ul>
<li>Fix pythagorean triples link</li>
</ul>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>This can create issues when <code>Or</code> takes an infinite stream, but in
practice <code>Or</code> has been rarely used (<code>And</code> is far more common), so it hasn&rsquo;t
yet been a problem and other solutions would add a lot of complexity.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>All code can be found at
<a href="https://gitlab.com/smaller_infinity/lazy_combinations" target="_blank" rel="noopener noreffer ">https://gitlab.com/smaller_infinity/lazy_combinations</a>, and all the examples
here have been run on <code>elixir 1.17.3</code> and <code>erlang 26.2.5</code>.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>This is only possible because we don&rsquo;t need to support <code>Not</code>.
In Guesswork, answer sets are more complex.
But that would distract from our core problem.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>So surprising that when I first pulled this code from Guesswork&rsquo;s
commit history I didn&rsquo;t believe it worked &ndash; despite the fact that I had written
(and tested) this code myself, some ten or eleven months ago.
I&rsquo;ve experienced a lot of imposter syndrome in my time, but I don&rsquo;t think I&rsquo;ve ever
given it to myself so directly.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>A good example of this is in the <a href="https://hexdocs.pm/guesswork/pythagorean_triples.html" target="_blank" rel="noopener noreffer ">Pythagorean triples example</a> for Guesswork.&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p>This is, as of writing, done by a library in Guesswork.
However, I want to have as few dependencies as possible when explaining this issue,
and I was able to find this <a href="https://elixirforum.com/t/best-way-to-iterate-a-stream/24382" target="_blank" rel="noopener noreffer ">thread on elixirforum</a> that goes through, not just how
to do this, but why the standard library doesn&rsquo;t supply a way.&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7">
<p>This is obviously not how Guesswork does it.
We will need to try out (slightly) different versions of our state but only need
one version in the real implementation.&#160;<a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:8">
<p>My apologies for having to show so much code with so little change.
I think it is important that the examples be real, in that they are actually run
when building this page.&#160;<a href="#fnref:8" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></description>
</item>
</channel>
</rss>
