<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title type="text">Richard Kallos</title>
  <link rel="self" href="https://rkallos.com/feeds/all.atom.xml"/>
  <link href="https://rkallos.com/index.html"/>
  <id>urn:https-www-rkallos-com:-index-html</id>
  <updated>2025-02-09 01:45:00Z</updated>
  <entry>
    <title type="text">ETS Match Specification Pitfall: Maps</title>
    <link rel="alternate" href="https://rkallos.com/blog/2025-02-09-ets-match-spec-pitfall.html"/>
    <id>urn:https-rkallos-com:blog-2025-02-09-ets-match-spec-pitfall-html</id>
    <published>2025-02-09T01:45:00Z</published>
    <updated>2025-02-09T01:45:00Z</updated>
    <author>
      <name>Richard Kallos</name>
    </author>
    <content type="html">&lt;p&gt;
During my work,
I had a problem where I needed to efficiently write tree-shaped data.
I also needed the ability to query ranges of the data.
I reached for ETS tables,
using the &lt;code class=&quot;inline&quot;&gt;ordered_set&lt;/code&gt; table setting,
and relied on
&lt;a href=&quot;https://www.erlang.org/doc/apps/erts/match_spec.html&quot;&gt;match specifications&lt;/a&gt;
to read ranges from the table.&lt;/p&gt;
&lt;p&gt;
&lt;a href=&quot;https://www.erlang.org/doc/apps/stdlib/ets.html#match/2&quot;&gt;ETS tables&lt;/a&gt;
can have different storage backends.
See the documentation for &lt;a href=&quot;https://www.erlang.org/doc/apps/stdlib/ets.html#new/2&quot;&gt;ets:new/2&lt;/a&gt; for details.
These backends reside within in a &lt;a href=&quot;https://github.com/erlang/otp/blob/4726e39654c7a7412f7c2f66c7eb567c978a9947/erts/emulator/beam/erl_db.h#L88&quot;&gt;union type&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;
In many cases,
an ETS match specification will scan a whole table.
I specifically wanted to avoid this.
The documentation for &lt;a href=&quot;https://www.erlang.org/doc/apps/stdlib/ets.html#match/2&quot;&gt;ets:match/2&lt;/a&gt; says this:&lt;/p&gt;
&lt;blockquote&gt;
  If the key is specified in the pattern, the match is very efficient. If the key is not specified, that is, if it is a variable or an underscore, the entire table must be searched. The search time can be substantial if the table is very large.&lt;/blockquote&gt;
&lt;p&gt;
I learned of an interesting caveat to the above statement;
When using &lt;code class=&quot;inline&quot;&gt;ordered_set&lt;/code&gt; tables,
matching can be made more efficient by specifying a partial key!&lt;/p&gt;
&lt;p&gt;
(Note: Code snippets are based on &lt;a href=&quot;https://github.com/erlang/otp/blob/4726e39654c7a7412f7c2f66c7eb567c978a9947/erts/emulator/beam/erl_db_util.c#L3943-L3945&quot;&gt;this commit of erlang/otp&lt;/a&gt;)&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;makeup c&quot;&gt;&lt;span class=&quot;kt&quot;&gt;enum&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ms_key_boundness&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;/*&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt; Order significant, larger means more &amp;quot;boundness&amp;quot; =&amp;gt; less iteration &lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;*/&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;    MS_KEY_UNBOUND           &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;    MS_KEY_PARTIALLY_BOUND   &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;    MS_KEY_BOUND             &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;    MS_KEY_IMPOSSIBLE        &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;/*&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt; This structure is filled in by analyze_pattern() for the select
&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt; functions.
&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;*/&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mp_info&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;enum&lt;/span&gt;&lt;span class=&quot;n&quot;&gt; ms_key_boundness key_boundness&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;    Eterm least&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                &lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;/*&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt; The lowest matching key (possibly
&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                                 &lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt; partially bound expression) &lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;*/&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;    Eterm most&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                 &lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;/*&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt; The highest matching key (possibly
&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                                 &lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt; partially bound expression) &lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;*/&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;    Binary &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                 &lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;/*&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt; The compiled match program &lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;*/&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
&lt;a href=&quot;https://github.com/erlang/otp/blob/4726e39654c7a7412f7c2f66c7eb567c978a9947/erts/emulator/beam/erl_db_tree.c#L218-L224&quot;&gt;(source)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;
The &lt;code class=&quot;inline&quot;&gt;analyze_pattern()&lt;/code&gt; function determines the ‘boundness’ of the key,
and will recursively calculate the least and greatest values that can match the
provided partial key.&lt;/p&gt;
&lt;p&gt;
One interesting bit of the code that calculates key boundness is as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;makeup c&quot;&gt;&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt; am_Underscore &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;db_is_variable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;db_has_map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keyp &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt; key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;n&quot;&gt; MS_KEY_PARTIALLY_BOUND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
&lt;a href=&quot;https://github.com/erlang/otp/blob/4726e39654c7a7412f7c2f66c7eb567c978a9947/erts/emulator/beam/erl_db_tree.c#L3835-L3840&quot;&gt;(source)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;
If the key contains a map,
it cannot be considered fully bound.
Consider this comment in the &lt;code class=&quot;inline&quot;&gt;db_is_fully_bound()&lt;/code&gt; function:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;makeup c&quot;&gt;&lt;span class=&quot;cm&quot;&gt;/*&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt; Like in Erlang code, &amp;quot;literal&amp;quot; maps in a pattern match any
&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt; map that has the given elements, so they must be considered
&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt; variable. &lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;*/&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
&lt;a href=&quot;https://github.com/erlang/otp/blob/4726e39654c7a7412f7c2f66c7eb567c978a9947/erts/emulator/beam/erl_db_util.c#L3943-L3945&quot;&gt;(source)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;
The explanation is intuitive,
but the ramifications are important.
If you have an &lt;code class=&quot;inline&quot;&gt;ordered_set&lt;/code&gt; ETS table,
consider changing out maps
(or Elixir structs)
for tuples and records instead.&lt;/p&gt;
&lt;p&gt;
I’ve encountered this issue at least twice now.
By being clever about the types and ordering of ETS key terms,
one can  massively improve the performance of
&lt;code class=&quot;inline&quot;&gt;ets:select/2&lt;/code&gt; calls,
even when the ETS tables contain thousands of rows.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title type="text">Case Study: Using CRDTs to improve a distributed system</title>
    <link rel="alternate" href="https://rkallos.com/blog/2021-08-22-case-study-using-crdts-to-improve-a-distributed-system.html"/>
    <id>urn:https-rkallos-com:blog-2021-08-22-case-study-using-crdts-to-improve-a-distributed-system-html</id>
    <published>2021-09-10T22:00:00Z</published>
    <updated>2021-09-10T22:00:00Z</updated>
    <author>
      <name>Richard Kallos</name>
    </author>
    <content type="html">&lt;p&gt;
At a previous job,
I was lucky enough to be faced with the challenge of
improving a distributed service’s robustness to network faults.
In this post,
I share how I attacked the problem using
Conflict-free/Convergent Replicated Data Types (CRDTs).&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h1&gt;
A Chronicle of Counters&lt;/h1&gt;
&lt;p&gt;
The service was responsible for managing the state of a set of counters.
These counters had lifetimes and rules attached to them,
dictating when they could be incremented.
Many counters needed to be incremented steadily over time.
Other counters needed to be incremented slowly at the beginning,
then more quickly toward the end of the counter’s life.
The service didn’t care what the counters stood for.
The service’s job was to inform clients when counters could be incremented,
and to acknowledge and remember when clients did increment counters.&lt;/p&gt;
&lt;p&gt;
The service supported four types of requests:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;
Clients send (ASK, abc, 10) requests,
asking the service if counter &lt;em&gt;abc&lt;/em&gt; can be incremented by 10.
The server responds with YES or NO.  &lt;/li&gt;
  &lt;li&gt;
Clients send (TELL, abc, 5) requests,
informing the service that counter &lt;em&gt;abc&lt;/em&gt; should be incremented by 5.  &lt;/li&gt;
  &lt;li&gt;
Replicas broadcast (ANNOUNCE, abc, 5) messages,
informing other replicas that the counter &lt;em&gt;abc&lt;/em&gt; has been incremented by 5.  &lt;/li&gt;
  &lt;li&gt;
Sometime between one hour and the next, e.g. 10:00 and 11:00,
a periodic job sends (STATE, 09:59, abc, 3) to every replica,
stating that the state of counter &lt;em&gt;abc&lt;/em&gt; at time 09:59 was 3.
The service does its best to track the values of counters,
but for reasons I won’t get in to,
the service can be wrong about the states of counters.
STATE requests are a source of truth.
This will be very important later.  &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
Some other facts about the system:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
High performance. Peak hours saw over 1 million QPS with 99th percentile latency under 2ms.  &lt;/li&gt;
  &lt;li&gt;
Servers run &lt;a href=&quot;https://en.wikipedia.org/wiki/Network_Time_Protocol&quot;&gt;NTP&lt;/a&gt;; their physical clocks are synchronized.
I won’t really get in to why this matters in this post,
but having bounds on clock drift is essential when one wants servers to coordinate using
&lt;a href=&quot;https://rkallos.com/blog/2019/10/05/presentation-hybrid-logical-clocks-pwlmtl/&quot;&gt;Logical Physical Clocks&lt;/a&gt;.  &lt;/li&gt;
  &lt;li&gt;
ASK requests do not always turn into TELL requests.  &lt;/li&gt;
  &lt;li&gt;
STATE requests are not comprised of the sum of all TELL requests made to the system.
It’s close, but it’s different enough to be a source of chaos.  &lt;/li&gt;
  &lt;li&gt;
The distribution of requests is uneven.
One server may get 30% of all ASK/TELL requests for counter &lt;em&gt;abc&lt;/em&gt;,
but 0% of requests for counter &lt;em&gt;xyz&lt;/em&gt;.  &lt;/li&gt;
  &lt;li&gt;
The service tries to keep counters close to their limits at any given time.
Over-incrementing is a bad outcome when a counter corresponds to a monetary budget.
Under-incrementing is a bad outcome when a counter corresponds to achievement of some important goal.  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
Before I worked on this service:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
Replicas maintained connections to all other replicas.
The number of open connections in the system was N(N-1)/2.
O(N²) isn’t that bad;
Erlang defaults to a fully connected cluster
and scales to a decently high number of machines.
However, we felt that we needed to do better in order to scale out the service,
because…  &lt;/li&gt;
  &lt;li&gt;
If any connection between any two connections was broken or stalled,
all replicas would resort to the safe option of saying NO to every ASK request.
With O(N²) connections,
the likelihood of any connection breaking and entering the bad state of saying NO to every request
would increase sharply as nodes get added to the cluster.  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
My task was to change the service
so that it could be scaled out
without increasing the likelihood
of entering a bad state.
To achieve this, I sought to:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
Remove the requirement for full network connectivity
for saying YES to ASK requests.  &lt;/li&gt;
  &lt;li&gt;
Relax the requirement from needing fresh state from all replicas
to only needing fresh state from a majority of replicas.  &lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;
Data Meets Math&lt;/h1&gt;
&lt;p&gt;
One of the first things I noticed about the ANNOUNCE protocol was that
it was very sensitive to messages being dropped.
I changed the data in ANNOUNCE messages from being
“everything a replica learned since the previous broadcast”
to
“everything a replica knows about the state of the system”.
The ANNOUNCE messages got much larger in size,
but I strongly believe the properties of these larger messages were worth the cost.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
Messages changed from being independent events
to being members of a partially ordered set.
The message {(abc, 10), (xyz, 5)} could be considered greater than
{(abc, 8), (xyz, 3)} or {(abc, 1)}, but not greater than {(xyz, 8)}.
Orders, even partial ones, are &lt;strong&gt;transitive&lt;/strong&gt;.  &lt;/li&gt;
  &lt;li&gt;
It became possible to ‘merge’ messages according to some least upper bound function.
Given the messages {(abc, 10)} and {(xyz, 5)}, the LUB is the union; {(abc, 10), (xyz, 5)}.
For sets of pairs where the first element is the same,
a common LUB is to take the pairwise maximum,
for example: {(abc, 10), (xyz, 5)} ⊔ {(xyz, 8)} would be {(abc, 10), (xyz, 8)}.
In my case, I also had to account for replicas,
so my messages resembled {(A, abc, 10), (A, xyz, 5), (B, abc, 3)},
where the state of counter &lt;em&gt;abc&lt;/em&gt; would be 10 + 3 = 13.
This merge function is &lt;strong&gt;associative&lt;/strong&gt;, &lt;strong&gt;commutative&lt;/strong&gt; and &lt;strong&gt;idempotent&lt;/strong&gt;.
Associativity can be useful when you have to merge a lot of messages
(merging messages in a tree shape can be faster than merging in a list shape),
but commutativity and idempotency were essential to making the counter service protocol more robust.  &lt;/li&gt;
  &lt;li&gt;
A partially ordered set with a least upper bound function is a
&lt;a href=&quot;https://en.wikipedia.org/wiki/Semilattice&quot;&gt;join-semilattice&lt;/a&gt;.
Semilattices are &lt;strong&gt;monotonic&lt;/strong&gt;,
which is another great property for data flowing in a distributed system.  &lt;/li&gt;
  &lt;li&gt;
A join-semilattice with an ‘update’ function,
like a TELL message,
can be considered a
&lt;a href=&quot;https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type#State-based_CRDTs&quot;&gt;State-based CRDT&lt;/a&gt;,
a form of data that provides &lt;strong&gt;strong eventual consistency&lt;/strong&gt;;
which means that once all replicas exchange and merge their state-based CRDTs;
the replicas have &lt;em&gt;equivalent&lt;/em&gt; state.
Eventually consistent systems might need expensive reconciliation protocols,
but this is not necessary in a system with strong eventual consistency.  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
Once applying ANNOUNCE messages to local state became &lt;strong&gt;idempotent&lt;/strong&gt;,
it became safe to retransmit messages without fear of
introducing inconsistencies.&lt;/p&gt;
&lt;p&gt;
Once applying ANNOUNCE messages to local state became &lt;strong&gt;commutative&lt;/strong&gt; and &lt;strong&gt;monotonic&lt;/strong&gt;,
it became almost harmless for the network to drop or reorder messages.
ANNOUNCE messages contained the full knowledge of some replica,
so it became easy to determine which ANNOUNCE message was the most recent;
the greatest/maximum message according to our partial ordering.
As long as ANNOUNCE messages were delivered in a timely manner,
it did not matter if individual messages were dropped or reordered.&lt;/p&gt;
&lt;p&gt;
Once ANNOUNCE messages to local state became &lt;strong&gt;transitive&lt;/strong&gt;,
the service no longer relied on direct connections between replicas.
If replica A ANNOUNCE’d at replica B,
then replica B ANNOUNCE’d at replica C,
the state would be the same as if replica A had also ANNOUNCE’d at replica C.
Transitivity in ANNOUNCE messages relaxed the connectivity graph requirement from
needing to be fully connected to needing to be
&lt;a href=&quot;https://en.wikipedia.org/wiki/Connectivity_(graph_theory)&quot;&gt;strongly connected&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;
Problems in Paradise&lt;/h1&gt;
&lt;p&gt;
Idempotency, transitivity, commutativity and monotonicity are all
excellent properties to have in a distributed system protocol.
However, there are obstacles that prevent CRDTs being used in production everywhere.
I encountered two specific issues:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;
Counter state was not always monotonic.
As I mentioned earlier,
STATE messages were a source of truth that often declared that
a counter’s state was lower than the service expected.  &lt;/li&gt;
  &lt;li&gt;
How could I shrink a message that was supposed to be monotonic?  &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
Both problems were solved by the fact that STATE messages corresponded
to some ground truth rooted at a specific point in the past.&lt;/p&gt;
&lt;p&gt;
STATE messages always reflected the true state of the system at the end of some hour.
By changing my (Replica, Counter, State) triples into (Replica, Counter, Hour, State) tuples,
and relying on the fact that STATE messages are always delivered to all replicas,
a replica could stop sending tuples where &lt;em&gt;Hour&lt;/em&gt; was before the latest STATE message.
STATE messages helped put a practical bound on the size of ANNOUNCE messages.&lt;/p&gt;
&lt;p&gt;
Splitting counter state into hourly buckets also helped to account for nonmonotonic updates from STATE messages;
While counter values are monotonically non-decreasing in ANNOUNCE messages,
the &lt;em&gt;Hour&lt;/em&gt; element of STATE messages were &lt;em&gt;also&lt;/em&gt; monotonically non-decreasing.
and I was able to change my least upper bound operation into a &lt;em&gt;lexicographic&lt;/em&gt; least upper bound,
which I feel is best explained by example: (0, 5) &amp;lt; (0, 500) &amp;lt; (1, 0).
Applied to STATE messages, (08:59, 5000) &amp;lt; (09:59, 3).&lt;/p&gt;
&lt;h1&gt;
Mo’ computers? No problem.&lt;/h1&gt;
&lt;p&gt;
With that final change,
I was finished.
After writing, testing, and deploying the code to production,
I was delighted to see that when a single instance of the service went down,
whether due to a network partition,
or perhaps a deployment,
the rest of the cluster was healthy and answering YES to ASK requests.&lt;/p&gt;
&lt;p&gt;
I had a lot of fun working on that problem.
I also had a really great time researching CRDTs and eventual consistency,
implementing two different distributed system simulators,
and eventually implementing the changes to the protocol.
I’m hoping to write another post in the near future showcasing my favorite papers around this topic.&lt;/p&gt;
&lt;p&gt;
For the sake of brevity,
many fascinating details about the service and protocol have been left out.
I hope nevertheless that you remember and possibly consider CRDTs when you find yourself tasked with
making some distributed system more robust.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title type="text">Generating code with jq</title>
    <link rel="alternate" href="https://rkallos.com/blog/2021-07-08-generating-code-with-jq.html"/>
    <id>urn:https-rkallos-com:blog-2021-07-08-generating-code-with-jq-html</id>
    <published>2021-07-12T21:15:00Z</published>
    <updated>2021-07-12T21:15:00Z</updated>
    <author>
      <name>Richard Kallos</name>
    </author>
    <content type="html">&lt;p&gt;
When I first learned about jq,
I thought it was a handy tool for extracting from JSON.
Little did I know that I was only scratching the surface of what jq can do.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;
&lt;a href=&quot;https://stedolan.github.io/jq/&quot; title=&quot;jq homepage&quot;&gt;jq&lt;/a&gt; is a
“lightweight and flexible command-line JSON processor”.
When I first heard of the tool,
I mainly saw it being used for extracting relevant data from large JSON objects.
If I had a sequence of large JSON objects,
I could pipe them through a small jq program to extract the key(s) that I wanted.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;$ jq &amp;#39;.first.inside_first.thing&amp;#39; &amp;lt; my_big_object.json&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
What a relief!
No more trying to massage the json into a form that could be mined with &lt;code class=&quot;inline&quot;&gt;sed&lt;/code&gt; or &lt;code class=&quot;inline&quot;&gt;awk&lt;/code&gt;!
No more writing and debugging Python scripts to transform JSON!
No more copying and pasting into my browser’s dev console!&lt;/p&gt;
&lt;p&gt;
If your interest is piqued,
I highly recommend going and watching Charles Chamberlain’s delightful
&lt;a href=&quot;https://www.youtube.com/watch?v=PS_9pyIASvQ&quot; title=&quot;YouTube link to !!Con presentation about jq&quot;&gt;!!Con 2017 talk about jq&lt;/a&gt;.
It’s short (9 minutes 32 seconds), engaging,
and shows glimpses at the true power of jq.
If your interest is not piqued,
I recommend watching the video anyway.
Maybe it’ll pique your interest.&lt;/p&gt;
&lt;p&gt;
I happily added jq to my toolbox,
and used it for all my JSON parsing and extracting needs.&lt;/p&gt;
&lt;p&gt;
Fast forward to earlier this year.
I started writing a
&lt;a href=&quot;https://www.ponylang.io/&quot; title=&quot;Ponylang homepage&quot;&gt;Pony&lt;/a&gt;
port of the
&lt;a href=&quot;https://mustache.github.io/mustache.5.html&quot; title=&quot;Mustache homepage&quot;&gt;Mustache&lt;/a&gt;
templating language.
What I thought was kinda cool is that the
&lt;a href=&quot;https://github.com/mustache/spec&quot; title=&quot;mustache/spec on Github&quot;&gt;test suite for mustache implementations&lt;/a&gt;
is written in YAML and JSON.&lt;/p&gt;
&lt;p&gt;
In retrospect,
this approach makes sense to me.
If you have a specification of some pure function,
and you want to make it easier for implementers to
write implementations of your function,
you could do worse than to use an extremely common
data representation format to represent your tests.
With specification tests in JSON,
implementers might have an easier time testing their implementations
as long as their language can parse JSON.
I know I had an easier time because of this.&lt;/p&gt;
&lt;p&gt;
I found myself with a bunch of JSON objects representing tests.
What I wanted were a bunch of Pony classes representing tests.
Thinking about my problem,
I remembered a
&lt;a href=&quot;https://twitter.com/thingskatedid/status/1388056167113367552&quot; title=&quot;Twitter link to @thingskatedid/status/1388056167113367552&quot;&gt;tweet&lt;/a&gt;
by Kate, describing how she used jq to generate C code.&lt;/p&gt;
&lt;p&gt;
In Pony, lists of tests are collected by actors (or possibly classes)
that implement the &lt;code class=&quot;inline&quot;&gt;TestList&lt;/code&gt; interface.
A &lt;code class=&quot;inline&quot;&gt;TestList&lt;/code&gt; has a function &lt;code class=&quot;inline&quot;&gt;tests()&lt;/code&gt; that accepts a &lt;code class=&quot;inline&quot;&gt;PonyTest&lt;/code&gt; object,
and calls the &lt;code class=&quot;inline&quot;&gt;PonyTest.apply()&lt;/code&gt; behavior with the name of each test class in the list.
With these incoming messages, the &lt;code class=&quot;inline&quot;&gt;PonyTest&lt;/code&gt; actor knows what tests to run.
&lt;a href=&quot;https://github.com/rkallos/pony-mustache/blob/189c189c5ba96f80d94b19333493ff2317b2f223/mustache/tests/sections/_test.pony#L5-L32&quot; title=&quot;Github link to pony-mustache/mustache/tests/sections/_test.pony&quot;&gt;Here’s an example&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;
&lt;code class=&quot;inline&quot;&gt;TestLists&lt;/code&gt; can also be comprised of other &lt;code class=&quot;inline&quot;&gt;TestList&lt;/code&gt;s.
Mustache’s tests are categorized,
and so I adopted the same categorization for
&lt;a href=&quot;https://github.com/rkallos/pony-mustache/blob/189c189c5ba96f80d94b19333493ff2317b2f223/mustache/_test.pony#L13-L22&quot; title=&quot;Github link to pony-mustache/mustache/_test.pony&quot;&gt;my tests&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;
Some of these lists of tests are quite long,
and I did not want to write and maintain all these tests without any assistance.
jq to the rescue!&lt;/p&gt;
&lt;p&gt;
jq programs are essentially composed of filters that operate on some input JSON.
I have some preamble to add to my Pony test files,
so I start with a string literal,
which can be thought of as a filter that ignores its input and produces the desired string.
I then use the comma operator,
which concatenates the results of two filters together,
and end with a filter that extracts and transforms the names of each test into an appropriate name for a private Pony class.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;#!/usr/bin/jq -Mfr

# meant to be used with comments.json from
# https://github.com/mustache/spec

def testClass(n): &amp;quot;_Test&amp;quot; + (n | gsub(&amp;quot;[() -]&amp;quot;; &amp;quot;&amp;quot;));

&amp;quot;use \&amp;quot;ponytest\&amp;quot;
use \&amp;quot;json\&amp;quot;
use \&amp;quot;../..\&amp;quot;

actor Main is TestList
  new create(env: Env) =&amp;gt; PonyTest(env, this)

  new make() =&amp;gt; None

  fun tag tests(test: PonyTest) =&amp;gt;&amp;quot;,
(.tests[].name | (&amp;quot;    test(&amp;quot; + testClass(.) + &amp;quot;)&amp;quot;)),&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
The only remaining thing to do is to generate the code for each class.
We have a function for coming up with the name of a Pony test class.
We have the templates, variable bindings, and expected results from the JSON test cases.
I injected JSON string literals into the test files,
and used Pony’s JSON package to parse those literals into Pony data.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;pony&quot;&gt;(.tests[] | (&amp;quot;
class iso &amp;quot; + testClass(.name) + &amp;quot; is UnitTest
  fun name(): String =&amp;gt; \&amp;quot;interpolation/&amp;quot; + .name + &amp;quot;\&amp;quot;

  fun apply(h: TestHelper) ? =&amp;gt;
    let template = &amp;quot; + (.template | @json) + &amp;quot;
    let expected = &amp;quot; + (.expected | @json) + &amp;quot;
    let data = (recover val
      JsonDoc.&amp;gt;parse(\&amp;quot;\&amp;quot;\&amp;quot;&amp;quot; + (.data | tojson) + &amp;quot;\&amp;quot;\&amp;quot;\&amp;quot;)?
    end).data

    let m = Mustache(template)?
    h.log(m.print_tokens())

    h.assert_eq[String](expected, m.render(data))&amp;quot;))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
Lastly, to generate a Pony file for the tests in &lt;code class=&quot;inline&quot;&gt;interpretation.json&lt;/code&gt;,
I pass &lt;code class=&quot;inline&quot;&gt;interpretation.json&lt;/code&gt; through my jq script.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;$ ./gen.jq &amp;lt; /path/to/interpretation.json &amp;gt; interpretation/_test.pony&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
Repeating these steps for the remaining Mustache test suites left me with
Pony files comprised of Pony classes for each of the tests.
After using these tests to debug my implementation of Mustache in Pony,
my next task is to improve the API to make it easier for everyday use.&lt;/p&gt;
&lt;p&gt;
Hopefully you’ve learned that jq is useful
for more than shell pipelines and one-liners!
If you’d like to learn more,
I strongly recommend reading through jq’s manpage.
It’s dense, but comprehensive and precise.
Thanks for reading!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title type="text">What my choices told me about my priorities</title>
    <link rel="alternate" href="https://rkallos.com/blog/2020-04-04-what-my-choices-told-me-about-my-priorities.html"/>
    <id>urn:https-rkallos-com:blog-2020-04-04-what-my-choices-told-me-about-my-priorities-html</id>
    <published>2020-04-04T22:57:41Z</published>
    <updated>2020-04-04T22:57:41Z</updated>
    <author>
      <name>Richard Kallos</name>
    </author>
    <content type="html">&lt;p&gt;
This was a useful exercise for me. I think I’ll try and check in again sometime soon. Here were my thoughts from today.&lt;/p&gt;
&lt;h1&gt;
I was prioritizing long-term storage&lt;/h1&gt;
&lt;ul&gt;
  &lt;li&gt;
When I started using plain text files to store my notes as Org mode files (which I am trying to replace)  &lt;/li&gt;
  &lt;li&gt;
When I started using &lt;a href=&quot;https://tiddlywiki.com&quot;&gt;TiddlyWiki&lt;/a&gt;, which can be opened/reused wherever I have access to a browser that executes Javascript. It doesn’t necessarily have the longevity of plain text, but it’s close.  &lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;
I was prioritizing (re-)reading and reflection&lt;/h1&gt;
&lt;ul&gt;
  &lt;li&gt;
When I stopped transcribing my notes into Org mode file, where I would never happen across them unless I specifically went looking for them.  &lt;/li&gt;
  &lt;li&gt;
When I started transcribing my handwritten notes into TiddlyWiki, breaking my notes into smaller, more reusable chunks (tiddlers), organizing them all, and making small changes to TiddlyWiki’s interface to encourage exploration.  &lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;
I am prioritizing focus&lt;/h1&gt;
&lt;ul&gt;
  &lt;li&gt;
When I step away from the computer to think with pen and paper.  &lt;/li&gt;
  &lt;li&gt;
When I write notes by hand while reading papers or watching talks.  &lt;/li&gt;
  &lt;li&gt;
When I close Slack after realizing I’ve wasted too much time clicking on unread channels and not enough time working.  &lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title type="text">A Bit of Math: Triple Elimination Tournament</title>
    <link rel="alternate" href="https://rkallos.com/blog/2019-12-22-a-bit-of-math-triple-elimination-tournament.html"/>
    <id>urn:https-rkallos-com:blog-2019-12-22-a-bit-of-math-triple-elimination-tournament-html</id>
    <published>2019-12-22T14:58:40Z</published>
    <updated>2019-12-22T14:58:40Z</updated>
    <author>
      <name>Richard Kallos</name>
    </author>
    <content type="html">&lt;p&gt;
A while ago, my father,
who is a competitive backgammon player,
had a puzzle for me.
He wanted to know the fraction of players remaining after a certain number of
rounds of play in a triple-elimination tournament.
This blog post goes over my work to figure out the answer.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;
A triple-elimination tournament is more-or-less how it sounds.
If you lose 3 times,
you’re eliminated.
Since the problem doesn’t need to deal with any sort of bracket structure,
I’m leaving it out completely.&lt;/p&gt;
&lt;p&gt;
In any triple-elimination tournament,
there are 3 groups of players;
those with zero, one, and two losses.
The number of players with zero losses is easiest to figure out.
After each match, there is one winner and one loser.
After the first round,
half of the players won,
and half of the players lost.
After the second round,
half of the winning players won again.
The proportion of players with zero losses after x rounds of play is&lt;/p&gt;
&lt;img src=&quot;/img/triple-elimination/ec3973e78287db3e3a792ab2770be7c6e229e028.svg&quot; alt=&quot;zeroes(x) = 2^{-x}&quot;&gt;
&lt;p&gt;
The number of players with one loss is a little trickier.
After the first round,
half of the players lost the first game.
After the second round,
the fraction of players with one loss is the sum of those who lost their first game and won the second,
and those who won their first game and lost the second.
Each of those groups comprise one quarter of the total number of players,
so the fraction of players with one loss at the end of the second round is still half.
After the third round,
half of the half of players with one loss won their third game,
and half of the quarter of players with zero losses lost their third game,
so the fraction of players with one loss after round three is (1/4) + (1/8) = (3/8)
After the fourth round,
half of the three eighths of players with one loss won their fourth game,
and one eighth of the players with zero losses lost their fourth game.&lt;/p&gt;
&lt;p&gt;
If we expand a little further,
a clear pattern emerges.
1/2, 2/4, 3/8, 4/16, 5/32.
The proportion of players with one loss after x rounds of play is&lt;/p&gt;
&lt;img src=&quot;/img/triple-elimination/f744ad5278e44a1d77738f45da74d338711f0b3c.svg&quot; alt=&quot;ones(x) = \frac{x}{2^x}&quot;&gt;
&lt;p&gt;
Finally, there are the players with two losses.
Seeing as it’s not possible to lose twice in zero or one rounds of play,
I’m going to start counting at x = 2 rounds.&lt;/p&gt;
&lt;p&gt;
After two rounds, one in four players lost twice.
After the third round,
half of those players lost for the third time,
resulting in their elimination,
and half won,
giving one in eight players.
These players are joined by the quarter of players with one loss who lost their second match,
giving 3/8.
After the fourth round,
half of the 3/8 won, giving 3/16.
These lucky few are joined by 3/16 of people with one loss who lost their fourth match,
giving 6/16.
After the fifth round,
half of the 6/16 won, giving 6/32,
who are joined by the 4/32 of people with one loss who lost their fifth match,
giving 10/16.
If we look at the number of rounds, and the sequence of values in the numerator, we see [(2, 1), (3, 3), (4, 6), (5, 10)], which matches the following equation:&lt;/p&gt;
&lt;img src=&quot;/img/triple-elimination/680002cfc6a22253b33dd7046499484d4894358e.svg&quot; alt=&quot;g(x) = \sum_{i = 1}^{x-1} i = \frac{x \times (x - 1)}{2}&quot;&gt;
&lt;p&gt;
That means the proportion of people with two losses is equal to&lt;/p&gt;
&lt;img src=&quot;/img/triple-elimination/1e61aa17b7d106aece7925e0fa5c3cef829dc775.svg&quot; alt=&quot;twos(x) = \frac{1}{2^x} \frac{x \times (x - 1)}{2}&quot;&gt;
&lt;p&gt;
Now, the number of people in the tournament after x rounds (where x is greater than 2) is equal to&lt;/p&gt;
&lt;img src=&quot;/img/triple-elimination/a52efd8d4837d59b908a00b8ff75edbb47ea50a5.svg&quot; alt=&quot;f(x) &amp;= zeroes(x) + ones(x) + twos(x) \\ &amp;= \frac{1}{2^x} + \frac{x}{2^x} + \frac{1}{2^x} \frac{x \times (x - 1)}{2} \\ &amp;= \frac{1}{2^x} \left( 1 + x + \frac{x \times (x - 1)}{2} \right) \\ &amp;= 2^{x-1} \left( x^2 + x + 2 \right)&quot;&gt;
</content>
  </entry>
  <entry>
    <title type="text">Presentation: Hybrid Logical Clocks @ PWLMTL</title>
    <link rel="alternate" href="https://rkallos.com/blog/2019-10-05-presentation-hybrid-logical-clocks-pwlmtl.html"/>
    <id>urn:https-rkallos-com:blog-2019-10-05-presentation-hybrid-logical-clocks-pwlmtl-html</id>
    <published>2019-10-05T11:22:37Z</published>
    <updated>2019-10-05T11:22:37Z</updated>
    <author>
      <name>Richard Kallos</name>
    </author>
    <content type="html">&lt;p&gt;
This past thursday (2019-10-03), I presented &lt;a href=&quot;https://cse.buffalo.edu/tech-reports/2014-04.pdf&quot;&gt;this
paper&lt;/a&gt; at Papers We Love
Montreal. I had a really fun time!&lt;/p&gt;
&lt;p&gt;
&lt;a href=&quot;../../../../../presentations/hybrid-logical-clocks/index.html&quot;&gt;Here are my slides&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title type="text">Imperium in Imperio - A Bridge is Made of Planks; Every Plank is a Bridge</title>
    <link rel="alternate" href="https://rkallos.com/blog/2018-05-09-imperium-in-imperio-a-bridge-is-made-of-planks-every-plank-is-a-bridge.html"/>
    <id>urn:https-rkallos-com:blog-2018-05-09-imperium-in-imperio-a-bridge-is-made-of-planks-every-plank-is-a-bridge-html</id>
    <published>2019-04-22T10:30:00Z</published>
    <updated>2019-04-22T10:30:00Z</updated>
    <author>
      <name>Richard Kallos</name>
    </author>
    <content type="html">&lt;p&gt;
In the &lt;a href=&quot;https://rkallos.com/blog/2019/03/16/memento-mori-seeing-the-end/&quot; title=&quot;&quot;&gt;previous&lt;/a&gt; &lt;a href=&quot;https://rkallos.com/blog/2019/03/17/esse-quam-videri-seeing-what-s-in-front-of-you/&quot; title=&quot;&quot;&gt;three&lt;/a&gt; &lt;a href=&quot;https://rkallos.com/blog/2019/03/18/inveniam-viam-building-bridges/&quot; title=&quot;&quot;&gt;installments&lt;/a&gt;, I discussed visualizing
ideal final states of things, examining the current state of your life, and how
to bridge the two. In the final post in this series, I show how these techniques
can be applied at any scale; from a daily to-do list to planning the course of
your entire life.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;
In the &lt;a href=&quot;https://rkallos.com/blog/2019/03/18/inveniam-viam-building-bridges/&quot; title=&quot;&quot;&gt;previous post&lt;/a&gt;, I showed how how I write Structural Tension
charts. The paragraph on top is where I write the ideal state of some thing that
I want to accomplish, whether it’s a finished project, an instilled habit or an
attained achievement. The paragraph on the bottom is where I describe as
objectively and nonjudgmentally as possible the current reality of the thing I’m
setting out to accomplish. The space in between the two paragraphs gets filled
with a list of actionable steps I can take to move from where I am to where I
want to be.&lt;/p&gt;
&lt;p&gt;
To me, these steps resemble planks on a bridge. The easier and smaller steps
tend to go down near the bottom of the page, and the later tasks tend to be a
bit more abstract, large in scope, and usually depend on previous
tasks. Sometimes the steps I write out are really big items, like “Get a job as
a software developer”. Writing this down doesn’t fill me with the will to go out
and get things done. How do I even go about starting such a huge task?  The
answer: Make a ST chart for that task. If there are any tasks in that new chart
that are too large, you can make yet more charts. In the end, you wind up with a
forest (in the graph theory sense) of charts that all serve to plot a course for
your life.&lt;/p&gt;
&lt;p&gt;
For example, the ST chart I shared with you in the &lt;a href=&quot;https://rkallos.com/blog/2019/03/18/inveniam-viam-building-bridges/&quot; title=&quot;&quot;&gt;previous post&lt;/a&gt; could
have been a single step in a larger chart where I set a course to gain better
insight to my emotions, and the step titled “Experiment with active forms of
meditation” could have its own chart where I describe the styles I’ve tried,
what’s gone well, and what has yet to be tested. Furthermore, I could have a
completely separate chart about setting up a regular journaling habit where I
list “Try keeping a meditation journal” as one of the steps.&lt;/p&gt;
&lt;p&gt;
So far the method that I’ve had the most success with is to keep track of these
charts on paper, but as much as I deeply enjoy the feel of pen and paper, I
can’t help but think that organizing these charts is a task that computers are
well-suited to. I’ve noticed there is a lot of software that could be great at
managing these ST charts, but I think the two most promising ones are
&lt;a href=&quot;https://www.gnu.org/software/emacs/&quot; title=&quot;&quot;&gt;Emacs&lt;/a&gt; and &lt;a href=&quot;https://tiddlywiki.com/&quot; title=&quot;&quot;&gt;TiddlyWiki&lt;/a&gt;. I’ll hopefully have more to say in the
future about if/how I’ve adapted my system to allow for the help of software.&lt;/p&gt;
&lt;p&gt;
In conclusion, the process of writing ST charts may lead you to write out steps
that are a bit too large to tackle on their own. Fortunately, you can take
advantage of the recursive nature of ST charts and break each step down into
their own chart, and it becomes easier to plan out projects or ambitions of
any size.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title type="text">Inveniam viam - Building Bridges</title>
    <link rel="alternate" href="https://rkallos.com/blog/2018-04-30-inveniam-viam-building-bridges.html"/>
    <id>urn:https-rkallos-com:blog-2018-04-30-inveniam-viam-building-bridges-html</id>
    <published>2019-03-18T23:00:00Z</published>
    <updated>2019-03-18T23:00:00Z</updated>
    <author>
      <name>Richard Kallos</name>
    </author>
    <content type="html">&lt;p&gt;
If you know where you are and where you want to be, you can start to plot a
course between the two points. Following on the ideas presented in the previous
two posts, I describe a practice I learned from reading Robert Fritz’s book Your
Life as Art.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;
I spent a few years of my life voraciously consuming self-help books. I believe
the journey started with Eckhart Tolle’s &lt;a href=&quot;https://en.wikipedia.org/wiki/The_Power_of_Now&quot; title=&quot;&quot;&gt;The Power of Now&lt;/a&gt;, and
it more-or-less ended with &lt;a href=&quot;https://en.wikipedia.org/wiki/Meditations&quot; title=&quot;&quot;&gt;Meditations&lt;/a&gt;,
&lt;a href=&quot;http://donaldrobertson.name/2013/11/21/new-book-teach-yourself-stoicism-and-the-art-of-happiness-2013-3/&quot; title=&quot;&quot;&gt;Stoicism and the Art of Happiness&lt;/a&gt;, and
&lt;a href=&quot;https://www.robertfritz.com/wp/product/your-life-as-art/&quot; title=&quot;&quot;&gt;Your Life as Art&lt;/a&gt;. I’ll probably wind up writing about my path
to Stoicism some other time. This post is about what I learned from Robert
Fritz.&lt;/p&gt;
&lt;p&gt;
Your Life as Art is filled with insight about navigating the complex murky space
of life while juggling the often competing aspects of spontaneity and rigid
structure. My collection of notes about Your Life as Art is nearly a thousand
lines long, and there’s definitely far too much good stuff to fit into a single
blog post. At this point, I’ll focus on what Robert Fritz calls structural
tension, and his technique for plotting a course with the help of a chart.&lt;/p&gt;
&lt;p&gt;
Hopefully I convinced you with the previous two posts about the importance of
objectively “seeing” where you are and where you want to be. These two activites
form the foundation of what Fritz calls &lt;em&gt;structural tension&lt;/em&gt;, a force that stems
from the contrast between reality and an ideal state and seeks to relieve the
tension by moving you from your present state to your ideal state.&lt;/p&gt;
&lt;p&gt;
Writing is a handy exercise generating this force. A structural tension chart
(or ST chart) has your desired ideal state at the top of a page, your current
state at the bottom, and a series of steps bridging the gap between the
two. First you write the ideal section, then the real section, and finally add
the steps in the middle. It’s very important to be as objective and detailed as
possible about your ideal and current states. Here’s an example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;--- Ideal ---
I meditate every day for at least 15 minutes. My mind is calm
and focused as I go about my daily activities. I feel comfortable
sitting for the duration of my practice, no matter how long it is.
-------------

- Try keeping a meditation journal
- Experiment with active forms of meditation
- Experiment with seating positions
- Give myself time in the morning to meditate
- Wake up at the same time every day

--- Real ----
I meditate approximately once per week. I have difficulty
finding a regular time during the day to devote to meditation,
making it difficult to create a habit that sticks. I find that
I become uncomfortable sitting with my legs crossed for more
than 5 minutes. I do not often remember how good I feel after
meditating, which results in difficulty deciding to sit.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
If you’re interested in reading more, I highly recommend Your Life as
Art. Robert Fritz’s books are filled with great ideas. While this is basically a
slightly more detailed to-do list, I find the process to be very grounding.&lt;/p&gt;
&lt;p&gt;
In conclusion, once you know where you are and where you want to be, try writing
a structural tension chart in order to set a course.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title type="text">Esse quam videri - Seeing what&apos;s in front of you</title>
    <link rel="alternate" href="https://rkallos.com/blog/2018-04-29-esse-quam-videri-seeing-what-s-in-front-of-you.html"/>
    <id>urn:https-rkallos-com:blog-2018-04-29-esse-quam-videri-seeing-what-s-in-front-of-you-html</id>
    <published>2019-03-17T20:00:00Z</published>
    <updated>2019-03-17T20:00:00Z</updated>
    <author>
      <name>Richard Kallos</name>
    </author>
    <content type="html">&lt;p&gt;
As humans, we are easily fooled. Our five senses are the primary way we get an
idea of what’s happening around us, and it’s been shown time and time again that
our senses are unreliable. In this post, I try to explain the importance of
‘seeing’ what’s in front of you, and how to practice it.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;
See &lt;a href=&quot;https://www.youtube.com/watch?v=vJG698U2Mvo&quot; title=&quot;&quot;&gt;this video&lt;/a&gt; a classic example of our incredible ability at missing
important details.&lt;/p&gt;
&lt;p&gt;
Rembrandt used to train his students by making them copy his
self-portraits. This exercise forced them to see their subject as objectively as
possible, which was essential to make an accurate reproduction. Only after
mastering their portraiturne skills did Rembrandt’s students go on to develop
their own artistic styles.&lt;/p&gt;
&lt;p&gt;
It is important to periodically evaluate your position and course in life. It’s
something you do whether you’re aware of it or not. When you plan something,
you’re setting a course. When you’re reflecting on past events, you’re
estimating your position. For the sake of overloading words like sight, vision,
and planning, let’s refer to this act as life portraiture.&lt;/p&gt;
&lt;p&gt;
Life portraiture can be compared to navigating on land, air, or sea, except the
many facets of our lives results in a space of many more dimensions. We can
consider our position and course on axes like physical health, emotional health,
career, finance, and social life. If we want finer detail, we can split any of
those axes into more dimensions.&lt;/p&gt;
&lt;p&gt;
Objective life portraiture is &lt;em&gt;not&lt;/em&gt; easy. We are all vulnerable to cognitive
biases. Following the above analogy with navigation, our inaccuracy at
objectively evaluating our lives is akin to inaccurately navigating a ship or
airplane. If you’re not well-practiced at seeing, your only tool for navigation
might be &lt;a href=&quot;https://en.wikipedia.org/wiki/Dead_reckoning&quot; title=&quot;&quot;&gt;dead reckoning&lt;/a&gt;.  If you practice drawing
self-portraits of your life, you might suddenly find yourself in possession of a
sextant and an almanac, so you can navigate using the stars. The ideal in this
case would be to have something like GPS, which might look like &lt;a href=&quot;http://quantifiedself.com/about/&quot; title=&quot;&quot;&gt;Quantified
Self&lt;/a&gt; with an incredible amount of detail.&lt;/p&gt;
&lt;p&gt;
It’s worth mentioning that our ability to navigate varies across different
dimensions. This is an idea that doesn’t really carry over to navigating Earth,
but it’s important to recognize. For example, if you’re thorough with your
personal finances, you could have tools akin to GPS for navigating that part of
your life. At the same time, if you don’t check in with your emotions, or do
anything to improve your emotional health, you might be lost in those spaces.&lt;/p&gt;
&lt;p&gt;
There are ways to improve our navigating abilities depending on the spaces we’re
looking at. To improve navigating your personal finances, you can regularly
consult your banking statements, make budgets, and explore different methods of
investing. To improve navigating your physical health, you can perform one of
many different fitness tests, or consult a personal trainer. To improve
navigating your emotional health, you could try journaling, or maybe begin
seeing a therapist. Any and all of these could help you locate yourself in the
vast space where your life could be.&lt;/p&gt;
&lt;p&gt;
In order to get where you want to go, you need to know where you are, and what
direction you’re moving in.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title type="text">Memento Mori - Seeing the End</title>
    <link rel="alternate" href="https://rkallos.com/blog/2017-03-30-seeing-the-end.html"/>
    <id>urn:https-rkallos-com:blog-2017-03-30-seeing-the-end-html</id>
    <published>2019-03-16T17:30:00Z</published>
    <updated>2019-03-16T17:30:00Z</updated>
    <author>
      <name>Richard Kallos</name>
    </author>
    <content type="html">&lt;p&gt;
Memento Mori (translated as “remember death”) is a powerful idea and practice.
In this post, I make the case that it’s important to think not just about your
death, but to clearly define what it means to be &lt;em&gt;finished&lt;/em&gt; in whatever you set
out to do.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;
We are mites on a marble floating in the endless void.  Our lifespans are blinks
in cosmic history.  Furthermore, for many of us, our contributions are likely to
be forgotten soon after we rejoin the earth, if not sooner.&lt;/p&gt;
&lt;p&gt;
This is great news.&lt;/p&gt;
&lt;p&gt;
Whenever I’m feeling nervous or embarrassed, I start to feel better when I
realize that nobody in front of me is going to be alive 100 years from now, and
I doubt that they’ll be telling their grandchildren about that time when Richard
made a fool of himself, because I try to make a fool of myself often enough that
it’s usually not worth telling people about.&lt;/p&gt;
&lt;p&gt;
Knowing that we are finite is also pretty motivating.  I feel less resistance to
starting new things.  It doesn’t have to be perfect, in fact, it’s probably
going to be average.  However, it’s my journey, so it’s special to me, and I
probably (hopefully?) learned and improved on the way.&lt;/p&gt;
&lt;p&gt;
It’s important to think about the ends of things, even when we don’t necessarily
want things to end.  Endings are as much a part of life as beginnings are, to
think otherwise is delusion.  Endings tend to have a reputation for being sad,
but they don’t always have to be.&lt;/p&gt;
&lt;p&gt;
For example, some developers of open source software get stuck working on their
projects for far longer than they expected.  It’s unfortunate that creating
something that people enjoy can turn into a source of grief and resentment.&lt;/p&gt;
&lt;p&gt;
Specifying an end of any endeavor is an important task.  If no ‘end state’ is
declared, it’s possible that a project will continue to take up time and effort,
perpetually staying on the back-burner of things you have going on, draining you
of resources until you are no longer able to start anything new.&lt;/p&gt;
&lt;p&gt;
Spending time thinking about what your finished project will look like sets a
target for you to achieve, which is a point I’ll elaborate on very soon.  This
exercise, along with evaluating where you are currently on your path toward
achieving your goal/finishing your project, are immensely useful for getting
your brain to focus on the intermediate tasks that need to be finished in order
to get to that idealized ‘end state’.&lt;/p&gt;
&lt;p&gt;
All in all, while it’s sometimes nice to simply wander, it’s important to
acknowledge that you are always going somewhere, even when you think you’re
standing still. You should be the one who decides where you go, not someone
else.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title type="text">In Defense of gen_event</title>
    <link rel="alternate" href="https://rkallos.com/blog/2018-05-22-in-defense-of-gen-event.html"/>
    <id>urn:https-rkallos-com:blog-2018-05-22-in-defense-of-gen-event-html</id>
    <published>2018-05-22T18:00:00Z</published>
    <updated>2018-05-22T18:00:00Z</updated>
    <author>
      <name>Richard Kallos</name>
    </author>
    <content type="html">&lt;p&gt;
&lt;code class=&quot;inline&quot;&gt;gen_event&lt;/code&gt; has drawn ire from Erlang and Elixir developers for a while. Many
posts have been written, and more than one conference talk has been given on
various alternatives to gen_event. In this post, I make the case that despite
its warts and age, &lt;code class=&quot;inline&quot;&gt;gen_event&lt;/code&gt; is still a very useful module to know. I also
make the case that its core design is ultimately a lot more flexible than many
people give it credit for.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;
Certain design decisions give rise to flexible systems. For example, Erlang’s
asynchronous sending of messages and synchronous receiving of messages with
timeouts offer developers the full range of message passing behaviour. For
synchronous sending, send your message, ideally with a unique reference, then do
a receive, pattern matching on the reference you sent. This is basically what
goes on when you use &lt;code class=&quot;inline&quot;&gt;gen_*:call&lt;/code&gt;. For asynchronous receives, you can use a
receive block with &lt;code class=&quot;inline&quot;&gt;after 0&lt;/code&gt; to instantly time out if no messages can be
matched against. Here is some code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;makeup erlang&quot;&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sync_send&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;5454599510-1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Msg&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;5454599510-1&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ref&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;make_ref&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;5454599510-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;5454599510-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pid&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;5454599510-3&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ref&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Msg&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;5454599510-3&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;receive&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;5454599510-4&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ref&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Reply&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;5454599510-4&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Reply&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

  &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;async_recv&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;5454599510-5&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;5454599510-5&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;receive&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;M&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;5454599510-6&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;M&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;5454599510-6&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
Question: If Erlang gave you synchronous sends and asynchronous receives, how
would you go about implementing asynchronous sends and synchronous receives?&lt;/p&gt;
&lt;p&gt;
The core design of Erlang’s message passing is flexible enough to express the
message passing semantics that it didn’t implement by default. Please keep this
in mind as we turn our attention to &lt;code class=&quot;inline&quot;&gt;gen_event&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;
For those of you who are unfamiliar with &lt;code class=&quot;inline&quot;&gt;gen_event&lt;/code&gt;, it works more-or-less like
this: &lt;code class=&quot;inline&quot;&gt;gen_event:start_link&lt;/code&gt; starts what’s called an &lt;em&gt;event manager&lt;/em&gt;
process. When you write a module that implements the &lt;code class=&quot;inline&quot;&gt;gen_event&lt;/code&gt; behaviour, you
are writing what’s called an &lt;em&gt;event handler&lt;/em&gt;. An event manager has a list of
installed event handlers, each with their own state.  when a process calls
&lt;code class=&quot;inline&quot;&gt;gen_event:notify&lt;/code&gt;, the event manager will invoke its installed handlers one at
a time (after all, the event manager is just one process).  I’ll repeat for
emphasis; an event manager process executes all of its installed event handlers
one at a time. This decision draws some ire, particularly from José Valim, who
has a &lt;a href=&quot;http://blog.plataformatec.com.br/2016/11/replacing-genevent-by-a-supervisor-genserver/&quot; title=&quot;&quot;&gt;blog post&lt;/a&gt; on how to replace &lt;code class=&quot;inline&quot;&gt;gen_event&lt;/code&gt; with a supervisor (as the
event manager) and a bunch of gen_servers (as the event handlers).&lt;/p&gt;
&lt;p&gt;
I know I’m strange, but I think that the default behaviour of &lt;code class=&quot;inline&quot;&gt;gen_event&lt;/code&gt; is
perfectly fine, and ultimately much more flexible than the solution with
concurrent handlers as suggested by Jose. If you want events to be handled
concurrently, then you can trivally implement fanning out by installing event
handlers that forward messages to existing processes or spawn processes to
execute the event handling code. However, if conccurent handling of events is
the default behaviour, how would you set about implementing sequential handling
of events? I suspect that you’ll basically wind up implementing a less efficient
version of &lt;code class=&quot;inline&quot;&gt;gen_event&lt;/code&gt;; less efficient because you’ll be sending unnecessary
messages.&lt;/p&gt;
&lt;p&gt;
Question: If &lt;code class=&quot;inline&quot;&gt;gen_event&lt;/code&gt; ran handlers concurrently by default, how would you go
about running handlers sequentially?&lt;/p&gt;
&lt;p&gt;
In conclusion, &lt;code class=&quot;inline&quot;&gt;gen_event&lt;/code&gt;, much like Erlang’s message passing, provides a
flexible base upon which you can easily implement different behaviour. I’m happy
that &lt;code class=&quot;inline&quot;&gt;gen_event&lt;/code&gt; exists and that it was decided that sequential handling of
events would be the default behaviour.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title type="text">Stream&lt;Consciousness&gt;</title>
    <link rel="alternate" href="https://rkallos.com/blog/2018-04-22-stream-consciousness.html"/>
    <id>urn:https-rkallos-com:blog-2018-04-22-stream-consciousness-html</id>
    <published>2018-05-09T18:30:00Z</published>
    <updated>2018-05-09T18:30:00Z</updated>
    <author>
      <name>Richard Kallos</name>
    </author>
    <content type="html">&lt;p&gt;
A very common piece of programming wisdom that gets passed around is to think
before you code. My favorite expression of this idea is “Weeks of programming
can save you hours of planning”. That being said, not all thinking/planning is
equal. In this post, I share a creative writing technique I’ve put into practice
to great effect at work; freewriting.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;
The idea is simple; you write down your thoughts as they arise. Forget about
editing. Forget about grammar. Just write down a stream of consciousness. I
prefer to use pen and paper for this, but I find the practice equally effective
at the computer. I usually start each exercise with a sentence like “I don’t
know what to do for this thing”, then go into detail about the problem, as well
as any solutions that crop up and their respective tradeoffs. After a while, I
find that as the words fill the page, my mind becomes more focused on the
problem. More often than not, I come up with an effective solution to the
problem I describe toward the beginning of the stream.&lt;/p&gt;
&lt;p&gt;
I’ve started freewriting whenever I feel stifled at home or at work. I find it
to be a great way to break away from the computer while still thinking deeply
about programming. In addition, I relish every opportunity to write with my
fountain pens.&lt;/p&gt;
&lt;p&gt;
One reason why I think the technique is so effective is that you quickly begin
using System 2 (borrowing Daniel Kahneman’s terminology) to digest the task at
hand. Furthermore, by writing down your thoughts, you make yourself more
resilient to distractions. Suppose someone comes by and ask you a question. If
you’ve been writing down your thoughts, you can simply re-read what you’ve
written in order to recover your train of thought. This can avoid situations
like &lt;a href=&quot;http://twitpic.com/dj27dh&quot; title=&quot;&quot;&gt;this&lt;/a&gt; (credit to Jason Heeris).&lt;/p&gt;
&lt;p&gt;
For your viewing pleasure, &lt;a href=&quot;/img/stream-of-consciousness.png&quot; title=&quot;&quot;&gt;here&lt;/a&gt; (830KB) is a photo of one of my
sessions. TL;DR: I’ve been wrestling with how to write a a good API for a set of
NIFs I’m writing for a fast metrics library. In the end, I decided it’s a better
idea to focus on implementing static configuration and putting machinery in
place to allow for property testing.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title type="text">Introducing wrek, a miniature Erlang graph engine</title>
    <link rel="alternate" href="https://rkallos.com/blog/2018-01-26-introducing-wrek-a-miniature-erlang-graph-engine.html"/>
    <id>urn:https-rkallos-com:blog-2018-01-26-introducing-wrek-a-miniature-erlang-graph-engine-html</id>
    <published>2018-04-22T15:15:00Z</published>
    <updated>2018-04-22T15:15:00Z</updated>
    <author>
      <name>Richard Kallos</name>
    </author>
    <content type="html">&lt;p&gt;
This is a copy of &lt;a href=&quot;https://codesync.global/media/introducing-wrek-a-miniature-erlang-graph-engine/&quot; title=&quot;&quot;&gt;this article&lt;/a&gt;, kept for posterity on my blog.&lt;/p&gt;
&lt;p&gt;
I gave a talk about Wrek at Code BEAM SF last month. You can view it
&lt;a href=&quot;https://www.youtube.com/watch?v=dYDdnZoH0uk&quot; title=&quot;&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;
&lt;a href=&quot;https://github.com/rkallos/wrek&quot; title=&quot;&quot;&gt;Wrek&lt;/a&gt; is an Erlang library I wrote for concurrently executing task
dependency graphs. It’s intended purpose is to run a set of pre-defined tasks
that have a partial ordering between them. In this post, I explain why I wrote
wrek, what it can be used for, and how to use it.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h1&gt;
Motivation&lt;/h1&gt;
&lt;p&gt;
Wrek emerged as the result of two distinct forces. First, my amateurish
enjoyment of graph theory made me try to see graphs wherever I could, laying the
conceptual foundation for this library. Second, I realized that a project I was
working on at &lt;a href=&quot;https://adgear.com/&quot; title=&quot;&quot;&gt;Adgear&lt;/a&gt; would benefit from such a library, causing me to finally
start writing Wrek.&lt;/p&gt;
&lt;h2&gt;
Conceptual&lt;/h2&gt;
&lt;p&gt;
The graph is a pervasive data structure in computing. When I learned about graph
algorithms in school, I was amazed by their broad applications. Graphs play
essential roles in compilers and build systems. Various graph algorithms form
the backbone of how we communicate with each other over the internet.&lt;/p&gt;
&lt;p&gt;
Our everyday lives tend to be filled with lists. We have to-do lists, shopping
lists, recipes, checklists, instructions, and much, much more. One day, I
realized that some of these lists are deceiving. Some of these lists are
actually graphs hiding in list’s clothing; these lists are &lt;a href=&quot;https://en.wikipedia.org/wiki/Topological_sorting&quot; title=&quot;&quot;&gt;topological
orderings&lt;/a&gt; of &lt;a href=&quot;https://en.wikipedia.org/wiki/Directed_acyclic_graph&quot; title=&quot;&quot;&gt;directed acyclic graphs&lt;/a&gt;. Two of the more obvious
cases of this are to-do lists and recipes. You can’t send a letter that you
haven’t written yet, nor can you cook spaghetti if you haven’t boiled a pot of
water.&lt;/p&gt;
&lt;p&gt;
Once the sneaky graphs were discovered, I spent some time thinking about how to
benefit from treating various lists like the DAGs that they secretly are. The
clearest analogies between these lists and DAGs were to-do lists and
recipes. These very closely resembled &lt;a href=&quot;https://en.wikipedia.org/wiki/Dependency_graph&quot; title=&quot;&quot;&gt;dependency graphs&lt;/a&gt;. Unlike in their
list representation, these dependency graphs show which vertices (individual
tasks) can be executed concurrently. Sometimes this is obvious (e.g: I can chop
vegetables while I wait for the pot of water to boil! I can begin preparing a
second baking sheet of cookies while the first batch is in the oven!), but I had
my hopes that the act of reformulating lists as dependency graphs could expose
more opportunities for concurrency.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;                Boil water -- Add pasta -- Cook pasta --.
                                                         \
  Purée tomatoes --.                                      \
                    \                                      \
  Chop vegetables -- Combine in saucepan -- Simmer sauce -- Combine pasta and sauce -- Serve
                    /
      Add spices --&amp;#39;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
Edges between vertices in the above graph express a partial ordering. Vertices
with no path between them can be executed concurrently. The relationship between
vertices in the graph reflects the truth in the kitchen. We can cook the pasta
while our sauce is simmering. It doesn’t matter whether we chop vegetables or
purée tomatoes first; they both need to be done if we’re going to make any
sauce.&lt;/p&gt;
&lt;p&gt;
Despite my best efforts, I am still a disaster in the kitchen. As a means of
changing this inconvenient fact, I thought it would be fun to try representing
recipes as directed graphs, with extra data like how long each step can be
expected to take. This sat in my list of ‘someday projects’ for a long time, and
I was only reminded of it when I begin to think about a project I was working on
at $JOB.&lt;/p&gt;
&lt;h2&gt;
Practical&lt;/h2&gt;
&lt;p&gt;
One of the projects I accomplished last year at Adgear was removing an expensive
computation that was taking place on each of our already maxed-out edge servers,
and replaced it with a system that performed the expensive computation on a
single machine, then distributed the result. The system worked when nobody
touched it, but it was the programming equivalent of twigs and duct tape; cron
jobs and shell scripts. This system continued to be painful to use, and more
examples of expensive computations being done on CPU-starved machines were
cropping up. At this point, it made sense start writing a more robust system.&lt;/p&gt;
&lt;p&gt;
These expensive computations decomposed nicely into lists of steps to be
performed. Fetch this data, transform it, transform it some more, send some of
the data to this group of servers, send this other data to some other group of
servers. Shell scripts are pretty good at encoding these pipelines, so it was an
acceptable choice at the time of implementation. After a while, it dawned on me
that these lists of steps were not really lists; they were dependency graphs. I
tried to expose the latent concurrency in the shell scripts by spicing them up
with some background jobs and &lt;code class=&quot;inline&quot;&gt;waitpid&lt;/code&gt;, but it was decided that it would make
more sense to switch to Erlang and benefit from all that OTP has to offer.&lt;/p&gt;
&lt;h1&gt;
Let’s get &lt;code class=&quot;inline&quot;&gt;wrek&lt;/code&gt;ed&lt;/h1&gt;
&lt;p&gt;
(I’m really sorry. I couldn’t resist.)&lt;/p&gt;
&lt;p&gt;
Wrek accepts dependency graphs like the above spaghetti recipe as input, and
executes each vertex. The nature of concurrently executing actions as soon as
they are able to execute is &lt;em&gt;generic&lt;/em&gt; to any problem that can be represented by
a dependency graph. The structure of a dependency graph, as well as the tasks
involved in each of the graph’s vertices are &lt;em&gt;specific&lt;/em&gt; to the user’s
wishes. Following the same generic/specific split as OTP; the generic portion of
this class of problems is solved by &lt;code class=&quot;inline&quot;&gt;wrek&lt;/code&gt; and &lt;code class=&quot;inline&quot;&gt;wrek_vert&lt;/code&gt; modules. The specific
portion is provided by the user in the form of an Erlang map describing each
vertex in the graph, and a set of callback modules that implement the
&lt;code class=&quot;inline&quot;&gt;wrek_vert&lt;/code&gt; behaviour.&lt;/p&gt;
&lt;p&gt;
The &lt;code class=&quot;inline&quot;&gt;wrek_vert&lt;/code&gt; behaviour consists of a single callback &lt;code class=&quot;inline&quot;&gt;run/2&lt;/code&gt;, where the first
argument is a list of arguments to be sent to the callback function, and the
second argument is the ID of a process which can provide information generated
by other vertices. The expected result of this callback function is either &lt;code class=&quot;inline&quot;&gt;{ok, Any :: any()}&lt;/code&gt; or &lt;code class=&quot;inline&quot;&gt;{error, Reason :: any()}&lt;/code&gt;. If the callback function succeeds,
&lt;code class=&quot;inline&quot;&gt;Any&lt;/code&gt; will be taken by wrek and made available to other &lt;code class=&quot;inline&quot;&gt;wrek_vert&lt;/code&gt;
processes. If the callback function crashes or returns an error, the entire
graph will be shut down.&lt;/p&gt;
&lt;h2&gt;
Making Erlang Pasta&lt;/h2&gt;
&lt;p&gt;
Following the contrived example above, let’s set about using wrek to make
pasta. Of course, our program won’t really make pasta, but it’s output ought to
fool somebody.&lt;/p&gt;
&lt;p&gt;
Looking at the above graph, each step seems to do one of three things:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;
Add an ingredient  &lt;/li&gt;
  &lt;li&gt;
Combine ingredients in a vessel  &lt;/li&gt;
  &lt;li&gt;
Do something to ingredients in a vessel  &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
Let’s go ahead write some &lt;code class=&quot;inline&quot;&gt;wrek_vert&lt;/code&gt;s for each of those actions. If you don’t
feel like following along in a text editor, the full code can be found
&lt;a href=&quot;https://github.com/rkallos/wrek_examples&quot; title=&quot;&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;makeup erlang&quot;&gt;&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;3488358567-1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;cook_add&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;3488358567-1&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;behaviour&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;3488358567-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;wrek_vert&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;3488358567-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;3488358567-3&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;3488358567-4&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;3488358567-4&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;3488358567-3&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;3488358567-5&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;3488358567-6&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ingredient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Quantity&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;3488358567-6&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pid&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;3488358567-5&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;3488358567-7&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;adding &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;~s&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;. amount: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;~s&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;~n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;3488358567-8&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ingredient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Quantity&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;3488358567-8&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;3488358567-7&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;3488358567-9&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;3488358567-10&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;added&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;3488358567-11&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;3488358567-12&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ingredient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Quantity&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;3488358567-12&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;3488358567-11&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;3488358567-10&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;3488358567-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
That’s all for &lt;code class=&quot;inline&quot;&gt;cook_add&lt;/code&gt;. It prints a message, then produces a
map with a key &lt;code class=&quot;inline&quot;&gt;added&lt;/code&gt; whose value is a proplist with a single pair.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;makeup erlang&quot;&gt;&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9209454785-1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;cook_heat&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9209454785-1&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;behaviour&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9209454785-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;wrek_vert&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9209454785-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9209454785-3&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9209454785-4&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9209454785-4&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9209454785-3&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9209454785-5&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9209454785-6&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Verb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Noun&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9209454785-6&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pid&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9209454785-5&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9209454785-7&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;~p&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ing &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;~p&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;~n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9209454785-8&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Verb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Noun&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9209454785-8&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9209454785-7&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9209454785-9&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9209454785-10&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9209454785-10&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9209454785-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
&lt;code class=&quot;inline&quot;&gt;cook_heat&lt;/code&gt; is also pretty short. It’s also very abstract. It could be used to
print a message about &lt;code class=&quot;inline&quot;&gt;Verb&lt;/code&gt;ing any &lt;code class=&quot;inline&quot;&gt;Noun&lt;/code&gt;, not just cooking ingredients!&lt;/p&gt;
&lt;p&gt;
Our final callback module is a little longer, because it does a little bit more
than printing a message.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;makeup erlang&quot;&gt;&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;cook_combine&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-1&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;behaviour&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;wrek_vert&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-3&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-4&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-4&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-3&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-5&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-6&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ingredients&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vessel&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-6&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pid&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-5&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fun&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-7&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Step&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Acc&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-7&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stuff&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;wrek_vert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-8&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Step&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;added&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-8&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;combining &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;~p&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; with &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;~p&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; in &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;~p&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;~n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-10&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stuff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Acc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vessel&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-10&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stuff&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Acc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stuff&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;lists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;foldl&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-11&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-12&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-12&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ingredients&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-11&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-13&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;~p&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; now contains: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;~p&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;~n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-14&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vessel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stuff&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-14&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-13&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-15&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-16&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;added&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stuff&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-16&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9673201915-15&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
&lt;code class=&quot;inline&quot;&gt;Ingredients&lt;/code&gt; is expected to be a list of vertex names. We finally use our
parent process’s &lt;code class=&quot;inline&quot;&gt;Pid&lt;/code&gt; as an argument to &lt;code class=&quot;inline&quot;&gt;wrek_vert:get/3&lt;/code&gt;. This lets us consume
the data produced by the &lt;code class=&quot;inline&quot;&gt;cook_add&lt;/code&gt; callback module. After combining everything,
we returns a new collection of ingredients.&lt;/p&gt;
&lt;p&gt;
Alright! We’re nearly done describing the specific portion of our problem! The
last step is to represent our dependency graph in terms of these callback
modules and the arguments we want to pass to them.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;makeup erlang&quot;&gt;&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-1&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;wrek_example&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-1&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-3&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;make_pasta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-3&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;make_pasta&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-4&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ensure_all_started&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-5&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;wrek&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-5&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Graph&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-6&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;tomatoes&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-7&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;cook_add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-8&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;pureed tomatoes&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;1 can&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-8&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;deps&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-9&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-9&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
       &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-7&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;vegetables&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-10&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;cook_add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-11&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;chopped vegetables&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;lots&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-11&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;deps&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-12&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-12&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
       &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-10&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;spices&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-13&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;cook_add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-14&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;spices&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;to taste&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-14&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;deps&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-15&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-15&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
       &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-13&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;saucepan&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-16&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;cook_combine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-17&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-18&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;tomatoes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;vegetables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;spices&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-18&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;saucepan&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-17&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;deps&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-19&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;tomatoes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;vegetables&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;spices&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-19&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
       &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-16&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;simmer_sauce&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-20&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;cook_heat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-21&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;simmer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;sauce&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-21&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;deps&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-22&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;saucepan&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-22&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
       &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-20&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;boil_water&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-23&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;cook_heat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-24&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;boil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;water&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-24&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;deps&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-25&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-25&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
       &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-23&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;add_pasta&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-26&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;cook_add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-27&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;pasta&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;1 handful&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-27&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;deps&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-28&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;boil_water&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-28&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
       &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-26&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;cook_pasta&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-29&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;cook_heat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-30&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;cook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;pasta&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-30&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;deps&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-31&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;add_pasta&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-31&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
       &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-29&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;mix_pasta_with_sauce&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-32&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;cook_combine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-33&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-34&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;saucepan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;add_pasta&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-34&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;saucepan&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-33&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;deps&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-35&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;simmer_sauce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;cook_pasta&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-35&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
       &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-32&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
     &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-6&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;wrek&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-36&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Graph&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;1293859336-36&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
That’s an eyeful! What we’ve done here is created an Erlang map whose keys
represent the names of vertices in our original dependency graph, and whose
values are maps that specify a callback module, arguments to pass to the
callback module, as well as any dependencies the vertex may have. I congratulate
those of you who noticed that we are never straining the pasta; I did confess to
being a disaster in the kitchen. I promise I learned my lesson.&lt;/p&gt;
&lt;p&gt;
Alright, we’re done coding! Let’s start up a shell and make some pasta!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ rebar3 shell
[...]
1&amp;gt; wrek_example:make_pasta().
adding spices. amount: to taste.
adding puréed tomatoes. amount: 1 can.
adding chopped vegetables. amount: lots.
adding pasta. amount: 1 handful.
{ok,&amp;lt;0.133.0&amp;gt;}
2&amp;gt; boiling water.
cooking pasta.
combining [{&amp;quot;puréed tomatoes&amp;quot;,&amp;quot;1 can&amp;quot;}] with [] in saucepan.
combining [{&amp;quot;chopped vegetables&amp;quot;,&amp;quot;lots&amp;quot;}] with [{&amp;quot;puréed tomatoes&amp;quot;,&amp;quot;1 can&amp;quot;}] in saucepan.
combining [{&amp;quot;spices&amp;quot;,&amp;quot;to taste&amp;quot;}] with [{&amp;quot;chopped vegetables&amp;quot;,&amp;quot;lots&amp;quot;},
                                        {&amp;quot;puréed tomatoes&amp;quot;,&amp;quot;1 can&amp;quot;}] in saucepan.
saucepan now contains: [{&amp;quot;spices&amp;quot;,&amp;quot;to taste&amp;quot;},
                        {&amp;quot;chopped vegetables&amp;quot;,&amp;quot;lots&amp;quot;},
                        {&amp;quot;puréed tomatoes&amp;quot;,&amp;quot;1 can&amp;quot;}].
simmering sauce.
combining [{&amp;quot;spices&amp;quot;,&amp;quot;to taste&amp;quot;},
           {&amp;quot;chopped vegetables&amp;quot;,&amp;quot;lots&amp;quot;},
           {&amp;quot;puréed tomatoes&amp;quot;,&amp;quot;1 can&amp;quot;}] with [] in saucepan.
combining [{&amp;quot;pasta&amp;quot;,&amp;quot;1 handful&amp;quot;}] with [{&amp;quot;spices&amp;quot;,&amp;quot;to taste&amp;quot;},
                                        {&amp;quot;chopped vegetables&amp;quot;,&amp;quot;lots&amp;quot;},
                                        {&amp;quot;puréed tomatoes&amp;quot;,&amp;quot;1 can&amp;quot;}] in saucepan.
saucepan now contains: [{&amp;quot;pasta&amp;quot;,&amp;quot;1 handful&amp;quot;},
                        {&amp;quot;spices&amp;quot;,&amp;quot;to taste&amp;quot;},
                        {&amp;quot;chopped vegetables&amp;quot;,&amp;quot;lots&amp;quot;},
                        {&amp;quot;puréed tomatoes&amp;quot;,&amp;quot;1 can&amp;quot;}].&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
It isn’t pretty, but neither is most of my cooking. However, we’ve just used
&lt;code class=&quot;inline&quot;&gt;wrek&lt;/code&gt; to teach Erlang how to concurrently prepare pasta! If you run
&lt;code class=&quot;inline&quot;&gt;wrek_example:make_pasta()&lt;/code&gt; over and over, you may notice that the order of the
output changes, but never enough that the steps followed in order don’t produce
valid pasta.&lt;/p&gt;
&lt;h1&gt;
Uses for wrek, additional features, and plans for the future&lt;/h1&gt;
&lt;p&gt;
I believe that Wrek is useful for more than pretending to make pasta. I used
Wrek at $JOB to concurrently execute various small shell scripts, collect their
output, and pass up to Erlang through and down to other small shell scripts.&lt;/p&gt;
&lt;p&gt;
Wrek also contains features that I haven’t covered in this article. For
instance, you can pass a &lt;code class=&quot;inline&quot;&gt;gen_event&lt;/code&gt; process to &lt;code class=&quot;inline&quot;&gt;wrek:start/2&lt;/code&gt; to get Wrek to
call &lt;code class=&quot;inline&quot;&gt;gen_event:notify/2&lt;/code&gt; when various events take place. This gives users a way
to monitor the execution of their graphs. Combined with Sergey Aleynikov’s
&lt;code class=&quot;inline&quot;&gt;erlexec&lt;/code&gt; library, you can collect &lt;code class=&quot;inline&quot;&gt;stdout&lt;/code&gt; and &lt;code class=&quot;inline&quot;&gt;stderr&lt;/code&gt; from any shell command
you run in a vertex using &lt;code class=&quot;inline&quot;&gt;wrek_exec:exec/4&lt;/code&gt;. I invite interested readers to
take a peek at the &lt;a href=&quot;https://github.com/rkallos/wrek/tree/master/src&quot; title=&quot;&quot;&gt;source code for Wrek&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;
Wrek is still a very young project, and I am still inexperienced at writing
libraries. I am virtually certain that the API is going to go through
significant changes in the near future. I believe this is for the best, because
the whole process of specifying callback modules and passing data between
vertices can be improved dramatically. Here are some of the ideas I have for
improving Wrek:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
Add support for limiting the number of active wrek_vert processes  &lt;/li&gt;
  &lt;li&gt;
Avoid forced namespacing by vertex names, perhaps through the use of ETS
tables as a &lt;a href=&quot;https://en.wikipedia.org/wiki/Tuple_space&quot; title=&quot;&quot;&gt;tuple space&lt;/a&gt;  &lt;/li&gt;
  &lt;li&gt;
(experimental) Describing dependency graphs by annotating vertices by the
data they produce and consume, rather than by explicitly naming other vertices
as dependencies.  &lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;
Conclusion&lt;/h1&gt;
&lt;p&gt;
I had a really nice time writing Wrek, and I’m excited to be using it in
production at Adgear. I am also eagerly looking forward to presenting Wrek at
&lt;a href=&quot;https://codesync.global/conferences/code-beam-sf-2018/&quot; title=&quot;&quot;&gt;Code BEAM SF in March 2018&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;
Thank you very much for reading!&lt;/p&gt;
&lt;p&gt;
P.S: As I mentioned above, this is a copy of a post that was published earlier
in the year. As a result, the above paragraph doesn’t make much sense.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title type="text">Erlang Debug VMs: What, Why and How</title>
    <link rel="alternate" href="https://rkallos.com/blog/2017-03-06-erlang-debug-vms-what-why-and-how.html"/>
    <id>urn:https-rkallos-com:blog-2017-03-06-erlang-debug-vms-what-why-and-how-html</id>
    <published>2018-04-22T15:00:00Z</published>
    <updated>2018-04-22T15:00:00Z</updated>
    <author>
      <name>Richard Kallos</name>
    </author>
    <content type="html">&lt;p&gt;
Erlang can load and run C libraries.  This can be useful if you want to speed up
parts of your code, or if you want to interact with some OS-specific features.
These special C libraries are written specially to work with the Erlang VM, and
can’t easily be run in isolation without extra code.  This makes these libraries
difficult to debug.  In this post, I introduce Erlang debug VMs, explain how to
build them, and show why they can be tremendously useful for debugging linked-in
drivers and NIFs.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;
Once you load and run arbitrary C code, the Erlang VM becomes very easy to
kill.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;makeup erl&quot;&gt;&lt;span class=&quot;sc&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;erl&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Erlang&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OTP&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;19&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9961854964-1&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;erts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;8.2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9961854964-1&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9961854964-2&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9961854964-2&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9961854964-3&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;64&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;bit&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9961854964-3&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9961854964-4&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;smp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9961854964-4&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9961854964-5&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;threads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9961854964-5&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9961854964-6&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;hipe&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9961854964-6&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9961854964-7&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;kernel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;poll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9961854964-7&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Eshell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;V8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;2.2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9961854964-8&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9961854964-8&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;gp unselectable&quot;&gt;
1&amp;gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;test_nif&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;do_something_cool&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9961854964-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9961854964-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Segmentation&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;fault&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;p&quot; data-group-id=&quot;9961854964-10&quot;&gt;(&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- TODO: Shrink font, or remove line numbers --&gt;
&lt;p&gt;
There are so many great tools for debugging Erlang code (&lt;a href=&quot;http://erlang.org/doc/man/dbg.html&quot; title=&quot;&quot;&gt;dbg&lt;/a&gt;,
&lt;a href=&quot;https://github.com/massemanet/redbug&quot; title=&quot;&quot;&gt;redbug&lt;/a&gt;, &lt;a href=&quot;https://github.com/andytill/erlyberly&quot; title=&quot;&quot;&gt;erlyberly&lt;/a&gt;, &lt;a href=&quot;http://ferd.github.io/recon/&quot; title=&quot;&quot;&gt;recon&lt;/a&gt;) as well as excellent
tools for debugging C code (&lt;a href=&quot;https://www.gnu.org/software/gdb/&quot; title=&quot;&quot;&gt;gdb&lt;/a&gt;, &lt;a href=&quot;http://valgrind.org/&quot; title=&quot;&quot;&gt;valgind&lt;/a&gt;,
&lt;a href=&quot;http://lcamtuf.coredump.cx/afl/&quot; title=&quot;&quot;&gt;afl-fuzz&lt;/a&gt;).  However, there is a bit of a shortage of good tooling
for debugging C code that gets executed by the BEAM.  There’s &lt;a href=&quot;https://github.com/tokenrove/niffy&quot; title=&quot;&quot;&gt;niffy&lt;/a&gt;,
which is a neat tool that acts like a reasonable subset of the Erlang VM,
letting you load and call NIFs.  Niffy and your NIF can then be run through
something like afl-fuzz or your own test suite to try and uncover bugs.  As far
as I am aware, no such tool exists for Port Drivers.&lt;/p&gt;
&lt;h1&gt;
Enter Debug VMs&lt;/h1&gt;
&lt;p&gt;
I’ve gotten into the habit of building Erlang/OTP from source.  This affords me
more flexibility than using a package manager.  One particularly nice thing
about building Erlang from source is that you can compile &lt;em&gt;debug&lt;/em&gt; VMs, modified
BEAM binaries intended to be run inside of tools like GDB and Valgrind. This
lets us debug any C code we’ve written that gets executed by BEAM.  In addition,
there is also a bonus set of GDB scripts called &lt;a href=&quot;https://github.com/erlang/otp/blob/master/erts/etc/unix/etp-commands.in&quot; title=&quot;&quot;&gt;ETP&lt;/a&gt; which can
pretty-print Erlang terms from inside of gdb! Debug VM binaries and ETP make the
ordeal of digging through core dumps of crashed BEAM processes much easier, as
you can poke around various parts of memory, and print the Erlang terms they
correspond to.&lt;/p&gt;
&lt;h1&gt;
How I Build My Debug VMs&lt;/h1&gt;
&lt;p&gt;
If you want to build some debug VMs for yourself, the first thing you need to do
is clone the Erlang/OTP git repository.  (For managing software compiled from
source, I &lt;em&gt;highly&lt;/em&gt; recommend &lt;a href=&quot;https://www.gnu.org/software/stow/&quot; title=&quot;&quot;&gt;GNU Stow&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;
Here’s the general workflow of building and installing Erlang from source, using
the git repository. Please note that these commands are somewhat symbolic; if
you copy and paste them into your terminal, they may not work.&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;
Clone &lt;a href=&quot;https://github.com/erlang/otp.git&quot;&gt;https://github.com/erlang/otp.git&lt;/a&gt;  &lt;/li&gt;
  &lt;li&gt;
&lt;code class=&quot;inline&quot;&gt;export ERL_TOP=&amp;quot;$(pwd)/otp&amp;quot;; cd $ERL_TOP&lt;/code&gt;  &lt;/li&gt;
  &lt;li&gt;
&lt;code class=&quot;inline&quot;&gt;git checkout OTP-X.Y.Z&lt;/code&gt;  &lt;/li&gt;
  &lt;li&gt;
&lt;code class=&quot;inline&quot;&gt;$PAGER HOWTO/INSTALL.md&lt;/code&gt;  &lt;/li&gt;
  &lt;li&gt;
&lt;code class=&quot;inline&quot;&gt;./otp_build autoconf&lt;/code&gt;  &lt;/li&gt;
  &lt;li&gt;
&lt;code class=&quot;inline&quot;&gt;./configure --help | $PAGER&lt;/code&gt;  &lt;/li&gt;
  &lt;li&gt;
Figure out which options I want from step 6.  &lt;/li&gt;
  &lt;li&gt;
&lt;code class=&quot;inline&quot;&gt;./configure --prefix=/usr/local/stow/erlang-X.Y.Z --and-other-opts&lt;/code&gt;  &lt;/li&gt;
  &lt;li&gt;
&lt;code class=&quot;inline&quot;&gt;make &amp;amp;&amp;amp; make docs&lt;/code&gt;  &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
The following steps are optional:&lt;/p&gt;
&lt;ol start=&quot;9&quot;&gt;
  &lt;li&gt;
&lt;code class=&quot;inline&quot;&gt;sudo make install install-docs&lt;/code&gt;  &lt;/li&gt;
  &lt;li&gt;
&lt;code class=&quot;inline&quot;&gt;cd /usr/local/stow&lt;/code&gt;  &lt;/li&gt;
  &lt;li&gt;
&lt;code class=&quot;inline&quot;&gt;readlink $(which erl)&lt;/code&gt; to figure out the current version in use  &lt;/li&gt;
  &lt;li&gt;
&lt;code class=&quot;inline&quot;&gt;sudo stow -D erlang-X.(Y-1).W&lt;/code&gt; to unlink the old version  &lt;/li&gt;
  &lt;li&gt;
&lt;code class=&quot;inline&quot;&gt;sudo stow erlang-X.Y.Z&lt;/code&gt; to link the new version  &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
We haven’t built any debug VMs yet. Let’s do that now.  There are quite a couple
of different VMs you can build.  The ones I use the most are the &lt;code class=&quot;inline&quot;&gt;debug&lt;/code&gt; and
&lt;code class=&quot;inline&quot;&gt;valgrind&lt;/code&gt; variants.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;$ cd $ERL_TOP/erts/emulator
$ make debug valgrind gcov gprof lcnt icount&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
After this, you should have a shell script called &lt;code class=&quot;inline&quot;&gt;cerl&lt;/code&gt; in &lt;code class=&quot;inline&quot;&gt;$ERL_TOP/bin&lt;/code&gt;.
This is the script to invoke when you want to run a special BEAM VM.  Adding
that script to your path will let you invoke a debug VM whenever you like!&lt;/p&gt;
&lt;h1&gt;
Invoking &lt;code class=&quot;inline&quot;&gt;cerl&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;
Now that you have built debug VMs, and have &lt;a href=&quot;https://github.com/erlang/otp/blob/master/erts/etc/unix/cerl.src&quot; title=&quot;&quot;&gt;&lt;code class=&quot;inline&quot;&gt;cerl&lt;/code&gt;&lt;/a&gt; somewhere in your
path, we can start the BEAM inside of gdb or valgrind using &lt;code class=&quot;inline&quot;&gt;cerl -rgdb&lt;/code&gt; or
&lt;code class=&quot;inline&quot;&gt;cerl -valgrind&lt;/code&gt;.  After that, you can pass arguments to the script as if you
were passing them to &lt;code class=&quot;inline&quot;&gt;erl&lt;/code&gt;.
&lt;strong&gt;NOTE:&lt;/strong&gt; If you type &lt;code class=&quot;inline&quot;&gt;cerl -gdb&lt;/code&gt;, the script will
start Emacs to control your gdb session, which can be surprising!&lt;/p&gt;
&lt;h1&gt;
Conclusion&lt;/h1&gt;
&lt;p&gt;
I hope this inspires some of you to play around with Erlang debug VMs!  There’s
a lot of great stuff to be discovered!  In the near future, I hope to publish
another post showing off some use cases of debug VMs!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title type="text">Starting a Blog, Take Two</title>
    <link rel="alternate" href="https://rkallos.com/blog/2017-01-26-starting-a-blog-take-two.html"/>
    <id>urn:https-rkallos-com:blog-2017-01-26-starting-a-blog-take-two-html</id>
    <published>2017-01-28T00:00:00Z</published>
    <updated>2017-01-28T00:00:00Z</updated>
    <author>
      <name>Richard Kallos</name>
    </author>
    <content type="html">&lt;p&gt;
My attempt at blogging solely with Emacs and Org mode has come to an end. I
blame my perfectionist tendencies and my lack of understanding of Org mode
internals. I’ve since set up &lt;a href=&quot;https://github.com/greghendershott/frog&quot;&gt;Frog&lt;/a&gt;, a
static site generator written in &lt;a href=&quot;https://racket-lang.org&quot;&gt;Racket&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;
There are some things that I will miss from Org mode, like source blocks and
embedded LaTeX, but Frog has some pretty nice features of its own, like
integration with &lt;a href=&quot;http://pygments.org&quot;&gt;Pygments&lt;/a&gt; for syntax highlighting
and &lt;a href=&quot;http://disqus.com&quot;&gt;Disqus&lt;/a&gt; for comments. I’m also looking forward to
writing some posts using &lt;a href=&quot;https://docs.racket-lang.org/scribble/&quot;&gt;Scribble&lt;/a&gt;, in
the likely event that I will want to do some complex bloggy things like
executing Racket code in a blog post.&lt;/p&gt;
&lt;!-- more --&gt;
</content>
  </entry>
  <entry>
    <title type="text">Static site generation with Org Mode</title>
    <link rel="alternate" href="https://rkallos.com/blog/2017-01-02-static-site-generation-with-org-mode.html"/>
    <id>urn:https-rkallos-com:blog-2017-01-02-static-site-generation-with-org-mode-html</id>
    <published>2017-01-02T00:00:00Z</published>
    <updated>2017-01-28T00:01:00Z</updated>
    <author>
      <name>Richard Kallos</name>
    </author>
    <content type="html">&lt;p&gt;
&lt;strong&gt;UPDATE (2017-01-28)&lt;/strong&gt;:
I no longer use Emacs and Org mode for blogging. I’ve since switched
to &lt;a href=&quot;https://github.com/greghendershott/frog&quot;&gt;Frog&lt;/a&gt;. I’ve kept this post up for
shame and to those who may be interested in blogging with Org.&lt;/p&gt;
&lt;p&gt;
Let’s get this out of the way; I love Emacs and Org mode. I’ve been using Emacs
as my daily editor for the past several years. I was using Vim before that, but
tried Emacs and switched over when I wanted a system for organizing lecture
notes. Don’t get me wrong, I think Vim is fantastic, and I still use it
occasionally, but Emacs has won my heart.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;
&lt;a href=&quot;/img/org-mode-blogging/blogging.png&quot;&gt;  &lt;img src=&quot;/img/org-mode-blogging/thumb.png&quot; alt=&quot;I wrote this post in Emacs&quot; title=&quot;I wrote this post in Emacs&quot;&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
&lt;code class=&quot;inline&quot;&gt;org-publish&lt;/code&gt; as a blogging system&lt;/h1&gt;
&lt;p&gt;
This site is generated using Org mode’s publishing features in
&lt;code class=&quot;inline&quot;&gt;org-publish&lt;/code&gt;. Each post is a .org file that gets transpiled to an HTML file
when I run the &lt;code class=&quot;inline&quot;&gt;org-publish&lt;/code&gt; command. My setup takes care of RSS, images, custom
Javascript (if any) and CSS. I’m also thinking of incorporating either TRAMP or
rsync to push changes to my website.&lt;/p&gt;
&lt;p&gt;
The rest of this blog post is going to go over the various settings I use. To
start off, let’s pull in some org-mode libraries that we’ll use to generate our
site map.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;emacs-lisp&quot;&gt;(require &amp;#39;org)
(require &amp;#39;ox-html)
(require &amp;#39;ox-publish)
(require &amp;#39;ox-rss)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
Next, let’s set a couple of variables. I maintain 2 separate custom files in my
&lt;code class=&quot;inline&quot;&gt;\~/.emacs.d&lt;/code&gt; folder, one for Windows and another for Linux, so I create a
variable that is editable through Emacs’s &lt;code class=&quot;inline&quot;&gt;customize&lt;/code&gt; command. The rest of the
variables are boilerplate HTML that will go into every page on the site.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;emacs-lisp&quot;&gt;(defcustom blog-base-dir &amp;quot;~/blog/&amp;quot; &amp;quot;Base directory for blog files&amp;quot;)

(setq website-html-head &amp;quot;&amp;lt;link rel=\&amp;quot;stylesheet\&amp;quot; href=\&amp;quot;css/site.css\&amp;quot;
  type=\&amp;quot;text/css\&amp;quot;/&amp;gt;&amp;quot;)

(setq website-html-preamble
  &amp;quot;&amp;lt;h1&amp;gt;Richard Kallos&amp;lt;/h1&amp;gt;
&amp;lt;hr&amp;gt;&amp;lt;/hr&amp;gt;
&amp;lt;div class=\&amp;quot;nav\&amp;quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=\&amp;quot;/\&amp;quot;&amp;gt;Home&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=\&amp;quot;/static/about.html\&amp;quot;&amp;gt;About&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=\&amp;quot;https://github.com/rkallos\&amp;quot;&amp;gt;GitHub&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=\&amp;quot;/static/resume.html\&amp;quot;&amp;gt;Résumé&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=\&amp;quot;/index.xml\&amp;quot;&amp;gt;RSS&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;&amp;quot;)

(setq website-html-postamble &amp;quot;&amp;lt;div class=\&amp;quot;footer\&amp;quot;&amp;gt; Copyright 2016 %a (%v
  HTML).&amp;lt;br&amp;gt; Last updated %C.&amp;lt;br&amp;gt; Built with %c.  &amp;lt;/div&amp;gt;&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
Finally, let’s dig into the meat and potatoes of the system,&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;emacs-lisp&quot;&gt;(setq org-publish-project-alist
      `((&amp;quot;blog&amp;quot;
         :base-directory ,(concat blog-base-dir &amp;quot;/raw&amp;quot;)
         :base-extension &amp;quot;org&amp;quot;
         :publishing-directory ,(concat blog-base-dir &amp;quot;/out&amp;quot;)
         :publishing-function org-html-publish-to-html

         :export-with-tags nil
         :exclude-tags (&amp;quot;todo&amp;quot; &amp;quot;noexport&amp;quot;)
         :exclude &amp;quot;level-.*\\|.*\.draft\.org&amp;quot;
         :with-title t
         :section-numbers nil
         :headline-levels 4
         :with-toc nil
         :with-date t

         :auto-sitemap t
         :sitemap-filename &amp;quot;index.org&amp;quot;
         :sitemap-title &amp;quot;Home&amp;quot;
         :sitemap-sort-files anti-chronologically
         :sitemap-file-entry-format &amp;quot;%d - %t&amp;quot;
         :sitemap-function org-publish-org-sitemap

         :html-doctype &amp;quot;html5&amp;quot;
         :html-html5-fancy t
         :html-head ,website-html-head
         :html-head-extra
         &amp;quot;&amp;lt;link rel=\&amp;quot;alternate\&amp;quot; type=\&amp;quot;application/rss+xml\&amp;quot;
                href=\&amp;quot;https://rkallos.com/posts.xml\&amp;quot;
                title=\&amp;quot;RSS feed\&amp;quot;&amp;gt;&amp;quot;
         :html-preamble ,website-html-preamble
         :html-postamble ,website-html-postamble)

        (&amp;quot;images&amp;quot;
         :base-directory ,(concat blog-base-dir &amp;quot;/img&amp;quot;)
         :base-extension &amp;quot;jpg\\|gif\\|png\\|svg&amp;quot;
         :publishing-directory ,(concat blog-base-dir &amp;quot;/out/img&amp;quot;)
         :recursive t
         :publishing-function org-publish-attachment)

        (&amp;quot;js&amp;quot;
         :base-directory ,(concat blog-base-dir &amp;quot;/js&amp;quot;)
         :base-extension &amp;quot;js&amp;quot;
         :publishing-directory ,(concat blog-base-dir &amp;quot;/out/js&amp;quot;)
         :recursive t
         :publishing-function org-publish-attachment)

        (&amp;quot;css&amp;quot;
         :base-directory ,(concat blog-base-dir &amp;quot;/css&amp;quot;)
         :base-extension &amp;quot;css&amp;quot;
         :publishing-directory ,(concat blog-base-dir &amp;quot;/out/css&amp;quot;)
         :recursive t
         :publishing-function org-publish-attachment)

        (&amp;quot;rss&amp;quot;
         :base-directory ,(concat blog-base-dir &amp;quot;/raw&amp;quot;)
         :base-extension &amp;quot;org&amp;quot;
         :publishing-directory ,(concat blog-base-dir &amp;quot;/out&amp;quot;)
         :publishing-function (org-rss-publish-to-rss)
         :html-link-use-abs-url t

         :export-with-tags nil
         :exclude-tags (&amp;quot;todo&amp;quot; &amp;quot;noexport&amp;quot;)
         :exclude &amp;quot;level-.*\\|.*\.draft\.org&amp;quot;
         :with-title t
         :section-numbers nil
         :headline-levels 4
         :with-toc nil
         :with-date t)

        (&amp;quot;static&amp;quot;
         :base-directory ,(concat blog-base-dir &amp;quot;/raw/static&amp;quot;)
         :base-extension &amp;quot;org&amp;quot;
         :publishing-directory ,(concat blog-base-dir &amp;quot;/out/static&amp;quot;)
         :recursive t
         :publishing-function org-html-publish-to-html

         :with-title t
         :section-numbers nil
         :headline-levels 4
         :with-toc nil

         :html-doctype &amp;quot;html5&amp;quot;
         :html-html5-fancy t
         :html-head ,website-html-head
         :html-preamble ,website-html-preamble
         :html-postamble ,website-html-postamble)

        (&amp;quot;website&amp;quot; :components (&amp;quot;blog&amp;quot; &amp;quot;static&amp;quot; &amp;quot;images&amp;quot; &amp;quot;js&amp;quot; &amp;quot;css&amp;quot; &amp;quot;rss&amp;quot;))))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
This snippet defines 7 different ‘projects’ for &lt;code class=&quot;inline&quot;&gt;org-publish&lt;/code&gt;; &lt;code class=&quot;inline&quot;&gt;blog&lt;/code&gt;, &lt;code class=&quot;inline&quot;&gt;images&lt;/code&gt;,
&lt;code class=&quot;inline&quot;&gt;js&lt;/code&gt;, &lt;code class=&quot;inline&quot;&gt;css&lt;/code&gt;, &lt;code class=&quot;inline&quot;&gt;rss&lt;/code&gt;, &lt;code class=&quot;inline&quot;&gt;static&lt;/code&gt;, and the aggregate project &lt;code class=&quot;inline&quot;&gt;website&lt;/code&gt;. When drafting
a blog post, I can hit &lt;code class=&quot;inline&quot;&gt;C-c C-e&lt;/code&gt; to open Org mode’s handy export buffer, then
hit &lt;code class=&quot;inline&quot;&gt;P p&lt;/code&gt; to publish the blog.&lt;/p&gt;
&lt;h1&gt;
Reasons to love blogging with Org Mode&lt;/h1&gt;
&lt;h2&gt;
My editor, my way&lt;/h2&gt;
&lt;p&gt;
I’ve spent years honing my Emacs configuration. I find reading and exiting text
in Emacs to be a pleasure, and I find myself increasingly wanting to do
everything I can inside of Emacs. However, that doesn’t justify why I don’t use
a different format like Markdown.&lt;/p&gt;
&lt;h2&gt;
Portability&lt;/h2&gt;
&lt;p&gt;
Since Org mode and &lt;code class=&quot;inline&quot;&gt;org-publish&lt;/code&gt; are written in Emacs Lisp, I can generate my
blog anywhere where Emacs runs with virtually no hassle.&lt;/p&gt;
&lt;h2&gt;
Single input, multiple documents&lt;/h2&gt;
&lt;p&gt;
Org mode has many different output formats. With a single .org file, you can
generate HTML, LaTeX, OpenDocument, and many other formats. If you want to
generate multiple output files from a single input file, using Org mode makes a
lot of sense. Should I so choose, I could turn my blog into a LaTeX Ebook
without much effort. From what I understand, I think only &lt;a href=&quot;http://pandoc.org/&quot;&gt;Pandoc&lt;/a&gt; rivals Org mode
in terms of compatible file formats. Well, guess what! Org mode supports
exporting to Pandoc with the &lt;code class=&quot;inline&quot;&gt;ox-pandoc&lt;/code&gt; library, so I can use all of Pandoc’s
formats with Org mode files.&lt;/p&gt;
&lt;h2&gt;
Living Inside Emacs&lt;/h2&gt;
&lt;p&gt;
Much like a craftsman’s workshop, a text editor can be very personal and highly
customized. I’ve spent a great deal of time molding both myself and my text
editor to be as unobtrusive as possible. As a result, I prefer working in Emacs
to anything else. At this point, blogging with Org Mode presents very little
friction to me, as I am already accustomed (and addicted) to using Emacs and Org
Mode.&lt;/p&gt;
&lt;h2&gt;
Static HTML&lt;/h2&gt;
&lt;p&gt;
As someone who debugs software for a living, I’ve grown to appreciate
software with as few ‘moving parts’ as possible. Static HTML with a splash of
CSS can give me a simple website that’s (hopefully) beautiful.&lt;/p&gt;
&lt;h1&gt;
Conclusion&lt;/h1&gt;
&lt;p&gt;
Emacs’s fabulous Org mode is extraordinarily flexible. Given my love of Emacs
and Org mode, writing my blog using Org mode and &lt;code class=&quot;inline&quot;&gt;org-publish&lt;/code&gt; seemed like a
great choice. I can’t say I’ve used this system extensively yet, since this is
my first post on this new domain, but I’m excited and hopeful for future posts!&lt;/p&gt;
</content>
  </entry>
</feed>