Functional Imperative Programming with Elixir (2018)
Fun to see someone make a complete while-loop macro. Occasionally I get tempted to make one, but almost always find it&#x27;s better to rethink a while loop into a Stream or just a for loop if possible to keep my logic in-place.<p>Take:<p><pre><code>    while(fn -&gt; Node.ping(some_node) != :ping end, 30_000)
</code></pre>
Could be written:<p><pre><code>    Stream.unfold(:pang, fn
       :pang -&gt; {:some_node, Node.ping(:&quot;some_node&quot;)}
       :ping -&gt; nil
    end) 
    |&gt; Stream.zip(Stream.interval(1_000)) 
    |&gt; Stream.take_while(fn {_, ts_cnt} -&gt; ts_cnt &lt; 30 end) 
    |&gt; Stream.run() 
</code></pre>
Which in Python would probably be something like (not using the Walrus, and made up system lib names):<p><pre><code>    node_response = Node.ping(&quot;some_node&quot;)
    start_time = Time.millis()
    while node_response == &quot;pang&quot;:
       node_response = Node.ping(&quot;some_node&quot;)
       if Time.time() - start_time &gt; 30000:
          break
       Time.sleep(1000)
</code></pre>
The Python version is pretty simple, but really isn&#x27;t any shorter. It also intertwines the timing logic and node ping actions. Although it takes a bit of practice to become fluent with the stream approach, it&#x27;s much more compose-able. Take the example in the article where you end up wanting to run a bunch of functions with a timeout on some interval, you can cut the middle 2 lines into a new function in a utility library. Then you could rewrite the code like (notice it&#x27;s just cutting and pasting complete lines):<p><pre><code>    Stream.unfold(:pang, fn
       :pang -&gt; {:&quot;some_node&quot;, Node.ping(:&quot;some_node&quot;)}
       :ping -&gt; nil
    end)
    |&gt; MyUtils.with_time(max: 30_000, every: 1_000)
    |&gt; MyUtils.log_max_ping_count() 
    |&gt; Stream.run()
</code></pre>
Usually re-writing something into this form makes me think of many programming tasks as a set of discrete steps. Do the first stream, then add the second step, and so forth. This tends to make it easier to dive back into code after a while as each step has to be incremental. Also a side note, if you _really_ want to cheat and need to use a mutable variable, you can use (abuse?) `Process.get&#x2F;1` and `Process.put&#x2F;2` with a for-loop. CouchDB uses a similar idea to create stateful iterators if I recall the code correctly.