<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Lorenzo Barasti</title>
    <description>The latest articles on DEV Community by Lorenzo Barasti (@lbarasti).</description>
    <link>https://dev.to/lbarasti</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F328972%2F45bf7ead-36e0-4259-a836-a33607118046.jpg</url>
      <title>DEV Community: Lorenzo Barasti</title>
      <link>https://dev.to/lbarasti</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lbarasti"/>
    <language>en</language>
    <item>
      <title>Crystal JSON beyond the basics</title>
      <dc:creator>Lorenzo Barasti</dc:creator>
      <pubDate>Tue, 06 Oct 2020 22:50:35 +0000</pubDate>
      <link>https://dev.to/lbarasti/crystal-json-beyond-the-basics-5d45</link>
      <guid>https://dev.to/lbarasti/crystal-json-beyond-the-basics-5d45</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;When modelling a business domain, you will often find yourself defining custom data types on the top of the language's primitives. If you've been exposed to some functional programming, you're likely to strive for &lt;a href="https://jrsinclair.com/articles/2019/algebraic-data-types-what-i-wish-someone-had-explained-about-functional-programming/"&gt;sum types&lt;/a&gt;, in particular.&lt;/p&gt;

&lt;p&gt;In Crystal, we can represent sum types as composite types inheriting from an abstract one. As a side benefit, this pattern makes it straightforward to encode (and decode) custom types into (and from) JSON, as hinted in the &lt;a href="https://crystal-lang.org/api/0.35.1/JSON/Serializable.html"&gt;official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this article, we'll look at how we can JSON-encode and decode sum types in Crystal using the &lt;code&gt;json&lt;/code&gt; module and its powerful macros.&lt;/p&gt;

&lt;p&gt;We'll cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatic encoding with &lt;code&gt;JSON::Serializable&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Type resolution with discriminators&lt;/li&gt;
&lt;li&gt;Encoding of nested composite data types&lt;/li&gt;
&lt;li&gt;Considerations on the extensibility of this approach &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Case study - a P2P client
&lt;/h2&gt;

&lt;p&gt;Suppose we want to model events related to a peer to peer application. We'll focus on two domain events:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Connected&lt;/code&gt; event: a connection with a peer is established&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Started&lt;/code&gt; event: a file piece download is started.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A common pattern in this scenario is to represent the various types of event as classes or structs inheriting from a base &lt;code&gt;Event&lt;/code&gt; type. Events are inherently immutable, so it makes sense to model them as structs with getters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="no"&gt;Peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;

&lt;span class="n"&gt;abstract&lt;/span&gt; &lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;Event&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;Connected&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Event&lt;/span&gt;
  &lt;span class="kp"&gt;getter&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@peer&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Peer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;Started&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Event&lt;/span&gt;
  &lt;span class="kp"&gt;getter&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;piece&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@peer&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Peer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@piece&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;UInt32&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In the snippet above&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On line 1, a &lt;code&gt;Peer&lt;/code&gt; is represented by a string - its IP address.&lt;/li&gt;
&lt;li&gt;On line 3, we define an abstract struct &lt;code&gt;Event&lt;/code&gt; which serves as base type for all the concrete event types. Mind that the &lt;a href="https://crystal-lang.org/reference/syntax_and_semantics/virtual_and_abstract_types.html"&gt;abstract&lt;/a&gt; identifier makes it so that &lt;code&gt;Event&lt;/code&gt; objects cannot be instantiated - meaning &lt;code&gt;Event.new&lt;/code&gt; won't compile.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  JSON encoding
&lt;/h4&gt;

&lt;p&gt;As we are contemplating our nicely designed events hierarchy, a requirement comes in saying that we need to persist all the P2P events processed by our application for auditing purposes. After an intense discussion with the team, we decide to go for the JSON format. Let's update our code so that we can turn &lt;code&gt;Event&lt;/code&gt; instances into JSON&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"json"&lt;/span&gt;

&lt;span class="n"&gt;abstract&lt;/span&gt; &lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;Event&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Serializable&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here we are importing the JSON package (line 1), and then simply &lt;em&gt;mixing&lt;/em&gt; the &lt;code&gt;JSON::Serializable&lt;/code&gt; module &lt;em&gt;into&lt;/em&gt; &lt;code&gt;Event&lt;/code&gt; (line 4).&lt;/p&gt;

&lt;p&gt;Is that it? Well, let's see...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="n"&gt;e0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Connected&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; Connected(@peer="0.0.0.0")&lt;/span&gt;
&lt;span class="n"&gt;e1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Started&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; Started(@peer="0.0.0.0", @piece=2)&lt;/span&gt;

&lt;span class="n"&gt;e0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; {"peer":"0.0.0.0"}&lt;/span&gt;
&lt;span class="n"&gt;e1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; {"peer":"0.0.0.0","piece":2}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, that's impressive! Simply including the &lt;code&gt;JSON::Serializable&lt;/code&gt; module into the base type resulted in equipping its subtypes with working &lt;code&gt;#to_json&lt;/code&gt; methods. The following works, too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="no"&gt;Connected&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; Connected(@peer="0.0.0.0")&lt;/span&gt;
&lt;span class="no"&gt;Started&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; Started(@peer="0.0.0.0", @piece=2)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This is nice, e.g. for testing purposes, but mind that the exact type of an event will likely be unknown to us at compile time, so what we'd &lt;em&gt;actually&lt;/em&gt; like to run is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="no"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# raises "Error: can't instantiate abstract struct Event"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Unfortunately, this raises an error: by default, the deserializer defined within the &lt;code&gt;JSON::Serializable&lt;/code&gt; module tries to instantiate an &lt;code&gt;Event&lt;/code&gt; object. As we mentioned above, this is not possible, due to the abstract nature of the type. So, where do we go from here?&lt;/p&gt;

&lt;h4&gt;
  
  
  Discriminators to the rescue
&lt;/h4&gt;

&lt;p&gt;Here is an idea: in order to deserialize a JSON payload to the correct runtime type, we will attach some extra metadata about the event type to the JSON itself. We call this field a &lt;em&gt;discriminator&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Luckily, the &lt;code&gt;json&lt;/code&gt; module comes with an aptly named &lt;code&gt;use_json_discriminator&lt;/code&gt; macro. This will give us the deserialization capability we are looking for, but it's up to us to make sure that the discriminator field is populated properly at serialization time.&lt;/p&gt;

&lt;p&gt;Let's update our code to add support for discriminators.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="n"&gt;abstract&lt;/span&gt; &lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;Event&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Serializable&lt;/span&gt;

  &lt;span class="n"&gt;use_json_discriminator&lt;/span&gt; &lt;span class="s2"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="ss"&gt;connected: &lt;/span&gt;&lt;span class="no"&gt;Connected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;started: &lt;/span&gt;&lt;span class="no"&gt;Started&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;Connected&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Event&lt;/span&gt;
  &lt;span class="kp"&gt;getter&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt;
  &lt;span class="kp"&gt;getter&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"connected"&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@peer&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Peer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;Started&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Event&lt;/span&gt;
  &lt;span class="kp"&gt;getter&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;piece&lt;/span&gt;
  &lt;span class="kp"&gt;getter&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"started"&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@peer&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Peer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@piece&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;UInt32&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;OK, what's going here?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On line 4, we call &lt;code&gt;use_json_discriminator&lt;/code&gt; by providing a mapping between a discriminator field value and a type. The deserializer expects the discriminator to appear under the &lt;em&gt;"type"&lt;/em&gt; field, in this case.&lt;/li&gt;
&lt;li&gt;lines 12 and 18 ensure that the &lt;code&gt;type&lt;/code&gt; field is populated according to the event type name.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You'll notice a correspondence between the value of each &lt;code&gt;type&lt;/code&gt; field and the discriminator mapping.&lt;/p&gt;

&lt;p&gt;Let's check how this affects our serializer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="n"&gt;e0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; {"type":"connected","peer":"0.0.0.0"}&lt;/span&gt;
&lt;span class="n"&gt;e1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; {"type":"started","peer":"0.0.0.0","piece":2}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Notice how the type metadata is now part of the generated JSON. This in turn makes the following work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="no"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; Connected(@type="connected", @peer="0.0.0.0")&lt;/span&gt;
&lt;span class="no"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; Started(@type="started", @peer="0.0.0.0", @piece=2)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Brilliant!&lt;/p&gt;

&lt;h4&gt;
  
  
  Composing composite types
&lt;/h4&gt;

&lt;p&gt;The above works all right with composite types where fields are primitive types, but what if we were to define composite types on the top of other composite types? 😱&lt;/p&gt;

&lt;p&gt;Let's expand the definition of &lt;code&gt;Peer&lt;/code&gt; to check this out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;Peer&lt;/span&gt;
  &lt;span class="kp"&gt;getter&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;
  &lt;span class="kp"&gt;getter&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;e0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Connected&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Peer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8020&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;e1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Started&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Peer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8020&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now the following fails&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="n"&gt;e0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;
&lt;span class="c1"&gt;# raises "Error: no overload matches 'Peer#to_json' with type JSON::Builder"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The compiler is pretty explicit here: it does not know how to turn a &lt;code&gt;Peer&lt;/code&gt; object into JSON.&lt;/p&gt;

&lt;p&gt;🤔 I know this one! Let's include &lt;code&gt;JSON::Serializable&lt;/code&gt; into &lt;code&gt;Peer&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;Peer&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Serializable&lt;/span&gt;

  &lt;span class="kp"&gt;getter&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;
  &lt;span class="kp"&gt;getter&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And now try&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="n"&gt;e0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; {"type":"connected","peer":{"address":"0.0.0.0","port":8020}}&lt;/span&gt;
&lt;span class="n"&gt;e1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; {"type":"started","peer":{"address":"0.0.0.0","port":8020},"piece":2}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Success! What about deserialization?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="no"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; Connected(@type="connected", @peer=Peer(@address="0.0.0.0", @port=8020))&lt;/span&gt;
&lt;span class="no"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; Started(@type="started", @peer=Peer(@address="0.0.0.0", @port=8020), @piece=2)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Excellent! This is really all there is to it. Let's wrap up with an interesting trick and some more considerations on the extensibility of this method.&lt;/p&gt;

&lt;h4&gt;
  
  
  Adding Event subtypes
&lt;/h4&gt;

&lt;p&gt;At present, both the base event type and its implementation are JSON-aware, meaning the code in both includes bits related to the JSON support.&lt;/p&gt;

&lt;p&gt;This is not an issue in itself, but it feels like the JSON logic is leaking implementation details into the &lt;code&gt;Event&lt;/code&gt; subtypes definition. Could we make it so that an &lt;code&gt;Event&lt;/code&gt; implementer does not have to know about the &lt;em&gt;type&lt;/em&gt; field? After all, the &lt;code&gt;type&lt;/code&gt; getter returns a value that can be computed programmatically  - in this case, a downcase version of the type name.&lt;/p&gt;

&lt;p&gt;It turns out we can:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="n"&gt;abstract&lt;/span&gt; &lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;Event&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Serializable&lt;/span&gt;

  &lt;span class="n"&gt;use_json_discriminator&lt;/span&gt; &lt;span class="s2"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;connected: &lt;/span&gt;&lt;span class="no"&gt;Connected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;started: &lt;/span&gt;&lt;span class="no"&gt;Started&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;macro&lt;/span&gt; &lt;span class="n"&gt;inherited&lt;/span&gt;
    &lt;span class="kp"&gt;getter&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="vi"&gt;@type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;downcase&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Wait, what is this &lt;code&gt;macro inherited&lt;/code&gt; on line 6 about? It's a special macro &lt;em&gt;hook&lt;/em&gt; that injects the code in its body into any type inheriting from &lt;code&gt;Event&lt;/code&gt;. This is exactly what we need, as it gives us the opportunity to inject the &lt;code&gt;type&lt;/code&gt; getter into each implementation of &lt;code&gt;Event&lt;/code&gt; and set it to the type name, stringified and downcased. On line 7, note that occurrences of &lt;code&gt;@type&lt;/code&gt; in a macro resolve to the name of the instantiating type.&lt;/p&gt;

&lt;p&gt;Now the rest of the code looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;Connected&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Event&lt;/span&gt;
  &lt;span class="kp"&gt;getter&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@peer&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Peer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;Started&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Event&lt;/span&gt;
  &lt;span class="kp"&gt;getter&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;piece&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@peer&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Peer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@piece&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;UInt32&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;No trace of JSON logic 🎉&lt;/p&gt;

&lt;p&gt;Let's introduce a new &lt;code&gt;Event&lt;/code&gt; type to demonstrate this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="c1"&gt;# An event indicating the completion of a file piece.&lt;/span&gt;
&lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;Completed&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Event&lt;/span&gt;
  &lt;span class="kp"&gt;getter&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;piece&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@peer&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Peer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@piece&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;UInt32&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;No extra logic on the implementer side, as expected, but mind that we still need to update the discriminator mapping:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="n"&gt;abstract&lt;/span&gt; &lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;Event&lt;/span&gt;
  &lt;span class="n"&gt;use_json_discriminator&lt;/span&gt; &lt;span class="s2"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="ss"&gt;connected: &lt;/span&gt;&lt;span class="no"&gt;Connected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;started: &lt;/span&gt;&lt;span class="no"&gt;Started&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;completed: &lt;/span&gt;&lt;span class="no"&gt;Completed&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Challenge.&lt;/strong&gt; Can we avoid having to manually update the mapping?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Hint:&lt;/em&gt; the following macro generates exactly the &lt;code&gt;NamedTupleLiteral&lt;/code&gt; we're looking for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="n"&gt;abstract&lt;/span&gt; &lt;span class="kp"&gt;struct&lt;/span&gt; &lt;span class="no"&gt;Event&lt;/span&gt;
  &lt;span class="n"&gt;macro&lt;/span&gt; &lt;span class="n"&gt;subclasses&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sx"&gt;% for &lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="vi"&gt;@type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subclasses&lt;/span&gt; &lt;span class="sx"&gt;%}
      {{ name.stringify.downcase.id }&lt;/span&gt;&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sx"&gt;% end &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subclasses&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; {connected: Connected, started: Started, completed: Completed}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Unfortunately, the following does not work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="n"&gt;use_json_discriminator&lt;/span&gt; &lt;span class="s2"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subclasses&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; raises Error: mapping argument must be a HashLiteral or a NamedTupleLiteral, not Call&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This is because at the time when the &lt;code&gt;use_json_discriminator&lt;/code&gt; macro is expanded, &lt;code&gt;Event.subclasses&lt;/code&gt; hasn't been expanded, yet. I've seen this kind of issues arising often when working with macros: they can save you from writing a lot of code, but composing them can be frustratingly complicated.&lt;/p&gt;

&lt;p&gt;Here is my recommendation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;when working with macros, keep it simple. If something feels too complicated, it probably is.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Anyhow, leave a comment in the section below, if you'd like to share your &lt;em&gt;&lt;del&gt;hack&lt;/del&gt;&lt;/em&gt; solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;This article was inspired by my experience writing a &lt;a href="https://github.com/lbarasti/torrent_client/blob/183d9d0d4e4ac95b61937d13b6d5f77d4b034a9e/src/lib/reporter.cr"&gt;BitTorrent client&lt;/a&gt; in Crystal.&lt;/li&gt;
&lt;li&gt;If you'd like to find out more about Algebraic Data Types, I recommend &lt;a href="https://jrsinclair.com/articles/2019/algebraic-data-types-what-i-wish-someone-had-explained-about-functional-programming/"&gt;this article&lt;/a&gt; by James Sinclair.&lt;/li&gt;
&lt;li&gt;You can find the official &lt;code&gt;json&lt;/code&gt; module documentation &lt;a href="https://crystal-lang.org/api/0.35.1/JSON.html"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;To read more about Crystal's macro hooks, check out the official Crystal &lt;a href="https://crystal-lang.org/reference/syntax_and_semantics/macros/hooks.html"&gt;reference&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope you enjoyed this JSON-themed article and learned something new about Crystal. If you have any question or learning in the JSON-serialization space, then I'd love to read about it in the comments section below.&lt;/p&gt;

&lt;p&gt;If you'd like to stay in touch, you can subscribe or follow me on &lt;a href="https://twitter.com/lbarasti"&gt;Twitter&lt;/a&gt;. You can also find me live coding on &lt;a href="https://www.twitch.tv/lbarasti"&gt;Twitch&lt;/a&gt;, sometimes 📺&lt;/p&gt;

</description>
      <category>crystal</category>
      <category>json</category>
      <category>serialization</category>
      <category>adt</category>
    </item>
    <item>
      <title>Building an interactive DSL</title>
      <dc:creator>Lorenzo Barasti</dc:creator>
      <pubDate>Sat, 11 Jul 2020 08:16:15 +0000</pubDate>
      <link>https://dev.to/lbarasti/building-an-interactive-dsl-19id</link>
      <guid>https://dev.to/lbarasti/building-an-interactive-dsl-19id</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Over the past couple of months, I've released a series of videos exploring the fundamentals of building an interactive Domain-Specific Language (iDSL) from the ground up.&lt;/p&gt;

&lt;p&gt;To be more specific, the series describes how to build an &lt;em&gt;external&lt;/em&gt; DSL and REPL, by means of a general-purpose language - &lt;a href="https://crystal-lang.org/"&gt;Crystal&lt;/a&gt;, in this case. Concepts you'll find here are 100% transferable, so you should be able to replicate the behaviour our iDSL in your favourite language 🚀&lt;/p&gt;

&lt;p&gt;I've collected all the episodes in this article, with some further technical comments, reflections and reading recommendations.&lt;br&gt;
You can find the project's source code on &lt;a href="https://github.com/lbarasti/byoidsl"&gt;GitHub&lt;/a&gt;, in case you feel like following along.&lt;/p&gt;
&lt;h2&gt;
  
  
  Getting ready
&lt;/h2&gt;

&lt;p&gt;I've broken our external iDSL architecture down into 5 components.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--t6WewExd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/sz2kv7j4xdzt3sjuzm8c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--t6WewExd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/sz2kv7j4xdzt3sjuzm8c.png" alt="Interactive DSL architecture"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  1. Read-eval-print-loop (REPL)
&lt;/h4&gt;

&lt;p&gt;A REPL is "an interactive computer programming environment that takes single user inputs (i.e., single expressions), evaluates (executes) them, and returns the result to the user" (source: &lt;a href="https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop"&gt;Wikipedia&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Together with the Runtime System, this component is what makes our language truly &lt;em&gt;interactive&lt;/em&gt;. A REPL can make on-boarding new users easier, and let them explore our language API in a frictionless way, e.g. thanks to some auto-completion feature.&lt;/p&gt;
&lt;h4&gt;
  
  
  2. Parser
&lt;/h4&gt;

&lt;p&gt;A parser takes instructions written in our DSL, and translates them into commands digestible by the language interpreter.&lt;/p&gt;

&lt;p&gt;All programming languages have one. Some languages parse input into commands and evaluate them in one go, others hand over the parsed command to an interpreter or are part of a compiler's &lt;a href="https://en.wikipedia.org/wiki/Compiler#Front_end"&gt;phases&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In our DSL, the parser will turn user input into an &lt;em&gt;intermediate representation&lt;/em&gt; for our interpreter to consume.&lt;/p&gt;
&lt;h4&gt;
  
  
  3. Interpreter
&lt;/h4&gt;

&lt;p&gt;In our architecture¹, the interpreter is the component that evaluates commands coming from the parser - the &lt;em&gt;Eval&lt;/em&gt; in REPL - and computes some result to be handed back to the user.&lt;/p&gt;

&lt;p&gt;In very simple languages, commands can be interpreted in isolation, with no context of previously run instructions - a calculator supporting basic arithmetic is a good example of this. If we want to take this further, and interpret commands in context, then we need to leverage a &lt;strong&gt;runtime system&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;¹According to this Wikipedia &lt;a href="https://en.wikipedia.org/wiki/Interpreter_(computing)"&gt;definition of interpreter&lt;/a&gt;, our parser covers steps 1 and 2, while our interpreter covers step 3.&lt;/p&gt;
&lt;h4&gt;
  
  
  4. Runtime system
&lt;/h4&gt;

&lt;p&gt;A runtime system is the access point to the DSL's parser and interpreter, and manages the state of the system at runtime. Interestingly, not all runtime systems support running commands interactively. We'll make sure ours does 😉&lt;/p&gt;
&lt;h4&gt;
  
  
  5. Command Line Interface (CLI)
&lt;/h4&gt;

&lt;p&gt;To enable our users to launch scripts or run the REPL from the command line, we'll provide the user with a command-line interface. Our CLI will be akin to &lt;a href="https://docs.python.org/3/using/cmdline.html"&gt;python&lt;/a&gt;, in particular.&lt;/p&gt;
&lt;h2&gt;
  
  
  Getting the REPL out of the way
&lt;/h2&gt;

&lt;p&gt;I first had the idea to make a video on how to build a REPL when I stumbled upon &lt;a href="https://github.com/Papierkorb/fancyline"&gt;Fancyline&lt;/a&gt;, a Crystal library that makes it extremely easy to build complex behaviour into your CLI.&lt;/p&gt;

&lt;p&gt;This series was the perfect use case to try it out, so, in session 1, we leverage Fancyline to write a general-purpose REPL featuring error handling, command history and backward search. Just brilliant 🌠&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/dD5Qn3xsooU"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code highlight.&lt;/strong&gt; In session 5, we'll add support for one more feature: loading scripts from the REPL via the magic-command &lt;code&gt;%load&lt;/code&gt;. Here is what that looks like in the &lt;a href="https://github.com/lbarasti/byoidsl/blob/master/src/lib/repl.cr#L10"&gt;code&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;starts_with?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"%load"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;filepath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lchop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"%load"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;
  &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="no"&gt;Repl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eval_and_print&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
  &lt;span class="no"&gt;Repl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eval_and_print&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Parsing with parser combinators
&lt;/h2&gt;

&lt;p&gt;In session 2, we build a parser for a prototypical iDSL called &lt;em&gt;IGOL&lt;/em&gt; (Interactive [Conway's] Game Of Life).&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href="https://github.com/voximity/pars3k"&gt;Pars3k&lt;/a&gt;, a Crystal library implementing parser combinators, we can define a modular, human-readable and &lt;a href="https://github.com/lbarasti/byoidsl/blob/master/spec/igol_interpreter_spec.cr"&gt;testable&lt;/a&gt; parser in a few lines of code.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/7P2_dgnHFZk"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;I've known about parser combinators for a while, but this &lt;a href="https://www.lihaoyi.com/post/EasyParsingwithParserCombinators.html"&gt;introduction&lt;/a&gt; by Li Haoyi - an exceptional member of the Scala community - reignited my interest in building something with them 🙏&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code highlight.&lt;/strong&gt; I used the &lt;a href="https://github.com/lbarasti/dataclass"&gt;dataclass&lt;/a&gt; macro to define the Algebraic Data Types representing the commands. Here is an example from the &lt;a href="https://github.com/lbarasti/byoidsl/blob/master/src/igol/commands.cr#L25"&gt;code&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="n"&gt;dataclass&lt;/span&gt; &lt;span class="no"&gt;Apply&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;coord&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;Int32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Int32&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;VarName&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="no"&gt;Pattern&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Command&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Reminder.&lt;/strong&gt; Unlike &lt;a href="https://crystal-lang.org/reference/syntax_and_semantics/structs.html"&gt;structs&lt;/a&gt;, classes support defining recursive data types. This is a pretty desirable feature when defining the grammar of a language - see &lt;a href="https://www.lihaoyi.com/post/EasyParsingwithParserCombinators.html#recursive-parsers"&gt;this example&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interpreting commands
&lt;/h2&gt;

&lt;p&gt;In session 3, we define the interpreter as a function that transforms a &lt;code&gt;State&lt;/code&gt; and a &lt;code&gt;Command&lt;/code&gt; into a new &lt;code&gt;State&lt;/code&gt;, plus some return value. A signature for such a function could look like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;interpret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;State&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;State&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;where &lt;code&gt;T&lt;/code&gt; is the type of the return value.&lt;/p&gt;

&lt;p&gt;In this first implementation, we limit state management to the &lt;code&gt;GameOfLife&lt;/code&gt; grid, and always return a &lt;code&gt;String&lt;/code&gt; as return type.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/C8R5GYJ9KYo"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code highlight.&lt;/strong&gt; We leverage Crystal's &lt;code&gt;case&lt;/code&gt; statement to safely match a command to its runtime type. Note how the return type of &lt;code&gt;interpret&lt;/code&gt; is a tuple containing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the new &lt;code&gt;State&lt;/code&gt; of the system&lt;/li&gt;
&lt;li&gt;the result of the command execution - this will be presented to the user by the &lt;em&gt;Print&lt;/em&gt; step of our REPL.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;interpret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;State&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;State&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;Show&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;Evolve&lt;/span&gt;
    &lt;span class="n"&gt;new_grid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;evolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;new_state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;grid: &lt;/span&gt;&lt;span class="n"&gt;new_grid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;new_state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_grid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can look at the code above in context &lt;a href="https://github.com/lbarasti/byoidsl/blob/master/src/igol/interpreter.cr#L5"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generic runtime systems
&lt;/h2&gt;

&lt;p&gt;In session 4, we define a generic runtime system to support &lt;em&gt;any&lt;/em&gt; interactive DSL. To prove it, we'll define a minimal DSL for a &lt;a href="https://github.com/lbarasti/byoidsl/blob/master/src/counter/counter.cr"&gt;counter&lt;/a&gt; - not the most exciting DSL in the world, I admit - and show how effortless it is to plug it into the runtime.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/LjsRoAOt0H4"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code highlight.&lt;/strong&gt; The runtime class is a beautiful display of Crystal generics: in the code below, &lt;code&gt;State&lt;/code&gt;, &lt;code&gt;Cmd&lt;/code&gt; and &lt;code&gt;Err&lt;/code&gt; are type parameters, meaning you can instantiate the &lt;code&gt;Runtime&lt;/code&gt; class with any state, parser and interpreter satisfying the given generic signatures.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;State&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Cmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="vi"&gt;@state&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;State&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="vi"&gt;@parser&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Cmd&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="no"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="vi"&gt;@interpreter&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;State&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Cmd&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;State&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt;
    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;Cmd&lt;/span&gt;
      &lt;span class="vi"&gt;@state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@interpreter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ends_with?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;";"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="s2"&gt;"Syntax error: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  A user friendly CLI
&lt;/h2&gt;

&lt;p&gt;In session 5, we define a user-friendly CLI that will either launch the REPL or run a script, depending on whether a file path is provided.&lt;/p&gt;

&lt;p&gt;We'll see how &lt;a href="https://github.com/at-grandpa/clim"&gt;Clim&lt;/a&gt;, a Crystal library to build CLIs, makes it simple to put together a CLI that is both pretty and extensible, in a few lines of code.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/9WEw1Bqlfxo"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code highlight.&lt;/strong&gt; To make the CLI reusable by other iDSLs, we used &lt;a href="https://crystal-lang.org/reference/syntax_and_semantics/macros.html"&gt;macros&lt;/a&gt; 🔮&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;CLI&lt;/span&gt;
  &lt;span class="n"&gt;macro&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="n"&gt;_CLI&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Clim&lt;/span&gt;
      &lt;span class="n"&gt;main&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"{{mod}} interpreter"&lt;/span&gt;
        &lt;span class="n"&gt;usage&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;downcase&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;" [option] [filepath]"&lt;/span&gt;
        &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s2"&gt;"Version &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;}.version}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;short: &lt;/span&gt;&lt;span class="s2"&gt;"-v"&lt;/span&gt;
        &lt;span class="n"&gt;help&lt;/span&gt; &lt;span class="ss"&gt;short: &lt;/span&gt;&lt;span class="s2"&gt;"-h"&lt;/span&gt;
        &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="s2"&gt;"filepath"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;desc: &lt;/span&gt;&lt;span class="s2"&gt;"path to {{mod}} script"&lt;/span&gt;
        &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This allows us to instantiate a CLI for any iDSL, provided that their module defines the &lt;code&gt;#version&lt;/code&gt; and &lt;code&gt;#runtime&lt;/code&gt; methods. Here is the final &lt;code&gt;main&lt;/code&gt; for the &lt;em&gt;IGOL&lt;/em&gt; iDSL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"./lib/cli"&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"./igol"&lt;/span&gt;

&lt;span class="no"&gt;CLI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;IGOL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And here is the end result:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JzbPMUVN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://github.com/lbarasti/byoidsl/raw/master/examples/demo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JzbPMUVN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://github.com/lbarasti/byoidsl/raw/master/examples/demo.gif" alt="IGOL demo"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;This is it! If you managed to read this far, then you deserve a ⭐&lt;br&gt;&lt;br&gt;
I hope you enjoyed the write-up and learned something new in the process. If you have any question or would like to share your iDSL stories, then I'd love to read them in the comments section below.&lt;/p&gt;

&lt;p&gt;If you'd like to stay in touch, you can follow me on &lt;a href="https://twitter.com/lbarasti"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was originally published on &lt;a href="https://lbarasti.com/post/byoidsl/"&gt;lbarasti.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>dsl</category>
      <category>crystallang</category>
      <category>parser</category>
      <category>interpreter</category>
    </item>
    <item>
      <title>Conway's Game of Life, sparse</title>
      <dc:creator>Lorenzo Barasti</dc:creator>
      <pubDate>Sat, 20 Jun 2020 23:41:50 +0000</pubDate>
      <link>https://dev.to/lbarasti/conway-s-game-of-life-sparse-43j5</link>
      <guid>https://dev.to/lbarasti/conway-s-game-of-life-sparse-43j5</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;A lot has been written about Conway's Game of Life. If you're reading this, chances are you've implemented a version of it yourself.&lt;/p&gt;

&lt;p&gt;This article offers a visual guide to a less-common formulation of the game and presents a surprisingly succinct implementation of it, mostly for fun.&lt;/p&gt;

&lt;p&gt;I'll assume you're already familiar with the game. If that's not the case, I'd recommend reading the &lt;a href="https://web.stanford.edu/class/sts145/Library/life.pdf"&gt;original article&lt;/a&gt; published on &lt;em&gt;Scientific American&lt;/em&gt; in 1970.&lt;/p&gt;

&lt;h2&gt;
  
  
  Canonical formulation
&lt;/h2&gt;

&lt;p&gt;When you think of Game of Life, you probably think about a grid where live and dead cells are represented with different colors, and the following 3 rules define how to evolve the grid from the current state to the next (source: &lt;a href="https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life"&gt;Wikipedia&lt;/a&gt;).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Any live cell with two or three live neighbours survives.&lt;/li&gt;
&lt;li&gt;Any dead cell with three live neighbours becomes a live cell.&lt;/li&gt;
&lt;li&gt;All other live cells die in the next generation. Similarly, all other dead cells stay dead.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These rules emphasise the role of both live and dead cells and lead to a straight-forward implementation where the state of the system is represented by a matrix of boolean values where each &lt;em&gt;(i, j)&lt;/em&gt; entry represents a cell's state: &lt;code&gt;true&lt;/code&gt; for live, &lt;code&gt;false&lt;/code&gt; for dead.&lt;/p&gt;

&lt;h2&gt;
  
  
  A sparse formulation
&lt;/h2&gt;

&lt;p&gt;Compact as the above might be, I've always found the following, equivalent formulation more interesting.&lt;/p&gt;

&lt;p&gt;Consider an infinite grid of cells defined as per the canonical formulation. For each live cell, gather the coordinates &lt;em&gt;(i, j)&lt;/em&gt; of all the cell's neighbours, plus the coordinates of the live cell itself.&lt;/p&gt;

&lt;p&gt;Now count the number of times each cell appears in the collection.&lt;br&gt;
Only the cells abiding by the following rules will be part of the next generation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Any cell that occurs 3 times&lt;/li&gt;
&lt;li&gt;Any cell that occurs 4 times &lt;em&gt;and&lt;/em&gt; is currently live.&lt;/li&gt;
&lt;/ol&gt;



&lt;p&gt;One thing I enjoy about this formulation, is the fact that it leads to a very natural implementation based on a sparse representation of the grid, where we only keep track of live cells.&lt;/p&gt;

&lt;p&gt;Given a set of live cells - aptly called &lt;code&gt;population&lt;/code&gt; - the next generation can be computed as follows - the code below is in &lt;a href="https://crystal-lang.org/"&gt;Crystal&lt;/a&gt;, but your favourite language's implementation shouldn't differ much from this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="n"&gt;population&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flat_map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;expand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tally&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;population&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;expand&lt;/code&gt; is a simple function that returns the 9 cells in the 3x3 square centered in the cell passed as argument.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="no"&gt;OriginSquare&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;expand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;OriginSquare&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Let's explore the code line by means of an example.&lt;/p&gt;

&lt;p&gt;Consider the L-shaped population&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="n"&gt;population&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FROBasW7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vy7i4un6pasflopwwace.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FROBasW7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vy7i4un6pasflopwwace.png" alt="The initial population [{2, 0}, {1, 0}, {0, 0}, {0, 1}]"&gt;&lt;/a&gt;&lt;/p&gt;
Figure 1. The picture shows the initial population [{2, 0}, {1, 0}, {0, 0}, {0, 1}] on a two-dimensional orthogonal grid of square cells. We can think of the grid as a Cartesian plane where a live cell {x, y} is represented by a gray square with bottom left corner in {x, y}.



&lt;p&gt;We iterate over the live cells and &lt;em&gt;expand&lt;/em&gt; them into the neighbouring cells plus the live cell itself - see Figure 2 below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="n"&gt;population&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flat_map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;expand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; [{-1, 2}, {0, 2}, {1, 2}, {-1, 1}, ..., {2, -1}, {3, -1}]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NWu9psFE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/uwi7syl832rdcm3gi1io.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NWu9psFE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/uwi7syl832rdcm3gi1io.png" alt="Each live cell is expanded into its neighbouring square"&gt;&lt;/a&gt;&lt;/p&gt;
Figure 2. The picture represents the expansion of each live cell into the corresponding 3x3 square. Cells in such squares are accumulated into a collection, with repetition. Notice that the number of *stars* in each cell at the end of iteration 4 - bottom right picture - reflects the number of times a cell appears in any expanded square.



&lt;p&gt;We now count the number of times a cell appears in the computed collection. In Crystal, this is as easy as calling &lt;code&gt;#tally&lt;/code&gt; on an array. This returns a dictionary from cell to number of occurrences.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="n"&gt;population&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flat_map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;expand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tally&lt;/span&gt;
  &lt;span class="c1"&gt;# =&amp;gt; {{-1, 2} =&amp;gt; 1, {0, 2} =&amp;gt; 1, ..., {3, -1} =&amp;gt; 1}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jOJxDID_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/7i9v5sl2ve5kfepqcsmk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jOJxDID_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/7i9v5sl2ve5kfepqcsmk.png" alt="The figure shows the number of occurrences for each cell in the expanded squares"&gt;&lt;/a&gt;&lt;/p&gt;
Figure 3. The figure shows the number of occurrences for each cell in the expanded squares.



&lt;p&gt;Finally, we filter out any cell that doesn't match either rule 1 or rule 2 defined above. This is done by invoking &lt;code&gt;.select&lt;/code&gt; on the dictionary with a predicate representing the rules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="n"&gt;population&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flat_map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;expand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tally&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;population&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;# =&amp;gt; {{0, 1} =&amp;gt; 3, {0, 0} =&amp;gt; 3, {1, 0} =&amp;gt; 4, {1, -1} =&amp;gt; 3}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1C3pJHWj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/kg0xhduaznvibikn1cde.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1C3pJHWj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/kg0xhduaznvibikn1cde.png" alt="The figure highlights the new generation of live cells"&gt;&lt;/a&gt;&lt;/p&gt;
Figure 4. The numbers in bold show the new generation of live cells. The light gray squares where the count is 4 are kept alive. Any cell with a count of 3 will be live in the next generation.



&lt;p&gt;We now discard the number of occurrences, and keep the cells' coordinates.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="n"&gt;population&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flat_map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;expand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tally&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;population&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;
  &lt;span class="c1"&gt;# =&amp;gt; [{0, 1}, {0, 0}, {1, 0}, {1, -1}]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GUgSTH64--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/xkd3l1lhwz4seuod05jw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GUgSTH64--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/xkd3l1lhwz4seuod05jw.png" alt="The new generation of live cells"&gt;&lt;/a&gt;&lt;/p&gt;
Figure 5. The new generation of live cells is represented by the squares in light gray.



&lt;p&gt;Excellent! Let's talk about some of the features of this algorithm, but first...&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Challenge.&lt;/strong&gt; Now that we've discussed a script to evolve a population, can you extract this into a function or instance method, so that we can run an arbitrary number of evolutions? I'm thinking about something like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight crystal"&gt;&lt;code&gt;&lt;span class="n"&gt;new_population&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;GameOfLife&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;evolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;population&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;times: &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Some notes on efficiency
&lt;/h2&gt;

&lt;p&gt;The memory-efficiency of the sparse implementation makes evolving some configurations feasible, where they wouldn't be on the dense one. In particular, we can cope with cells patterns moving arbitrarily far from each other without going out of memory. This is not true for the dense implementation, where an initial population featuring, for example, two &lt;a href="https://en.wikipedia.org/wiki/Spaceship_(cellular_automaton)"&gt;spaceships&lt;/a&gt; heading in different directions will exhaust your memory fairly quickly.&lt;/p&gt;

&lt;p&gt;I like how the sparse implementation encodes a strong system invariant: in terms of state, nothing can ever change far from live cells.&lt;/p&gt;

&lt;p&gt;Hence, the sparse implementation is more memory-efficient than the dense one, but the battle for best performance is totally open and depends on the configuration we're evolving. If you're thinking that the dense implementation might enjoy performance gains thanks to GPU architectures, then you're &lt;a href="https://www.google.com/search?q=gpu+game+of+life"&gt;right on point&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Challenge.&lt;/strong&gt; No matter the underlying implementation, printing a grid on the screen still poses a challenge, as we're materialising a possibly very large grid of cells. Can you think of strategies to display the current state of the grid that don't require printing it in its entirety?&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;In case you missed it, here is the &lt;a href="https://web.stanford.edu/class/sts145/Library/life.pdf"&gt;original article&lt;/a&gt; where Conway's Game of Life was introduced.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Towards data science&lt;/em&gt; offers an &lt;a href="(https://towardsdatascience.com/from-scratch-the-game-of-life-161430453ee3)"&gt;exhaustive coverage&lt;/a&gt; of both dense and sparse implementations of Conway's Game of Life in Python and in Haskel.&lt;/li&gt;
&lt;li&gt;If you'd like to know more about the Crystal programming language, then you can check out &lt;a href="https://crystal-lang.org/"&gt;this page&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Thanks for reading, I hope this article made you want to sprint to your laptop and code the sparse Game of Life in your favourite language. You can share your Game of Life implementation in the comments section below, I'd love to see what you came up with.&lt;/p&gt;

&lt;p&gt;If you'd like to stay in touch, then subscribe or follow me on &lt;a href="https://twitter.com/lbarasti"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was originally published on &lt;a href="https://lbarasti.com/post/game_of_life/"&gt;lbarasti.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>gameoflife</category>
      <category>crystal</category>
      <category>automata</category>
      <category>crystallang</category>
    </item>
  </channel>
</rss>
