<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Erlang | krondo</title><link>https://krondo.com/category/erlang/</link><description>Posts in Erlang on krondo</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><copyright>Dave Peticolas</copyright><lastBuildDate>Sun, 03 Oct 2010 04:40:10 +0000</lastBuildDate><atom:link href="https://krondo.com/category/erlang/" rel="self" type="application/rss+xml"/><item><title>Wheels within Wheels: Twisted and Erlang</title><link>https://krondo.com/twisted-and-erlang/</link><pubDate>Sun, 03 Oct 2010 04:40:10 +0000</pubDate><guid isPermaLink="true">https://krondo.com/twisted-and-erlang/</guid><category>Blather</category><category>Erlang</category><category>Programming</category><category>Python</category><category>Software</category><description>Part 20: Wheels within Wheels: Twisted and Erlang This continues the introduction started here. You can find an index to the entire series here.
Introduction One fact we’ve uncovered in this series is that mixing synchronous “plain Python” code with asynchronous Twisted code is not a straightforward task, since blocking for an indeterminate amount of time in a Twisted program will eliminate many of the benefits you are trying to achieve using the asynchronous model.</description><content:encoded>&lt;h2 id="part-20-wheels-within-wheels-twisted-and-erlang"&gt;Part 20: Wheels within Wheels: Twisted and Erlang&lt;/h2&gt;
&lt;p&gt;This continues the introduction started &lt;a href="https://krondo.com/in-which-we-begin-at-the-beginning/"&gt;here&lt;/a&gt;. You can find an index to the entire series &lt;a href="https://krondo.com/an-introduction-to-asynchronous-programming-and-twisted/"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;
&lt;p&gt;One fact we&amp;rsquo;ve uncovered in this series is that mixing synchronous &amp;ldquo;plain Python&amp;rdquo; code with asynchronous Twisted code is not a straightforward task, since blocking for an indeterminate amount of time in a Twisted program will eliminate many of the benefits you are trying to achieve using the asynchronous model.&lt;/p&gt;
&lt;p&gt;If this is your first introduction to asynchronous programming it may seem as if the knowledge you have gained is of somewhat limited applicability. You can use these new techniques inside of Twisted, but not in the much larger world of general Python code. And when working with Twisted, you are generally limited to libraries written specifically for use as part of a Twisted program, at least if you want to call them directly from the thread running the reactor.&lt;/p&gt;
&lt;p&gt;But asynchronous programming techniques have been around for quite some time and are hardly confined to Twisted. There are in fact a startling number of asynchronous programming frameworks in Python alone. A bit of &lt;a href="http://www.google.com/search?q=python+async+frameworks"&gt;searching around&lt;/a&gt; will probably yield a couple dozen of them. They differ from Twisted in their details, but the basic ideas (asynchronous I/O, processing data in small chunks across multiple data streams) are the same. So if you need, or choose, to use an alternative framework you will already have a head start having learned Twisted.&lt;/p&gt;
&lt;p&gt;And moving outside of Python, there are plenty of other languages and systems that are either based around, or make use of, the asynchronous programming model. Your knowledge of Twisted will continue to serve you as you explore the wider areas of this subject.&lt;/p&gt;
&lt;p&gt;In this Part we&amp;rsquo;re going to take a very brief look at &lt;a href="http://erlang.org"&gt;Erlang&lt;/a&gt;, a programming language and runtime system that makes extensive use of asynchronous programming concepts, but does so in a unique way. Please note this is not meant as a general introduction to Erlang. Rather, it is a short exploration of some of the ideas embedded in Erlang and how they connect with the ideas in Twisted. The basic theme is the knowledge you have gained learning Twisted can be applied when learning other technologies.&lt;/p&gt;
&lt;h3 id="callbacks-reimagined"&gt;Callbacks Reimagined&lt;/h3&gt;
&lt;p&gt;Consider &lt;a href="https://krondo.com/our-eye-beams-begin-to-twist/#figure6"&gt;Figure 6&lt;/a&gt;, a graphical representation of a callback. The principle callback in &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-client-3/get-poetry.py#L1"&gt;Poetry Client 3.0&lt;/a&gt;, introduced in &lt;a href="https://krondo.com/and-then-we-took-it-higher/"&gt;Part 6&lt;/a&gt;, and all subsequent poetry clients is the &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-client-3/get-poetry.py#L56"&gt;&lt;code&gt;dataReceived&lt;/code&gt;&lt;/a&gt; method. That callback is invoked each time we get a bit more poetry from one of the poetry servers we have connected to.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s say our client is downloading three poems from three different servers. Looking at things from the point of view of the reactor (and that&amp;rsquo;s the viewpoint we&amp;rsquo;ve emphasized the most in this series), we&amp;rsquo;ve got a single big loop which makes one or more callbacks each time it goes around. See Figure 40:&lt;/p&gt;
&lt;figure id="figure40" class="wp-caption alignnone"&gt;
&lt;a href="https://krondo.com/uploads/2010/09/reactor-2.png"&gt;&lt;img src="https://krondo.com/twisted-and-erlang/images/reactor-2.png" alt="Figure 40: callbacks from the reactor viewpoint" width="203" height="286" loading="lazy" decoding="async"&gt;&lt;/a&gt;
&lt;figcaption&gt;Figure 40: callbacks from the reactor viewpoint&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This figure shows the reactor happily spinning around, calling &lt;code&gt;dataReceived&lt;/code&gt; as the poetry comes in. Each invocation of &lt;code&gt;dataReceived&lt;/code&gt; is applied to one particular instance of our &lt;code&gt;PoetryProtocol&lt;/code&gt; class. And we know there are three instances because we are downloading three poems (and so there must be three connections).&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s think about this picture from the point of view of &lt;em&gt;one&lt;/em&gt; of those Protocol instances. Remember each Protocol is only concerned with a single connection (and thus a single poem). That instance &amp;ldquo;sees&amp;rdquo; a stream of method calls, each one bearing the next piece of the poem, like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;dataReceived&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;When I have fears&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;dataReceived&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34; that I may cease to be&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;dataReceived&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Before my pen has glea&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;dataReceived&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;n&amp;#39;d my teeming brain&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;While this isn&amp;rsquo;t strictly speaking an actual Python loop, we can conceptualize it as one:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;poetry_stream&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="c1"&gt;# pseudo-code&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;dataReceived&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can envision this &amp;ldquo;callback loop&amp;rdquo; in Figure 41:&lt;/p&gt;
&lt;figure id="figure41" class="wp-caption alignnone"&gt;
&lt;a href="https://krondo.com/uploads/2010/09/callback-loop.png"&gt;&lt;img src="https://krondo.com/twisted-and-erlang/images/callback-loop.png" alt="Figure 41: A virtual callback loop" width="192" height="168" loading="lazy" decoding="async"&gt;&lt;/a&gt;
&lt;figcaption&gt;Figure 41: A virtual callback loop&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Again, this is not a &lt;code&gt;for&lt;/code&gt; loop or a &lt;code&gt;while&lt;/code&gt; loop. The only significant Python loop in our poetry clients is the reactor. But we can think of each Protocol as a virtual loop that ticks around once each time some poetry for that particular poem comes in. With that in mind we can re-imagine the entire client in Figure 42:&lt;/p&gt;
&lt;figure id="figure42" class="wp-caption alignnone"&gt;
&lt;a href="https://krondo.com/uploads/2010/09/reactor-3.png"&gt;&lt;img src="https://krondo.com/twisted-and-erlang/images/reactor-3.png" alt="Figure 42: the reactor spinning some virtual loops" width="207" height="260" loading="lazy" decoding="async"&gt;&lt;/a&gt;
&lt;figcaption&gt;Figure 42: the reactor spinning some virtual loops&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In this figure we have one big loop, the reactor, and three virtual loops, the individual poetry protocol instances. The big loop spins around and, in so doing, causes the virtual loops to tick over as well, like a set of interlocking gears.&lt;/p&gt;
&lt;h3 id="enter-erlang"&gt;Enter Erlang&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://erlang.org"&gt;Erlang&lt;/a&gt;, like Python, is a general purpose dynamically typed programming language originally created in the 80&amp;rsquo;s. Unlike Python, Erlang is functional rather than object-oriented, and has a syntax reminiscent of &lt;a href="http://en.wikipedia.org/wiki/Prolog"&gt;Prolog&lt;/a&gt;, the language in which Erlang was originally implemented. Erlang was designed for building highly reliable distributed telephony systems, and thus Erlang contains extensive networking support.&lt;/p&gt;
&lt;p&gt;One of Erlang&amp;rsquo;s most distinctive features is a concurrency model involving lightweight processes. An Erlang process is neither an operating system process nor an operating system thread. Rather, it is an independently running function inside the Erlang runtime with its own stack. Erlang processes are not lightweight threads because Erlang processes cannot share state (and most data types are immutable anyway, Erlang being a functional programming language). An Erlang process can interact with other Erlang processes only by sending messages, and messages are always, at least conceptually, copied and never shared.&lt;/p&gt;
&lt;p&gt;So an Erlang program might look like Figure 43:&lt;/p&gt;
&lt;figure id="figure43" class="wp-caption alignnone"&gt;
&lt;a href="https://krondo.com/uploads/2010/09/erlang-11.png"&gt;&lt;img src="https://krondo.com/twisted-and-erlang/images/erlang-11.png" alt="Figure 43: An Erlang program with three processes" width="203" height="290" loading="lazy" decoding="async"&gt;&lt;/a&gt;
&lt;figcaption&gt;Figure 43: An Erlang program with three processes&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In this figure the individual processes have become &amp;ldquo;real&amp;rdquo;, since processes are first-class constructs in Erlang, just like objects are in Python. And the runtime has become &amp;ldquo;virtual&amp;rdquo;, not because it isn&amp;rsquo;t there, but because it&amp;rsquo;s not necessarily a simple loop. The Erlang runtime may be multi-threaded and, as it has to implement a full-blown programming language, it&amp;rsquo;s in charge of a lot more than handling asynchronous I/O. Furthermore, a language runtime is not so much an extra construct, like the reactor in Twisted, as the medium in which the Erlang processes and code execute.&lt;/p&gt;
&lt;p&gt;So an even better picture of an Erlang program might be Figure 44:&lt;/p&gt;
&lt;figure id="figure44" class="wp-caption alignnone"&gt;
&lt;a href="https://krondo.com/uploads/2010/09/erlang-2.png"&gt;&lt;img src="https://krondo.com/twisted-and-erlang/images/erlang-2.png" alt="Figure 44: An Erlang program with several processes" width="333" height="239" loading="lazy" decoding="async"&gt;&lt;/a&gt;
&lt;figcaption&gt;Figure 44: An Erlang program with several processes&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Of course, the Erlang runtime does have to use asynchronous I/O and one or more select loops, because Erlang allows you to create &lt;em&gt;lots&lt;/em&gt; of processes. Large Erlang programs can start tens or hundreds of thousands of Erlang processes, so allocating an actual OS thread to each one is simply out of the question. If Erlang is going to allow multiple processes to perform I/O, and still allow other processes to run even if that I/O blocks, then asynchronous I/O will have to be involved.&lt;/p&gt;
&lt;p&gt;Note that our picture of an Erlang program has each process running &amp;ldquo;under its own power&amp;rdquo;, rather than being spun around by callbacks. And that is very much the case. With the job of the reactor subsumed into the fabric of the Erlang runtime, the callback no longer has a central role to play. What would, in Twisted, be solved by using a callback would, in Erlang, be solved by sending an asynchronous message from one Erlang process to another.&lt;/p&gt;
&lt;h3 id="an-erlang-poetry-client"&gt;An Erlang Poetry Client&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s look at an Erlang poetry client. We&amp;rsquo;re going to jump straight to a working version instead of building up slowly like we did with Twisted. Again, this isn&amp;rsquo;t meant as a complete Erlang introduction. But if it piques your interest, we suggest some more in-depth reading at the end of this Part.&lt;/p&gt;
&lt;p&gt;The Erlang client is listed in &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/erlang-client-1/get-poetry#L1"&gt;erlang-client-1/get-poetry&lt;/a&gt;. In order to run it you will, of course, need &lt;a href="http://erlang.org"&gt;Erlang&lt;/a&gt; installed. Here&amp;rsquo;s the code for the &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/erlang-client-1/get-poetry#L96"&gt;&lt;code&gt;main&lt;/code&gt;&lt;/a&gt; function, which serves a similar purpose as the main functions in our Python clients:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-erlang" data-lang="erlang"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;([])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;Addresses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;Main&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nn"&gt;erlang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;spawn_monitor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;get_poetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;TaskNum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;TaskNum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Addr&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Addresses&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;collect_poems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Addresses&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[]).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you&amp;rsquo;ve never seen Prolog or a similar language before then Erlang syntax is going to seem a little odd. But some people say that about Python, too. The main function is defined by two separate clauses, separated by a semicolon. Erlang chooses which clause to run by matching the arguments, so the first clause only runs if we execute the client without providing any command line arguments, and it just prints out a help message. The second clause is where all the action is.&lt;/p&gt;
&lt;p&gt;Individual statements in an Erlang function are separated by commas, and all functions end with a period. Let&amp;rsquo;s take each line in the second clause one at a time. The first line is just parsing the command line arguments and binding them to a variable (all variables in Erlang must be capitalized). The second line is using the Erlang &lt;code&gt;self&lt;/code&gt; function to get the process ID of the currently running Erlang process (not OS process). Since this is the main function you can kind of think of it as the equivalent of the &lt;code&gt;__main__&lt;/code&gt; module in Python. The third line is the most interesting:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-erlang" data-lang="erlang"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nn"&gt;erlang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;spawn_monitor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;get_poetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;TaskNum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;TaskNum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Addr&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Addresses&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This statement is an Erlang list comprehension, with a syntax similar to that in Python. It is spawning new Erlang processes, one for each poetry server we need to contact. And each process will run the same function (&lt;code&gt;get_poetry&lt;/code&gt;) but with different arguments specific to that server. We also pass the PID of the main process so the new processes can send the poetry back (you generally need the PID of a process to send a message to it).&lt;/p&gt;
&lt;p&gt;The last statement in &lt;code&gt;main&lt;/code&gt; calls the &lt;code&gt;collect_poems&lt;/code&gt; function which waits for the poetry to come back and for the &lt;code&gt;get_poetry&lt;/code&gt; processes to finish. We&amp;rsquo;ll look at the other functions in a bit, but first you might compare this Erlang &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/erlang-client-1/get-poetry#L96"&gt;&lt;code&gt;main&lt;/code&gt;&lt;/a&gt; function to the &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/twisted-client-4/get-poetry.py#L96"&gt;equivalent main&lt;/a&gt; in one of our Twisted clients.&lt;/p&gt;
&lt;p&gt;Now let&amp;rsquo;s look at the Erlang &lt;code&gt;get_poetry&lt;/code&gt; function. There are actually two functions in our script called &lt;code&gt;get_poetry&lt;/code&gt;. In Erlang, a function is identified by both name and arity, so our script contains two separate functions, &lt;code&gt;get_poetry/3&lt;/code&gt; and &lt;code&gt;get_poetry/4&lt;/code&gt; which accept three and four arguments respectively. Here&amp;rsquo;s &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/erlang-client-1/get-poetry#L79"&gt;&lt;code&gt;get_poetry/3&lt;/code&gt;&lt;/a&gt;, which is spawned by &lt;code&gt;main&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-erlang" data-lang="erlang"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;get_poetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Tasknum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;Addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;gen_tcp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;binary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;false&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;packet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}]),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;get_poetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Tasknum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This function first makes a TCP connection, just like the Twisted client &lt;code&gt;get_poetry&lt;/code&gt;. But then, instead of returning, it proceeds to use that TCP connection by calling &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/erlang-client-1/get-poetry#L85"&gt;&lt;code&gt;get_poetry/4&lt;/code&gt;&lt;/a&gt;, listed below:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-erlang" data-lang="erlang"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;get_poetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Tasknum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Packets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nn"&gt;gen_tcp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Packet&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Task &lt;/span&gt;&lt;span class="si"&gt;~w&lt;/span&gt;&lt;span class="s"&gt;: got &lt;/span&gt;&lt;span class="si"&gt;~w&lt;/span&gt;&lt;span class="s"&gt; bytes of poetry from &lt;/span&gt;&lt;span class="si"&gt;~s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Tasknum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Packet&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;peername&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;)]),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;get_poetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Tasknum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Packet&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="nv"&gt;Packets&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;_}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;Main&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;poem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;list_to_binary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;lists&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Packets&lt;/span&gt;&lt;span class="p"&gt;))}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This Erlang function is doing the work of the &lt;code&gt;PoetryProtocol&lt;/code&gt; from our Twisted client, except it does so using blocking function calls. The &lt;code&gt;gen_tcp:recv&lt;/code&gt; function waits until some data arrives on the socket (or the socket is closed), however long that might be. But a &amp;ldquo;blocking&amp;rdquo; function in Erlang only blocks the process running the function, not the entire Erlang runtime. That TCP socket isn&amp;rsquo;t really a blocking socket (you can&amp;rsquo;t make a true blocking socket in pure Erlang code). For each of those Erlang sockets there is, somewhere inside the Erlang runtime, a &amp;ldquo;real&amp;rdquo; TCP socket set to non-blocking mode and used as part of a select loop.&lt;/p&gt;
&lt;p&gt;But the Erlang process doesn&amp;rsquo;t have to know about any of that. It just waits for some data to arrive and, if it blocks, some other Erlang process can run instead. And even if a process never blocks, the Erlang runtime is free to switch execution from that process to another at any time. In other words, Erlang has a non-cooperative concurrency model.&lt;/p&gt;
&lt;p&gt;Notice that &lt;code&gt;get_poetry/4&lt;/code&gt;, after receiving a bit of poem, proceeds by recursively calling itself. To an imperative language programmer this might seem like a recipe for running out of memory, but the Erlang compiler can optimize &amp;ldquo;tail&amp;rdquo; calls (function calls that are the last statement in a function) into loops. And this highlights another curious parallel between the Erlang and Twisted clients. In the Twisted client, the &amp;ldquo;virtual&amp;rdquo; loops are created by the reactor calling the same function (&lt;code&gt;dataReceived&lt;/code&gt;) over and over again. And in the Erlang client, the &amp;ldquo;real&amp;rdquo; processes running (&lt;code&gt;get_poetry/4&lt;/code&gt;) form loops by calling &lt;em&gt;themselves&lt;/em&gt; over and over again via &lt;a href="http://stackoverflow.com/questions/310974/what-is-tail-call-optimization"&gt;tail-call optimization&lt;/a&gt;. How about that.&lt;/p&gt;
&lt;p&gt;If the connection is closed, the last thing &lt;code&gt;get_poetry&lt;/code&gt; does is send the poem to the main process. That also ends the process that &lt;code&gt;get_poetry&lt;/code&gt; is running, as there is nothing left for it to do.&lt;/p&gt;
&lt;p&gt;The remaining key function in our Erlang client is &lt;a href="http://github.com/jdavisp3/twisted-intro/blob/master/erlang-client-1/get-poetry#L58"&gt;&lt;code&gt;collect_poems&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-erlang" data-lang="erlang"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;collect_poems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Poems&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;~s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;P&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="nv"&gt;P&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="nv"&gt;Poems&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;collect_poems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;N&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Poems&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;receive&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;&amp;#39;DOWN&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;_,&lt;/span&gt; &lt;span class="p"&gt;_,&lt;/span&gt; &lt;span class="p"&gt;_,&lt;/span&gt; &lt;span class="p"&gt;_}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;collect_poems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;N&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Poems&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;poem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Poem&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;collect_poems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;N&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Poem&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="nv"&gt;Poems&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This function is run by the main process and, like &lt;code&gt;get_poetry&lt;/code&gt;, it recursively loops on itself. It also blocks. The &lt;code&gt;receive&lt;/code&gt; statement tells the process to wait for a message to arrive that matches one of the given patterns, and then extract the message from its &amp;ldquo;mailbox&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;collect_poems&lt;/code&gt; function waits for two kinds of messages: poems and &amp;ldquo;DOWN&amp;rdquo; notifications. The latter is a message sent to the main process when one of the &lt;code&gt;get_poetry&lt;/code&gt; processes dies for any reason (this is the &lt;code&gt;monitor&lt;/code&gt; part of &lt;code&gt;spawn_monitor&lt;/code&gt;). By counting &lt;code&gt;DOWN&lt;/code&gt; messages, we know when all the poetry has finished. The former is a message from one of the &lt;code&gt;get_poetry&lt;/code&gt; processes containing one complete poem.&lt;/p&gt;
&lt;p&gt;Ok, let&amp;rsquo;s take the Erlang client out for a spin. First start up three slow poetry servers:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python blocking-server/slowpoetry.py --port &lt;span class="m"&gt;10001&lt;/span&gt; poetry/fascination.txt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python blocking-server/slowpoetry.py --port &lt;span class="m"&gt;10002&lt;/span&gt; poetry/science.txt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python blocking-server/slowpoetry.py --port &lt;span class="m"&gt;10003&lt;/span&gt; poetry/ecstasy.txt --num-bytes &lt;span class="m"&gt;30&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we can run the Erlang client, which has a similar command-line syntax as the Python clients. If you are on a Linux or other UNIX-like system, then you should be able to run the client directly (assuming you have Erlang installed and available in your PATH). On Windows you will probably need to run the escript program, with the path to the Erlang client as the first argument (with the remaining arguments for the Erlang client itself).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./erlang-client-1/get-poetry &lt;span class="m"&gt;10001&lt;/span&gt; &lt;span class="m"&gt;10002&lt;/span&gt; &lt;span class="m"&gt;10003&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After that you should see output like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Task 3: got 30 bytes of poetry from 127:0:0:1:10003
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Task 2: got 10 bytes of poetry from 127:0:0:1:10002
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Task 1: got 10 bytes of poetry from 127:0:0:1:10001
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is just like one of our earlier Python clients where we print a message for each little bit of poetry we get. When all the poems have finished the client should print out the complete text of each one. Notice the client is switching back and forth between all the servers depending on which one has some poetry to send.&lt;/p&gt;
&lt;p&gt;Figure 45 shows the process structure of our Erlang client:&lt;/p&gt;
&lt;figure id="figure45" class="wp-caption alignnone"&gt;
&lt;a href="https://krondo.com/uploads/2010/09/erlang-3.png"&gt;&lt;img src="https://krondo.com/twisted-and-erlang/images/erlang-3.png" alt="Figure 45: Erlang poetry client" width="274" height="236" loading="lazy" decoding="async"&gt;&lt;/a&gt;
&lt;figcaption&gt;Figure 45: Erlang poetry client&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This figure shows three &lt;code&gt;get_poetry&lt;/code&gt; processes (one per server) and one main process. You can also see the messages that flow from the poetry processes to main process.&lt;/p&gt;
&lt;p&gt;So what happens if one of those servers is down? Let&amp;rsquo;s try it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./erlang-client-1/get-poetry &lt;span class="m"&gt;10001&lt;/span&gt; &lt;span class="m"&gt;10005&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The above command contains one active port (assuming you left all the earlier poetry servers running) and one inactive port (assuming you aren&amp;rsquo;t running any server on port 10005). And we get some output like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Task 1: got 10 bytes of poetry from 127:0:0:1:10001
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;=ERROR REPORT==== 25-Sep-2010::21:02:10 ===
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Error in process &amp;lt;0.33.0&amp;gt; with exit value: {{badmatch,{error,econnrefused}},[{erl_eval,expr,3}]}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Task 1: got 10 bytes of poetry from 127:0:0:1:10001
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Task 1: got 10 bytes of poetry from 127:0:0:1:10001
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And eventually the client finishes downloading the poem from the active server, prints out the poem, and exits. So how did the &lt;code&gt;main&lt;/code&gt; function know that both processes were done? That error message is the clue. The error happens when &lt;code&gt;get_poetry&lt;/code&gt; tries to connect to the server and gets a connection refused error instead of the expected value (&lt;code&gt;{ok, Socket}&lt;/code&gt;). The resulting exception is called badmatch because Erlang &amp;ldquo;assignment&amp;rdquo; statements are really pattern-matching operations.&lt;/p&gt;
&lt;p&gt;An unhandled exception in an Erlang process causes the process to &amp;ldquo;crash&amp;rdquo;, which means the process stops running and all of its resources are garbage collected. But the &lt;code&gt;main&lt;/code&gt; process, which is monitoring all of the &lt;code&gt;get_poetry&lt;/code&gt; processes, will receive a &lt;code&gt;DOWN&lt;/code&gt; message when any of those processes stops running for any reason. And thus our client exits when it should instead of running forever.&lt;/p&gt;
&lt;h3 id="discussion"&gt;Discussion&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s take stock of some of the parallels between the Twisted and Erlang clients:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Both clients connect (or try to connect) to all the poetry servers at once.&lt;/li&gt;
&lt;li&gt;Both clients receive data from the servers as soon as it comes in, regardless of which server delivers the data.&lt;/li&gt;
&lt;li&gt;Both clients process the poetry in little bits, and thus have to save the portion of the poems received thus far.&lt;/li&gt;
&lt;li&gt;Both clients create an &amp;ldquo;object&amp;rdquo; (either a Python object or an Erlang process) to handle all the work for one particular server.&lt;/li&gt;
&lt;li&gt;Both clients have to carefully determine when all the poetry has finished, regardless of whether a particular download succeeded or failed.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And finally, the &lt;code&gt;main&lt;/code&gt; functions in both clients asynchronously receive poems and &amp;ldquo;task done&amp;rdquo; notifications. In the Twisted client this information is delivered via a &lt;code&gt;Deferred&lt;/code&gt; while the Erlang client receives inter-process messages.&lt;/p&gt;
&lt;p&gt;Notice how similar both clients are, in both their overall strategy and the structure of their code. The mechanics are a bit different, with objects, deferreds, and callbacks on the one hand and processes and messages on the other. But the high-level mental models of both clients are quite similar, and it&amp;rsquo;s pretty easy to move from one to the other once you are familiar with both.&lt;/p&gt;
&lt;p&gt;Even the reactor pattern reappears in the Erlang client in miniaturized form. Each Erlang process in our poetry client eventually turns into a recursive loop that:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Waits for something to happen (a bit of poetry comes in, a poem is delivered, another process finishes), and&lt;/li&gt;
&lt;li&gt;Takes some appropriate action.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can think of an Erlang program as a big collection of little reactors, each spinning around and occasionally sending a message to another little reactor (which will process that message as just another event).&lt;/p&gt;
&lt;p&gt;And if you delve deeper into Erlang you will find callbacks making an appearance. The Erlang &lt;a href="http://www.erlang.org/doc/man/gen_server.html"&gt;&lt;code&gt;gen_server&lt;/code&gt;&lt;/a&gt; process is a generic reactor loop that you &amp;ldquo;instantiate&amp;rdquo; by providing a fixed set of callback functions, a pattern repeated elsewhere in the Erlang system.&lt;/p&gt;
&lt;p&gt;So if, having learned Twisted, you ever decide to give Erlang a try I think you will find yourself in familiar mental territory.&lt;/p&gt;
&lt;h3 id="further-reading"&gt;Further Reading&lt;/h3&gt;
&lt;p&gt;In this Part we&amp;rsquo;ve focused on the similarities between Twisted and Erlang, but there are of course many differences. One particularly unique feature of Erlang is its approach to error handling. A large Erlang program is structured as a tree of processes, with &amp;ldquo;supervisors&amp;rdquo; in the higher branches and &amp;ldquo;workers&amp;rdquo; in the leaves. And if a worker process crashes, a supervisor process will notice and take some action (typically restarting the failed worker).&lt;/p&gt;
&lt;p&gt;If you are interested in learning more Erlang then you are in luck. Several Erlang books have either been published recently, or will be published shortly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.amazon.com/exec/obidos/ASIN/193435600X/krondonet-20"&gt;Programming Erlang&lt;/a&gt; — written by one of Erlang&amp;rsquo;s inventors. A great introduction to the language.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.amazon.com/exec/obidos/ASIN/0596518188/krondonet-20"&gt;Erlang Programming&lt;/a&gt; — this complements the Armstrong book and goes into more detail in several key areas.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.amazon.com/exec/obidos/ASIN/1933988789/krondonet-20"&gt;Erlang and OTP in Action&lt;/a&gt; — this hasn&amp;rsquo;t been published yet, but I am eagerly awaiting my copy. Neither of the first two books really addresses OTP, the Erlang framework for building large apps. Full disclosure: two of the authors are friends of mine.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Well that&amp;rsquo;s it for Erlang. In the &lt;a href="https://krondo.com/twisted-and-haskell/"&gt;next Part&lt;/a&gt; we will look at Haskell, another functional language with a very different feel from either Python or Erlang. Nevertheless, we shall endeavor to find some common ground.&lt;/p&gt;
&lt;h3 id="suggested-exercises-for-the-highly-motivated"&gt;Suggested Exercises for the Highly Motivated&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Go through the Erlang and Python clients and identify where they are similar and where they differ. How do they each handle errors (like a failure to connect to a poetry server)?&lt;/li&gt;
&lt;li&gt;Simplify the Erlang client so it no longer prints out each bit of poetry that comes in (so you don&amp;rsquo;t need to keep track of task numbers either).&lt;/li&gt;
&lt;li&gt;Modify the Erlang client to measure the time it takes to download each poem.&lt;/li&gt;
&lt;li&gt;Modify the Erlang client to print out the poems in the same order as they were given on the command line.&lt;/li&gt;
&lt;li&gt;Modify the Erlang client to print out a more readable error message when we can&amp;rsquo;t connect to a poetry server.&lt;/li&gt;
&lt;li&gt;Write Erlang versions of the poetry servers we made with Twisted.&lt;/li&gt;
&lt;/ol&gt;</content:encoded></item><item><title>Book: Concurrent Programming in Erlang (2nd Edition)</title><link>https://krondo.com/book-concurrent-programming-in-erlang-2nd-edition/</link><pubDate>Mon, 10 May 2010 00:15:31 +0000</pubDate><guid isPermaLink="true">https://krondo.com/book-concurrent-programming-in-erlang-2nd-edition/</guid><category>Books</category><category>Erlang</category><category>Programming</category><category>Software</category><description> Although out of date in its technical details, the later chapters contain some excellent examples of distributed software construction in Erlang.</description><content:encoded>&lt;p&gt;&lt;a href="http://www.amazon.com/exec/obidos/ASIN/013508301X/krondonet-20"&gt;&lt;img src="https://krondo.com/book-concurrent-programming-in-erlang-2nd-edition/images/61JA1MHH2XL._SL160_.jpg" alt="" width="118" height="160" loading="lazy" decoding="async"&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Although out of date in its technical details, the later chapters contain some excellent examples of distributed software construction in Erlang.&lt;/p&gt;</content:encoded></item><item><title>Book: Erlang Programming</title><link>https://krondo.com/book-erlang-programming/</link><pubDate>Tue, 06 Apr 2010 05:20:50 +0000</pubDate><guid isPermaLink="true">https://krondo.com/book-erlang-programming/</guid><category>Books</category><category>Erlang</category><category>Programming</category><category>Software</category><description>This is a nice complement to the Armstrong book. In particular, it has a great discussion of the Erlang tracing mechanisms.</description><content:encoded>&lt;p&gt;This is a nice complement to the Armstrong &lt;a href="https://krondo.com/programming-erlang/"&gt;book&lt;/a&gt;. In particular, it has a great discussion of the Erlang tracing mechanisms.&lt;/p&gt;</content:encoded></item><item><title>Book: Programming Erlang</title><link>https://krondo.com/programming-erlang/</link><pubDate>Mon, 29 Oct 2007 01:40:46 +0000</pubDate><guid isPermaLink="true">https://krondo.com/programming-erlang/</guid><category>Books</category><category>Erlang</category><category>Programming</category><category>Software</category><description> An excellent introduction to the intriguing programming language and distributed software framework Erlang.</description><content:encoded>&lt;p&gt;&lt;a href="http://www.amazon.com/exec/obidos/ASIN/193435600X/krondonet-20"&gt;&lt;img src="https://krondo.com/programming-erlang/images/41XOOpdBzKL._SL160_.jpg" alt="" width="134" height="160" loading="lazy" decoding="async"&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;An excellent introduction to the intriguing programming language and distributed software framework &lt;a href="http://erlang.org"&gt;Erlang&lt;/a&gt;.&lt;/p&gt;</content:encoded></item></channel></rss>