<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Alistair Israel on Medium]]></title>
        <description><![CDATA[Stories by Alistair Israel on Medium]]></description>
        <link>https://medium.com/@alistairisrael?source=rss-c0e66d40ab34------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*-zRq149r-neSAV_dXZB1bQ.jpeg</url>
            <title>Stories by Alistair Israel on Medium</title>
            <link>https://medium.com/@alistairisrael?source=rss-c0e66d40ab34------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 08:00:00 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@alistairisrael/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Introducing: datu — a data file utility]]></title>
            <link>https://medium.com/@alistairisrael/introducing-datu-a-data-file-utility-7ccd4c4aa857?source=rss-c0e66d40ab34------2</link>
            <guid isPermaLink="false">https://medium.com/p/7ccd4c4aa857</guid>
            <category><![CDATA[rust]]></category>
            <category><![CDATA[data-pipeline]]></category>
            <category><![CDATA[parquet]]></category>
            <category><![CDATA[avro]]></category>
            <category><![CDATA[cursor]]></category>
            <dc:creator><![CDATA[Alistair Israel]]></dc:creator>
            <pubDate>Mon, 16 Feb 2026 02:34:04 GMT</pubDate>
            <atom:updated>2026-06-12T13:48:41.954Z</atom:updated>
            <content:encoded><![CDATA[<h4><em>datu</em> (Filipino) — a traditional chief or local leader</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*D1sESS0N3Vfpd5wGSHKCWw.png" /></figure><p><a href="https://github.com/aisrael/datu">https://github.com/aisrael/datu</a></p><p>My software engineering journey has certainly taken me places. From hacking on Win32 drivers in C/C++, to Web apps in Ruby on Rails, to mobile apps, to “Digital Transformation” and “Big Data”, to Cloud infrastructure, then DevOps, and, most recently, data engineering and ML/AI.</p><p>Of course, through all that I’ve encountered and worked with a variety of data file formats, from the ubiquitous CSV, to non-technical friendly .XLS/.XLSX, to JSON, YAML, and then industry heavyweights like <a href="https://parquet.apache.org/">Parquet</a>, <a href="https://avro.apache.org/">Avro</a>, <a href="https://arrow.apache.org/docs/python/orc.html">ORC</a>, and rookies like <a href="https://github.com/future-file-format/F3">f3</a>.</p><p>What I found remarkable was, when I wanted to quickly convert a sample .parq file to .avro, there didn’t seem to be a widely-used, simple CLI tool to do so.</p><p>I thought to myself, surely there must be something like <a href="https://imagemagick.org/">ImageMagick</a>, but for data files instead of images? (The closest I’ve found is <a href="https://miller.readthedocs.io/">Miller</a>, but it only handles text-based formats (e.g. CSV, TSV, JSON).</p><p>So, me being me and me loving writing tools in Rust, I set about to write one.</p><p>The hardest part was choosing a name. All the TLAs I wanted were already taken on <a href="https://crates.io/">crates.io</a> — “dtf”, “dfu”, “dmt” (data multi-tool). Once I found “dtfu” available, I started using that, then realized with a small change, another, cooler name was also available — thus, datu.</p><h3>A type-safe pipeline in Rust</h3><p>Also me being me, I couldn’t just write this as a straightforward collection of convert_avro_to_parquet(), convert_parquet_to_avro(), etc. functions.</p><p>Instead, I decided to model it internally as a pipeline — after all, pretty much all of DevOps, data/ML/AI engineering these days are pipelines all the way down.</p><p>I also didn’t just want to model this as some ‘generic’ pipeline that had some ‘catch-all’ context or state that could be passed around any/all steps, essentially a glorified Hash or dict that could contain anything.</p><p>Having read enough articles about how Rust lets you write type-safe state machines where the compiler itself prevents illegal state transitions, I thought to myself: we should be able to write a type-safe pipeline in Rust, where the compiler itself can prevent illegal stage transitions.</p><p>I looked at existing crates if they could be useful, such as <a href="https://github.com/mavdi/deltaflow">deltaflow</a> and <a href="https://github.com/Industrial/streamweave">streamweave</a>, and while impressive, I thought they were a bit overkill for all I needed to do, for now.</p><p>I took me a while to express what I wanted and how I wanted it to work, but eventually came up with something that works and let me move on.</p><p>That is, given a</p><pre>trait Step {<br>  type Input;<br>  type Output;<br><br>  fn execute(self, input: Self::Input) <br>    -&gt; Result&lt;Self::Output&gt;<br>}</pre><p>Then one can have a</p><pre>impl Step for FileReader {<br>  Input = String<br>  Output = Vec&lt;String&gt;<br><br>  ...</pre><p>And</p><pre>impl Step for Printer {<br>  Input = Vec&lt;String&gt;<br>  Output = ()<br><br>  ...</pre><p>And the Rust compiler itself <strong>will prevent you from composing a pipeline that goes </strong>Printer |&gt; FileReader!</p><p>That was probably the hardest part, and the part that I wrote the most code in by hand.</p><p>Because once that fell into place, everything else could be written by Cursor.</p><h3>Vibe-coding a data file extraction and transformation tool</h3><p>At $WORK we use Cursor (and Claude, and pretty much every AI provider) <em>exhaustively</em>. It’s not a “nice to have”, but a baseline requirement.</p><p>Naturally, I also subscribe to Cursor for my personal projects.</p><p>For the past couple of years prior to this, though, I’d been using Cursor (and Windsurf, and GitHub Copilot, etc.) as just amped-up autocomplete.</p><p>That is, I’d start typing, and watch gleefully as the AI magically completed my thoughts, sometimes suggesting entire functions/methods based on something I had just written.</p><p>I started writing datu fully expecting a similar workflow.</p><p>However, this time around I think I had a couple of things going my way:</p><ol><li><a href="https://arrow.apache.org/">Apache Arrow</a> have published a full-featured, quite mature <a href="https://arrow.apache.org/rust/arrow/index.html">Rust crate</a> that can handle just about anything I needed</li><li>Cursor, and other AI tools, have gotten to the point where, yes, it can understand simple one-line instructions and translate that to entire architectural rewrites across dozens of files in the codebase.</li></ol><p>In other words, once I’d hand-coded (with ample AI assistance) the initial Parquet to CSV conversion pipeline, I admittedly pretty much vibe-coded the rest.</p><p>(At first, I ‘practiced’ by writing <a href="https://github.com/aisrael/parq">parq</a>, a Rust equivalent to <a href="https://pypi.org/project/parq-cli/">parq-cli</a>, written in Python. Once I saw how Arrow + Cursor could get me that far, I became confident I could write a more powerful and flexible tool.)</p><blockquote>“Add a --select option to the convert command, that uses a SelectColumnStep to only propagate the selected columns. Use a SelectColumnRecordBatchReader that wraps an underlying RecordBatchReader and projects the schema and all RecordBatches.”</blockquote><p>“Add a test for SelectColumnStep”</p><p>“Add support for converting Parquet to Avro”</p><p>“Add full read &amp; write support and conversion for both Parquet and Avro”</p><p>“Add aschema command, based on parq.”</p><p>“Add a head command, based on parq.”</p><p>“Add a tail command, based on parq.”</p><p>“Add an --output argument to the schema, head, and tail commands to allow output as JSON or YAML.”</p><p>“Write more cucumber tests in features for all the commands and arguments we have now.”</p><p>“Add support for json-pretty output.”</p><p>“Add support for ORC.”</p><p>“For all the .feature files that output JSON or YAML, add a check And the file should be valid JSON or And the file should be valid YAML, and use the corresponding JSON or YAML parser to validate the output”</p><p>I mean, I’m paraphrasing most of the above prompts (except the last, which I added/instructed just now), but they illustrate pretty much how it all went.</p><p>The AI dutifully added code, making sure unit tests and .feature files were updated and passed, and the README.md was also updated as we went along.</p><p>It even went so far as correctly detecting, identifying, and trying to fix errors like:</p><pre>The Avro-to-YAML conversion was failing because userdata5.avro has Unicode<br>paragraph separators (U+2029) in the comments field. Without quoting, these<br>produced invalid YAML.</pre><p>(To be fair, it didn’t fully fix the error the first round. I had to tell Cursor “the avro to yaml conversions are still failing”, then it rewrote some stuff and fixed the tests. But that’s maybe just because I didn’t have “After major changes, make sure all linting and tests pass” in my AGENTS.md)</p><p>Would I have caught that? Maybe in minutes or a couple of hours.</p><p>Cursor wrote code in seconds, found the bug in seconds, and (after another prompt), fixed the code in seconds.</p><h3>Am I cheating? Does this feel hollow?</h3><p>Am I ashamed? Do I feel, as James Randall says, that <a href="https://www.jamesdrandall.com/posts/the_thing_i_loved_has_changed/">programming now feels hollowed out</a>?</p><p>Maybe, to a certain extent?</p><p>I mean, it feels like cheating?</p><p>It feels like I didn’t really solve all the interesting puzzles of how to use the Arrow Rust crate properly?</p><p>But at the same time, over the course of several 1–2 hour sessions over several nights, I now have a tool that can convert across several popular file formats used in data engineering, examine their schema, and display their first/last lines, among other things.</p><p>It’s a tool that I, and others in my field can use <em>now</em>, instead of maybe in a couple of more weeks if I didn’t have Cursor and had to implement everything by hand.</p><p>If I could do it all over again, would I do it any different? No. My dopamine-hit from figuring out the API and code by myself has been replaced by just watching in amazement as the AI understands my intent, sees the existing patterns I’ve already placed, and writes the code, most of the time close to how I would’ve written it myself.</p><p>When I see it writing code that I think could be better, I simply tell it.</p><p>“Consolidate all the Write*Args into a single WriteArgs since they all have the same structure.”</p><p>“I rewrote the Step trait (so execute() takes the Input), fix all the implementations and make sure all linting and tests pass.”</p><p>The experience went from “being an experienced engineer finding pleasure in puzzling about how to write code”, to “being an experienced engineer wearing a product manager and architect hat, finding pleasure in directing a smart, but inexperienced junior engineer in how to write code as if I had written it.”</p><p>Is that so bad?</p><p>Is this the future?</p><p>I dunno. All I know is I’ve stopped writing commit messages.</p><p>And I have a working tool.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7ccd4c4aa857" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Introducing yaml-schema]]></title>
            <link>https://medium.com/@alistairisrael/introducing-yaml-schema-e53773237651?source=rss-c0e66d40ab34------2</link>
            <guid isPermaLink="false">https://medium.com/p/e53773237651</guid>
            <category><![CDATA[rust]]></category>
            <category><![CDATA[yaml]]></category>
            <category><![CDATA[schema]]></category>
            <dc:creator><![CDATA[Alistair Israel]]></dc:creator>
            <pubDate>Thu, 24 Jul 2025 01:14:58 GMT</pubDate>
            <atom:updated>2025-07-24T01:23:49.439Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/770/1*-z1OIwwmX2yrGSHIQm-S5Q.png" /></figure><p><a href="https://yaml.org/">YAML</a> (short for “YAML Ain’t Markup Language”) is a human-readable data serialization format designed to be easy to read and write, which has become widely popular, especially for configuration files.</p><p>Whether deploying to the cloud, configuring CI/CD pipelines, or setting up local development environments, you’ve probably run into YAML.</p><p>YAML’s popularity and flexibility are strengths — but they’re also double-edged swords.</p><p>If you’re like me, you know how even small misconfigurations when writing or templating YAML can lead to serious consequences: broken builds, failed deployments, or worse, production incidents.</p><p>The root of many of these issues is simple: <strong>we often don’t validate our YAML</strong>.</p><p>Unlike some data serialization formats, such as <a href="https://protobuf.dev/">Protobuf</a> or <a href="https://avro.apache.org/">Avro</a>, YAML is typically written and shipped without guaranteeing that it conforms to an expected structure. A missing key or an incorrect type might silently pass through the pipeline only to cause problems downstream.</p><p>Given how central YAML is to infrastructure and automation today, having a way to validate that it conforms to a specific schema isn’t just a nice-to-have — it’s essential for reliability and safety.</p><h3>What about JSON Schema?</h3><p><a href="https://json-schema.org/">JSON Schema</a> is an existing and mature specification for defining the structure and validation rules of JSON documents. With it, you can describe required fields, enforce data types, apply string patterns, and much more. It’s widely adopted and well-supported across tools and languages.</p><p>Because YAML is a superset of JSON, in theory, you can use JSON Schema to validate YAML files by first parsing them into JSON.</p><p>Using these JSON-first tools, however, just felt off. These tools typically convert YAML into JSON before validation — a process that may introduce inefficiencies in both performance and fidelity by stripping away YAML-specific features.</p><p>Many of these validators are written in higher-overhead languages like JavaScript or Python, which can struggle with performance and memory usage, especially when embedded in larger systems or working with large configuration files.</p><h3>yaml-schema</h3><p>Inspired by this, I set out to write a YAML <em>native</em> schema specification <em>and</em> validation tool.</p><p>I chose to implement it in Rust with an eye towards memory safety and performance, especially when dealing with larger YAML documents.</p><p>Admittedly, memory safety in a CLI tool may not be such an essential requirement. However, I also designed <strong>yaml-schema</strong> to be usable as a library embedded in other tools, where memory usage, safety and performance may be paramount.</p><p><strong>yaml-schema</strong> is released as open source under the MIT License, available at <a href="https://github.com/yaml-schema/yaml-schema">https://github.com/yaml-schema/yaml-schema</a></p><p>For now, the best way to install and use it is through <a href="https://doc.rust-lang.org/cargo/">cargo</a>, Rust’s package manager:</p><pre>cargo install yaml-schema</pre><p>Should install the ys binary in your path.</p><h3>Basic Examples</h3><p><strong>Empty or </strong><strong>true</strong></p><p>A YAML schema that’s empty or simply states</p><pre>true</pre><p>will accept any YAML data. That is, according to the schema, all of the following documents are valid</p><pre>42</pre><pre>Foo</pre><pre>an:<br>  - arbitrarily<br>  - nested<br>data: structure</pre><p><strong>false</strong></p><p>Conversely, a YAML schema that simply says false will reject <em>any</em> YAML data, so all the above examples will fail to validate.</p><h3><strong>Strings</strong></h3><p>A YAML schema that states</p><pre>type: string</pre><p>Will accept &quot;I&#39;m a string&quot; but reject 42.</p><p>Strings can also specify minimum/maximum length constraints:</p><pre>type: string<br>minLength: 2<br>maxLength: 3</pre><p>Will validate &quot;one&quot; but not &quot;a&quot; or &quot;abcd&quot;.</p><p>Finally, strings can be validated against a regular expression pattern:</p><pre>type: string<br>pattern: &quot;^(\\([0-9]{3}\\) ?)?[0-9]{3}-[0-9]{4}$&quot;</pre><p>Will accept 867–5309 and (519) 867–5309 but reject 5555–1212 or 123-456-7890</p><h3><strong>Numbers</strong></h3><p>type: number and type: integer behave alike, but type: integer only accepts whole numbers, while type: number will accept floating point numbers.</p><p>Both can accept range constraints: minimum / exclusiveMinimum and maximum / eclusiveMaximum</p><p>That is, given a schema that specifies</p><pre>type: integer<br>minimum: 0<br>exclusiveMaximum: 100</pre><p>Then it should accept 0, 1, and 99. But it will not validate -1, 100 nor 101.</p><h3>Enums</h3><p>YAML schema can validate against a fixed set of values. For example, the schema</p><pre>enum:<br>  - red<br>  - amber<br>  - green</pre><p>Will accept &quot;red&quot;, &quot;amber&quot;, and &quot;green&quot;, but reject 42 or &quot;blue&quot;</p><h3>Objects</h3><p><strong>yaml-schema</strong>’s utility is best demonstrated when we start validating objects.</p><p>A basic schema of</p><pre>type: object</pre><p>Will accept any YAML hash, such as</p><pre># Unlike JSON, YAML allows numeric keys, which are treated as strings<br>0.01: cm<br>1: m<br>1000: km</pre><p>We can refine this by specifying property names and their respective schemas:</p><pre>type: object<br>properties:<br>  number:<br>    type: number<br>  streetName:<br>    type: string<br>  streetType:<br>    enum: [Street, Avenue, Boulevard]</pre><p>The schema above will accept</p><pre>number: 1600<br>streetName: Pennsylvania<br>streetType: Avenue</pre><p>But it will not accept</p><pre>number: &quot;1600&quot; # here, the quotes turn 1600 into a string<br>streetName: Pennsylvania<br>streetType: Avenue</pre><h3>Lists</h3><p>YAML schema can also validate arrays or lists</p><pre>type: array<br>items:<br>  type: number</pre><p>The above schema should accept</p><pre>- 1<br>- 2<br>- 3<br>- 5<br>- 8</pre><p>But not</p><pre>- 1<br>- 2<br>- &quot;3&quot; # a single non-number cause the entire array to be invalid<br>- 5<br>- 8</pre><h3>Composition</h3><p>Finally, we can combine schemas usingallOf, anyOf or oneOf. They behave similarly, but anyOf will accept the data as long as <em>any</em> one of the subschemas successfully validates. oneOf will only accept the data if o<em>ne, and only one</em> of the subschemas successfully validates. Finally, allOf will require <em>all</em> the subschemas to successfully validate.</p><p>That is, given a schema</p><pre>anyOf:<br>  - type: number<br>    multipleOf: 3<br>  - type: number<br>    multipleOf: 5</pre><p>Then that should accept 10 or 15.</p><p>If it were oneOf instead of anyOf, then it would reject 15 since it’s both a multiple of 3 and 5.</p><p>If it were allOf, then it would accept 15 and 30, but not 10 or 9.</p><h3>But wait, there’s more!</h3><p>The complete set of schema validation rules are expressed as <a href="https://cucumber.io/">Cucumber</a> features at <a href="https://github.com/yaml-schema/yaml-schema/tree/main/features">https://github.com/yaml-schema/yaml-schema/tree/main/features</a></p><p><strong>yaml-schema</strong> is a work-in-progress, but we hope you’ll try it out and give feedback, suggestions, or feature requests.</p><p>Better yet, if you’re so inclined, open an issue or submit a pull request to <a href="https://github.com/yaml-schema/yaml-schema">https://github.com/yaml-schema/yaml-schema</a> and help improve the tool for everyone!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e53773237651" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[AI-Powered Programming is Now]]></title>
            <link>https://medium.com/@alistairisrael/ai-powered-programming-is-now-427745cf7734?source=rss-c0e66d40ab34------2</link>
            <guid isPermaLink="false">https://medium.com/p/427745cf7734</guid>
            <category><![CDATA[aws-comprehend]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[aws-s3]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[cursor-ai]]></category>
            <dc:creator><![CDATA[Alistair Israel]]></dc:creator>
            <pubDate>Sat, 14 Sep 2024 15:18:40 GMT</pubDate>
            <atom:updated>2024-09-14T22:21:41.849Z</atom:updated>
            <content:encoded><![CDATA[<h3>How Cursor Helped Me Write A Poor Man’s AWS Macie</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_CA2Eyp1DJBghSoq1Nsx0w.png" /></figure><p>I tried out <a href="https://www.cursor.com/">Cursor AI-assisted code editor</a> for real the other day.</p><p>I’ve been using GitHub Copilot even during technical preview. If you’re already impressed by GitHub Copilot, you’ll be blown away by Cursor.</p><p>Python is <em>not </em>my go-to scripting language, so I’m far from being a Python expert.</p><p>But, during a live call with colleagues I was able to create a tool to sample objects in your S3 buckets then use AWS Comprehend to check if they (potentially) contain PII.</p><p>In less than 30 minutes, from scratch!</p><p>So, I wrote a poor man’s version of AWS Macie: <a href="https://github.com/aisrael/gumshoe">https://github.com/aisrael/gumshoe</a></p><p>What really funny was, I started out by writing one line at the top of the script about what I intended it for, and Cursor happily suggested list_s3_buckets() then get_s3_bucket_object() functions as we went along.</p><p>I especially liked that Cursor could detect exceptions in the program output, explain the error, <em>and</em> suggest a possible code fix!</p><p>Then, you can highlight any part of your code and ask it to write more code to add a feature, or refactor whole parts of it.</p><p>When I decided to add program arguments, first I asked Cursor to add an ArgParser to accept aws-region, bucket-name, etc. arguments, and it did it.</p><p>Then I asked Cursor to refactor the script, comprising several functions, into a class, and it did so flawlessly!</p><p>It even figured out which arguments I intended to turn into instance variables, so I wouldn’t have to keep passing them around as function parameters, and generated the appropriate __init__() code!</p><p>My friend <a href="http://twitter.com/kixpanganiban">@kixpanganiban</a> was right! This is like when you first went from Notepad to VSCode (or, from VSCode to JetBrains’ Rubymine/PyCharm/RustRover).</p><p>AI-assisted programming is here to stay, and I can’t see myself programming any other way now.</p><p>(Can’t wait ‘till JetBrains gets the same level, or better, of GenAI though… their code introspection and navigation capabilities are still top notch.)</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=427745cf7734" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Getting Started: Phoenix 1.7 with Svelte]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/@alistairisrael/phoenix-1-7-with-svelte-12257d853ed1?source=rss-c0e66d40ab34------2"><img src="https://cdn-images-1.medium.com/max/1846/1*mgw9sanwH9ifoKg7Grug6w.png" width="1846"></a></p><p class="medium-feed-snippet">Cybernetically enhanced Phoenix Web applications</p><p class="medium-feed-link"><a href="https://medium.com/@alistairisrael/phoenix-1-7-with-svelte-12257d853ed1?source=rss-c0e66d40ab34------2">Continue reading on Medium »</a></p></div>]]></description>
            <link>https://medium.com/@alistairisrael/phoenix-1-7-with-svelte-12257d853ed1?source=rss-c0e66d40ab34------2</link>
            <guid isPermaLink="false">https://medium.com/p/12257d853ed1</guid>
            <category><![CDATA[phoenix-framework]]></category>
            <category><![CDATA[svelte]]></category>
            <category><![CDATA[elixir]]></category>
            <dc:creator><![CDATA[Alistair Israel]]></dc:creator>
            <pubDate>Thu, 09 Mar 2023 17:46:41 GMT</pubDate>
            <atom:updated>2023-03-24T22:48:18.298Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[ProtoServer: Implementing Elixir’s GenServer from scratch]]></title>
            <link>https://levelup.gitconnected.com/protoserver-implementing-an-elixir-genserver-from-scratch-d8948c5bba77?source=rss-c0e66d40ab34------2</link>
            <guid isPermaLink="false">https://medium.com/p/d8948c5bba77</guid>
            <category><![CDATA[genserver]]></category>
            <category><![CDATA[elixir]]></category>
            <dc:creator><![CDATA[Alistair Israel]]></dc:creator>
            <pubDate>Sun, 24 Jul 2022 18:39:58 GMT</pubDate>
            <atom:updated>2026-02-18T00:04:34.700Z</atom:updated>
            <content:encoded><![CDATA[<h4>Peel back the magic behind Elixir GenServers all the way starting from Erlang processes</h4><p>One of the most remarkable features of Elixir, which it owes to its foundation on Erlang and the <a href="https://www.erlang.org/blog/a-brief-beam-primer/">BEAM</a> VM, is having the <em>process</em> as a core abstraction.</p><p>Elixir improves usability on top of raw processes with <a href="https://hexdocs.pm/elixir/1.12/GenServer.html">GenServer</a>, then takes it up to higher levels with <a href="https://hexdocs.pm/elixir/1.13.2/Agent.html">Agents,</a> <a href="https://hexdocs.pm/elixir/Application.html">Applications</a>, and <a href="https://hexdocs.pm/gen_stage/GenStage.html">GenStage</a>.</p><p>When I was beginning to use Elixir, though, GenServers were one of those things that seemed like magic me. While I ended up using them extensively, how they actually worked and how they related to the <a href="https://www.erlang.org/doc/man/supervisor.html">supervision tree</a> were a mystery.</p><p>So here we’ll attempt to unriddle how Elixir GenServers <em>really</em> work by building up an approximation of how they’re implemented starting all the way from Erlang processes. We won’t be covering <em>everything</em> about processes, the Supervisor, or Elixir Applications — just enough to better understand how GenServers work under the hood.</p><p>At the end of this exercise, we will have written a “prototype GenServer” module, which we’ll call ProtoServer, which we can use just as a regular GenServer and provides the basic GenServer.call syntax and functionality.</p><h3>Processes</h3><p>Erlang <a href="https://www.erlang.org/doc/reference_manual/processes.html">processes</a> are how it can achieve massive, reliable concurrency.</p><p>Let’s look at a textbook example just to see how they’re written in Erlang, then we’ll switch to Elixir from thereon.</p><pre>-module(spawn).<br>-export([start/0, greet/1]).</pre><pre>greet(Arg1) -&gt;<br>   io:format(&quot;Hello, ~p!~n&quot;, [Arg1]).<br>start() -&gt;<br>   Pid = spawn(?MODULE, greet, [&quot;world&quot;]),<br>   io:fwrite(&quot;~p&quot;,[Pid]).</pre><p>If we write the above code out to spawn.erl, we can execute it using the erl shell:</p><pre>$ erl<br>Erlang/OTP 24 [erts-12.2] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] [jit]</pre><pre>Eshell V12.2  (abort with ^G)<br>1&gt; c(&#39;spawn&#39;).<br>{ok,spawn}<br>2&gt; spawn:start().<br>&lt;0.86.0&gt;Hello, &quot;world&quot;!<br>ok</pre><p>So, spawn can take a Module, a function name, and a list of arguments. It then executes the specified function (passing it the list of arguments) as an independent process.</p><p>The equivalent of the above in Elixir would be something like:</p><pre>defmodule Spawn do<br>  def greet(name), do: IO.puts &quot;Hello, #{name}&quot;<br>end</pre><pre>:erlang.spawn(Spawn, :greet, [&quot;world&quot;])</pre><p>Elixir provides a much simpler <a href="https://elixir-lang.org/getting-started/processes.html#spawn">spawn/1</a> function, which can be used if we just want to execute some code in the background (aka “fire and forget”):</p><pre>spawn(fn -&gt; IO.puts(&quot;Hello in the background!&quot;) end)</pre><p>We can see for ourselves that this is in the background if we make it wait for a few milliseconds:</p><pre>iex&gt; spawn(fn -&gt; :timer.sleep(500); IO.puts(&quot;Hello in the background!&quot;) end)<br>#PID&lt;0.107.0&gt;<br>Hello in the background!</pre><blockquote><a href="https://elixir-lang.org/getting-started/processes.html#links">spawn_link/1</a> is an alternative to spawn/1 that propagates or bubbles up errors to the parent process. We won’t go into it much here, but just know that with spawn_link creates a ‘link’ between the parent process and the spawned child process, such that if the child process fails (or raises an error), the error “bubbles up” to the parent.</blockquote><p>Let’s take this further by writing a function that calls itself (a recursive infinite loop):</p><pre>iex&gt; defmodule Loop do<br>  def loop() do<br>    IO.puts(&quot;Looping...&quot;)<br>    :timer.sleep(1000)<br>    loop()<br>  end<br>end</pre><pre>iex&gt; pid_loop = spawn(&amp;Loop.loop/0)<br>Looping...<br>#PID&lt;0.117.0&gt;<br>Looping...<br>Looping...<br>Looping...<br>...</pre><p>Note that spawn/1 returns a PID or <em>process identifier</em>. As you can see, our Loop.loop/0 function never exits and runs continuously in the background.</p><pre>iex&gt; inspect(pid_loop)<br>&quot;#PID&lt;0.118.0&gt;&quot;</pre><p>Now if all we could do was execute code in the background, then we wouldn’t have much over naked <a href="https://en.wikipedia.org/wiki/Thread_(computing)">threads</a>.</p><blockquote>If all you need is to execute code in the background, and perhaps determine if it completed successfully or inspect and handle any failures, then you should be using Tasks rather than <em>spawn/1</em>. See <a href="https://elixir-lang.org/getting-started/processes.html#tasks">https://elixir-lang.org/getting-started/processes.html#tasks</a></blockquote><h3>Inter-Process Communication</h3><p>Elixir processes let us do so much more than low-level threads because processes aren’t just a means to execute code concurrently — they also provide <em>messaging</em> (<em>signals</em> in Erlang).</p><p>To exit our infinite looping function earlier, we just send it the exit signal:</p><pre>Looping...<br>Looping...<br>iex&gt; Process.exit(pid_loop, 0)<br>true</pre><pre>iex&gt;</pre><p>Now let’s write another function that loops indefinitely, but this time we’ll demonstrate how to <a href="https://elixir-lang.org/getting-started/processes.html#send-and-receive">send and receive</a> meaningful messages:</p><pre>iex&gt; defmodule Receiver do<br>  def loop() do</pre><pre>    # receive blocks, awaiting a message<br>    receive do<br>      {:greet, name} -&gt; IO.puts(&quot;Hello, #{name}&quot;)<br>      msg -&gt; IO.puts &quot;Received unknown message: #{inspect(msg)}&quot;<br>    end<br>    loop()<br>  end<br>end</pre><pre>iex&gt; pid = spawn(&amp;Receiver.loop/0)<br>#PID&lt;0.119.0&gt;</pre><p>When the receive block is executed, the process waits until a message is sent to it, pattern matches on the received message, then executes the matching branch. Receiver then loops back on itself, awaiting the next message:</p><pre>iex&gt; send(pid, {:greet, &quot;world&quot;})<br>Hello, world<br>{:greet, &quot;world&quot;}</pre><pre>iex&gt; send(pid, &quot;foo&quot;)<br>Received unknown message: &quot;foo&quot;<br>&quot;foo&quot;</pre><p>We now have the beginnings of a persistent, background <em>actor</em>.</p><h4>Mailbox</h4><p>Elixir signalling isn’t just some elaborate way to call functions in the background. When we say Elixir has messaging, we mean that each process actually has a <em>mailbox</em> that can act as a message queue!</p><p>We can see this by adding an artificial delay in the receiver, then sending several messages one after the other:</p><pre>iex&gt; defmodule Mailbox do<br>  def loop() do<br>    receive do<br>      {:greet, name} -&gt; greet(name)<br>      msg -&gt; IO.puts &quot;Received unknown message: #{inspect(msg)}&quot;<br>    end<br>    loop()<br>  end<br>  def greet(name) do<br>    IO.puts(&quot;Hello, #{name}&quot;)<br>    :timer.sleep(1000)<br>  end<br>end</pre><pre>iex&gt; Enum.each(1..5, fn i -&gt; send(pid, {:greet, [&quot;world #{i}&quot;]}) end)<br>Hello, world 1<br>:ok<br>Hello, world 2<br>Hello, world 3<br>Hello, world 4<br>Hello, world 5</pre><p>Here we can see that even after the Enum.each/2 loop has returned after sending all messages, the messages are held in the mailbox until receive is called. Once all messages have been received, the next call to receive blocks awaiting another message.</p><h3>State</h3><p>In object oriented programming, persistent objects are used to hold state in the heap, until they’re explicitly freed, garbage collected, or the program exits.</p><p>Since Elixir is functional, rather than having persistent “objects” occupying memory on the heap and responding to method calls, we have processes that run concurrently and respond to messages. But what about persistent state?</p><p>Simple. Function calls can take arguments, and there’s no reason a function can’t recursively call itself with arguments as well!</p><p>First let’s write a simple countdown function:</p><pre>defmodule Count do<br>  # call the loop with initial state<br>  def down do<br>    IO.puts(&quot;Counting down...&quot;)<br>    loop(5)<br>  end</pre><pre>  # this recursively calls itself with its own &#39;state&#39;<br>  def loop(n) do<br>    if n &gt; 0 do<br>      IO.puts(&quot;#{n}&quot;)<br>      :timer.sleep(1000)<br>      loop(n-1)<br>    end<br>  end<br>end</pre><pre>pid = spawn(&amp;Count.down/0)<br>IO.puts(&quot;spawn(&amp;Count.down/0) -&gt; #{inspect(pid)}&quot;)<br>:timer.sleep(5000)</pre><p>If we write this to, say, count_down.exs and run it with elixir count_down.exs, we get the output:</p><pre>elixir count_down.exs<br>Counting down...<br>5<br>spawn(&amp;Count.down/0) -&gt; #PID&lt;0.100.0&gt;<br>4<br>3<br>2<br>1</pre><p>As we can see, even while the process loops in the background, it maintains an ever decreasing count as ‘state’.</p><p>Now let’s see a more useful example, a counter that can receive meaningful messages and modify its own state:</p><pre>defmodule Counter do<br>  # call the loop with initial state of 0<br>  def init do<br>    IO.puts(&quot;Initial state: 0&quot;)<br>    message_loop(0)<br>  end</pre><pre>  # Recursively calls itself with its own &#39;state&#39;, and receive messages<br>  def message_loop(n) do<br>    receive do<br>      :inc -&gt; IO.puts(&quot;#{n} + 1 -&gt; #{n + 1}&quot;); message_loop(n+1)<br>      :dec -&gt; IO.puts(&quot;#{n} - 1 -&gt; #{n - 1}&quot;); message_loop(n-1)<br>      :print -&gt; IO.puts(&quot;Current state: #{n}&quot;); message_loop(n)<br>      msg -&gt; raise &quot;Received unknown message: #{inspect(msg)}&quot;<br>    end<br>  end<br>end</pre><pre>pid = spawn(&amp;Counter.init/0)<br>IO.puts(&quot;spawn(&amp;Counter.init/0) -&gt; #{inspect(pid)}&quot;)<br>send(pid, :inc) # 0 + 1 -&gt; 1<br>send(pid, :inc) # 1 + 1 -&gt; 2<br>send(pid, :inc) # 2 + 1 -&gt; 3<br>send(pid, :dec) # 3 - 1 -&gt; 2<br>send(pid, :inc) # 2 + 1 -&gt; 3<br>send(pid, :print) # Current state: 3</pre><p>When we run this using elixir counter.exs, we get the expected output:</p><pre>Initial state: 0<br>spawn(&amp;Counter.init/0) -&gt; #PID&lt;0.100.0&gt;<br>0 + 1 -&gt; 1<br>1 + 1 -&gt; 2<br>2 + 1 -&gt; 3<br>3 - 1 -&gt; 2<br>2 + 1 -&gt; 3<br>Current state: 3</pre><h3>Receiving responses</h3><p>If all our background process could do was print out its own state, then it’s not quite as useful as a proper object, right? We also need a way for our object to respond to messages, and perhaps return its own state (or something related to its internal state).</p><p>Recall that we can send/2 a message to background process’ mailbox. Well, the current process or caller also has a mailbox and can also receive messages!</p><p>For us to receive a response to a sent message we’ll need to:</p><ol><li>Tell the receiver how to send back messages to the caller, by sending along the caller’s process id. We can retrieve the current process id using self/0.</li><li>Setup a receive block in the calling process to await and handle the message sent back.</li></ol><p>Let’s build upon our Counter example to see how this goes. First, we’ll modify our receive_loop so that it can respond to a message with a <em>tuple</em> of {:get, pid} where the pid is the caller’s process id:</p><pre>  def receive_loop(n) do<br>    receive do<br>      :inc -&gt; IO.puts(&quot;#{n} + 1 -&gt; #{n + 1}&quot;); receive_loop(n+1)<br>      :dec -&gt; IO.puts(&quot;#{n} - 1 -&gt; #{n - 1}&quot;); receive_loop(n-1)<br>      {:get, caller} -&gt; send(caller, {:counter, n}); receive_loop(n)<br>      msg -&gt; raise &quot;Received unknown message: #{inspect(msg)}&quot;<br>    end<br>  end</pre><p>Next, instead of just sending :print, we send {:get, self()}. Right after sending that message, in the calling process, we also set up a receive block to await the response from Counter:</p><pre>send(pid, {:get, self()})<br>receive do<br>  {:counter, n} -&gt; IO.puts(&quot;Got response from counter: #{n}&quot;)<br>  msg -&gt; raise &quot;Received unknown message: #{inspect(msg)}&quot;<br>end</pre><p>Now when we run our modified counter, the output, as expected, is:</p><pre>Initial state: 0<br>spawn(&amp;Counter.init/0) -&gt; #PID&lt;0.100.0&gt;<br>0 + 1 -&gt; 1<br>1 + 1 -&gt; 2<br>2 + 1 -&gt; 3<br>3 - 1 -&gt; 2<br>2 + 1 -&gt; 3<br>Got response from counter: 3</pre><p>Demonstrating that Counter is indeed sending a reply <em>back</em> to the main process that called it.</p><h3>Abstraction</h3><p>We now have the beginnings of a proper send &amp; receive framework. But before we can extract it to a reusable module, let’s abstract away the spawn, send, and receive bits, as so callers only interact with Counter.</p><p>First, let’s add a start() function to Counter that just calls spawn and returns the counter’s PID:</p><pre>  # spawn the Counter in the background and return its pid<br>  def start() do<br>    spawn(&amp;init/0)<br>  end</pre><p>Next, let’s generalize Counter’s receive_loop and have it expect that <em>all</em> messages are a tuple of the caller’s PID and the actual message. Upon receiving a message, it’ll delegate the handling to a handle_msg function, which will return the new state. It will then send the new state as a reply to the caller, then recursively loop back on itself.</p><pre>  def receive_loop(state) do<br>    receive do<br>      {from, msg} -&gt;<br>        new_state = handle_msg(msg, state)<br>        send(from, new_state)<br>        receive_loop(new_state)<br>      msg -&gt; raise &quot;Received unknown message: #{inspect(msg)}&quot;<br>    end<br>  end</pre><p>We can now express Counter’s logic more clearly in handle_msg. What we’re really doing is also abstracting away the send and receive logic from the core of Counter.</p><pre>  def handle_msg(:inc, n), do: n + 1<br>  def handle_msg(:dec, n), do: n - 1<br>  def handle_msg(:get, n), do: n</pre><p>Finally, we’ll add ‘wrapper’ functions to Counter so callers also won’t need write a receive block:</p><pre>  # Increment the counter and get the new count<br>  def inc(pid) do<br>    send(pid, {self(), :inc})<br>    receive do<br>      resp -&gt; IO.puts(&quot;Counter.inc() -&gt; #{resp}&quot;)<br>    end<br>  end</pre><pre>  # Decrement the counter and get the new count<br>  def dec(pid) do<br>    send(pid, {self(), :inc})<br>    receive do<br>      resp -&gt; IO.puts(&quot;Counter.dec() -&gt; #{resp}&quot;)<br>    end<br>  end</pre><pre>  # Just get the current count<br>  def get(pid) do<br>    send(pid, {self(), :inc})<br>    receive do<br>      resp -&gt; IO.puts(&quot;Counter.get() -&gt; #{resp}&quot;)<br>    end<br>  end</pre><p>With this version of Counter, the calling code is now simplified:</p><pre>pid = Counter.start()<br>IO.puts(&quot;Counter.start() -&gt; #{inspect(pid)}&quot;)<br>Counter.inc(pid) # 1<br>Counter.inc(pid) # 2<br>Counter.inc(pid) # 3<br>Counter.dec(pid) # 2<br>Counter.inc(pid) # 3<br>Counter.get(pid) # 3</pre><h4>Named Processes</h4><p>The only thing remaining from a caller’s perspective is that we’re still handling the PID. Fortunately, there’s a way to register a name for a PID using <a href="https://hexdocs.pm/elixir/1.12/Process.html#register/2">Process.register/2</a>.</p><p>Let’s rewrite our Counter.start to use the module name (conveniently available as __MODULE__) as the registered name of the process:</p><pre>  # spawn the Counter in the background and return its pid<br>  def start() do<br>    pid = spawn(&amp;init/0)<br>    Process.register(pid, __MODULE__)<br>    pid<br>  end</pre><p>With that, we can now simplify all our inc, dec, and get calls by using Process.whereis/1 to retrieve the PID, like the following:</p><pre>  def inc() do<br>    pid = Process.whereis(__MODULE__)<br>    send(pid, {self(), :inc})<br>    receive do<br>      resp -&gt; IO.puts(&quot;Counter.inc() -&gt; #{resp}&quot;)<br>    end<br>  end</pre><p>With that, our calling code is vastly simplified!</p><pre>Counter.start() # We can discard the returned PID<br>Counter.inc() # 1<br>Counter.inc() # 2<br>Counter.inc() # 3<br>Counter.dec() # 2<br>Counter.inc() # 3<br>Counter.get() # 3</pre><h3>ProtoServer</h3><p>Let’s wrap up everything we’ve learned and abstract away the underlying Process calls and message sending and receiving into a reusable module.</p><p>We want our ProtoServer to behave just like a GenServer. Meaning:</p><ul><li>When we call ProtoServe.start_link/3 (or start/3) we want to pass in our module, the initial state, and accept some options</li><li>If the name: option is given, use that as the process name, so we don’t have to track the PID</li><li>When the process is started, call our module’s init/1 callback with the initial state</li><li>Allow init/1 to return either :ok and the initial state, or :error and some error. On :ok, proceed to the message loop. On :error, immediately raise the error</li><li>In the message loop, on a :call message, call our handle_call/3 callback with the message, the caller PID, and the current state</li></ul><p>Whew! That’s a lot to unpack, but don’t worry, it’s actually quite easy to implement and follow.</p><p>We want our module to take care of all the necessary “boilerplate” above, and be as simple to use by, well, <a href="https://elixir-lang.org/getting-started/alias-require-and-import.html#use">use</a>.</p><p>In Elixir, when we use another module, we allow that module module to “inject” any code into the current module using Elixir <a href="https://elixir-lang.org/getting-started/meta/macros.html">macros</a>. Explaining elixir macros fully is out of scope for now — it’s enough to know that to make use work, we just need to implement a __using__ macro!</p><p>Let’s go ahead and start writing our ProtoServer and implement start_link/3:</p><pre># A prototype GenServer<br>defmodule ProtoServer do</pre><pre>  # Just link GenServer.start_link/3, spawns the calling<br>  # calling module, and returns the PID. Also, support the<br>  # optional `name:` option <br>  def start_link(module, state, options \\ %{}) do<br>    pid = spawn_link(module, :on_spawn, [state])</pre><pre>    if Keyword.has_key?(options, :name) do<br>      Process.register(pid, options[:name])<br>    end<br>    pid<br>  end</pre><p>So, start_link merely calls spawn_link, but we provide our own on_spawn function that will implement the ‘magic’ of calling the module’s init/1 callback. We also support the optional name: option and register the process using that name, to make it easier to call our ProtoServer module later on.</p><p>Now we can’t just def on_spawn in ProtoServer itself, because when we spawn_link we need to pass in the calling module. Otherwise, we’d be writing spawn_link(ProtoServer, :on_spawn, ...) which would make it difficult to call the our module’s init/1 callback function!</p><p>Instead, this is where we rely on the __using__ macro to write on_spawn in the calling module itself! It’s easier to show than explain:</p><pre>  defmacro __using__(_opts) do<br>    quote do<br>      def on_spawn(args) do<br>        case init(args) do<br>          {:ok, initial_state} -&gt; receive_loop(initial_state)<br>          {:error, reason} -&gt; raise reason<br>        end<br>      end</pre><p>In ProtoServer.__using__, we use a quote block and write the on_spawn/1 startup function <em>as if we were writing it in the calling module itself!</em></p><p>That is, when we write, for example:</p><pre>defmodule Counter do<br>  use ProtoServer</pre><p>It’s as if we had written:</p><pre>defmodule Counter do<br>  def on_spawn(args) do...</pre><p>In on_spawn/1, we call init/1 with the same initial arguments, then depending on whether init/1 returns :ok or :error, we either proceed to call the receive_loop or fail.</p><p>We use the same quote block in the __using__ macro to write out receive_loop/1 as if we were writing it in the calling module:</p><pre>      # Recursively calls itself with its own &#39;state&#39;, <br>      # and receive messages<br>      def receive_loop(state) do<br>        receive do<br>          {:call, from, msg} -&gt; case handle_call(msg, from, state) do<br>            {:reply, response, new_state} -&gt; send(from, response); receive_loop(new_state)<br>            {:no_reply, new_state} -&gt; receive_loop(new_state)<br>            result -&gt; raise &quot;Received unknown result: #{inspect(result)}&quot;<br>          end<br>          msg -&gt; raise &quot;Received unknown message: #{inspect(msg)}&quot;<br>        end<br>      end</pre><p>The receive_loop is almost similar to the one we wrote earlier, except this time we handle the :call message and delegate to the module’s handle_call/3 callback. We then send back a response, do nothing, or raise an error depending on whether handle_call/3 returns :reply, :no_reply, or an unknown return value.</p><p>That’s all we need in our __using__ macro! Now we just need to implement ProtoServer.call/2. For call/2, we provide a convenient way to pass in a symbol (the module name) rather than a PID, and useProcess.whereis/1 ourselves:</p><pre>def call(to, msg) do<br>    case to do<br>      pid when is_pid(pid) -&gt; send(pid, {:call, self(), msg})<br>      name when is_atom(name) -&gt; send(Process.whereis(name), {:call, self(), msg})<br>    end<br>    receive do<br>      resp -&gt; resp<br>    end<br>  end</pre><p>Our final implementation of our fully featured Counter can be written with use ProteServer exactly <em>as if</em> we had used a GenServer instead. You can try it out for yourself by replacing ProtoServer with GenServer in the following code!</p><pre>defmodule Counter do<br>  use ProtoServer</pre><pre>  def start_link() do<br>    ProtoServer.start_link(__MODULE__, 0, name: __MODULE__)<br>  end</pre><pre>  def init(initial_value) do<br>    IO.puts(&quot;init(#{initial_value})&quot;)<br>    {:ok, initial_value}<br>  end</pre><pre>  def handle_call(msg, _from, state) do<br>    resp = handle_msg(msg, state)<br>    {:reply, resp, resp}<br>  end</pre><pre>  def handle_msg(:inc, n), do: n + 1<br>  def handle_msg(:dec, n), do: n - 1<br>  def handle_msg(:get, n), do: n</pre><pre>  def inc() do<br>    resp = ProtoServer.call(__MODULE__, :inc)<br>    IO.puts(&quot;Counter.inc() -&gt; #{resp}&quot;)<br>  end</pre><pre>  def dec() do<br>    resp = ProtoServer.call(__MODULE__, :dec)<br>    IO.puts(&quot;Counter.inc() -&gt; #{resp}&quot;)<br>  end</pre><pre>  def get() do<br>    resp = ProtoServer.call(__MODULE__, :get)<br>    IO.puts(&quot;Counter.inc() -&gt; #{resp}&quot;)<br>  end<br>end</pre><blockquote>The implementation of ProtoServer.cast/2 is left as an exercise to the reader.</blockquote><p>This article ran a bit longer than I wanted, and covered more than a few topics — but I hope that you were able to follow along and now have a deeper understanding of GenServer and greater confidence in using it!</p><p>Cheers!</p><h3>Level Up Coding</h3><p>Thanks for being a part of our community! Before you go:</p><ul><li>👏 Clap for the story and follow the author 👉</li><li>📰 View more content in the <a href="https://levelup.gitconnected.com/?utm_source=pub&amp;utm_medium=post">Level Up Coding publication</a></li><li>🔔 Follow us: <a href="https://twitter.com/gitconnected">Twitter</a> | <a href="https://www.linkedin.com/company/gitconnected">LinkedIn</a> | <a href="https://newsletter.levelup.dev/">Newsletter</a></li></ul><p>🚀👉 <a href="https://jobs.levelup.dev/talent/welcome?referral=true"><strong>Join the Level Up talent collective and find an amazing job</strong></a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d8948c5bba77" width="1" height="1" alt=""><hr><p><a href="https://levelup.gitconnected.com/protoserver-implementing-an-elixir-genserver-from-scratch-d8948c5bba77">ProtoServer: Implementing Elixir’s GenServer from scratch</a> was originally published in <a href="https://levelup.gitconnected.com/">Level Up Coding</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Containerizing a Phoenix 1.6 Umbrella Project]]></title>
            <link>https://medium.com/@alistairisrael/containerizing-a-phoenix-1-6-umbrella-project-8ec03651a59c?source=rss-c0e66d40ab34------2</link>
            <guid isPermaLink="false">https://medium.com/p/8ec03651a59c</guid>
            <category><![CDATA[docker-compose]]></category>
            <category><![CDATA[phoenix-framework]]></category>
            <category><![CDATA[elixir]]></category>
            <category><![CDATA[docker]]></category>
            <dc:creator><![CDATA[Alistair Israel]]></dc:creator>
            <pubDate>Tue, 04 Jan 2022 11:42:25 GMT</pubDate>
            <atom:updated>2022-01-08T03:58:24.939Z</atom:updated>
            <content:encoded><![CDATA[<h4>Packaging and Deploying a Phoenix 1.6 Umbrella Project using Docker</h4><blockquote>While there are existing guides on how to package a Phoenix application using Docker, most of them deal with ‘regular’ Phoenix apps, not <a href="https://elixir-lang.org/getting-started/mix-otp/dependencies-and-umbrella-projects.html">umbrella projects</a>. In this guide, we’ll take our earlier Phoenix 1.6 umbrella project, build a minimal release binary, package it as a Docker image and deploy it as a Docker container.</blockquote><p>The accompanying source code to this article is also at <a href="https://github.com/aisrael/elixir-phoenix-typescript-react">https://github.com/aisrael/elixir-phoenix-typescript-react</a></p><p>Now that we’ve seen how to create a <a href="https://medium.com/@alistairisrael/phoenix-1-6-with-typescript-react-bea7f3a792d5?source=your_stories_page----------------------------------------">Phoenix 1.6 project with TypeScript and React</a>, let’s now explore how to release, package, and deploy it using Docker.</p><h4>What/Why/How to Release?</h4><p>An Elixir or Erlang/OTP <a href="https://www.erlang.org/doc/design_principles/release_structure.html">Release</a> is a self-contained artifact that allows us to run our Elixir application, with minimal dependencies and no source code. I like to think of it as something like a binary executable that contains everything needed to run our application, and nothing more.</p><p>In production environments, we don’t really need or want the entire Elixir/Erlang and Node toolchains. Some of those weigh in at dozens, even hundreds of MB and when deploying at scale, every MB saved matters.</p><p>Prior to Elixir v1.9, we would need to use <a href="https://github.com/bitwalker/distillery">Distillery</a> to package and release our Elixir or Phoenix app. Starting with v1.9, however, Elixir provided the ability to mix release our app without any additional tooling.</p><p>We can still use Distillery, especially if we want some of its additional features such as hot code upgrades. Since we’re also looking to deploy our application as a container, however, then hot code upgrading becomes less of a concern.</p><p>I won’t go too much into how containers have become the preferred means of packaging and deploying server-side applications and services, or why Docker is the <em>de facto</em> standard, having popularized and made containers accessible in the beginning.</p><p>But for any large-scale Web application or service, chances are you’ll want to deploy them as containers using an orchestration platform such as <a href="https://kubernetes.io/">Kubernetes</a>, so this is where we’re headed in this guide.</p><h3>Phoenix Umbrella Project Releases</h3><p>The Phoenix framework helpfully provides us with a <a href="https://hexdocs.pm/phoenix/releases.html#releases-assemble">helper task</a>, mix phx.gen.release that can even generate a Dockerfile that serves as a great starting point for us!</p><p>However, if we try to run mix phx.gen.release from the root of a Phoenix umbrella project, we are greeted with:</p><pre>$ mix phx.gen.release<br>** (Mix) mix phx.gen.release is not supported in umbrella applications.</pre><pre>Run this task in your web application instead.</pre><p>So, let’s try it in apps/hello_web instead.</p><pre>$ cd apps/hello_web</pre><p>This time, we’ll also supply the --docker option to get a Dockerfile we can work with. This creates a few files we’ll be needing:</p><pre>hello_web$ mix phx.gen.release --docker<br>* creating rel/overlays/bin/server<br>* creating rel/overlays/bin/server.bat<br>* creating rel/overlays/bin/migrate<br>* creating rel/overlays/bin/migrate.bat<br>* creating lib/hello_web/release.ex<br>* creating Dockerfile<br>* creating .dockerignore</pre><pre>...</pre><p>As well as printing out a few more helpful instructions. At this point, though, if we try mix release like the instructions suggest, while it <em>will</em> complete successfully <em>and</em> the resulting executable does run — it doesn’t actually run our Web application.</p><h3>Going Back to Our Root</h3><p>We need our <em>entire</em> umbrella project, so we need to go back to working from the project root. First, move the generated Dockerfile and .dockerignore files into the project root</p><pre>hello_web$ cd ../..<br>$ mv apps/hello_web/Dockerfile apps/hello_web/.dockerignore .</pre><p>From this point forward, we’ll also want to make sure our MIX_ENV is set to prod, because we <em>are</em> trying to build a release for <em>production</em>:</p><pre>$ export MIX_ENV=prod</pre><p>Now, if we try to mix release again from the root, we run into:</p><pre>$ mix release<br>** (Mix) Umbrella projects require releases to be explicitly defined with a non-empty applications key that chooses which umbrella children should be part of the releases:</pre><pre>    releases: [<br>      foo: [<br>        applications: [child_app_foo: :permanent]<br>      ],<br>      bar: [<br>        applications: [child_app_bar: :permanent]<br>      ]<br>    ]</pre><pre>Alternatively you can perform the release from the children applications</pre><p>Mix is telling us that we need to configure our umbrella app and tell it what the child applications are.</p><p>To do that, we’ll have to edit mix.exs and add something like the following lines (in <strong>bold</strong>) to the project definition:</p><pre>  def project do<br>    [<br>      apps_path: &quot;apps&quot;,<br>      version: &quot;0.1.0&quot;,<br>      start_permanent: Mix.env() == :prod,<br>      deps: deps(),<br>      aliases: aliases(),<br><strong>      releases: [<br>        hello_umbrella: [<br>          applications: [<br>            hello: :permanent,<br>            hello_web: :permanent<br>          ]<br>        ]<br>      ]</strong><br>    ]<br>  end</pre><p>At this point, mix release <em>will</em> complete <em>and</em> build a more or less self-contained executable:</p><pre>$ MIX_ENV=prod mix release<br>==&gt; hello<br>Generated hello app<br>==&gt; hello_web<br>Compiling 4 files (.ex)<br>Generated hello_web app<br>Release hello_umbrella-0.1.0 already exists. Overwrite? [Yn] y<br>* assembling hello_umbrella-0.1.0 on MIX_ENV=prod<br>* using config/runtime.exs to configure the release at runtime</pre><pre>Release created at _build/prod/rel/hello_umbrella!</pre><pre># To start your system<br>    _build/prod/rel/hello_umbrella/bin/hello_umbrella start</pre><p>If we do what it says now and try to _build/prod/rel/hello_umbrella/bin/hello_umbrella start, we’ll quickly discover some runtime configuration errors.</p><p>Rather than explain how to address them at this point, let’s skip that for now and try to build our Docker image so we focus on running our Phoenix application as a container.</p><h4>server: true</h4><p>One small, but <em>important</em> thing that’s not very well documented and might cause you some level of head-scratching and frustration (it did me) is that somewhere inside config/runtime.exs, there’s a section that goes</p><pre>  # ## Using releases<br>  #<br>  # If you are doing OTP releases, you need to instruct Phoenix<br>  # to start each relevant endpoint:<br>  #<br>  # config :hello_web, HelloWeb.Endpoint, server: true</pre><p>Since we <em>are</em> using OTP releases, we <em>will</em> have to comment that last line out. If you don’t everything else below <em>will</em> seem to work, but when you actually try to navigate to your Web application you’ll just keep getting no response!</p><p>So make sure you uncomment the last line, as follows:</p><pre>config :hello_web, HelloWeb.Endpoint, server: true</pre><h3>Tweaking the Dockerfile</h3><p>While Phoenix was helpful enough to generate a Dockerfile for us, it also assumes a regular Phoenix project rather than an umbrella project.</p><h4>Release Builder Stage</h4><p>Notably, since we’re coming from a project that uses React, we’ll still need npm but <em>only in the builder stage</em>. So, in the Dockerfile where it says # install build dependencies we’ll want to add npm, like so:</p><pre># install build dependencies<br>RUN apt-get update -y &amp;&amp; apt-get install -y build-essential git <strong>npm</strong> \<br>  &amp;&amp; apt-get clean &amp;&amp; rm -f /var/lib/apt/lists/*_*</pre><p>Next, the generated Dockerfile only copies mix.exs and mix.lock from the project root into the build container. Because we have an umbrella project, we also need to copy in mix.exs for <em>all</em> child apps.</p><blockquote>We can compress all the steps by simply going COPY . . but that defeats the purpose of the multiple, explicit steps in the generated Dockerfile, namely, being able to cache earlier steps that involve infrequently changed files.</blockquote><p>Let’s add mix.exs for all the child apps:</p><pre>COPY apps/hello/mix.exs ./apps/hello/mix.exs<br>COPY apps/hello_web/mix.exs ./apps/hello_web/mix.exs</pre><p>Next, the generated Dockerfile tries to copy over some folders such as priv and assets from the project root, but in our case, those folders actually reside under apps/hello_web/. We’ll need to modify the following lines accordingly:</p><pre># COPY priv priv<br>COPY apps/hello_web/priv apps/hello_web/priv</pre><p>And</p><pre># COPY assets assets<br>COPY apps/hello_web/assets apps/hello_web/assets</pre><p>Now, once again when compiling the assets, we’ll want to modify the Dockerfile to run the command under apps/hello_web:</p><pre># compile assets<br>RUN cd apps/hello_web &amp;&amp; mix assets.deploy</pre><p>Almost there! Now, where a typical Phoenix app has all its source under lib, for an umbrella project we want everything under apps, so replace the line COPY lib lib with:</p><pre># Compile the release<br>COPY apps apps</pre><h4>Runner Image Stage</h4><p>Now we’re done with the release builder stage, we just need a couple of adjustments in the final image stage.</p><p>In particular, the generated Dockerfile was for hello_web but the actual release name is hello_umbrella:</p><pre><em># Only copy the final release from the build stage</em></pre><pre>COPY --from=builder --chown=nobody:root /app/_build/prod/rel/hello_umbrella ./</pre><p>Finally, the generated Dockerfile uses the CMD /app/bin/server, which is problematic.</p><p>First since the file app/bin/server doesn’t actually exist. Rather, what we’ll find there is app/bin/hello_umbrella.</p><p>Second, Docker best practices tell us to prefer ENTRYPOINT and not just CMD —this lets us specify program arguments at run time.</p><p>So, finally, we replace the last line with:</p><pre>ENTRYPOINT [ &quot;/app/bin/hello_umbrella&quot; ]</pre><blockquote>For a quick summary of all the changes in the Dockerfile, see this <a href="https://github.com/aisrael/elixir-phoenix-typescript-react/commit/5ec3427cf9ff60618acf3ea0f9e9c07629451f9b">commit</a>.</blockquote><p>At this point, we should be able to successfully build and tag our Docker image using:</p><pre>$ docker build -t hello .</pre><h3>Up and Running!</h3><p>After our Docker image has completed building, if we just try to run it using docker run hello we should just see:</p><pre>Usage: hello_umbrella COMMAND [ARGS]</pre><pre>The known commands are:</pre><pre>    start          Starts the system<br>    ...</pre><p>Now, if we try docker run hello start we’ll get a runtime error telling us that the environment variable DATABASE_URL is missing, and that it probably should look like ecto://USER:PASS@HOST/DATABASE.</p><h4>DATABASE_URL</h4><p>Recall that in our earlier article we used <a href="https://medium.com/@alistairisrael/phoenix-1-6-with-typescript-react-bea7f3a792d5#96a6">Docker Compose to run Postgres</a>. While Postgres should be listening on localhost relative to our workstation, within Docker, a container by default can’t just refer to the localhost of the host.</p><p>Instead, we need to run our container as part of the same Docker internal network that Docker Compose used to run Postgres. To get this network, we have to issue the command docker network ls, which should give us something like:</p><pre>$ docker network ls<br>NETWORK ID     NAME                              DRIVER    SCOPE<br>ddd978e072b8   bridge                            bridge    local<br>0e787c1bff92   hello_umbrella_default            bridge    local</pre><p>So our network name is hello_umbrella_default.</p><p>Next, we’ll need to figure out what the hostname of the Postgres server is within Docker — by default, that’s just container name, which we can get by issuing docker ps:</p><pre>$ docker ps<br>CONTAINER ID   IMAGE           COMMAND                  CREATED         STATUS         PORTS                    NAMES<br>c69d93a2f9bb   postgres:14.1   &quot;docker-entrypoint.s…&quot;   4 seconds ago   Up 3 seconds   0.0.0.0:5432-&gt;5432/tcp   hello_umbrella-postgres-1</pre><p>So, our Postgres hostname is hello_umbrella-postgres-1.</p><p>Using the default credentials (and the development database), our complete DATABASE_URL now becomes:</p><pre>export DATABASE_URL=ecto://postgres:postgres@hello_umbrella-postgres-1/hello_dev</pre><h4>SECRET_KEY_BASE</h4><p>With DATABASE_URL out of the way, if we try to run our Docker image again, we’ll get another error message saying environment variable SECRET_KEY_BASE is missing.</p><p>The SECRET_KEY_BASE is used by Phoenix to sign/encrypt cookies and other secrets. In development, it’s generated at project creation and hard-coded in config/dev.exs.</p><p>In production, however, we <em>don’t</em> want secrets to be committed to source code, <em>and</em>, ideally should only be accessible to operations, if at all.</p><p>For now, all we need to do is supply some value, at least 64 bytes long, encoded as Base64. Since we already have Elixir, we can use it generate a suitably encoded random value:</p><pre>$ export SECRET_KEY_BASE=$(elixir --eval &#39;IO.puts(:crypto.strong_rand_bytes(64) |&gt; Base.encode64(padding: false))&#39;)</pre><h3>Hello, Phoenix from Docker!</h3><p>Let’s put together our final docker run command.</p><blockquote>Those of you more experienced with Docker can probably craft a docker-compose.yml file that avoids having to type out the command below by hand, but the purpose of this guide was to educate the reader about the individual configuration items that are needed to run a Phoenix release as a container.</blockquote><p>To recap, we need:</p><ul><li>To specify the network, --net hello_umbrella_default</li><li>The DATABASE_URLvariable</li><li>The SECRET_KEY_BASE variable</li></ul><p>We also need to publish the port that our Phoenix app is serving on, 4000 to our localhost, using -p 4000:4000.</p><p>The final docker run command thus looks like:</p><pre>$ docker run -p 4000:4000 --net hello_umbrella_default -e DATABASE_URL=$DATABASE_URL -e SECRET_KEY_BASE=$SECRET_KEY_BASE hello start</pre><p>If we navigate to <a href="http://localhost:4000/">http://localhost:4000</a> now, we should be greeted with our “Welcome to Phoenix with TypeScript and React!” landing page.</p><p>I hope this is helpful to people exploring releasing and deploying their Elixir+Phoenix applications as containers.</p><p>In the future I hope to find time to write about adding a GraphQL API into the mix.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8ec03651a59c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Phoenix 1.6 With TypeScript and React]]></title>
            <link>https://medium.com/better-programming/phoenix-1-6-with-typescript-react-bea7f3a792d5?source=rss-c0e66d40ab34------2</link>
            <guid isPermaLink="false">https://medium.com/p/bea7f3a792d5</guid>
            <category><![CDATA[typescript]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[react]]></category>
            <dc:creator><![CDATA[Alistair Israel]]></dc:creator>
            <pubDate>Sat, 01 Jan 2022 13:02:23 GMT</pubDate>
            <atom:updated>2023-02-13T16:33:17.265Z</atom:updated>
            <content:encoded><![CDATA[<h4>Elixir, Phoenix, TypeScript, and React — the 2022 edition</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*H5544gnRnvK6C6LR" /><figcaption>Photo by <a href="https://unsplash.com/@lukesouthern?utm_source=medium&amp;utm_medium=referral">Luke Southern</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><blockquote><em>This is the 2022 update to my earlier </em><a href="https://levelup.gitconnected.com/elixir-phoenix-typescript-and-react-2020-edition-32ceb753705"><em>article</em></a><em> on building up a modern Web application stack.</em></blockquote><blockquote>The code is also up on Github: <a href="https://github.com/aisrael/elixir-phoenix-typescript-react">aisrael/elixir-phoenix-typescript-react</a>.</blockquote><p>The release of <a href="https://www.phoenixframework.org/blog/phoenix-1.6-released">Phoenix 1.6</a> brings with it a couple of notable improvements, such as HEEx templating. To me, though, the most remarkable thing that improves developer experience was dropping webpack in favor of <a href="https://github.com/evanw/esbuild">esbuild</a>.</p><p>Not only does esbuild promise faster build times, it supports TypeScript <em>out of the box</em>, and adding React is also so much easier!</p><p>The entire process has become so streamlined that in this article we can skip entire sections from its predecessors.</p><p>With that out of way, let’s go ahead and create a new project using Elixir and Phoenix 1.6 with TypeScript and React.</p><h3><strong>Prerequisites</strong></h3><p>This guide will require you to have the following, with indicated versions as of this writing:</p><ul><li><a href="https://www.erlang.org/">Erlang</a> (24.2+)</li><li><a href="https://elixir-lang.org/">Elixir</a> (1.13+)</li><li><a href="https://www.npmjs.com/">npm</a> (7.6.0+)</li><li><a href="https://git-scm.com/">git</a></li><li><a href="https://www.docker.com/">Docker</a> with Docker Compose</li></ul><p>On macOS, the easiest way to get started is to install <a href="https://brew.sh/">Homebrew</a>, then use Homebrew to install Node, Git, Docker, and <a href="https://github.com/asdf-vm/asdf">asdf</a>.</p><pre>$ brew install node git homebrew/cask/docker asdf</pre><p>We can then use asdf to install both Erlang and Elixir:</p><pre>$ asdf plugin add erlang<br>$ asdf install erlang 24.2<br>$ asdf global erlang 24.2</pre><pre>$ asdf plugin add elixir<br>$ asdf install elixir 1.13.1<br>$ asdf global elixir 1.13.1</pre><h4>Phoenix</h4><blockquote>For those already familiar with Elixir and Phoenix, you may wish to skip ahead straight to integrating TypeScript and React with Phoenix 1.6.</blockquote><p>If you haven’t already done so, let’s install Phoenix following the <a href="https://hexdocs.pm/phoenix/installation.html#content">Phoenix installation instructions</a>.</p><p>First we’ll want to get the Hex package manager:</p><pre>$ mix local.hex<br>Are you sure you want to install &quot;https://repo.hex.pm/installs/1.13.1/hex-1.0.1.ez&quot;? [Yn]</pre><p>Then install the Phoenix application generator:</p><pre>$ mix archive.install hex phx_new<br>Resolving Hex dependencies...<br>Dependency resolution completed:<br>New:<br>phx_new 1.6.5<br>* Getting phx_new (Hex package)<br>All dependencies are up to date<br>Compiling 11 files (.ex)<br>Generated phx_new app<br>Generated archive &quot;phx_new-1.6.5.ez&quot; with MIX_ENV=prod<br>Are you sure you want to install &quot;phx_new-1.6.5.ez&quot;? [Yn]</pre><p>You can check if Phoenix installation went well using mix phx.new --version:</p><pre>$ mix phx.new --version<br>Phoenix installer v1.6.5</pre><h3>Generate the Phoenix app</h3><pre>$ mix phx.new hello --umbrella</pre><p>This will generate a <a href="https://elixir-lang.org/getting-started/mix-otp/dependencies-and-umbrella-projects.html">Phoenix umbrella project</a> named hello_umbrella in the current directory, with the following directory structure:</p><pre>.<br>├── apps<br>│   ├── hello<br>│   └── hello_web<br>└── config</pre><p>The two Elixir apps are /apps/hello (the domain model) and /apps/hello_web (the Web application).</p><p>Each app will have its own dependency configuration, though the entire umbrella project will have a shared dependency library (in /deps) for all apps.</p><p>All child apps will also share the same root configuration in the /config folder.</p><p>We start with an umbrella project because it helps organise code as the application grows in size and complexity. I’ve also found that it’s easier to refactor an umbrella project down to a single app project than it is to go the other way around.</p><p>If you think you only need a single app project, simply use mix phx.new hello (without the --umbrella option).</p><h4>PostgreSQL, MySQL, or None</h4><p>Phoenix by default uses Postgres for its database.</p><p>If you want to use MySQL rather than Postgres, then you’ll need to generate your Phoenix app using</p><pre>mix phx.new hello_react --umbrella --database mysql</pre><p>If you won’t be needing a database or only wish to follow along without one, then create your Phoenix app using</p><pre>mix phx.new hello_react --umbrella --no-ecto</pre><p>The rest of this guide, however, assumes the default which is Postgres.</p><h4><strong>Git</strong></h4><p>At this point, we should start using Git to track our changes. If you don’t need to use Git, feel free to skip to the next section.</p><pre>$ cd hello_umbrella</pre><pre>~/hello_umbrella$ git init<br>Initialized empty Git repository in /Users/aisrael/hello_umbrella/.git/</pre><pre>~/hello_umbrella$ git add -A<br>~/hello_umbrella$ git commit -m &quot;mix phx.new hello --umbrella&quot;</pre><h4>Docker Compose</h4><p>We’ll be needing a PostgreSQL server to run our Phoenix app. For local development and testing purposes I’ve found that using Docker, or specifically, <a href="https://docs.docker.com/compose/">Docker Compose</a> makes dealing with service dependencies a breeze.</p><p>Create the following docker-compose.yml file in the project root:</p><pre>version: &quot;3&quot;<br>services:<br>  postgres:<br>    image: postgres:14.1<br>    ports:<br>      - 5432:5432<br>    environment:<br>      POSTGRES_USER: postgres<br>      POSTGRES_PASSWORD: postgres<br>      POSTGRES_DB: hello_dev</pre><p>Note that we configure PostgreSQL (using the POSTRES_* environment variables) to work with the generated Phoenix app defaults.</p><p>Then, to run Postgres in the background you only need to go:</p><pre>$ docker compose up -d<br>[+] Running 2/2<br>⠿ Network hello_umbrella_default             Created<br>⠿ Container hello_umbrella_postgres_1        Started</pre><p>For other Docker Compose commands please just visit <a href="https://docs.docker.com/compose/reference/">https://docs.docker.com/compose/reference/</a></p><p>If you can’t or don’t want to use Docker &amp; Docker Compose, you’ll have to install PostgreSQL by hand on your local workstation. Make sure to configure it with the same defaults generated by mix phx.new, or, modify the respective config.exs files with the appropriate configuration.</p><h3>Welcome to Phoenix!</h3><p>At this point we should be able to run our Phoenix application. From the project root (you may wish to run this in a new terminal window or tab), just go:</p><pre><br>~/hello_umbrella$ mix phx.server</pre><p>(If you get a prompt asking if you want to install rebar3, just go ahead and say yes.)</p><p>Now if we visit <a href="http://localhost:4000/">http://localhost:4000</a> we should be able to see the familiar “Welcome to Phoenix!” page:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*iSlrLFH6YUaZ21vk7MWKuA.png" /><figcaption>Welcome to Phoenix!</figcaption></figure><h3>TypeScript in Phoenix 1.6</h3><p>The best thing about Phoenix 1.6 is that it ditched webpack in favor of esbuild.</p><p>Not only does this promise shorter build times, esbuild supports TypeScript <em>out of the box!</em>— no further tooling or configuration required!</p><h4>Welcome to Phoenix with TypeScript</h4><p>To demonstate TypeScript in action, let’s create a new TypeScript module apps/hello_web/assets/js/greeter.ts:</p><pre>function greet(name: string): string {<br>  return &quot;Welcome to &quot; + name + &quot; with TypeScript!&quot;;<br>}</pre><pre>export default greet;</pre><p>Then, in apps/hello_web/assets/js/app.js, add the following lines toward the end:</p><pre>import greet from &quot;./greeter&quot;;</pre><pre>document.querySelector(&quot;section.phx-hero h1&quot;).innerHTML = greet(&quot;Phoenix&quot;);</pre><p>Refresh the page at <a href="http://localhost:4000/">localhost:4000</a> and you should now see it say “Welcome to Phoenix with TypeScript!”:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5_4XBVE-Wsn_JFO9fX5T9w.png" /><figcaption>Welcome to Phoenix with TypeScript!</figcaption></figure><p>That’s it! No need for npm or fiddling with tsconfig.json and webpack.config.js!</p><h3>React</h3><p>Now we can add React into the mix. For this, we’ll now need npm to fetch and install the necessary packages.</p><p>First, make sure we’re in the apps/hello_web/assets directory:</p><pre>~/hello_umbrella$ cd apps/hello_web/assets</pre><p>From there, we’ll issue:</p><pre>npm install --save react react-dom<br>npm install --save-dev <a href="http://twitter.com/types/react">@types/react</a> <a href="http://twitter.com/types/react-dom">@types/react-dom</a></pre><h4>Our First Component</h4><p>Let’s rename greeter.ts to greeter.tsx (similar to how you would rename a regular .js file to .jsx).</p><p>Then, replace the contents of greeter.tsx with a Greeter React component:</p><pre>import React from &quot;react&quot;;</pre><pre>interface GreeterProps {<br>    name: string;<br>}</pre><pre>const Greeter: React.FC&lt;GreeterProps&gt; = (props: GreeterProps) =&gt; {<br>    const name = props.name;<br>    return (<br>        &lt;section className=&quot;phx-hero&quot;&gt;<br>          &lt;h1&gt;Welcome to {name} with TypeScript and React!&lt;/h1&gt;<br>          &lt;p&gt;Peace-of-mind from prototype to production&lt;/p&gt;<br>        &lt;/section&gt;<br>    );<br>};</pre><pre>export default Greeter;</pre><h4>Welcome to Phoenix with TypeScript and React</h4><p>Next, edit the file apps/hello_web/lib/hello_web/templates/page/index.html.heex and replace the entire section that goes:</p><pre>&lt;section class=&quot;phx-hero&quot;&gt;<br>  &lt;h1&gt;&lt;%= gettext &quot;Welcome to %{name}!&quot;, name: &quot;Phoenix&quot; %&gt;&lt;/h1&gt;<br>  &lt;p&gt;Peace of mind from prototype to production&lt;/p&gt;<br>&lt;/section&gt;</pre><p>With just</p><pre>&lt;div id=&quot;greeting&quot;/&gt;</pre><p>Now, we’ll need to rename apps/hello_web/assets/js/app.js to app.jsx, and replace the last two lines we added earlier with:</p><pre>import React from &quot;react&quot;;<br>import ReactDOM from &quot;react-dom&quot;;</pre><pre>import Greeter from &quot;./greeter&quot;;</pre><pre>const greeting = document.getElementById(&quot;greeting&quot;);<br>ReactDOM.render(&lt;Greeter name=&quot;Phoenix&quot; /&gt;, greeting);</pre><p>(We need to rename it from .js to .jsx to avoid a syntax error in the last line.)</p><p>Finally, we’ll need to edit config/config.exs to tell esbuild to look for app.jsx instead of app.js. Look for the section starting with # Configure esbuild and replace the line that goes</p><pre>~w(js/app.js --bundle --target=es2017 --outdir=../priv/static/assets --external:/fonts/* --external:/images/*),</pre><p>with</p><pre>~w(js/app.jsx --bundle --target=es2017 --outdir=../priv/static/assets --external:/fonts/* --external:/images/*)</pre><p>At this point, we might have to restart our Phoenix server (press Ctrl+C then (a)bort) and restart it again with mix phx.server.</p><p>When we reload <a href="http://localhost:4000/">http://localhost:4000</a>, we should then be greeted with:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*aSDhd-dGU81GacyffKxOKQ.png" /><figcaption>Welcome to Phoenix with TypeScript and React!</figcaption></figure><p><em>Voila!</em></p><p>I think that Phoenix 1.6 with esbuild provides one the best developer experiences I’ve ever encountered lately in setting up one of the most modern and productive Web app stacks around, and I hope this article has convinced you of how easy it is, especially compared to before.</p><p>Now get out there and build your next unicorn Web app!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bea7f3a792d5" width="1" height="1" alt=""><hr><p><a href="https://medium.com/better-programming/phoenix-1-6-with-typescript-react-bea7f3a792d5">Phoenix 1.6 With TypeScript and React</a> was originally published in <a href="https://betterprogramming.pub/">Better Programming</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Elixir, Phoenix, Typescript, and React — 2020 Edition]]></title>
            <link>https://levelup.gitconnected.com/elixir-phoenix-typescript-and-react-2020-edition-32ceb753705?source=rss-c0e66d40ab34------2</link>
            <guid isPermaLink="false">https://medium.com/p/32ceb753705</guid>
            <category><![CDATA[react]]></category>
            <category><![CDATA[typescript]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[elixir]]></category>
            <category><![CDATA[phoenix-framework]]></category>
            <dc:creator><![CDATA[Alistair Israel]]></dc:creator>
            <pubDate>Mon, 29 Jun 2020 07:42:50 GMT</pubDate>
            <atom:updated>2020-08-22T15:25:33.679Z</atom:updated>
            <content:encoded><![CDATA[<h3>Elixir, Phoenix, Typescript, and React — 2020 Edition</h3><h4>An updated guide to building up a modern Web application stack</h4><p><em>This is an updated edition of my earlier guide, published in </em><a href="https://dev.to/aisrael/elixir-phoenix-with-typescript-and-react-december-2019-edition-39l4"><em>December, 2019 on DEV.to</em></a><em>.</em></p><p>The code is also up on Github: <a href="https://github.com/aisrael/elixir-phoenix-typescript-react">aisrael/elixir-phoenix-typescript-react</a></p><p>While the essential approach has remained unchanged, somebody filed a ticket on my repo to upgrade it to Phoenix 1.5.</p><p>Rather than take the earlier codebase and upgrade it Phoenix 1.5 (which I’ve managed to do for a couple of other projects), I decided instead to update the original article for 2020.</p><p>Let’s jump right back in.</p><h3><strong>Prerequisites</strong></h3><p>This guide assumes you already have the following set up:</p><ul><li><strong>Elixir</strong> (1.10.3), which means you’ll also need</li><li><strong>Erlang</strong> (23.0.2)</li><li><strong>npm</strong> (@6.14.5)</li><li><strong>git</strong> (not really required but you’ll want to anyway)</li><li><strong>Docker Compose</strong> (for running PostgreSQL)</li></ul><p>If you don’t have Elixir (and Erlang) yet, I highly recommend using <a href="https://asdf-vm.com/">asdf</a> to manage Elixir/Erlang versions.</p><p>Install asdf according to your platform’s instructions, then use asdf to install both Erlang and Elixir:</p><pre>$ asdf plugin add erlang<br>$ asdf install erlang 23.0.2<br>$ asdf global erlang 23.0.2</pre><pre>$ asdf plugin add elixir<br>$ asdf install elixir 1.10.3<br>$ asdf global elixir 1.10.3</pre><h4><strong>Phoenix</strong></h4><p>(If you’re already experienced with Elixir Phoenix applications, you may wish to skip ahead to the Typescript and React bits.)</p><p>If you haven’t already done so, let’s install Phoenix following the <a href="https://hexdocs.pm/phoenix/installation.html">Phoenix installation instructions</a>.</p><p>First we’ll want to get the Hex package manager:</p><pre>$ mix local.hex<br>Are you sure you want to install &quot;<a href="https://repo.hex.pm/installs/1.10.0/hex-0.20.5.ez">https://repo.hex.pm/installs/1.10.0/hex-0.20.5.ez</a>&quot;? [Yn] y<br>* creating /Users/aisrael/.asdf/installs/elixir/1.10.3/.mix/archives/hex-0.20.5</pre><p>Then the Elixir Mix archive:</p><pre>$ mix archive.install hex phx_new 1.5.3<br>Resolving Hex dependencies…<br>Dependency resolution completed:<br>New:<br>phx_new 1.5.3<br>* Getting phx_new (Hex package)<br>All dependencies are up to date<br>Compiling 10 files (.ex)<br>Generated phx_new app<br>Generated archive &quot;phx_new-1.5.3.ez&quot; with MIX_ENV=prod<br>Are you sure you want to install &quot;phx_new-1.5.3.ez&quot;? [Yn] Y<br>* creating /Users/aisrael/.asdf/installs/elixir/1.10.3/.mix/archives/phx_new-1.5.3</pre><p>You can check if Phoenix installation went well using mix phx.new --version:</p><pre>$ mix phx.new --version<br>Phoenix v1.5.3</pre><h3><strong>Generate the Phoenix app</strong></h3><pre>$ mix phx.new hello_react --umbrella</pre><p>This will generate an Elixir + Phoenix <a href="https://elixir-lang.org/getting-started/mix-otp/dependencies-and-umbrella-projects.html">umbrella app</a> named hello_react_umbrella in the current directory with the following directory structure:</p><pre>.<br>├── apps<br>│ ├── hello_react<br>│ └── hello_react_web<br>├── config<br>└── deps</pre><p>The two Elixir apps are /apps/hello_react and apps/hello_react_web.</p><p>Each app will its own dependency configuration, though the entire umbrella project will have a shared dependency library (in /deps) for all apps.</p><p>All child apps also share the same root configuration in the /config folder.</p><p>We start with an umbrella app because it makes it easier to organize code as the application gets bigger and more complex. Besides, we’ve found that it’s easier to refactor an umbrella app project to a single app project than it is to go the other way around.</p><h4><strong>PostgreSQL, MySQL, or ` </strong><strong>— no-ecto`</strong></h4><p>Phoenix by default uses PostgreSQL for its database.</p><p>If you want to use MySQL rather than PostgreSQL, then you’ll need to generate your Phoenix app using</p><pre>mix phx.new hello_react --umbrella --database mysql</pre><p>If you won’t be needing a database or only wish to follow along without one, then create your Phoenix app using</p><pre>mix phx.new hello_react --umbrella --no-ecto</pre><p>The rest of this guide, however, assumes the default which is Postgres.</p><h4><strong>Git</strong></h4><p>At this point, we should start using Git to track our changes. If you don’t need to use Git, feel free to skip to the next section.</p><pre>$ cd hello_react_umbrella<br>~/hello_react_umbrella$</pre><p>Then</p><pre>$ git init<br>Initialized empty Git repository in ~/hello_react_umbrella/.git/<br>$ git add -A<br>$ git commit -m “mix phx.new hello_react --umbrella”</pre><h4><strong>Docker Compose</strong></h4><p>Since we’ll be needing a PostgreSQL server to run our Phoenix app, for local development and testing purposes we’ve found that using Docker, specifically, Docker Compose makes dealing with service dependencies a breeze.</p><p>Create the following docker-compose.yml in the project root:</p><pre>version: &quot;3&quot;<br>services:<br>  postgres:<br>    image: postgres:11.5<br>    ports:<br>      - 5432:5432<br>    environment:<br>      POSTGRES_USER: postgres<br>      POSTGRES_PASSWORD: postgres<br>      POSTGRES_DB: hello_react_dev</pre><p>Note that we configure PostgreSQL (using the POSTGRES_* environment variables) to work with the generated Phoenix app defaults.</p><p>Then, to run Postgres in the background you only need to go:</p><pre>$ docker-compose up -d<br>Creating network &quot;hello_react_umbrella_default&quot; with the default driver<br>Creating hello_react_umbrella_postgres_1 ... done</pre><p>Since Docker Compose is beyond the scope of this article, for other Docker Compose commands please just visit:</p><ul><li><a href="https://docs.docker.com/compose/reference/overview/">https://docs.docker.com/compose/reference/overview/</a></li></ul><p>If you can’t or don’t want to use Docker &amp; Docker Compose, you’ll have to install PostgreSQL by hand on your local workstation. Make sure to configure it with the same defaults generated by mix phx.new, or, modify the respective config/*.exs files with the appropriate credentials.</p><h4><strong>Node</strong></h4><p>Before we can run our Phoenix application, we need to initialise the generated CSS and Javascript assets using npm install:</p><pre>~/hello_react_umbrella$ cd apps/hello_react_web/assets<br>~/hello_react_umbrella/apps/hello_web/assets$ npm install</pre><h4><strong>Welcome to Phoenix!</strong></h4><p>At this point we should be able to run our Phoenix application. From the project root (you may wish to run this in a new terminal window or tab):</p><pre>$ mix phx.server</pre><p>(If you get a prompt asking if you want to install rebar3, just go ahead and say yes.)</p><p>Now if we visit <a href="http://localhost:4000/">http://localhost:4000</a> we should be able to see the familiar “Welcome to Phoenix!” page:</p><figure><img alt="Welcome to Phoenix!" src="https://cdn-images-1.medium.com/max/1024/1*v10hRBGa5JAAa6vCtT0_Rw.png" /></figure><h3><strong>Typescript</strong></h3><p>We’re ready to start adding Typescript to the frontend.</p><p>First, make sure we’re back in apps/hello_react_web/assets/. Add the Typescript libraries using:</p><pre>$ npm install --save-dev typescript ts-loader source-map-loader <a href="http://twitter.com/types/phoenix">@types/phoenix</a></pre><h4><strong>Typescript Configuration</strong></h4><p>Afterwards, let’s ask Typescript to generate a default tsconfig.json for us:</p><pre>$ ./node_modules/.bin/tsc --init<br>message TS6071: Successfully created a tsconfig.json file.</pre><p>We need to change a few things from the Typescript defaults. Here’s a minimal tsconfig.json with some of the necessary changes:</p><pre>{<br>  &quot;compilerOptions&quot;: {<br>    &quot;target&quot;: &quot;es5&quot;,<br>    &quot;module&quot;: &quot;ESNext&quot;,<br>    &quot;allowJs&quot;: true,<br>    &quot;jsx&quot;: &quot;react&quot;,<br>    &quot;outDir&quot;: &quot;./dist/&quot;,<br>    &quot;strict&quot;: true,<br>    &quot;esModuleInterop&quot;: true,<br>    &quot;forceConsistentCasingInFileNames&quot;: true<br>  },<br>  &quot;exclude&quot;: [<br>    &quot;/node_modules/**/*&quot;,<br>  ]<br>}</pre><h4><strong>Configure Webpack</strong></h4><p>Next, we’ll need to tell Webpack to recognise .ts files along with .js files:</p><p>Open apps/hello_react_web/assets/webpack.config.js and change the first module rule to:</p><pre>    rules: [<br>      {<br>        test: /\.(j|t)s$/,<br>        exclude: /node_modules/,<br>        use: [<br>          {<br>            loader: &quot;babel-loader&quot;<br>          },<br>          {<br>            loader: &quot;ts-loader&quot;<br>          }<br>        ]<br>      },</pre><p>Additionally, add an outermost &quot;resolve&quot; key after &quot;module&quot; as follows:</p><pre>  resolve: {<br>    extensions: [&quot;.ts&quot;, &quot;.js&quot;]<br>  },</pre><h4><strong>app.js</strong></h4><p>When we generated our Phoenix app, it created apps/hello_react_web/assets/js/app.js with an import css from &quot;../css/app.css&quot;; line.</p><p>This causes problems when that file is parsed by Typescript. You can see a lengthy discussion and several workarounds for this at <a href="https://stackoverflow.com/questions/41336858/how-to-import-css-modules-with-typescript-react-and-webpack">this Stackoverflow question</a> and on this <a href="https://medium.com/@sapegin/css-modules-with-typescript-and-webpack-6b221ebe5f10">page</a>.</p><p>The simplest (though not the best) way to fix this before proceeding (so Webpack will continue to process our app.css) is to change that line to use require:</p><pre>const _css = require(&quot;../css/app.css&quot;);</pre><p>Since we don’t actually use the variable (we only need it so Webpack can generate our app.css file properly), we prepend its name with an underscore to suppress the “unused variable” warning that Typescript would otherwise emit.</p><h4><strong>Welcome to Phoenix with Typescript!</strong></h4><p>To demonstrate Typescript in action, we’ll create a new Typescript module apps/hello_react_web/assets/js/hello.ts:</p><pre>function greet(name: string): string {<br>  return &quot;Welcome to &quot; + name + &quot; with Typescript!&quot;;<br>}</pre><pre>export default greet;</pre><p>Then, in assets/js/app.js add the following lines toward the end:</p><pre>import greet from &quot;./hello&quot;;</pre><pre>document.querySelector(&quot;section.phx-hero h1&quot;).innerHTML = greet(&quot;Phoenix&quot;);</pre><p>Refresh the page at localhost:4000 and you should now see it say “Welcome to Phoenix with Typescript!”.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8jOX-w9Hy7aY7W4RrFrYEA.png" /></figure><h3>React</h3><p>Let’s go ahead and add React roughly following the guide at: <a href="https://www.typescriptlang.org/docs/handbook/react-&amp;-webpack.html">https://www.typescriptlang.org/docs/handbook/react-&amp;-webpack.html</a></p><p>First, add the necessary packages:</p><pre>$ npm install --save react react-dom<br>$ npm install --save-dev <a href="http://twitter.com/types/react">@types/react</a> <a href="http://twitter.com/types/react-dom">@types/react-dom</a></pre><p>Once again we need to reconfigure webpack.config.js.</p><p>First, let’s recognize *.jsx and *.tsx files:</p><pre>    rules: [<br>      {<br>        test: /\.(j|t)sx?$/,<br>        exclude: /node_modules/,</pre><p>Also:</p><pre>  resolve: {<br>    extensions: [&quot;.ts&quot;, &quot;.tsx&quot;, &quot;.js&quot;, &quot;.jsx&quot;]<br>  },</pre><h4><strong>Our First Component</strong></h4><p>First let’s rename apps/hello_react_web/assets/js/hello.ts to apps/hello_react_web/assets/js/hello.tsx (similar to how you would name a React JS file .jsx from plain .js):</p><pre>$ mv apps/hello_react_web/assets/js/hello.ts apps/hello_react_web/assets/js/hello.tsx</pre><p>Then, replace the contents of hello.tsx with:</p><pre>import React from &quot;react&quot;;</pre><pre>interface GreeterProps {<br>  name: string;<br>}</pre><pre>const Greeter: React.FC&lt;GreeterProps&gt; = (props: GreeterProps) =&gt; {<br>  const name = props.name;<br>  return (<br>    &lt;section className=&quot;phx-hero&quot;&gt;<br>      &lt;h1&gt;Welcome to {name} with Typescript and React!&lt;/h1&gt;<br>      &lt;p&gt;Peace-of-mind from prototype to production&lt;/p&gt;<br>    &lt;/section&gt;<br>  );<br>};</pre><pre>export default Greeter;</pre><h4><strong>Welcome to Phoenix with Typescript and React</strong></h4><p>Next, in apps/hello_react_web/lib/hello_react_web/templates/page/index.html.eex, remove the section:</p><pre>&lt;section class=&quot;phx-hero&quot;&gt;<br>  &lt;h1&gt;&lt;%= gettext &quot;Welcome to %{name}!&quot;, name: &quot;Phoenix&quot; %&gt;&lt;/h1&gt;<br>  &lt;p&gt;Peace-of-mind from prototype to production&lt;/p&gt;<br>&lt;/section&gt;</pre><p>And replace it with simply:</p><pre>&lt;div id=”greeting”&gt;&lt;/div&gt;</pre><p>Then, in apps/hello_react_web/assets/js/app.js, replace the last few lines with:</p><pre>import React from &quot;react&quot;;<br>import ReactDOM from &quot;react-dom&quot;;</pre><pre>import Greeter from &quot;./hello&quot;;</pre><pre>const greeting = document.getElementById(&quot;greeting&quot;);<br>ReactDOM.render(&lt;Greeter name=&quot;Phoenix&quot; /&gt;, greeting);</pre><p>Finally (you may neeed to restart Phoenix using mix phx.server and wait a seconds for Webpack to compile everything), when we reload localhost:4000 we should see “Welcome to Phoenix with Typescript and React!`</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zU-aNMfqocNCOb9s2Qsz8Q.png" /></figure><p>Now you’re ready to rock building your next unicorn Web app using one of the most modern and productive Web app stacks around.</p><p>In the future, I hope to expound upon this by adding a React UI framework and an article on how to add GraphQL to you Phoenix + React app.</p><p><a href="https://skilled.dev/">Coding Interview Questions + Land Your Dev Job | Skilled.dev</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=32ceb753705" width="1" height="1" alt=""><hr><p><a href="https://levelup.gitconnected.com/elixir-phoenix-typescript-and-react-2020-edition-32ceb753705">Elixir, Phoenix, Typescript, and React — 2020 Edition</a> was originally published in <a href="https://levelup.gitconnected.com/">Level Up Coding</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How Fast Is Rust?]]></title>
            <link>https://medium.com/swlh/how-fast-is-rust-655f6dd90ff8?source=rss-c0e66d40ab34------2</link>
            <guid isPermaLink="false">https://medium.com/p/655f6dd90ff8</guid>
            <category><![CDATA[benchmarking]]></category>
            <category><![CDATA[rust]]></category>
            <category><![CDATA[golang]]></category>
            <category><![CDATA[prime-numbers]]></category>
            <dc:creator><![CDATA[Alistair Israel]]></dc:creator>
            <pubDate>Thu, 28 May 2020 09:07:20 GMT</pubDate>
            <atom:updated>2020-06-29T07:50:20.187Z</atom:updated>
            <content:encoded><![CDATA[<h4>Comparing a Sieve of Eratosthenes in Rust vs. Go.</h4><p>After seeing <a href="https://medium.com/u/b1f6e617cb03">8F3E</a>’s article, <a href="https://medium.com/swlh/how-fast-is-golang-135c658205eb"><em>How Fast Is Golang?</em></a> which compares the performance of Python vs. Go using a <a href="https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes">Sieve of Eratosthenes</a> as a microbenchmark, I thought it might be fun to see how Rust stacks up.</p><p>So, as an exercise to flex my Rust muscles before taking on a much harder challenge, I decided to fork 8F3E’s <a href="https://github.com/8F3E/sieve-of-eratosthenes">repo</a> into my own: <a href="https://github.com/aisrael/sieve-of-eratosthenes">https://github.com/aisrael/sieve-of-eratosthenes</a></p><p>I then ported the Go code to Rust code, almost <em>verbatim</em>:</p><pre>    let mut primes: Vec&lt;i32&gt; = Vec::new();</pre><pre>    for i in 2..=n {<br>        primes.push(i);<br>    }</pre><pre>    {<br>        let mut i = 0;<br>        while i &lt; primes.len() {<br>            let factor = primes[i];<br>            sieve(&amp;mut primes, factor);<br>            i += 1;<br>        }<br>    }</pre><pre>    for i in 0..primes.len() {<br>        println!(&quot;{}&quot;, primes[i])<br>    }</pre><p>And</p><pre>fn sieve(primes: &amp;mut Vec&lt;i32&gt;, factor: i32) {<br>    let mut i = 0;<br>    while i &lt; primes.len() {<br>        let value = primes[i];<br>        if value != factor {<br>            if value % factor == 0 {<br>                primes.remove(i);<br>            }<br>        }<br>        i += 1;<br>    }<br>}</pre><p>In most cases I had to use a while loop rather than a for loop, because in Rust, for loops take one of two forms:</p><pre>for i in 0..list.len() {<br>    if primes[i] != 0 {<br>        let factor = primes[i];<br>        sieve(&amp;mut primes, factor);<br>    }<br>}</pre><p>That won’t work if you mutate the vector and change its length (since the list.len() is evaluated only once, at the beginning of the loop). It will compile, but give us a run time error.</p><p>We might’ve also tried:</p><pre>for factor in primes {<br>    sieve(&amp;mut primes, factor);<br>}</pre><p>But this won’t even compile!</p><p>The reason is all about why Rust is considered a language that helps you write ‘safer’ and more robust code. In this particular case, Rust sees that we’re mutating the primes vector but we’re also trying to iterate over it which can lead to hard to debug errors later on.</p><p>For instance, Rust if you try to do this:</p><pre>sieve(&amp;mut primes, primes[i]);</pre><p>Rust complains with:</p><pre>28 |                 sieve(&amp;mut primes, primes[i]);<br>   |                 ----- -----------  ^^^^^^ immutable borrow occurs here<br>   |                 |     |<br>   |                 |     mutable borrow occurs here<br>   |                 mutable borrow later used by call</pre><p>In this case, we have to introduce another, immutable, variable just to ‘hold’ the value of primes[i] which we can then pass to the sieve() function.</p><p>So, while Rust may feel like it’s getting in the way by being strict, the workaround is just to be more <em>explicit</em>, telling Rust that, “Yes, we know what we’re doing.”</p><h4>EDIT: I rewrote the Rust implementation to more closely match the Go implementation</h4><p>So, now instead of having to use while, the Rust code looks like:</p><pre>    for i in 0..primes.len() {<br>        let factor = primes[i];<br>        if factor != 0 {<br>            sieve(&amp;mut primes, factor);<br>        }<br>    }</pre><p>See the changes in this commit: <a href="https://github.com/aisrael/sieve-of-eratosthenes/commit/567b05a0f545273aee94c56e9b101b36f0f88752">567b05a</a>.</p><p>I’ve also updated the results section after re-running the benchmarks.</p><h3>Building</h3><p>A quick note before just running the microbenchmarks: if we simply use rustc or even cargo using just no arguments, the Rust compiler will compile the resulting binary <em>without</em> optimisations and <em>with</em> debugging information.</p><p>This executable will be <em>slower</em> than the executable built with Go, because the <a href="https://github.com/golang/go/wiki/CompilerOptimizations">Go compiler performs optimisations</a> by default.</p><p>Hence, to be fair we need to pass flags to rustc to build the executable as if we had used Cargo and ran cargo build --release. I also took care to name the Rust binary differently from the Go binary:</p><pre>rustc -C debuginfo=0 -C opt-level=3 sieve.rs -o bin/sieve-rs</pre><h3>Running</h3><p>I ran and timed the Rust executable using zsh’s built-in time function:</p><pre>time bin/sieve-rs 10000</pre><p>Which, on my 2019 16&quot; Macbook Pro gives</p><pre>bin/sieve-rs 10000  0.01s user 0.00s system 14% cpu 0.075 total</pre><p>I then wrote up a Python script that could run both the Go and Rust executables, giving them different and progressively increasing parameters, then capturing the user time output for each:</p><pre>python bench.py</pre><p>We can even specify minimum and maximum limits, and the benchmark script will try to compute the ‘steps’ in between by using powers of 10. For example, if given:</p><pre>MIN=100 MAX=10000 python bench.py</pre><p>Then the benchmark is run for both Go and Rust for 100, 200, …, 800, 900, 1000, 2000, …, 8000, 9000, then 10000 primes.</p><p>The bench.py script also overwrites the timing_data.csv in the testing folder. I then modified <a href="https://medium.com/u/b1f6e617cb03">8F3E</a>’s original Jupyter notebook script to just use the CSV column headers for the graph labels.</p><p>Like <a href="https://medium.com/u/b1f6e617cb03">8F3E</a> I didn’t bother running each <em>n</em> multiple times to get the average, since the trend was clear with one pass.</p><h3>Results</h3><p>I’ll let the graphs speak for themselves (lower is better):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*6qnux4SlysEdGEh66DGz7Q.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*qoD5E_-sbflSL8N26GiFLQ.png" /><figcaption>Logarithmic plot</figcaption></figure><h3>Final Words</h3><p>Sure, microbenchmarks are fun and, when judiciously applied and carefully analysed, can provide some useful insight into a very specific choice between, say, one implementation or another.</p><p>In the real-world though, with large applications and complex systems, language or compiler level differences are overshadowed by a host of other factors: algorithms, program architecture, I/O, infrastructure, system architecture, and so on.</p><p>So please don’t take these results too seriously.</p><p>On the other hand, the reason why we use Rust is precisely what I mentioned earlier — while there’s added effort and cognitive load on the developer because we have to worry about ownership and mutability, this in the long run helps us write more performant code (since there’s no overhead for garbage collection) and more importantly, more reliable code.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=655f6dd90ff8" width="1" height="1" alt=""><hr><p><a href="https://medium.com/swlh/how-fast-is-rust-655f6dd90ff8">How Fast Is Rust?</a> was originally published in <a href="https://medium.com/swlh">The Startup</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Demystifying Closures, Futures and async-await in Rust–Part 3: Async & Await]]></title>
            <link>https://medium.com/@alistairisrael/demystifying-closures-futures-and-async-await-in-rust-part-3-async-await-9ed20eede7a4?source=rss-c0e66d40ab34------2</link>
            <guid isPermaLink="false">https://medium.com/p/9ed20eede7a4</guid>
            <category><![CDATA[rust]]></category>
            <dc:creator><![CDATA[Alistair Israel]]></dc:creator>
            <pubDate>Mon, 11 May 2020 10:20:39 GMT</pubDate>
            <atom:updated>2020-05-14T13:10:32.620Z</atom:updated>
            <content:encoded><![CDATA[<p>In this series of articles, I attempt to demystify and progress from Rust closures, to futures, and then eventually to async-await.</p><p>If you’ve been following along, in <a href="https://medium.com/@alistairisrael/demystifying-closures-futures-and-async-await-in-rust-part-1-closures-97e531e4dc50"><strong>Part 1: Closures</strong></a>, we first recollected our understanding of Rust closures.</p><p>Then in <a href="https://medium.com/@alistairisrael/demystifying-closures-futures-and-async-await-in-rust-part-2-futures-abe95ab332a2"><strong>Part 2: Futures</strong></a>, we flexed our understanding of the Tokio runtime, the Future trait and the futures crate.</p><h3>The Async you’ve been Awaiting for</h3><p>Let’s pretend we don’t know anything and jump right in and try to write our first async function. To do this, we simply prepend the async keyword to our fn declaration:</p><pre>async fn async_hello() {<br>    debug!(&quot;Hello, asynchronously!&quot;);<br>}</pre><p>That’s all it takes, really!</p><p>But what happens if we try to call that function directly? In our main() let’s try writing:</p><pre>fn main() {<br>    // ...</pre><pre>    async_hello();<br>}</pre><p>We get a compiler warning:</p><pre>warning: unused implementer of `core::future::future::Future` that must be used<br>   --&gt; src/main.rs:169:5<br>    |<br>169 |     async_hello();<br>    |     ^^^^^^^^^^^^^^<br>    |<br>    = note: `#[warn(unused_must_use)]` on by default<br>    = note: futures do nothing unless you `.await` or poll them</pre><p>Rust is telling us that async_hello() is returning a Future that is unused. (You may recall we’ve seen this warning before, when we tried to <a href="https://medium.com/@alistairisrael/demystifying-closures-futures-and-async-await-in-rust-part-2-futures-abe95ab332a2#fdb2">simply call our </a><a href="https://medium.com/@alistairisrael/demystifying-closures-futures-and-async-await-in-rust-part-2-futures-abe95ab332a2#fdb2">returns_delayed_future() in </a><a href="https://medium.com/@alistairisrael/demystifying-closures-futures-and-async-await-in-rust-part-2-futures-abe95ab332a2#fdb2">rt.enter()</a>.)</p><p>But what happens if we try to .await it just like it says?</p><p>Now we get a compiler error, since our main() is not async:</p><pre>error[E0728]: `await` is only allowed inside `async` functions and blocks<br>   --&gt; src/main.rs:169:5<br>    |<br>102 | fn main() {<br>    |    ---- this is not `async`<br>...<br>169 |     async_hello().await;<br>    |     ^^^^^^^^^^^^^^^^^^^ only allowed inside `async` functions and blocks</pre><p>The error mentions async blocks, let’s try wrapping that line in an async {}:</p><pre>async {<br>    async_hello().await;<br>}</pre><p>If that block is at the end of fn main() { ... }, we immediately run into:</p><pre>error[E0308]: mismatched types<br>   --&gt; src/main.rs:169:5<br>    |<br>102 |   fn main() {<br>    |             - expected `()` because of default return type<br>...<br>169 | /     async {<br>170 | |         async_hello().await;<br>171 | |     }<br>    | |     ^- help: try adding a semicolon: `;`<br>    | |_____|<br>    |       expected `()`, found opaque type</pre><p>Now if we add a semicolon after the block as it says:</p><pre>async {<br>    async_hello().await;<br>};</pre><p>We once again get:</p><pre>warning: unused implementer of `core::future::future::Future` that must be used<br>   --&gt; src/main.rs:169:5<br>    |<br>169 | /     async {<br>170 | |         async_hello().await;<br>171 | |     };<br>    | |______^<br>    |<br>    = note: `#[warn(unused_must_use)]` on by default<br>    = note: futures do nothing unless you `.await` or poll them</pre><p>But this time, at the end of the async block.</p><p>We’re just running around in circles!</p><h3>async -&gt; Future</h3><p>The key thing to remember is that <strong>whether we use </strong><strong>async for a block or a function, both times we get a </strong><strong>Future</strong>.</p><p>We already know what to do from <a href="https://medium.com/@alistairisrael/demystifying-closures-futures-and-async-await-in-rust-part-2-futures-abe95ab332a2#b17b"><strong>Part 2</strong></a> when we tried to call a function that returns a Future directly in main().</p><p>For a Future to be executed or polled, we need to pass our future to the <a href="https://tokio.rs/">Tokio</a> runtime. Thus we need to write:</p><pre>let mut rt = tokio::runtime::Runtime::new().unwrap();<br>rt.block_on(async_hello());</pre><p>So we get the output we desire:</p><pre>08:10:29 [DEBUG] (1) Hello, asynchronously!</pre><p>Now let’s try that async block again. First we’ll print something directly in the block:</p><pre>let async_block = async {<br>    debug!(&quot;in async_block&quot;);<br>};<br>rt.block_on(async_block);</pre><p>That works as expected. We can even ‘capture’ surrounding variables (Rust has a different and specific definition of async closure though and as of this writing, it’s a feature still deemed unstable):</p><pre>{<br>    let x = 42;<br>    let async_capture = async {<br>        debug!(&quot;in async_capture, x =&gt; {}&quot;, x);<br>    };<br>    rt.block_on(async_capture);<br>}</pre><p>You can think of the above code as equivalent to:</p><pre>{<br>    let x = 42;<br>    let async_capture = future::lazy(|_| {<br>        debug!(&quot;in async_capture, x =&gt; {}&quot;, x);<br>    });<br>    rt.block_on(async_capture);<br>}</pre><p>To recap we can think of async {...} as ‘desugaring’ to future::lazy(|_| {...}). They’re not <em>exactly</em> the same but for practical purposes they exhibit the same behavior.</p><h3>Async Functions</h3><p>Similarly, we can now begin to understand async fn as merely ‘sugar’ that returns a Future&lt;Output=T&gt;.</p><p>That is, the following functions are practically equivalent:</p><pre>async fn async_returns_i32() -&gt; i32 {<br>    42<br>}</pre><pre>fn returns_future_i32() -&gt; impl Future&lt;Output = i32&gt; {<br>    future::ready(42)<br>}</pre><p>So the way to read an async fn f() -&gt; T is simply as being equivalent to an ordinary fn f() -&gt; impl Future&lt;Output = T&gt;.</p><p>We can even return an async block as a Future:</p><pre>fn returns_async_block_i32() -&gt; impl Future&lt;Output = i32&gt; {<br>    async { 42 }<br>}</pre><p>All three above functions when called behave pretty much exactly the same way.</p><h3>From Futures to Async-Await</h3><p>Up until now, we’ve been calling rt.block_on() multiple times.</p><p>Suppose our code looks like this:</p><pre>rt.block_on(future::lazy(|_| debug!(&quot;in rt.block_on()&quot;)));<br>let r0 = rt.block_on(future::ready(&quot;Hello from rt.block_on()&quot;));<br>debug!(&quot;{}&quot;, r0);<br>let r1 = rt.block_on(returns_impl_future_i32());<br>debug!(&quot;returns_impl_future_i32() -&gt; {}&quot;, r1);<br>let r2 = rt.block_on(returns_dyn_future_i32());<br>debug!(&quot;returns_dyn_future_i32() -&gt; {}&quot;, r2);<br>let r3 = rt.block_on(returns_future_result());<br>debug!(&quot;returns_future_result() -&gt; {}&quot;, r3.unwrap());<br>let r4 = rt.block_on(returns_future_result_dyn_error());<br>debug!(&quot;returns_future_result_dyn_error() -&gt; {}&quot;, r4.unwrap());<br>let r5 = rt.block_on(returns_delayed_future()));<br>debug!(&quot;returns_delayed_future() -&gt; {}&quot;, r5))<br>let r6 = rt.block_on(wait_a_sec(future::ready(42)));<br>debug!(&quot;wait_a_sec(future::ready(42)) -&gt; {}&quot;, r6));<br>rt.block_on(async_hello());<br>let async_block = async {<br>    debug!(&quot;in async_block&quot;);<br>};<br>rt.block_on(async_block);<br>let x = 42;<br>let async_capture = async {<br>    debug!(&quot;in async_capture, x =&gt; {}&quot;, x);<br>};<br>rt.block_on(async_capture);<br>let r7 = rt.block_on(async_returns_i32());<br>debug!(&quot;async_returns_i32 -&gt; {}&quot;, r7);<br>let r8 = rt.block_on(returns_future_i32());<br>debug!(&quot;returns_future_i32 -&gt; {}&quot;, r8);<br>let r9 = rt.block_on(returns_async_block_i32());<br>debug!(&quot;returns_async_block_i32 -&gt; {}&quot;, r9);</pre><p>We can rewrite <em>all</em> the above <a href="https://medium.com/@alistairisrael/demystifying-closures-futures-and-async-await-in-rust-part-2-futures-abe95ab332a2#9247">using future combinators to return a single future chain</a>, <em>or</em> we can now truly start making use of the power and convenience async and await provide us.</p><p>As we’ve seen, an async {} and a future::lazy(|_| {…}) are practically interchangeable.</p><p>So first, we need to surround all those lines starting with future::lazy in a single async {...} block which we will block_on().</p><p>We then simply replace all the inner calls to rt.block_on(f) with f.await</p><p>The above code now becomes:</p><pre>rt.block_on(<br>    async {<br>        debug!(&quot;in rt.block_on()&quot;);<br>        let r0 = future::ready(&quot;Hello from rt.block_on()&quot;).await;<br>        debug!(&quot;{}&quot;, r0);<br>        let r1 = returns_impl_future_i32().await;<br>        debug!(&quot;returns_impl_future_i32() -&gt; {}&quot;, r1);<br>        let r2 = returns_dyn_future_i32().await;<br>        debug!(&quot;returns_dyn_future_i32() -&gt; {}&quot;, r2);<br>        let r3 = returns_future_result().await;<br>        debug!(&quot;returns_future_result() -&gt; {}&quot;, r3.unwrap());<br>        let r4 = returns_future_result_dyn_error().await;<br>        debug!(&quot;returns_future_result_dyn_error() -&gt; {}&quot;, r4.unwrap());<br>        let r5 = returns_delayed_future().await;<br>        debug!(&quot;returns_delayed_future() -&gt; {}&quot;, r5);<br>        let r6 = wait_a_sec(future::ready(42)).await;<br>        debug!(&quot;wait_a_sec(future::ready(42)) -&gt; {}&quot;, r6);<br>        async_hello().await;<br>        let async_block = async {<br>            debug!(&quot;in async_block&quot;);<br>        };<br>        async_block.await;<br>        let x = 42;<br>        let async_capture = async {<br>            debug!(&quot;in async_capture, x =&gt; {}&quot;, x);<br>        };<br>        async_capture.await;<br>        let r7 = async_returns_i32().await;<br>        debug!(&quot;async_returns_i32 -&gt; {}&quot;, r7);<br>        let r8 = returns_future_i32().await;<br>        debug!(&quot;returns_future_i32 -&gt; {}&quot;, r8);<br>        let r9 = returns_async_block_i32().await;<br>        debug!(&quot;returns_async_block_i32 -&gt; {}&quot;, r9);<br>    });</pre><p>Like rt.block_on(), .await works just as well whether for a Future or an async block or an async fn.</p><h3>#[tokio::main]</h3><p>Now if all our main() function does is equivalent to the following:</p><pre>fn main() {<br>    let mut rt = tokio::runtime::Runtime::new().unwrap();<br>    rt.block_on(async {<br>        // do async stuff<br>    });<br>}</pre><p>Then Tokio lets us pull another trick to further simplify our code and make it as if Rust had an asynchronous runtime built-in.</p><p>It uses a custom <a href="https://doc.rust-lang.org/reference/procedural-macros.html">Procedural Macro</a> that allows us to attach the <a href="https://docs.rs/tokio/0.2.20/tokio/attr.main.html">#[tokio::main] attribute macro</a> to our main() function, which lets Tokio ‘inject’ all the boilerplate code into the resulting binary.</p><p>This is how Tokio lets us simply rewrite the above as:</p><pre>#[tokio::main]<br>async fn main() {<br>    // do async stuff<br>}</pre><p>Which is now how most contemporary examples of Rust asynchronous code starts.</p><h3>Async Error Handling</h3><p>As with Futures, we also need to handle errors gracefully. Fortunately, Rust already provides us with a way to reduce boilerplate via the <a href="https://doc.rust-lang.org/edition-guide/rust-2018/error-handling-and-panics/the-question-mark-operator-for-easier-error-handling.html">? operator for error handling</a>, which works just as well with .awaited Results.</p><p>That is, suppose we have a function that may return an error:</p><pre>fn fallible() -&gt; Result&lt;(), Box&lt;dyn Error&gt;&gt; {<br>    let _f = std::fs::File::open(&quot;foo.txt&quot;)?;<br>    Ok(())<br>}</pre><p>We can call it directly from main() as such:</p><pre>let _ = fallible().unwrap();</pre><p>If we don’t care much for graceful error handling (we <em>should</em>, but this is just an exercise), we can avoid unwrapping by also changing main() to return a Result&lt;(), Box&lt;dyn Error&gt;&gt;:</p><pre>#[tokio::main]<br>async fn main() -&gt; Result&lt;(), Box&lt;dyn Error&gt;&gt; {<br>    // ...</pre><pre>    let _ = fallible()?;</pre><pre>    Ok(())<br>}</pre><p>Now we can easily turn fallible asynchronous, and we can just as easily .await?:</p><pre>async fn fallible() -&gt; Result&lt;(), Box&lt;dyn Error&gt;&gt; {<br>    let _f = std::fs::File::open(&quot;foo.txt&quot;)?;<br>    Ok(())<br>}</pre><pre>#[tokio::main]<br>async fn main() -&gt; Result&lt;(), Box&lt;dyn Error&gt;&gt; {<br>    // ...<br>    let _ = fallible().await?;<br>    Ok(())<br>}</pre><p>The key to error handling in Rust is being able to make sense of both the Result success and error types.</p><p>Because there are so many error types in Rust, I’ve found that using crates such as <strong>anyhow</strong> (<a href="https://github.com/dtolnay/anyhow">https://github.com/dtolnay/anyhow</a>) make dealing with return values and error handling a lot less confusing.</p><h3>Post Scriptum</h3><p>I originally started to write an article on how to implement an HTTP proxy in Rust from scratch, when I realized that there was so much material to cover and so decided to break it up into a series of articles instead.</p><p>This is the first part, which I’ve also long intended to write, where I attempt to bring the reader into a better understanding of Rust closures, futures, async-await and error handling.</p><p>We’ve come a long way—and yet we have barely scratched the surface of all the nuances and intricacies of all these.</p><p>However, it was never my intent to dive deep into all of that. In particular, we haven’t really looked into move closures, or when you’ll need to specify the Send marker trait, or Fn vs FnOnce vs FnMut.</p><p>These are important concepts, though the beginner in Rust asynchronous programming hopefully won’t need to worry about them.</p><p>Instead, I hope that, like me, as you’ve progressed in this rather long series you’ve come to a more practical intuition of how all these concepts relate and work together to provide a <em>safe</em> asynchronous programming environment that is truly performant, since there are no hidden costs of thread management, cloning, referencing counting, or garbage collection.</p><p>As the maxim goes <strong>Explicit is Better Than Implicit</strong>. All of the above <em>are</em> available in Rust, but <em>only</em> if the programmer expressly chooses to use them.</p><p>Because of that, while a Rust programmer has to juggle with <em>a lot</em> more things than developers in other languages, its a trade-off we’re willing to do when we need the safety and performance that Rust can provide.</p><p>You can checkout the accompanying source code up on GitHub: <a href="https://github.com/aisrael/rust-closures-futures-async-await">https://github.com/aisrael/rust-closures-futures-async-await</a></p><p>In a future article or series, I will refer to these posts as I write about using Rust’s <strong>hyper HTTP client/server library</strong> to build an HTTP proxy.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9ed20eede7a4" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>