Distillations2024-03-06T01:44:28+00:00https://jasdev.meJasdev Singhjasdev@jasdev.meQuickly printing to reMarkable2021-03-20T00:00:00+00:00https://jasdev.me/remarkable-quick-print<p>(Assumed audience: folks with a working knowledge of the command line and macOS.)</p>
<h2 id="the-tldr-steps">The tl;dr steps</h2>
<p>You can map ⌘ + P + P — i.e. holding command and hitting P twice — to “print” PDFs directly to your <a href="https://remarkable.com">reMarkable</a> with the following steps:</p>
<ul>
<li>Open terminal and <code class="language-plaintext highlighter-rouge">cd ~</code> (or wherever you prefer to house the <a href="https://github.com/juruen/rmapi"><code class="language-plaintext highlighter-rouge">rmapi</code></a> binary).</li>
<li>Run <code class="language-plaintext highlighter-rouge">curl -L https://github.com/juruen/rmapi/releases/download/v0.0.13/rmapi-macosx.zip -o rmapi.zip</code> in that directory.</li>
<li><code class="language-plaintext highlighter-rouge">unzip rmapi.zip && rm rmapi.zip && ./rmapi</code></li>
<li>
<p>You’ll hit the following prompt:</p>
<p><code class="language-plaintext highlighter-rouge">Enter one-time code (go to …):</code></p>
<p>Head over to <a href="https://my.remarkable.com/">my.remarkable.com</a> ⇒ “Manage Device” ⇒ “Reconnect your reMarkable” ⇒ copy the provided one-time code and paste it back into Terminal.</p>
</li>
<li>If everything goes smoothly, you’ll see an empty shell, after which you can type <code class="language-plaintext highlighter-rouge">exit</code> to quit.</li>
<li>Open <a href="https://support.apple.com/guide/automator/welcome/mac">Automator</a> (pre-installed on macOS).</li>
<li>⌘ + N, select “Print Plugin.”</li>
<li>
<p>Search for the “Run Shell Script” action and drag it into the workflow.</p>
<p><img src="/public/images/remarkable_run_shell_script.png" alt="" /></p>
</li>
<li>
<p>Flip “Pass input:” from to “stdin” to “as arguments.”</p>
<p><img src="/public/images/remarkable_as_arguments.png" alt="" /></p>
</li>
<li>
<p>Replace the action body with the following (and subbing out <code class="language-plaintext highlighter-rouge">~</code> with another directory if you didn’t download <code class="language-plaintext highlighter-rouge">rmapi</code> there).</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>for f in "$@"
do
~/rmapi put "$f"
done
</code></pre></div> </div>
<p>(Note, you can specify an existing folder on your reMarkable for printed PDFs to land by appending a directory after the <code class="language-plaintext highlighter-rouge">put</code> command — e.g. <code class="language-plaintext highlighter-rouge">~/rmapi put "$f" /Inbox</code> or if it has a space in the name, something like <code class="language-plaintext highlighter-rouge">~/rmapi put "$f" /Blog\ Posts</code>.)</p>
</li>
<li>⌘ + S and save the plugin as “Print to reMarkable”.</li>
<li>
<p>Open the Shortcuts tab in the Keyboard system preference pane.</p>
<p><img src="/public/images/keyboard_preference_pane_shorcuts_tab.png" alt="" /></p>
</li>
<li>
<p>Add a new “App Shortcut” tucked under “All Applications” pointing to the menu title “Print to reMarkable” from above. Use ⌘ + P as the shortcut (binding it to the second P in the overall command).</p>
<p><img src="/public/images/print_shortcut.png" alt="" /></p>
</li>
<li>Click “Add” and you should be off to the races! I’ll often toggle Reader Mode in Safari before sending articles, blog posts, or the like to the device.</li>
</ul>
<div style="width:100%;height:0px;position:relative;padding-bottom:122.109%;"><iframe src="https://streamable.com/e/qqi1ss" frameborder="0" width="100%" height="100%" allowfullscreen="" style="width:100%;height:100%;position:absolute;left:0px;top:0px;overflow:hidden;"></iframe></div>
<h2 id="and-some-context">…and some context.</h2>
<p>My reMarkable usage has spanned both ends of the spectrum — there will be weeks where I read from it daily and dry spells from a weekend trip or heavier workload that shifts me out of the routine. Still, a constant has been queueing PDFs with the above shortcut. I’ve always fumbled this setup on new Macs, so this post doubles as finally writing down the steps for a future Jasdev.</p>
<p>reMarkable also offers a <a href="https://chrome.google.com/webstore/detail/read-on-remarkable/bfhkfdnddlhfippjbflipboognpdpoeh?hl=en">Chrome extension</a> for a similar workflow, but it’s of course restricted to Chrome whereas this approach works in any application that supports printing.</p>
<p>This entry is an adaption (and combination) of <code class="language-plaintext highlighter-rouge">rmapi</code>’s tutorial on <a href="https://github.com/juruen/rmapi/blob/ebfdf0e78bd56ff1e0759ef39771b42d84630874/docs/tutorial-print-macosx.md">printing directly to your reMarkable</a> and David Sparks’ post on <a href="https://www.macsparky.com/blog/2008/3/19/keyboard-shortcut-for-save-as-pdf-in-os-x.html">setting a shortcut for “Save as PDF.”</a></p>
<p>■</p>
Finding prime monomials2021-02-28T00:00:00+00:00https://jasdev.me/prime-monomials<p>(Assumed audience: folks with a working knowledge of Swift and <a href="https://en.wikipedia.org/wiki/Modular_arithmetic">modular arithmetic</a>.)</p>
<p><a href="https://twitter.com/aksimhal">Anish</a> nerd sniped me the other day — and while I usually tuck these kinds of posts away in my <a href="/notes/">technical notebook</a>, the problem’s solution threaded so many wonderful topics that it warranted a longer-form entry.</p>
<p>The problem statement is</p>
<blockquote>
<p>A prime number is a natural number greater than one whose only factors are one and itself. An <em>m</em>-digit number is <em>monomial</em> if it uses each of the numbers in the interval $\left[1, m\right]$ exactly once. For example, 2314 is monomial as it is four digits long and uses digits one through four exactly once.</p>
<p>Write a program that finds the largest monomial prime number in under a second (the faster the better).</p>
</blockquote>
<p>Woof. TIL there’s overloaded meaning to “<a href="https://en.wikipedia.org/wiki/Monomial">monomial</a>.<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>” I’ve usually seen it used in the single-term polynomial sense. Let’s sketch out an approach in Swift, assuming we have some helper methods like <code class="language-plaintext highlighter-rouge">BinaryInteger.isPrime</code> and <code class="language-plaintext highlighter-rouge">Collection.permutations</code> in hand and aren’t allowed to parallelize the search.</p>
<script src="https://gist.github.com/jasdev/5b8b83f23cdd2fb73ed6f6473a0f11f8.js"></script>
<p>(<a href="https://gist.github.com/jasdev/5b8b83f23cdd2fb73ed6f6473a0f11f8">Gist permalink</a>.)</p>
<p>First, let’s zoom in on <code class="language-plaintext highlighter-rouge">isPrime</code>. While an <code class="language-plaintext highlighter-rouge">Int</code>-constrained extension could’ve fit the bill since our monomial upper bound — 987654321 — is well below <code class="language-plaintext highlighter-rouge">Int.max</code>, I figured we could preserve generality by allowing <code class="language-plaintext highlighter-rouge">UInt</code> and other <a href="https://developer.apple.com/documentation/swift/binaryinteger"><code class="language-plaintext highlighter-rouge">BinaryInteger</code></a> conformances into the mix.</p>
<p>Primality testing is a whole field in and of itself, so let’s start with a <a href="https://en.wikipedia.org/wiki/Primality_test#Simple_methods">(slightly faster) division-based approach</a> and pull in <a href="https://github.com/apple/swift-algorithms"><code class="language-plaintext highlighter-rouge">swift-algorithms</code></a> for some sequence- and collection-related helpers.</p>
<script src="https://gist.github.com/jasdev/206b0d4fb63d42fe0c57a07f8ad11caf.js"></script>
<p>(<a href="https://gist.github.com/jasdev/206b0d4fb63d42fe0c57a07f8ad11caf">Gist permalink</a>.)</p>
<p>It’s worth pausing on the $6k \pm 1$ optimization because we could’ve simply let <code class="language-plaintext highlighter-rouge">divisors</code> be <code class="language-plaintext highlighter-rouge">(5...)</code> and called it a day (skipping over <code class="language-plaintext highlighter-rouge">4</code> for the lower bound since the multiple of two check would’ve covered that divisor). “All primes must be in the form $6k \pm 1$” is really a statement about the possible congruence classes (remainders) of primes <a href="https://developer.apple.com/documentation/swift/binaryinteger/2885003">modulo</a> $6$. That is, a prime, $p$, must satisfy $p \equiv \pm 1 \;(\bmod\; 6)$. To see why, let’s consider what it would mean if $p$ was congruent to 0, 2, 3, or 4 (the remaining classes):</p>
<ul>
<li>$\equiv 0 \Rightarrow 6 \mid p, \bot$;</li>
<li>$\equiv 2 \Rightarrow \exists k \in \mathbb{Z} \textrm{ such that } p = 6k + 2$<br />
$= 2(3k + 1) \Rightarrow 2 \mid p, \bot$;</li>
<li>$\equiv 3 \Rightarrow \exists k \in \mathbb{Z} \textrm{ such that } p = 6k + 3$<br />
$= 3(2k + 1) \Rightarrow 3 \mid p, \bot$;</li>
<li>$\equiv 4 \Rightarrow \exists k \in \mathbb{Z} \textrm{ such that } p = 6k + 4$<br />
$= 2(3k + 2) \Rightarrow 2 \mid p, \bot$;</li>
</ul>
<p>…they all lead to contradictions (!). This lets us trim down the <code class="language-plaintext highlighter-rouge">(5...)</code> divisor search space to $\left[6 \times 1 - 1, 6 \times 1 + 1, 6 \times 2 - 1, 6 \times 2 + 1, …\right]$. And to achieve this, we start with <code class="language-plaintext highlighter-rouge">6 * 1 - 1</code> and alternating-ly — pretend that’s a word hah — add <code class="language-plaintext highlighter-rouge">2</code> and <code class="language-plaintext highlighter-rouge">4</code> by <code class="language-plaintext highlighter-rouge">cycle</code>ing those offsets with <code class="language-plaintext highlighter-rouge">reductions</code><sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup> to get a running total.</p>
<p>Now to fill in the the <code class="language-plaintext highlighter-rouge">Collection.permutations</code> assumption. Prior art for implementing such a method seems to be <a href="https://alistairisrael.wordpress.com/2009/09/22/simple-efficient-pnk-algorithm/">Jeffrey Johnson’s SEPA permutation algorithm</a> and after some searching around, I noticed it’s thankfully <a href="https://github.com/apple/swift-algorithms/blob/7ca12bed60374858224bf82fef0e51d3cd78a031/Guides/Permutations.md">included</a> in <code class="language-plaintext highlighter-rouge">swift-algorithms</code> — phew. So, with that other base covered by the <code class="language-plaintext highlighter-rouge">Algorithms</code> import for <code class="language-plaintext highlighter-rouge">isPrime</code>, we can give our original monomial search scaffolding a speed test with <a href="https://github.com/google/swift-benchmark"><code class="language-plaintext highlighter-rouge">swift-benchmark</code></a> (also asserting that a consistent <code class="language-plaintext highlighter-rouge">largestPrimeMonomial</code> value is set).</p>
<script src="https://gist.github.com/jasdev/7b1ce20fe15c68c419b52955f1b85da5.js"></script>
<p>(<a href="https://gist.github.com/jasdev/7b1ce20fe15c68c419b52955f1b85da5">Gist permalink</a>.)</p>
<p>Hmmm, the approach clocks in at an average of 1.005 seconds across 25 runs, just above the question’s requirements. Sans squeezing out performance by dipping below <code class="language-plaintext highlighter-rouge">String</code>s to lower-level representations (since we’re squarely in ASCII territory) or beefing up <code class="language-plaintext highlighter-rouge">isPrime</code>, let’s see if we can trim the <code class="language-plaintext highlighter-rouge">ms</code> we consider in the first place.</p>
<p>An invariant we can lean on is the monomial-ness. Since each digit in $\left[1, m\right]$ is used exactly once, maybe the sum of the digits involved can reveal their hand and allow us to rule out specific $m$ lengths entirely?</p>
<p><em>Any</em> base-ten number, $a$, can be expanded out as follows (with $a_i$ representing the digit at the $i$th position):</p>
\[\begin{aligned}
a &= {a_n \cdot 10^n + … + a_2 \cdot 10^2 + a_1 \cdot 10^1 + a_0 \cdot 10^0} \\
&= {a_n \cdot (99…99) + … + a_2 \cdot 99 + a_1 \cdot 9 + a_0 \cdot 0 + \sum_{i=0}^n a_i} \\
&= {9(a_n \cdot (11…11) + … + a_2 \cdot 11 + a_1 \cdot 1 + a_0 \cdot 0) + \sum_{i=0}^n a_i} \\
&\Rightarrow a \equiv \sum_{i=0}^n a_i \;(\bmod\; 9)
\end{aligned}\]
<p>Or in prose, base-ten numbers are congruent to the sum of their digits modulo 9. This is huge — I smiled like a goof when I saw the proof sketched out in a <a href="https://math.stackexchange.com/a/99732">Mathematics Stack Exchange answer</a>.</p>
<p>Back in Swift land, we defined <code class="language-plaintext highlighter-rouge">ms</code> to be <code class="language-plaintext highlighter-rouge">(3...9).reversed()</code>. With the above congruence in hand, we can start knocking out candidate $m$ values (assume $M_i$ is the set of all monomials of length $i$ and $m_k$ is the digit in the $k$th position of the monomial).</p>
\[\begin{aligned}
i = 3 & \Rightarrow {\forall m \in M_3, \sum_{k=1}^3 m_k = \sum_{k=1}^3 k = 6 \equiv m \;(\bmod\; 9)} \\
& \Rightarrow 3 \mid m \textrm{ (by a similar argument to the one used in \texttt{isPrime})}\\
& \Rightarrow m \textrm{ can’t be prime.}
\end{aligned}\]
<p>Repeating this, we can rule out all but <code class="language-plaintext highlighter-rouge">4</code> and <code class="language-plaintext highlighter-rouge">7</code> as $m$-lengths possibly containing a prime. Or, in Swift, we can <code class="language-plaintext highlighter-rouge">filter</code> out members of <code class="language-plaintext highlighter-rouge">ms</code> with <em>digit sums</em> that are congruent to 0, 3, or 6 modulo 9 — the classes that aren’t <a href="https://en.wikipedia.org/wiki/Coprime_integers">relatively prime</a> with 9 itself, since that’d imply that monomials of that length contain no primes.</p>
<p>Adding that change in and re-running the suite speeds things up…dramatically.</p>
<script src="https://gist.github.com/jasdev/f5502dbd51ac3bd64507d7ffec30de8a.js"></script>
<p>(<a href="https://gist.github.com/jasdev/f5502dbd51ac3bd64507d7ffec30de8a">Gist permalink</a>.)</p>
<p>Woot! There we go. A ~100% decrease in runtime. This is consistent with the <code class="language-plaintext highlighter-rouge">7_652_413</code> <code class="language-plaintext highlighter-rouge">largestPrimeMonomial</code> value we asserted on earlier, since its $m$ length is in one of the two possible remaining congruence classes that could contain primes and, since we check it before <code class="language-plaintext highlighter-rouge">4</code>, we’re able to break <code class="language-plaintext highlighter-rouge">outerLoop</code> early.</p>
<p>So, long story short, 7,652,413 is the largest monomial prime<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>!</p>
<p>We could call it a day, but, it turns out that there’s something tangental, yet deeper, going on when we were able to rule out all congruence classes sans $\pm 1$ modulo 6 in <code class="language-plaintext highlighter-rouge">isPrime</code> and sans 4, 7 modulo 9 from <code class="language-plaintext highlighter-rouge">ms</code>. That result is (<em>sending with Lasers effect</em>)…</p>
<h2 id="dirichlets-theorem">Dirichlet’s theorem</h2>
<p>I don’t quite have the background needed to understand the proof of <a href="https://en.wikipedia.org/wiki/Dirichlet%27s_theorem_on_arithmetic_progressions">Dirichlet’s theorem</a> — <em>shakes fist at younger Jasdev’s decision not to take <a href="https://en.wikipedia.org/wiki/Complex_analysis">complex analysis</a></em><sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup> — still, I can provide an introduction (and also link out 3Blue1Brown’s wonderful <a href="https://youtu.be/EK32jo7i5LQ?t=886">treatment of the topic</a>).</p>
<p>Earlier we noted that primes must be $\pm 1$ modulo 6 and 4 or 7 modulo 9 or as you might’ve guessed, primes must fall into congruence classes that are <em>coprime</em> to the modulus at hand. Because if they weren’t, that’d imply the prime shares a divisor with the modulus, contradicting it being a prime in the first place. Dirichlet’s theorem takes this observation a further and proves that infinitely many primes fall into each of these congruence classes. Or in probability terms, the probability that a given prime, $p$, is congruent to some $a$ modulo $n$ where $a$ and $n$ are coprime is $\frac {1}{\varphi(d)}$, where $\varphi(d)$ is <a href="https://en.wikipedia.org/wiki/Euler%27s_totient_function">Euler’s totient function</a>, which counts the positive integers up to $d$ that are relatively prime to it. Pairing this with the <a href="https://en.wikipedia.org/wiki/Euclid%27s_theorem">infinitude of primes</a> implies that the primes are uniformly distributed across those congruence classes modulo $d$ (!).</p>
<p>(…but really, go check out the timestamped 3Blue1Brown link above for a visual overview because text doesn’t quite do it justice.)</p>
<p>I’ll pause here. Hopefully once I’ve learned enough complex analysis to understand the theorem’s proof, I’ll try to bring it down from orbit and into another post.</p>
<p>Until then.</p>
<p>■</p>
<hr />
<p>Special thanks to <a href="https://twitter.com/nnnnnnnn">Nate</a>, <a href="https://twitter.com/khanlou">Soroush</a>, and <a href="https://twitter.com/DeFrenZ">Davide</a> for workshopping an early version of <code class="language-plaintext highlighter-rouge">BinaryInteger.isPrime</code>. Also to <a href="https://twitter.com/danielctull">Daniel</a> for <a href="https://github.com/apple/swift-algorithms/pull/46">his work</a> on <code class="language-plaintext highlighter-rouge">Algorithms.Sequence.reductions(_:_:)</code> and another round of thanks to Nate for <a href="https://github.com/apple/swift-algorithms/blob/1e761dd787b0f148f0b7aec42a7ff401767c26fa/Guides/Cycle.md"><code class="language-plaintext highlighter-rouge">.Collection.cycled(times:)</code></a> and <a href="https://github.com/apple/swift-algorithms/blob/1e761dd787b0f148f0b7aec42a7ff401767c26fa/Guides/Permutations.md"><code class="language-plaintext highlighter-rouge">.Collection.permutations(ofCount:)</code></a> in the same package.</p>
<h2 id="footnotes">Footnotes</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>Turns out this question mirrors <a href="https://projecteuler.net/problem=41">Project Euler #41</a>, where the problem is phrased as “finding the largest <em>pandigital</em> prime.” <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p><code class="language-plaintext highlighter-rouge">swift-algorithm</code>’s <code class="language-plaintext highlighter-rouge">Sequence.reductions(_:_:)</code> carries the baton from an early revision of SE-0045 that included it under the <code class="language-plaintext highlighter-rouge">scan</code> naming, but was <a href="https://github.com/apple/swift-evolution/blob/d62d4ef738acbc7da0dcf94f1d0b832c42215fba/proposals/0045-scan-takewhile-dropwhile.md#previous-versions">ultimately nixed</a> because of low utility. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>Other variants of this problem expand the digit range from <code class="language-plaintext highlighter-rouge">(1...m)</code> to <code class="language-plaintext highlighter-rouge">(0...m)</code>. I’ll leave this variant as an exercise for the dedicated reader. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:4" role="doc-endnote">
<p>Timely enough, Rochard Borcherds started a <a href="https://www.youtube.com/playlist?list=PL8yHsr3EFj537_iYA5QrvwhvMlpkJ1yGN">series on the topic</a>. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Generating captions2021-02-06T00:00:00+00:00https://jasdev.me/generating-captions<p>(Assumed audience: folks familiar with Combine and optionally, Point-Free’s <a href="https://github.com/pointfreeco/swift-composable-architecture"><code class="language-plaintext highlighter-rouge">swift-composable-architecture</code></a> and <a href="https://github.com/pointfreeco/swift-parsing"><code class="language-plaintext highlighter-rouge">-parsing</code></a> packages.)</p>
<hr />
<p>While <a href="https://twitter.com/justRIFF">RIFF</a> wasn’t quite ready for App Store prime time while I worked on it, it was our responsibility as a primarily audio-based app to make sure the eventual launch was accessible for the Deaf and Hard of Hearing community<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>.</p>
<p>And in hopes of making captions generation more widely practiced, I wanted to write through an approach — but first, some context on the status quo of captions files.</p>
<h2 id="srt-files">SRT files</h2>
<p>A decent chuck of RIFFs are cross-posted to Twitter and the service’s captions file format of choice is <a href="https://en.wikipedia.org/wiki/SubRip#File_format">SubRip Text</a> (<code class="language-plaintext highlighter-rouge">.srt</code>, for short).</p>
<blockquote>
<p><a href="https://media.twitter.com/en_us/articles/blogs/2019/subtitles-now-available-in-ios-and-android.html">Subtitles, as we’re defining them, are transcripts of a video’s dialogue or audio contained in .srt files that are attached to videos through either Media Studio, ads.twitter.com, or via the API.</a></p>
</blockquote>
<p>Thankfully, SubRip Text files are relatively straightforward to generate, and in turn, parse.</p>
<p>The plaintext format is as follows:</p>
<ul>
<li>Subtitle groups are sequentially numbered from 1.</li>
<li>On the next line, the timecode that the subtitle should appear, followed by a <code class="language-plaintext highlighter-rouge">--></code> separator, and the timecode for disappearance.
<ul>
<li>Timecodes are formatted in hours:minutes:seconds,milliseconds with two zero-padded digits for the hours, minutes, and seconds and three for the milliseconds component (i.e. <code class="language-plaintext highlighter-rouge">hh:mm:ss,SSS</code>).</li>
</ul>
</li>
<li>The subtitle text on one or more lines.</li>
<li>A blank line, indicating the end of the subtitle group.</li>
<li>(Repeating the above for as many groups as needed.)</li>
</ul>
<p>Here’s an example (from the end of an old <a href="https://twitter.com/jasdev/status/1283222225810796545">Mama Singh</a> voicemail):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(…tail of file.)
97
00:00:32,189 --> 00:00:32,490
OK
98
00:00:33,060 --> 00:00:33,329
love
99
00:00:33,329 --> 00:00:33,509
you
100
00:00:33,509 --> 00:00:33,740
take
101
00:00:33,740 --> 00:00:33,910
care
102
00:00:33,910 --> 00:00:34,289
bye
</code></pre></div></div>
<p>Apple’s <a href="https://developer.apple.com/documentation/speech"><code class="language-plaintext highlighter-rouge">Speech</code> framework</a> reports back transcription segments as an array of <a href="https://developer.apple.com/documentation/speech/sftranscriptionsegment#"><code class="language-plaintext highlighter-rouge">SFTranscriptionSegment</code></a>s — we’ll get to requesting those in a bit, but let’s start with the assumption that we have a collection of them in hand and want to write a function from <code class="language-plaintext highlighter-rouge">([SFTranscriptionSegment]) -> String</code>, where the retuned string is SRT-formatted. Here’s some scaffolding we’ll work under:</p>
<script src="https://gist.github.com/jasdev/6b0b1510ba0dead45cdca13b2bac07c9.js"></script>
<p>(<a href="https://gist.github.com/jasdev/6b0b1510ba0dead45cdca13b2bac07c9">Gist permalink</a>.)</p>
<p>In prose, we’re,</p>
<ul>
<li>enumerating <code class="language-plaintext highlighter-rouge">segments</code> — and shifting the offset down one — to construct the subtitle group sequence numbers,</li>
<li>assuming we have a function <code class="language-plaintext highlighter-rouge">subRipTimingLine</code> in hand that’ll generate the <code class="language-plaintext highlighter-rouge">--></code>-separated timecodes,</li>
<li>tacking on <code class="language-plaintext highlighter-rouge">SFTranscriptionSegment.substring</code> (the transcription <code class="language-plaintext highlighter-rouge">Speech</code> relays back to us),</li>
<li>and finally joining each group’s lines together, then joining all groups with blank lines.</li>
</ul>
<p>Let’s fill in that <code class="language-plaintext highlighter-rouge">subRipTimingLine</code> gap.</p>
<script src="https://gist.github.com/jasdev/f35d690b67eb122ff7066efe014c0ba8.js"></script>
<p>(<a href="https://gist.github.com/jasdev/f35d690b67eb122ff7066efe014c0ba8">Gist permalink</a>.)</p>
<p>That was…a lot. Thankfully <code class="language-plaintext highlighter-rouge">subRipTimeIntervalFormatter</code> can do the zero-padding for us in the hours, minutes, and seconds positions (over at <code class="language-plaintext highlighter-rouge">(1)</code>) and we can hand roll it for the milliseconds value over at <code class="language-plaintext highlighter-rouge">(2)</code>. <code class="language-plaintext highlighter-rouge">time.truncatingRemainder(dividingBy: 1)</code> returns <code class="language-plaintext highlighter-rouge">time</code>’s fractional component, which can then be shifted up three spots by multiplying by <code class="language-plaintext highlighter-rouge">1_000</code>, rounded, converted to a string, and then take <em>up to</em> the first three characters of that final <code class="language-plaintext highlighter-rouge">Int</code>-turned-<code class="language-plaintext highlighter-rouge">String</code>. From there, we pad <code class="language-plaintext highlighter-rouge">millisecondsDigits - milliseconds.count</code> many zeros and then append it onto the formatted string from <code class="language-plaintext highlighter-rouge">subRipTimeIntervalFormatter</code>.</p>
<p>Which brings us to the question I punted earlier: how do we get an array <code class="language-plaintext highlighter-rouge">SFTranscriptionSegment</code>s from <code class="language-plaintext highlighter-rouge">Speech</code> in the first place?</p>
<h2 id="speech-recognition-requests">Speech recognition requests</h2>
<p>The framework packs a <a href="https://developer.apple.com/documentation/speech/sfspeechrecognitionrequest"><code class="language-plaintext highlighter-rouge">SFSpeechRecognitionRequest</code></a> base class for recognition requests and two subclasses: <a href="https://developer.apple.com/documentation/speech/sfspeechaudiobufferrecognitionrequest"><code class="language-plaintext highlighter-rouge">SFSpeechAudioBufferRecognitionRequest</code></a> and <a href="https://developer.apple.com/documentation/speech/sfspeechurlrecognitionrequest"><code class="language-plaintext highlighter-rouge">SFSpeechURLRecognitionRequest</code></a>. The former transcribes live audio and the latter, existing audio files — since RIFF recording is backed by <a href="https://developer.apple.com/documentation/avfoundation/avaudiorecorder"><code class="language-plaintext highlighter-rouge">AVAudioRecorder</code></a>, which requires an on-disk location for the final audio file, we’ll step through URL-backed recognition requests.</p>
<script src="https://gist.github.com/jasdev/ac104ddc9e189eb7e4d805e4ba2fc286.js"></script>
<p>(<a href="https://gist.github.com/jasdev/ac104ddc9e189eb7e4d805e4ba2fc286">Gist permalink</a>.)</p>
<p>(No sweat if you’re more familiar with Combine, proper, and not Point-Free’s <a href="https://github.com/pointfreeco/swift-composable-architecture/blob/11f215c3b6923dfcccec6d1de6a672963a0d3c6e/Sources/ComposableArchitecture/Effect.swift"><code class="language-plaintext highlighter-rouge">Effect</code> wrapper type</a> over it. You can read <code class="language-plaintext highlighter-rouge">Effect<Output, Failure></code> as <code class="language-plaintext highlighter-rouge">AnyPublisher<Output, Failure></code> and <a href="https://github.com/pointfreeco/swift-composable-architecture/blob/11f215c3b6923dfcccec6d1de6a672963a0d3c6e/Sources/ComposableArchitecture/Effect.swift#L66-L103"><code class="language-plaintext highlighter-rouge">Effect.future</code></a> as a <a href="https://developer.apple.com/documentation/combine/deferred"><code class="language-plaintext highlighter-rouge">Deferred</code></a> <a href="https://developer.apple.com/documentation/combine/future"><code class="language-plaintext highlighter-rouge">Future</code></a>, in the usual sense.)</p>
<p>We start off by guarding against either a <code class="language-plaintext highlighter-rouge">nil</code> <code class="language-plaintext highlighter-rouge">SFSpeechRecognizer</code> (the initializer can <code class="language-plaintext highlighter-rouge">nil</code> out if “<a href="https://developer.apple.com/documentation/speech/sfspeechrecognizer/1649878-init">if the user’s default language is not supported for speech recognition</a>”) and check if the recognizer is available, which is flipped to <code class="language-plaintext highlighter-rouge">true</code> after pinging <a href="https://developer.apple.com/documentation/speech/sfspeechrecognizer/1649892-requestauthorization"><code class="language-plaintext highlighter-rouge">SFSpeechRecognizer.requestAuthorization</code></a> and permissions are granted (we’ll get to this in a bit).</p>
<p>Lastly, we construct the request with the <code class="language-plaintext highlighter-rouge">audioURL</code> argument and kick off the recognition task. For RIFF’s case, we only need to final transcription, but if you’d like intermediate transcription results, flipping <a href="https://developer.apple.com/documentation/speech/sfspeechrecognitionrequest/1649392-shouldreportpartialresults"><code class="language-plaintext highlighter-rouge">SFSpeechURLRecognitionRequest.shouldReportPartialResults</code></a> to <code class="language-plaintext highlighter-rouge">true</code> will pipe them through the completion handler.</p>
<p>We’ll give the effect a spin with Point-Free’s Composable Architecture (abbreviated TCA). However, the logic above is UI-agnostic (sans <code class="language-plaintext highlighter-rouge">Effect</code> being included in TCA’s framework), so it can be dropped into a vanilla SwiftUI-and-Combine or UIKit-backed app <a href="https://gist.github.com/jasdev/da2ab0baf3c0482122336b8c604bcb5e">with minimal changes</a>.</p>
<p>Here’s a recording of <a href="/public/CaptionsGeneration.zip">a sample project</a> with <code class="language-plaintext highlighter-rouge">transcriptionEffect</code> in action.</p>
<div style="width:100%;height:0px;position:relative;padding-bottom:216.704%;"><iframe src="https://streamable.com/e/yv3c57?autoplay=1" frameborder="0" width="100%" height="100%" allowfullscreen="" allow="autoplay" style="width:100%;height:100%;position:absolute;left:0px;top:0px;overflow:hidden;"></iframe></div>
<p>There’s two bits of the project to focus in on in <code class="language-plaintext highlighter-rouge">ContentView.swift</code>: the reducer calling <code class="language-plaintext highlighter-rouge">SFSpeechRecognizer.requestAuthorization</code> when <code class="language-plaintext highlighter-rouge">Action.onAppear</code> is dispatched and how to call <code class="language-plaintext highlighter-rouge">transcriptionEffect</code> with a bundled audio file while making sure it’s subscribed to <em>off</em> the main thread and that results are returned back on that same thread for the reducer to handle.</p>
<script src="https://gist.github.com/jasdev/bdba31cd7f19d02a883c15a1b3bff80e.js"></script>
<p>(<a href="https://gist.github.com/jasdev/bdba31cd7f19d02a883c15a1b3bff80e">Gist permalink</a>.)</p>
<p>⬦</p>
<p>…aaaand there we have it — this sketch of captions generation and the above <a href="/public/CaptionsGeneration.zip">sample project</a> should hopefully help folks building audio-based apps with SRT file generation. It’s a baseline level of accessibility I’d love to see met more often.</p>
<p>For learning’s sake, let’s wrap up by writing an SRT <em>parser</em> to check that <code class="language-plaintext highlighter-rouge">transcriptionEffect</code>’s output is formatted to spec.</p>
<h3 id="srt-parsing">SRT parsing</h3>
<p>In compositional parsing fashion — which definitely isn’t high fashion — , let’s start with the most involved piece, the time code line, and then glue it together with the remaining bits.</p>
<p>The timecode format string is <code class="language-plaintext highlighter-rouge">hh:mm:ss,SSS --> hh:mm:ss,SSS</code>. Or, in plain English, two zero-padded hours, minutes, and seconds digits followed by a comma, a three digit, zero-padded milliseconds component, <code class="language-plaintext highlighter-rouge">--></code>, and then repeating the timecode part once more.</p>
<p>Let’s start with the zero-padded two- and three-digit numbers (flipping <code class="language-plaintext highlighter-rouge">isSigned</code> to <code class="language-plaintext highlighter-rouge">false</code> disallows leading <code class="language-plaintext highlighter-rouge">-</code> or <code class="language-plaintext highlighter-rouge">+</code> signs).</p>
<script src="https://gist.github.com/jasdev/10b020d235aee27ee86cf2c0afc4cc34.js"></script>
<p>(<a href="https://gist.github.com/jasdev/10b020d235aee27ee86cf2c0afc4cc34">Gist permalink</a>.)</p>
<p>And now splitting them across literal parsers for the <code class="language-plaintext highlighter-rouge">:</code> and <code class="language-plaintext highlighter-rouge">,</code> separators and repeating twice around the <code class="language-plaintext highlighter-rouge">--></code> arrow.</p>
<script src="https://gist.github.com/jasdev/009695f6865774884e60b3a63f3ed86e.js"></script>
<p>(<a href="https://gist.github.com/jasdev/009695f6865774884e60b3a63f3ed86e">Gist permalink</a>.)</p>
<p>Ah! Almost forgot to <code class="language-plaintext highlighter-rouge">map</code> on <code class="language-plaintext highlighter-rouge">timecodeParser</code> to squash <code class="language-plaintext highlighter-rouge">(Int, Int, Int, Int)</code> down to a <code class="language-plaintext highlighter-rouge">TimeInterval</code> value we can roll into a <code class="language-plaintext highlighter-rouge">SubtitleGroup</code> type that we’ll introduce soon.</p>
<script src="https://gist.github.com/jasdev/638aae403661a5fee8afa725f4770052.js"></script>
<p>(<a href="https://gist.github.com/jasdev/638aae403661a5fee8afa725f4770052">Gist permalink</a>.)</p>
<p>Much better.</p>
<p>Now we can zoom back out and start parsing subtitle groups at a time.</p>
<script src="https://gist.github.com/jasdev/79f23abd4f2d15893d00aafd268fae41.js"></script>
<p>(<a href="https://gist.github.com/jasdev/79f23abd4f2d15893d00aafd268fae41">Gist permalink</a>.)</p>
<p>Oof. <code class="language-plaintext highlighter-rouge">NewLine</code> <a href="https://github.com/pointfreeco/swift-parsing/blob/dd2e6f2e8e7ca0211e81bf790044a314602fd7dd/Sources/Parsing/Parsers/Newline.swift#L7">is constrained to UTF8 code units</a>; so, we’ll need to tee up our parsers to work with that constraint (note the added <code class="language-plaintext highlighter-rouge">.utf8</code>s in the snippet below).</p>
<script src="https://gist.github.com/jasdev/59342c18a99b2c5b3c797e7fac37ba43.js"></script>
<p>(<a href="https://gist.github.com/jasdev/59342c18a99b2c5b3c797e7fac37ba43">Gist permalink</a>.)</p>
<p>Onto the last two parts of each subtitle group: the caption itself and the double-newline separator. To package each group, let’s introduce a <code class="language-plaintext highlighter-rouge">SubtitleGroup</code> struct.</p>
<script src="https://gist.github.com/jasdev/68979b0e302fc1d46ca2c20f1fe68b7b.js"></script>
<p>(<a href="https://gist.github.com/jasdev/68979b0e302fc1d46ca2c20f1fe68b7b">Gist permalink</a>.)</p>
<p>Woot woot. We can finally repeat <code class="language-plaintext highlighter-rouge">srtGroupParser</code> with <code class="language-plaintext highlighter-rouge">Many</code>’s help and check that we’ve consumed all input with a trailing <code class="language-plaintext highlighter-rouge">.skip(End())</code>.</p>
<script src="https://gist.github.com/jasdev/049780b80c79cbf58a5361a477b0e2ec.js"></script>
<p>(<a href="https://gist.github.com/jasdev/049780b80c79cbf58a5361a477b0e2ec">Gist permalink</a>.)</p>
<p>We could stop here after checking it parses out some sample SRT-formatted strings. But, we let’s add a few validation checks to make sure sequence numbers are in <code class="language-plaintext highlighter-rouge">(1...)</code> form and that <code class="language-plaintext highlighter-rouge">startTimecode</code>s are strictly less than <code class="language-plaintext highlighter-rouge">endTimecode</code>s <em>within each group</em> (i.e. a caption group must have a positive presentation time) and <em>adjacent groups</em> have nondecreasing timecodes (a subsequent group shouldn’t overlap with — or come before — another).</p>
<script src="https://gist.github.com/jasdev/4f5f53d3f6f9ca977bcead97ae7b34c6.js"></script>
<p>(<a href="https://gist.github.com/jasdev/4f5f53d3f6f9ca977bcead97ae7b34c6">Gist permalink</a>.)</p>
<p>⬦</p>
<p>I’ll leave writing tests as an exercise for the reader — but, if you’ve made it through these 1.2k+ words, please take a break before then! You’ve more than earned it.</p>
<p>Until next time.</p>
<p>■</p>
<hr />
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>Maya Gold’s <a href="https://twitter.com/mb/status/1273994053668012034">apology for the lack of accessibility in audio tweets</a> was an incredibly honest and powerful example of how to admit responsibility that more of our industry should learn from. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
An inclusive overload of Publisher.prefix(while:)2021-01-17T00:00:00+00:00https://jasdev.me/prefix-inclusive<p>(Assumed audience: folks familiar with Combine.)</p>
<p>Combine ships with handful of prefixing operators:</p>
<ul>
<li><a href="https://developer.apple.com/documentation/combine/publisher/prefix(_:)"><code class="language-plaintext highlighter-rouge">Publisher.prefix(_:)</code></a> for a bounded number of elements,</li>
<li><a href="https://developer.apple.com/documentation/combine/publisher/prefix(while:)"><code class="language-plaintext highlighter-rouge">.prefix(while:)</code></a> — and a <a href="https://developer.apple.com/documentation/combine/publisher/tryprefix(while:)"><code class="language-plaintext highlighter-rouge">throw</code>ing variant</a> — for consecutive elements passing the provided <code class="language-plaintext highlighter-rouge">while</code> predicate,</li>
<li><a href="https://developer.apple.com/documentation/combine/publisher/prefix(untiloutputfrom:)"><code class="language-plaintext highlighter-rouge">.prefix(untilOutputFrom:)</code></a> for prefixing until another sequence publishes an element (which ended up being the backbone for <a href="/prefix-duration-notes"><code class="language-plaintext highlighter-rouge">CombineExt.Publisher.prefix(duration:)</code></a>).</li>
</ul>
<p>We’re going to focus in on the second overload: prefixing along a predicate.</p>
<p>For the uninitiated, “<a href="https://en.wikipedia.org/wiki/Predicate_(mathematical_logic)">predicate</a>” is five-dollar speak for the less-abstract idea of assigning <code class="language-plaintext highlighter-rouge">true</code> or <code class="language-plaintext highlighter-rouge">false</code> to some value. That is, a function from a type <code class="language-plaintext highlighter-rouge">A</code> to <code class="language-plaintext highlighter-rouge">Bool</code>.</p>
<p>A predicate to check for even integers? <code class="language-plaintext highlighter-rouge">let isEven = { $0 % 2 == 0 }</code>.</p>
<p>A predicate to filter in on administrator <code class="language-plaintext highlighter-rouge">User</code> models? <code class="language-plaintext highlighter-rouge">let admins = users.filter(\.isAdmin)</code>.</p>
<p>Yet, when it comes to prefixing, there are instances where we want to include the first element that <em>didn’t</em> pass the predicate. Think a publisher of valid game moves that are prefixed until an invalid one is made and rendered back to the player. Or intermediary steps of an algorithm being published, followed by completing with a goal state.</p>
<p>To modify Combine’s <code class="language-plaintext highlighter-rouge">Publisher.prefix(while:)</code> method to include the first predicate-failing element, we’ll need to listen in on upstream values, pass through those the predicate succeeds on, and once we find an element that fails, project it downstream before a completion event.</p>
<p>“Listen in on upstream values,” “pass through,” and “project downstream” are all hints at a <code class="language-plaintext highlighter-rouge">flatMap</code> — let’s start there.</p>
<script src="https://gist.github.com/jasdev/0db5153039e2eedb06ed341d07035139.js"></script>
<p>(<a href="https://gist.github.com/jasdev/0db5153039e2eedb06ed341d07035139">Gist permalink</a>.)</p>
<p>First, the case where <code class="language-plaintext highlighter-rouge">value</code> passes <code class="language-plaintext highlighter-rouge">predicate</code>,</p>
<script src="https://gist.github.com/jasdev/01a7497d7a5aa5d747c7abcb76f63773.js"></script>
<p>(<a href="https://gist.github.com/jasdev/01a7497d7a5aa5d747c7abcb76f63773">Gist permalink</a>.)</p>
<p>The <code class="language-plaintext highlighter-rouge">else</code> clause is trickier. We need to project <code class="language-plaintext highlighter-rouge">value</code> downstream , while also signaling upstream to complete successfully — maybe a small wrapper enumeration can encode this case and when <code class="language-plaintext highlighter-rouge">predicate(value)</code> passes.</p>
<script src="https://gist.github.com/jasdev/0d5aae487e5982df83ef31948d4ab393.js"></script>
<p>(<a href="https://gist.github.com/jasdev/0d5aae487e5982df83ef31948d4ab393">Gist permalink</a>.)</p>
<p>Now, rewriting the <code class="language-plaintext highlighter-rouge">prefixInclusive(while:)</code> scaffolding we were working under.</p>
<script src="https://gist.github.com/jasdev/9ef546fd9f535f043ab539128c93ff98.js"></script>
<p>(<a href="https://gist.github.com/jasdev/9ef546fd9f535f043ab539128c93ff98">Gist permalink</a>.)</p>
<p>To get things in compiling order, we’ll need to chain a couple of operators after the <code class="language-plaintext highlighter-rouge">flatMap</code> call to pass through <code class="language-plaintext highlighter-rouge">.isPredicatePassingValueOrIncluded</code> events and complete after an <code class="language-plaintext highlighter-rouge">.end</code> comes through.</p>
<script src="https://gist.github.com/jasdev/ccafe4d67c7cc625488a036eeab5fde7.js"></script>
<p>(<a href="https://gist.github.com/jasdev/ccafe4d67c7cc625488a036eeab5fde7">Gist permalink</a>.)</p>
<p>Now tidying up with type inference’s help (<em>the implementation is dense, so no frets if it takes a few passes for it to click</em>).</p>
<script src="https://gist.github.com/jasdev/cbc4aee0a184d6d4130697139799dad9.js"></script>
<p>(<a href="https://gist.github.com/jasdev/cbc4aee0a184d6d4130697139799dad9">Gist permalink</a>.)</p>
<p>And finally, giving it a spin with the <code class="language-plaintext highlighter-rouge">isEven</code> predicate.</p>
<script src="https://gist.github.com/jasdev/ed80515bc26c38216fb463406c00a15c.js"></script>
<p>(<a href="https://gist.github.com/jasdev/ed80515bc26c38216fb463406c00a15c">Gist permalink</a>.)</p>
<p>Phew. That was a lot. If you’d like to fold this prefix overload into your project, I <a href="https://github.com/CombineCommunity/CombineExt/pull/70">PR’d it</a> to CombineExt with the ability to switch between exclusive and inclusive prefixing (!), taking from <a href="https://github.com/ReactiveX/RxSwift/blob/6717eabbe91113327c4cdb59b954fdaa7e8c034d/RxSwift/Observables/TakeWithPredicate.swift#L100-L107">RxSwift.TakeBahavior’s prior art</a>.</p>
<h2 id="an-alternate-implementation">An alternate implementation</h2>
<p><a href="https://twitter.com/mergesort">Joe Fabisevich</a> asked in a DM:</p>
<blockquote>
<p>And I take it there aren’t other ways to do inclusive prefixing like making a subsequence of the predicate-failing elements and taking the first value?</p>
</blockquote>
<p>Turns out…we can! But, it requires a <code class="language-plaintext highlighter-rouge">CombineExt</code> import to make use of <a href="/multicasting"><code class="language-plaintext highlighter-rouge">Publisher.share(replay:)</code></a>.</p>
<script src="https://gist.github.com/jasdev/9b89edfd8ee1b64f4a144768ab4c00a3.js"></script>
<p>(<a href="https://gist.github.com/jasdev/9b89edfd8ee1b64f4a144768ab4c00a3">Gist permalink</a>.)</p>
<p>■</p>
<hr />
<h2 id="related-links">Related links</h2>
<p>⇒ Rob Mayoff’s answer to “<a href="https://forums.swift.org/t/how-to-make-combines-prefix-operator-inclusive/39216/8">How to make Combine’s <code class="language-plaintext highlighter-rouge">prefix</code> operator inclusive?</a>”</p>
<p>⇒ My <a href="https://github.com/CombineCommunity/CombineExt/pull/23">initial implementation</a> of <code class="language-plaintext highlighter-rouge">CombineExt.ReplaySubject</code> wasn’t quite thread-safe — here’s <a href="https://github.com/CombineCommunity/CombineExt/pull/72#issuecomment-761898018">a PR</a> to fix that (following <a href="https://www.onswiftwings.com/posts/share-replay-operator/#creating-the-replaysubject">Maksym Shcheglov’s lead</a>).</p>
Holding multiple truths2020-09-18T00:00:00+00:00https://jasdev.me/multiple-truths<p>(I’ve been wanting write through more personal topics alongside <a href="https://buttondown.email/letters-to-j/archive">Letters to J</a> and in shorter form than my older posts. That is, reframing Distillations as less of the hardened clay left over from experience and more so <a href="https://jmduke.com/2020/08/16/site-as-clay">clay I can shape</a> as I think things through).</p>
<p>⬦</p>
<p>A dynamic I’m trying to remember — and often forget — is that multiple things can be true at once.</p>
<p>I’m struggling with a seemingly-never-ending surgery recovery while also working on a project with people who’re fostering personal growth I wouldn’t have otherwise realized. A breakup can be deeply saddening in the near-term, yet the right call in the longer-term. We can celebrate individual wins <em>and</em> recognize the dark, macro state of affairs.</p>
<p>My therapist put this as avoiding “0–1 thinking.” Our emotional state doesn’t dipole to either bad (0) or good (1) — it lands somewhere in-between. Twinges of chronic pain that crop up while I’m with friends don’t zero out the otherwise cherished time. There’s agency in acknowledging “yes, this pain sucks and is frustrating and if I could wish it away, I would’ve months ago” to put bounds on the emotion while holding space for other truths in that moment.</p>
A screencast on deriving Publisher from Swift’s Sequence protocol2020-05-25T00:00:00+00:00https://jasdev.me/sequence-to-publisher<p>(Assumed audience: folks with working knowledge of Swift. It’s totally fine if you don’t have the <code class="language-plaintext highlighter-rouge">Sequence</code> protocol memorized, we’ll step through it together.)</p>
<hr />
<iframe width="560" height="315" src="https://www.youtube.com/embed/35rgnqsXtag" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<p>(<a href="https://youtu.be/35rgnqsXtag">YouTube permalink</a>.)</p>
<p>■</p>
<hr />
<h2 id="related-links">Related links</h2>
<p>⇒ Erik Meijer and Brian Beckman’s March ’14 <a href="https://youtu.be/looJcaeboBY?t=2164">Expert to Expert session</a>.</p>
<p>⇒ Justin Spahr-Summers’ June ’14 “<a href="https://www.youtube.com/watch?v=ICNjRS2X8WM&feature=youtu.be&t=358">Future Of ReactiveCocoa</a>” presentation.</p>
<p>⇒ Casey Liss’ “<a href="https://www.caseyliss.com/2019/6/13/building-up-to-combine">Building up to Combine</a>.”</p>
<p>⇒ “<a href="/duals">Deriving reactive from imperative: an introduction to duals</a>.”</p>
<p>⇒ A transcript of Rob Rix’s CocoaConf Columbus ’13 talk, “<a href="https://github.com/robrix/Postmodern-Programming/blob/8c556489635dc6bcb764d1617414cd32583269f7/Postmodern%20Programming.md">Postmodern Programming</a>.”</p>
A publisher temperature primer2020-04-29T00:00:00+00:00https://jasdev.me/publisher-temperature-primer<p>Here’s a fun<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> Combine challenge. What does the following snippet output?</p>
<script src="https://gist.github.com/jasdev/3c3319f7b8180341d3914328dff888bc.js"></script>
<p>(<a href="https://gist.github.com/jasdev/3c3319f7b8180341d3914328dff888bc">Gist permalink</a>.)</p>
<p>Somewhat surprisingly, the value event for <code class="language-plaintext highlighter-rouge">4</code> is missing.</p>
<script src="https://gist.github.com/jasdev/64db76aa84d176862092864de0b85e42.js"></script>
<p>(<a href="https://gist.github.com/jasdev/64db76aa84d176862092864de0b85e42">Gist permalink</a>.)</p>
<p>This is a subtle gotcha when subscribing to publishers—they implicitly have a “temperature.”</p>
<p>When receiving a subscriber, an upstream publisher hands it a <a href="https://developer.apple.com/documentation/combine/subscription"><code class="language-plaintext highlighter-rouge">Subscription</code></a> to work with. The subscription then brokers demand through <a href="https://developer.apple.com/documentation/combine/subscription/3213720-request"><code class="language-plaintext highlighter-rouge">Subscription.request(_:)</code></a> and cancellation by way of inheriting from <a href="https://developer.apple.com/documentation/combine/cancellable"><code class="language-plaintext highlighter-rouge">Cancellable</code></a>. But, that still begs the question, should a publisher “restart” if it receives a new subscriber, even after a completion event? What if the same subscriber attaches twice? Do we doubly send events downstream?</p>
<p>Restarting is helpful when a publisher backs one-off work. Think network requests or logging analytics events. On the other hand, what does it mean for a publisher backing button taps to “restart?”</p>
<p>That’s what temperature delineates.</p>
<p>A cold publisher is one that starts work only when a subscriber is received. It’s a declarative analog to Swift’s <code class="language-plaintext highlighter-rouge">lazy</code> keyword. A hot publisher is one that may send events at any time, regardless of the subscriber count. And <code class="language-plaintext highlighter-rouge">Subject</code> conformances hang out in the warmer climate.</p>
<p>So, let’s return to that snippet.</p>
<p><code class="language-plaintext highlighter-rouge">Publisher.retry</code> “<a href="https://developer.apple.com/documentation/combine/publisher/3204751-retry">attempts to recreate a failed subscription with the upstream publisher</a>.” Let’s perform the second subscription attempt manually to investigate further. We’ll need to lean on <a href="https://developer.apple.com/documentation/combine/publisher/3204756-subscribe"><code class="language-plaintext highlighter-rouge">Publisher.subscribe(_:)</code></a> (the <code class="language-plaintext highlighter-rouge">Subscriber</code>-accepting, not <code class="language-plaintext highlighter-rouge">Subject</code>-based overload), but, we had originally used lowercase-s <code class="language-plaintext highlighter-rouge">sink</code> and called it a day. The method uses a <code class="language-plaintext highlighter-rouge">Subscribers.Sink</code> instance underneath the hood, so we can run with that.</p>
<script src="https://gist.github.com/jasdev/fd93f909e39c360b1a89826c5250f135.js"></script>
<p>(<a href="https://gist.github.com/jasdev/fd93f909e39c360b1a89826c5250f135">Gist permalink</a>.)</p>
<p>We got two failure events‽ Wat.</p>
<p>No, wait—that makes sense. <code class="language-plaintext highlighter-rouge">Publisher.retry</code> <em>suppresses</em> the first error, tries again after the subject becomes inert post-<code class="language-plaintext highlighter-rouge">.failure(.anError)</code> and then sends the lone error downstream.</p>
<p>Manually re-subscribing reveals <code class="language-plaintext highlighter-rouge">Subject</code>’s warmer temperature. A new subscriber coming around didn’t “restart” it and allow the <code class="language-plaintext highlighter-rouge">4</code> to come through. Instead, it’s <em>inactive</em> after the first completion, and per the <code class="language-plaintext highlighter-rouge">Publisher</code> contract.</p>
<p>The choice of “inactive” there is specific. We can peak into Combine and see that they use the same phrasing.</p>
<script src="https://gist.github.com/jasdev/ed05f69fd1426022736b3b7ff091855a.js"></script>
<p>(<a href="https://gist.github.com/jasdev/ed05f69fd1426022736b3b7ff091855a">Gist permalink</a>.)</p>
<p><code class="language-plaintext highlighter-rouge">Subject</code>s keep track of lifecycle state and, if a subscriber shows up after a terminal event, it’s simply told that. Relatedly, if a subscriber is attached to a <code class="language-plaintext highlighter-rouge">CurrentValueSubject</code> post-completion, it also misses out on the last value—<a href="https://github.com/combineCommunity/CombineExt#ReplaySubject"><code class="language-plaintext highlighter-rouge">ReplaySubject</code> fills that gap</a>.</p>
<p>Time to hang with a cooler publisher, <a href="https://developer.apple.com/documentation/foundation/urlsession/3329708-datataskpublisher"><code class="language-plaintext highlighter-rouge">URLSession.dataTaskPublisher(for:)</code></a>.</p>
<p>It overlays <code class="language-plaintext highlighter-rouge">URLSession</code> with a coating of Combine. Now, It’d be weird if we constructed a <a href="https://developer.apple.com/documentation/foundation/urlsession/datataskpublisher"><code class="language-plaintext highlighter-rouge">URLSession.DataTaskPublisher</code></a> instance (the type returned from the method) and it went off and requested resources without anyone listening. In fact, doing so might put us in scenarios where we start network traffic before preconditions like authentication are met.</p>
<p>To show how cool this publisher is, let’s tee it up to fail once then succeed on the second attempt.</p>
<script src="https://gist.github.com/jasdev/3f41d757d9dab846405fb09ac5ac98a8.js"></script>
<p>(<a href="https://gist.github.com/jasdev/3f41d757d9dab846405fb09ac5ac98a8">Gist permalink</a>.)</p>
<p>Close readers might’ve noticed I substituted <code class="language-plaintext highlighter-rouge">AnySubscriber</code> for <code class="language-plaintext highlighter-rouge">Sink</code> compared to our subject double-subscribing. The reason is two-fold. First, <code class="language-plaintext highlighter-rouge">sink</code> (the method) uses a <code class="language-plaintext highlighter-rouge">Sink</code> under the hood. Second (and after some investigation), <code class="language-plaintext highlighter-rouge">Sink</code> maintains a sort of “subscription status” that cancels any subsequent subscriptions after a first is received.</p>
<p>The folks behind OpenCombine <a href="https://github.com/broadwaylamb/OpenCombine/blob/d680f09932fe68942c3d391d6df296d38b9dcd05/Sources/OpenCombine/Subscribers/Subscribers.Sink.swift#L52-L53">noticed this</a>, too.</p>
<p>Concretely, notice how the output changes when we swap back in <code class="language-plaintext highlighter-rouge">Sink</code> (and noting that it isn’t quite synonymous with <code class="language-plaintext highlighter-rouge">AnySubscriber</code> since it requests unlimited demand).</p>
<script src="https://gist.github.com/jasdev/860b2b8c06fa9d74c82ec51919158390.js"></script>
<p>(<a href="https://gist.github.com/jasdev/860b2b8c06fa9d74c82ec51919158390">Gist permalink</a>.)</p>
<p>Which then raises the question, why did the subject example replay that second failure when <code class="language-plaintext highlighter-rouge">coldPublisher</code> didn’t? That’s because warm publishers generally keep track of lifecycle state. If a long-living subject hits a completion event, it knows it can’t further emit values to subscribers, so it hangs onto the completion for future reference. In fact, the <code class="language-plaintext highlighter-rouge">dump(subject)</code> we did before revealed that Combine does just that, with an non-<code class="language-plaintext highlighter-rouge">public</code>, optional <code class="language-plaintext highlighter-rouge">completion</code> property.</p>
<script src="https://gist.github.com/jasdev/85690c8708faed14c434f27486dd5b3a.js"></script>
<p>(<a href="https://gist.github.com/jasdev/85690c8708faed14c434f27486dd5b3a">Gist permalink</a>.)</p>
<p>And our erased <code class="language-plaintext highlighter-rouge">DataTaskPublisher</code> doesn’t keep internal state. We can check this by subscribing two <em>distinct</em> <code class="language-plaintext highlighter-rouge">Sink</code>s (with some delay to prevent overlap).</p>
<script src="https://gist.github.com/jasdev/7015fbb8388203fc5d111b9c857f7693.js"></script>
<p>(<a href="https://gist.github.com/jasdev/7015fbb8388203fc5d111b9c857f7693">Gist permalink</a>.)</p>
<p>Using different <code class="language-plaintext highlighter-rouge">Sink</code>s restarts <code class="language-plaintext highlighter-rouge">coldPublisher</code>. So, how do individual <code class="language-plaintext highlighter-rouge">Sink</code> instances keep state? To answer this, I tried the following for some clues:</p>
<script src="https://gist.github.com/jasdev/22ee41391380af344f7620b7bc007d8b.js"></script>
<p>(<a href="https://gist.github.com/jasdev/22ee41391380af344f7620b7bc007d8b">Gist permalink</a>.)</p>
<p>No <code class="language-plaintext highlighter-rouge">dump</code> or mirrored children? That’s odd. The <a href="https://developer.apple.com/documentation/combine/subscribers/sink#relationships">type’s listed conformances</a> hint at why:</p>
<p><img src="/public/images/sink_custom_reflectable.png" alt="" /></p>
<p><code class="language-plaintext highlighter-rouge">CustomReflectable</code>, huh? I guess—and if you know the reason, please let me know (!)—Apple is trying to hide their implementation here by returning an empty mirroring? We’d at least expect the <code class="language-plaintext highlighter-rouge">@escaping</code> <code class="language-plaintext highlighter-rouge">receiveCompletion</code> and <code class="language-plaintext highlighter-rouge">receiveValue</code> closures to be reflected.</p>
<p>And again, OpenCombine <a href="https://github.com/broadwaylamb/OpenCombine/blob/d680f09932fe68942c3d391d6df296d38b9dcd05/Sources/OpenCombine/Subscribers/Subscribers.Sink.swift#L31-L33">caught this</a>, returning an <a href="https://developer.apple.com/documentation/swift/emptycollection#"><code class="language-plaintext highlighter-rouge">EmptyCollection</code></a> in their <code class="language-plaintext highlighter-rouge">Subscribers.Sink.customMirror</code> implementation (seriously, bravo to <a href="https://twitter.com/broadway_lamb">Sergej</a> and the project’s contributors).</p>
<p>Despite this, we can verify that <code class="language-plaintext highlighter-rouge">Sink</code> keeps track of lifecycle state by subscribing a single instance, twice, to a publisher that finishes the first subscription and see how it affects the second.</p>
<script src="https://gist.github.com/jasdev/b8fcd1bc1dc670a5e651715b43117a91.js"></script>
<p>(<a href="https://gist.github.com/jasdev/b8fcd1bc1dc670a5e651715b43117a91">Gist permalink</a>.)</p>
<p>Aha. The second received subscription is immediately cancelled after it received a finished event from the first.</p>
<p>…that was a winding detour—let’s drive back and recap where we’re at.</p>
<p>Publishers come in two temperatures: hot and cold. But, it’s sometimes tricky to elicit a cold publisher’s “coldness” because <code class="language-plaintext highlighter-rouge">Subscribers.Sink</code> keeps lifecycle state that cancels subsequent subscriptions.</p>
<p>That brings us to another juncture, how do we tell publisher types apart and switch between them?</p>
<h2 id="checking-temperatures">Checking temperatures</h2>
<p>I wish Combine had a better story here because, in short, there isn’t a great way to <em>statically</em> check if a publisher is hot or cold.</p>
<p>Some of Combine’s prior art made the distinction at the type level with <a href="https://github.com/ReactiveCocoa/ReactiveSwift/blob/e27ccdbf4ec36f154b60b91a0d7e0110c4e882cb/Documentation/FrameworkOverview.md#signals"><code class="language-plaintext highlighter-rouge">Signal</code></a> and <a href="https://github.com/ReactiveCocoa/ReactiveSwift/blob/e27ccdbf4ec36f154b60b91a0d7e0110c4e882cb/Documentation/FrameworkOverview.md#signal-producers"><code class="language-plaintext highlighter-rouge">SignalProducer</code></a>, respectively.</p>
<p>This does come with an ergonomic cost, since you then have to juggle output, failure, <em>and</em> temperature types when building pipelines. Still, better to check at compile time instead of running a fever at runtime or in production (sorry, that pun was bad).</p>
<p>Another, more manual approach is to trace any side effects performed by cold publishers. That is, open up <a href="https://proxyman.io">Proxyman</a>, turn on analytics logging, and the like to make sure effects are being run when and how often you expect.</p>
<p>If they aren’t, then it’s time to heat up or cool things down.</p>
<h2 id="heating-and-cooling">Heating and cooling</h2>
<h3 id="hot--cold">Hot ⇒ cold</h3>
<p><code class="language-plaintext highlighter-rouge">Deferred { Future<_, _> { promise in /* … */ } } </code> is a dance you’ll see often in everyday Combine. It cools down an eagerly-evaluated-by-default <a href="https://developer.apple.com/documentation/combine/future#"><code class="language-plaintext highlighter-rouge">Future</code></a> into one that kicks off its <code class="language-plaintext highlighter-rouge">attemptToFulfill</code> closure only when a subscriber is received.</p>
<p>(But even after deferring, there’s a tangential gotcha. <a href="https://developer.apple.com/documentation/combine/deferred"><code class="language-plaintext highlighter-rouge">Deferred</code></a> is a <code class="language-plaintext highlighter-rouge">struct</code>, which means every call site gets its own copy and in turn, creates its own <code class="language-plaintext highlighter-rouge">Future</code>. For reference semantics, you’ll want to tack on a <a href="https://developer.apple.com/documentation/combine/publisher/3204754-share"><code class="language-plaintext highlighter-rouge">share</code></a> operator.)</p>
<p>If you have a subject in hand, it’s not so much about changing temperature—since there’s no “work” backing them—and instead about caching values for future subscribers. <a href="https://developer.apple.com/documentation/combine/currentvaluesubject"><code class="language-plaintext highlighter-rouge">CurrentValueSubject</code></a> will do just that with the latest value and <em>before</em> any completion events are received. To replay more than one value, even after completion, you’ll need to reach for a <code class="language-plaintext highlighter-rouge">ReplaySubject</code>.</p>
<h3 id="cold--hot">Cold ⇒ hot</h3>
<p>Warming up a cold publisher is either a <code class="language-plaintext highlighter-rouge">share</code> or <a href="https://developer.apple.com/documentation/combine/publisher/3204733-multicast"><code class="language-plaintext highlighter-rouge">multicast</code></a> call away. The former simply converts from value to reference semantics, while the latter can be used for more control on how events are projected down to subscribers. In fact, it can even be used to <a href="https://github.com/CombineCommunity/CombineExt/pull/23">implement both <code class="language-plaintext highlighter-rouge">share</code> and <code class="language-plaintext highlighter-rouge">share(replay:)</code></a>.</p>
<p>My <a href="/multicasting">entry on multicasting</a> walks through this.</p>
<p>⬦</p>
<p>Back to our original quiz with what we learned—and the friends we made—along the way.</p>
<p><code class="language-plaintext highlighter-rouge">4</code> isn’t received since <code class="language-plaintext highlighter-rouge">PassthroughSubject</code> is warm and <code class="language-plaintext highlighter-rouge">retry</code>’s attempt to create another subscription is met with a replayed <code class="language-plaintext highlighter-rouge">.failure(.anError)</code> event that then ends the sequence.</p>
<p>A whole lot of detail packed into 25 lines, eh?</p>
<p>■</p>
<hr />
<h2 id="related-reading-and-footnotes">Related reading and footnotes</h2>
<p>⇒ The <a href="https://www.cocoawithlove.com/blog/twenty-two-short-tests-of-combine-part-2.html#hot-and-cold-publishers">“Hot and cold publishers” section</a> of Part II of Matt Gallagher’s three-part Combine series.</p>
<p>⇒ “<a href="https://www.davesexton.com/blog/post/Hot-and-Cold-Observables.aspx">Hot and Cold Observables</a>”</p>
<p>⇒ OpenCombine’s <a href="https://github.com/broadwaylamb/OpenCombine/blob/d680f09932fe68942c3d391d6df296d38b9dcd05/Sources/OpenCombine/Subscribers/Subscribers.Sink.swift">implementation of <code class="language-plaintext highlighter-rouge">Subscribers.Sink</code></a>.</p>
<p>⇒ <em>Understanding Combine</em>’s <a href="https://www.apeth.com/UnderstandingCombine/publishers/publishersfuture.html">section on <code class="language-plaintext highlighter-rouge">Future</code> and <code class="language-plaintext highlighter-rouge">Deferred</code></a>.</p>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>For some <a href="https://sarahcandersen.com/post/184643711649">hyper-specific</a> definition of “fun.” <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
A screencast on time-based prefixing in Combine2020-04-26T00:00:00+00:00https://jasdev.me/prefix-duration-notes<p>(Assumed audience: folks with some working knowledge of Combine.)</p>
<h3 id="updates">Updates:</h3>
<h4 id="61720">6/17/20:</h4>
<p>The test expectations have since been <a href="https://github.com/CombineCommunity/CombineExt/pull/43">swapped out</a> for Point-Free’s <code class="language-plaintext highlighter-rouge">TestScheduler</code> implementation.</p>
<h4 id="42820">4/28/20:</h4>
<p>While <a href="https://github.com/CombineCommunity/CombineExt/pull/27">PR’ing</a> the operator, I looked into whether or not the <code class="language-plaintext highlighter-rouge">prefix(1)</code> safety net is needed and…it isn’t!</p>
<p>Woot.</p>
<p>Combine uses back pressure to request a single value from <code class="language-plaintext highlighter-rouge">untilOutputFrom</code> and the subsequent finished event cancels everything out from there.</p>
<p>To check this, we can spin up a Playground:</p>
<script src="https://gist.github.com/jasdev/f0730d645025ffb5a385766159f34f22.js"></script>
<p>(<a href="https://gist.github.com/jasdev/f0730d645025ffb5a385766159f34f22">Gist permalink</a>.)</p>
<hr />
<p>I recorded my first screencast slash lecture slash live-coding episode thing!</p>
<p>In it—and from <a href="https://twitter.com/ohayon">David</a>’s mentioning in iOS Folks’ #reactive channel—, I implement a time-based overload on <a href="https://developer.apple.com/documentation/combine/publisher/3204737-prefix"><code class="language-plaintext highlighter-rouge">Publisher.prefix</code></a>, while talking through nuances like <a href="/fusion-primer">fusion</a>, <a href="https://developer.apple.com/documentation/combine/connectablepublisher"><code class="language-plaintext highlighter-rouge">ConnectablePublisher</code></a>, and <a href="https://github.com/tcldr/Entwine/blob/2cbad3312aefc95f0cf19ad7b2cc30faba727bcd/Sources/EntwineTest/TestScheduler/TestScheduler.swift"><code class="language-plaintext highlighter-rouge">TestScheduler</code></a>.</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/eRCSJAc2nfk" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<p>(<a href="https://youtu.be/eRCSJAc2nfk">YouTube permalink</a>.)</p>
<p>I hope this encourages folks to experiment with composed operators and, if they see fit, contribute to <a href="https://github.com/combineCommunity/CombineExt">CombineExt</a>.</p>
<p>■</p>
<hr />
<h2 id="related-links">Related links</h2>
<p>⇒ “<a href="/fusion-primer">An operator fusion primer</a>”</p>
<p>⇒ “<a href="/multicasting">Multicasting, Publisher.share(replay:), and ReplaySubject</a>”</p>
<p>⇒ Entwine’s implementation of a <a href="https://github.com/tcldr/Entwine/blob/2cbad3312aefc95f0cf19ad7b2cc30faba727bcd/Sources/EntwineTest/TestScheduler/TestScheduler.swift"><code class="language-plaintext highlighter-rouge">TestScheduler</code></a> type.</p>
<p>⇒ OpenCombine’s <a href="https://github.com/broadwaylamb/OpenCombine/blob/d680f09932fe68942c3d391d6df296d38b9dcd05/Tests/OpenCombineTests/Helpers/VirtualTimeScheduler.swift"><code class="language-plaintext highlighter-rouge">VirtualTimeScheduler</code></a>.</p>
Multicasting, Publisher.share(replay:), and ReplaySubject2020-04-20T00:00:00+00:00https://jasdev.me/multicasting<p>(Assumed audience: folks with conceptual understanding of the <a href="https://developer.apple.com/documentation/combine/subject"><code class="language-plaintext highlighter-rouge">Combine.Subject</code></a> protocol.)</p>
<h3 id="updates">Updates:</h3>
<h4 id="5120">5/1/20:</h4>
<p>There was a subtle inconsistency with how <code class="language-plaintext highlighter-rouge">ReplaySubject</code> handled double subscribes compared to its <code class="language-plaintext highlighter-rouge">CurrentValue-</code> and <code class="language-plaintext highlighter-rouge">Passthrough-</code> counterparts and <a href="https://github.com/CombineCommunity/CombineExt/pull/28">has since been fixed</a>.</p>
<hr />
<p>Or, probably a more fitting title: “how I learned multicasting the hard way and try to write out an easier path, for y’all.”</p>
<p><a href="https://github.com/CombineCommunity/CombineExt/pull/23">Implementing <code class="language-plaintext highlighter-rouge">Publisher.share(replay:)</code> and, in the process, <code class="language-plaintext highlighter-rouge">ReplaySubject</code></a>, threw me in the deep end of multicasting. I learned a lot on the swim down.</p>
<p>Like others on the Swift forums, I used to wonder: “<a href="https://forums.swift.org/t/combine-what-are-those-multicast-functions-for/26677">what’re those <code class="language-plaintext highlighter-rouge">multicast</code> functions for?</a>”</p>
<p>The <a href="https://developer.apple.com/documentation/combine/publisher/3204733-multicast">two</a> <a href="https://developer.apple.com/documentation/combine/publisher/3204734-multicast">overloads</a> are tucked away in the <code class="language-plaintext highlighter-rouge">Publisher</code> namespace and interestingly next to the <del>Cher</del> <a href="https://developer.apple.com/documentation/combine/publisher/3204754-share"><code class="language-plaintext highlighter-rouge">share</code> operator</a>.</p>
<p><code class="language-plaintext highlighter-rouge">share</code>’s documentation hints at why.</p>
<blockquote>
<p>Note that <code class="language-plaintext highlighter-rouge">Publishers.Share</code> is a <code class="language-plaintext highlighter-rouge">class</code> rather than a <code class="language-plaintext highlighter-rouge">struct</code> like most other publishers. This means you can use this operator to create a publisher instance that uses reference semantics.</p>
</blockquote>
<p>Huh, okay, reference semantics—I can nod along to this.</p>
<p>In the same way that instances are shared across all references, shared publishers are only subscribed to <em>once</em>, even with possibly many subscribers.</p>
<p>This is important for effectful publishers. Ones that fire off network requests, read from disk, output logs, or phone home to an analytics service. We wouldn’t want multiple views holding onto the same <code class="language-plaintext highlighter-rouge">ObservableObject</code> kicking off the same unit of work many times. Instead, we’ll often want to subscribe once and <em>broadcast</em> value and completion events to subscribers.</p>
<p>“Broadcast” inches us closer to, well, “multicast.”</p>
<p>Let’s start by implementing <code class="language-plaintext highlighter-rouge">share</code> with <code class="language-plaintext highlighter-rouge">multicast</code>.</p>
<p>To mimic Combine’s operator, we’ll need to subscribe to upstream once and then turn around and relay all events to any number of subscribers.</p>
<p>Here’s some scaffolding we’ll work under.</p>
<script src="https://gist.github.com/jasdev/e9b7c099544c7513415e15e789ed2975.js"></script>
<p>(<a href="https://gist.github.com/jasdev/e9b7c099544c7513415e15e789ed2975">Gist permalink</a>.)</p>
<p>As you probably guessed, the <code class="language-plaintext highlighter-rouge">fatalError</code> placeholder will need to be swapped out with one of the two <code class="language-plaintext highlighter-rouge">multicast</code> overloads.</p>
<p>They differ in the argument they accept. One takes an honest <code class="language-plaintext highlighter-rouge">Subject</code> instance, and the other, a closure that produces one.</p>
<p>Either can be used in our implementation. Yet, the <code class="language-plaintext highlighter-rouge">.multicast(subject:)</code> overload is particularly useful in scenarios where you want to attach upstream to the subject, while also keeping the ability to <a href="https://developer.apple.com/documentation/combine/subject#3233743">imperatively <code class="language-plaintext highlighter-rouge">send</code> on it</a>. For example,</p>
<script src="https://gist.github.com/jasdev/48e306c2e59dbe448620e5c89e3c958a.js"></script>
<p>(<a href="https://gist.github.com/jasdev/48e306c2e59dbe448620e5c89e3c958a">Gist permalink</a>.)</p>
<p>Still, for <code class="language-plaintext highlighter-rouge">cher</code>’s implementation, let’s reach for <code class="language-plaintext highlighter-rouge">multicast(_:)</code> (the non-<code class="language-plaintext highlighter-rouge">Subject</code> instance variant).</p>
<p>It might be tempting to <code class="language-plaintext highlighter-rouge">multicast</code>, update the return type, and call it a day.</p>
<script src="https://gist.github.com/jasdev/fafd68b75bc85a5b1011d979f8821c25.js"></script>
<p>(<a href="https://gist.github.com/jasdev/fafd68b75bc85a5b1011d979f8821c25">Gist permalink</a>.)</p>
<p>We won’t notice what’s wrong until we call <code class="language-plaintext highlighter-rouge">cher</code>.</p>
<script src="https://gist.github.com/jasdev/484933d93bf7de017f80b8ccacc621ba.js"></script>
<p>(<a href="https://gist.github.com/jasdev/484933d93bf7de017f80b8ccacc621ba">Gist permalink</a>.)</p>
<p>Now we can talk through that rogue <code class="language-plaintext highlighter-rouge">autoconnect</code> call in <code class="language-plaintext highlighter-rouge">AddressService.init</code>.</p>
<p>The <code class="language-plaintext highlighter-rouge">multicasting</code> operators—and a couple others<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>—return <a href="https://developer.apple.com/documentation/combine/connectablepublisher"><code class="language-plaintext highlighter-rouge">ConnectablePublisher</code> instances</a>, which inherits from <code class="language-plaintext highlighter-rouge">Publisher</code> and tacks on a <a href="https://developer.apple.com/documentation/combine/connectablepublisher/3204394-connect"><code class="language-plaintext highlighter-rouge">connect</code> method requirement</a>.</p>
<p>Connectable publishers warrant an entry of their own. For short, an intuition is that they allow subscribers to all line up and attach themselves to an upstream, connectable publisher, and any effectful work performed upon subscription is deferred until <code class="language-plaintext highlighter-rouge">connect</code> is called.</p>
<p>Metaphorically, the subscribers can all line up on the track and <code class="language-plaintext highlighter-rouge">connect</code> is the race’s starting pistol. This is particularly helpful when you have multiple subscribers, possibly attaching to upstream at different times, and you want to explicitly trigger subscription after everything is wired up.</p>
<p>But, Cher doesn’t need this behavior and to restore plain ol’ <code class="language-plaintext highlighter-rouge">Publisher</code>ness, we can call <code class="language-plaintext highlighter-rouge">autoconnect</code><sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>. For completeness, <code class="language-plaintext highlighter-rouge">autoconnect</code>ing’s opposite, i.e. taking a plain <code class="language-plaintext highlighter-rouge">Publisher</code> and making it connectable, can be done with <a href="https://developer.apple.com/documentation/combine/publisher/3235803-makeconnectable"><code class="language-plaintext highlighter-rouge">makeConnectable</code></a>.</p>
<p>Here’s <code class="language-plaintext highlighter-rouge">cher</code>’s final performance.</p>
<script src="https://gist.github.com/jasdev/9cade706a68844a00d160042bffce657.js"></script>
<p>(<a href="https://gist.github.com/jasdev/9cade706a68844a00d160042bffce657">Gist permalink</a>.)</p>
<p>Time to mile mark the trail we’ve followed.</p>
<p>We,</p>
<ul>
<li>re-wrote the built-in <code class="language-plaintext highlighter-rouge">Publisher.share</code> operator by way of <code class="language-plaintext highlighter-rouge">multicast</code>, <code class="language-plaintext highlighter-rouge">PassthroughSubject.init</code>, and <code class="language-plaintext highlighter-rouge">autoconnect</code>ing,</li>
<li>talked through <code class="language-plaintext highlighter-rouge">ConnectablePublisher</code>s,</li>
<li>and gave an example where you might want to reach for the <code class="language-plaintext highlighter-rouge">multicast(subject:)</code> instead of <code class="language-plaintext highlighter-rouge">multicast(_:)</code>.</li>
</ul>
<p>That’s a lot! Take a breather, get some water, and when you’re back, we’ll move onto replaying.</p>
<h2 id="replaying">Replaying</h2>
<p>Upgrading <code class="language-plaintext highlighter-rouge">cher</code> to <code class="language-plaintext highlighter-rouge">cherReplayingLatest</code>—we’ll fix the naming, in a bit—<em>might</em> seem a swapping of <code class="language-plaintext highlighter-rouge">PassthroughSubject</code> for <code class="language-plaintext highlighter-rouge">CurrentValueSubject</code> away (with the needed compilation updates).</p>
<script src="https://gist.github.com/jasdev/3b943c654931735f6c338204d58dc3db.js"></script>
<p>(<a href="https://gist.github.com/jasdev/3b943c654931735f6c338204d58dc3db">Gist permalink</a>.)</p>
<p>Even though the operator compiles, it doesn’t behave as we’d expect.</p>
<script src="https://gist.github.com/jasdev/5eee18dfcadf33f4e38f6c0eb7d0c090.js"></script>
<p>(<a href="https://gist.github.com/jasdev/5eee18dfcadf33f4e38f6c0eb7d0c090">Gist permalink</a>.)</p>
<p>Ideally, a <code class="language-plaintext highlighter-rouge">Second sink: 1</code> would be logged, too.</p>
<p>But, <code class="language-plaintext highlighter-rouge">CurrentValueSubject</code> is behaving according to spec. It only replays the current value to subscribers if it <em>hasn’t</em> received a completion event.</p>
<p>And <code class="language-plaintext highlighter-rouge">Just</code> finishes after emitting a value event, rendering the subject inert before <code class="language-plaintext highlighter-rouge">DispatchQueue.asyncAfter</code>’s deadline was met. We can demonstrate this with an even smaller example:</p>
<script src="https://gist.github.com/jasdev/8cf894fd9d62d096038ee0a0289f7972.js"></script>
<p>(<a href="https://gist.github.com/jasdev/8cf894fd9d62d096038ee0a0289f7972">Gist permalink</a>.)</p>
<p>Bummer. To extend <code class="language-plaintext highlighter-rouge">cher</code> to <code class="language-plaintext highlighter-rouge">cherReplayLatest</code>, we couldn’t lean on a <code class="language-plaintext highlighter-rouge">CurrentValueSubject</code> since it doesn’t <em>replay</em> any pre-completion events to subscribers that attach after a completion occurs.</p>
<p>And double-checking to make sure Apple doesn’t already have our backs.</p>
<p><img src="/public/images/replay_subject_search.png" alt="" /></p>
<p><code class="language-plaintext highlighter-rouge">ReplaySubject</code> is prior art from <a href="http://reactivex.io/documentation/subject.html">the ReactiveX spec.</a> (in fact, renaming <code class="language-plaintext highlighter-rouge">PassthroughSubject</code> to <code class="language-plaintext highlighter-rouge">PublishSubject</code> and <code class="language-plaintext highlighter-rouge">CurrentValueSubject</code> to <code class="language-plaintext highlighter-rouge">BehaviorSubject</code> shows how heavily Combine borrows from it).</p>
<p>Before we jump into <code class="language-plaintext highlighter-rouge">ReplaySubject</code>’s implementation. Here’s what we’ll be building towards (and unfortunately, dropping our ol’ Cher references).</p>
<script src="https://gist.github.com/jasdev/decfc21e2d5e833acc9c657db9b94bb9.js"></script>
<p>(<a href="https://gist.github.com/jasdev/decfc21e2d5e833acc9c657db9b94bb9">Gist permalink</a>.)</p>
<p>I do want to provide an off-ramp, though. Writing a custom <code class="language-plaintext highlighter-rouge">Subject</code> conformance—while honoring back pressure—is an advanced topic and if you’d rather skip to the final implementation, <a href="https://github.com/CombineCommunity/CombineExt/pull/23">here’s my <code class="language-plaintext highlighter-rouge">CombineExt</code> PR</a> adding both <code class="language-plaintext highlighter-rouge">ReplaySubject</code> and <code class="language-plaintext highlighter-rouge">Publisher.share(replay:)</code> to the package.</p>
<h2 id="implementing-replaysubject">Implementing <code class="language-plaintext highlighter-rouge">ReplaySubject</code></h2>
<p>If writing a <code class="language-plaintext highlighter-rouge">Publisher</code> conformance is Hard Mode, then custom <code class="language-plaintext highlighter-rouge">Subject</code>s are the Final Boss. Quite literally. <code class="language-plaintext highlighter-rouge">Subject</code> inherits from <code class="language-plaintext highlighter-rouge">Publisher</code> and—as far as I can tell—there aren’t any types inheriting from <code class="language-plaintext highlighter-rouge">Subject</code>.</p>
<p>There’s a few moving parts and my goal is to make sense of the boxes in this diagram (click through for a larger rendering).</p>
<p><a href="/public/diagrams/ReplaySubject_Lifecycle.svg"><img src="/public/diagrams/ReplaySubject_Lifecycle.svg" alt="" /></a></p>
<p>To start, we’ll leave the above <code class="language-plaintext highlighter-rouge">Publisher.share(replay:)</code> implementation in place and try to get things building again.</p>
<p>First, let’s sketch out the type with a <code class="language-plaintext highlighter-rouge">bufferSize</code> initializer, tack on a <code class="language-plaintext highlighter-rouge">Subject</code> conformance, and let the compiler guide us through the requirements.</p>
<script src="https://gist.github.com/jasdev/be106d7a1ad9668cd6a6ea58ff90fb3c.js"></script>
<p>(<a href="https://gist.github.com/jasdev/be106d7a1ad9668cd6a6ea58ff90fb3c">Gist permalink</a>.)</p>
<p>Adding a couple of generics, labeling ‘em as associated types, and ⌘ + B’ing.</p>
<script src="https://gist.github.com/jasdev/c580ba0db77935bdc8ad27a0954f1629.js"></script>
<p>(<a href="https://gist.github.com/jasdev/c580ba0db77935bdc8ad27a0954f1629">Gist permalink</a>.)</p>
<p>Listening to the compiler and adding the needed method.</p>
<script src="https://gist.github.com/jasdev/bc1577fc1eaecc1559f1ab3e44f8206c.js"></script>
<p>(<a href="https://gist.github.com/jasdev/bc1577fc1eaecc1559f1ab3e44f8206c">Gist permalink</a>.)</p>
<p>And now we can pause since we’re in compiling order.</p>
<p>We’ll come back to <code class="language-plaintext highlighter-rouge">Subject</code>’s three overloaded <code class="language-plaintext highlighter-rouge">send</code> requirements, but for now I want to focus on <a href="https://developer.apple.com/documentation/combine/publisher/3229093-receive"><code class="language-plaintext highlighter-rouge">Publisher.receive(subscriber:)</code></a>.</p>
<p>If we take a look at <code class="language-plaintext highlighter-rouge">Subscriber</code>’s affordances, we can <a href="https://developer.apple.com/documentation/combine/subscriber/3213653-receive">send values</a>, <a href="https://developer.apple.com/documentation/combine/subscriber/3213654-receive">completion events</a>, and <a href="https://developer.apple.com/documentation/combine/subscriber/3213655-receive">a <code class="language-plaintext highlighter-rouge">Subscription</code></a>. The last of which is the jumping off point for the publisher-subscriber attachment.</p>
<p>In fact, even though <code class="language-plaintext highlighter-rouge">Publisher</code>s seem to be the center of Combine’s party, it’s actually <code class="language-plaintext highlighter-rouge">Subscription</code>s doing the work.</p>
<p><code class="language-plaintext highlighter-rouge">Subscription</code> being a protocol means we’ll have to add another conformance. To keep things simple, let’s place it in <code class="language-plaintext highlighter-rouge">ReplaySubject</code>’s namespace over at <code class="language-plaintext highlighter-rouge">ReplaySubject.Subscription</code>.</p>
<script src="https://gist.github.com/jasdev/47fc9498b591a87a864d98c2ac4f4c1a.js"></script>
<p>(<a href="https://gist.github.com/jasdev/47fc9498b591a87a864d98c2ac4f4c1a">Gist permalink</a>.)</p>
<p>This is…a lot of moving parts—and there’s still one more to add. Let’s pause and walk through the <code class="language-plaintext highlighter-rouge">Subject</code> lifecycle, fill in the <code class="language-plaintext highlighter-rouge">/* … */</code> placeholders, and stumble upon needing our next type, <a href="https://github.com/CombineCommunity/CombineExt/blob/7ed4677e33fdc963af404423cb563e133c271d2b/Sources/Common/DemandBuffer.swift"><code class="language-plaintext highlighter-rouge">CombineExt.DemandBuffer</code></a><sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>.</p>
<p><code class="language-plaintext highlighter-rouge">ReplaySubject</code>s <em>receive</em> subscriptions, values, and completion events through the three <code class="language-plaintext highlighter-rouge">send</code> requirements in the <code class="language-plaintext highlighter-rouge">Subject</code> protocol.</p>
<p>Like <code class="language-plaintext highlighter-rouge">CurrentValue</code>- and <code class="language-plaintext highlighter-rouge">PassthroughSubjects</code>, <code class="language-plaintext highlighter-rouge">ReplaySubject</code>s will request unlimited demand—i.e. they’re able to receive arbitrarily many <code class="language-plaintext highlighter-rouge">send</code> calls.</p>
<script src="https://gist.github.com/jasdev/776d54edcdcaf8d634cd4322cea33291.js"></script>
<p>(<a href="https://gist.github.com/jasdev/776d54edcdcaf8d634cd4322cea33291">Gist permalink</a>.)</p>
<p>One down, five placeholders to go. Onto completion events. We’ll need to do two things upon a subject’s completion: mark the subject as being in an “inert” state, so future subscribers can have that terminal event played back after clearing the buffer, and also forward the completion event to any live subscriptions.</p>
<p>Which, means we need two-ish properties.</p>
<script src="https://gist.github.com/jasdev/56bc22532bda31797ab84a006806c874.js"></script>
<p>(<a href="https://gist.github.com/jasdev/56bc22532bda31797ab84a006806c874">Gist permalink</a>.)</p>
<p>Onto value events. Right, time to similarly check <code class="language-plaintext highlighter-rouge">isActive</code>, add to a replay buffer, and forward to subscriptions.</p>
<script src="https://gist.github.com/jasdev/430276e0484cb66cf6d35385c5a7faff.js"></script>
<p>(<a href="https://gist.github.com/jasdev/430276e0484cb66cf6d35385c5a7faff">Gist permalink</a>.)</p>
<p>Inching along. Now, we can focus on <code class="language-plaintext highlighter-rouge">ReplaySubject.receive(subscriber:)</code>, which, in turn will help us fil out <code class="language-plaintext highlighter-rouge">ReplaySubject.Subscription</code>’s interface.</p>
<script src="https://gist.github.com/jasdev/e79c3abf50e042c8504aee512fec9f21.js"></script>
<p>(<a href="https://gist.github.com/jasdev/e79c3abf50e042c8504aee512fec9f21">Gist permalink</a>.)</p>
<p>Onto the final stretch—if you’ve been following along, props. Writing subjects, especially ones that honor back pressure, is probably the most difficult corner of Combine.</p>
<p>I’m going to fold <code class="language-plaintext highlighter-rouge">ReplaySubject</code>’s implementation for now (we’ll need to update it once more later), so we can focus on <code class="language-plaintext highlighter-rouge">ReplaySubject.Subscription</code>.</p>
<script src="https://gist.github.com/jasdev/bab7184c5c2e689891f047730085c782.js"></script>
<p>(<a href="https://gist.github.com/jasdev/bab7184c5c2e689891f047730085c782">Gist permalink</a>.)</p>
<p><code class="language-plaintext highlighter-rouge">ReplaySubject.Subscription.request(_:)</code>, <code class="language-plaintext highlighter-rouge">.forward(completion:)</code>, and <code class="language-plaintext highlighter-rouge">.forward(value:)</code> all hint at a sort of “demand queue” we’ll need to maintain. That is, <code class="language-plaintext highlighter-rouge">ReplaySubject</code>—the subscription’s “upstream,” so to speak—will forward on events and we only want <code class="language-plaintext highlighter-rouge">subscriber</code> to <code class="language-plaintext highlighter-rouge">receive</code> those they’ve demanded for.</p>
<p>And this is what folks mean when they throw the phrase “handling back pressure” around.</p>
<p>We could do this demand-then-<code class="language-plaintext highlighter-rouge">receive</code> dance in an ad hoc way, but, Shai has provided shoulder we can stand on with <a href="https://github.com/CombineCommunity/CombineExt/blob/7ed4677e33fdc963af404423cb563e133c271d2b/Sources/Common/DemandBuffer.swift"><code class="language-plaintext highlighter-rouge">CombineExt.DemandBuffer</code></a>.</p>
<p>It’s a buffer we can use to queue both forwarded events and subscriber demand and it’ll handle the rest.</p>
<p>(For the curious reader. <a href="https://github.com/CombineCommunity/CombineExt/blob/7ed4677e33fdc963af404423cb563e133c271d2b/Sources/Common/DemandBuffer.swift#L77-L115"><code class="language-plaintext highlighter-rouge">DemandBuffer.flush(adding:)</code></a> holds the type’s core.)</p>
<script src="https://gist.github.com/jasdev/66820d1ae07ebde49f8683fb6473b251.js"></script>
<p>(<a href="https://gist.github.com/jasdev/66820d1ae07ebde49f8683fb6473b251">Gist permalink</a>.)</p>
<p>When leaning on <code class="language-plaintext highlighter-rouge">DemandBuffer</code>, <code class="language-plaintext highlighter-rouge">Subscription</code>’s implementation practically fills itself out. Requested demand? Delegate it to the buffer. Forwarded value or completion events? Forward it to the buffer. Canceled? <code class="language-plaintext highlighter-rouge">nil</code> the buffer to free it from memory.</p>
<p>But, you might’ve noticed one thing we forgot to clean up. <code class="language-plaintext highlighter-rouge">ReplaySubject.subscriptions</code>.</p>
<p>We need a way of signaling back to the parent when a subscription is cancelled.</p>
<script src="https://gist.github.com/jasdev/ac950c95e474e78d0bf850b9b612fa3c.js"></script>
<p>(<a href="https://gist.github.com/jasdev/ac950c95e474e78d0bf850b9b612fa3c">Gist permalink</a>.)</p>
<p>Aaand we made it! Here’s a <a href="https://gist.github.com/jasdev/bc66d03e53f1e86b4a8197dc087aea5e">gist of the full implementation</a> and for the code-reviewed version, <a href="https://github.com/CombineCommunity/CombineExt/pull/23">here’s the backing <code class="language-plaintext highlighter-rouge">CombineExt</code> PR</a>.</p>
<p>Reworking our <code class="language-plaintext highlighter-rouge">cherReplayingLatest</code> example with two sinks attaching to the same, shared publisher, with the second attaching three seconds behind the first.</p>
<script src="https://gist.github.com/jasdev/5ea990ff464fe626ca48cae59da18cdd.js"></script>
<p>(<a href="https://gist.github.com/jasdev/5ea990ff464fe626ca48cae59da18cdd">Gist permalink</a>.)</p>
<p>⬦</p>
<p>Phew. Readers who made it this far should pat themselves on the back. <code class="language-plaintext highlighter-rouge">ReplaySubject</code>’s implementation is involved.</p>
<p>If you want to read further—and I encourage you to do so—digging into Shai’s <code class="language-plaintext highlighter-rouge">DemandBuffer</code> I linked to earlier is a spot to start and I’ve also added a weekend-worth of related links and footnotes below.</p>
<p>■</p>
<hr />
<h2 id="related-reading-and-footnotes">Related reading and footnotes</h2>
<p>⇒ “<a href="https://www.davesexton.com/blog/post/To-Use-Subject-Or-Not-To-Use-Subject.aspx">To use subject or not to use subject?</a>” Dave Sexton’s writing on subjects is some of the best I’ve read—they also have a primer on “<a href="https://www.davesexton.com/blog/post/Hot-and-Cold-Observables.aspx">[publisher] temperature</a>,” a topic I hinted at when mentioning effectful sequences.</p>
<p>⇒ The <a href="https://www.apeth.com/UnderstandingCombine/operators/operatorsSplitters/operatorsmulticast.html">multicasting section</a> in <em>Understanding Combine</em>.</p>
<p>⇒ Matt Gallagher’s <a href="https://www.cocoawithlove.com/blog/twenty-two-short-tests-of-combine-part-2.html#sharing-via-multicast">multicasting coverage</a> in part two of his “22 short tests of Combine” series.</p>
<p>⇒ <code class="language-plaintext highlighter-rouge">Entwine</code>’s <a href="https://github.com/tcldr/Entwine/blob/2cbad3312aefc95f0cf19ad7b2cc30faba727bcd/Sources/Entwine/Operators/ReplaySubject.swift">implementation of <code class="language-plaintext highlighter-rouge">ReplaySubject</code></a>.</p>
<p>⇒ <a href="https://github.com/mattgallagher/CombineExploration/blob/619247224856313960cf3858f4c8ee5f213fb628/Sources/CombineExploration/BufferSubject.swift">Another implementation</a> of <code class="language-plaintext highlighter-rouge">ReplaySubject</code>, under the name <code class="language-plaintext highlighter-rouge">BufferSubject</code>.</p>
<p>⇒ <code class="language-plaintext highlighter-rouge">OpenCombine</code>’s <a href="https://github.com/broadwaylamb/OpenCombine/blob/d680f09932fe68942c3d391d6df296d38b9dcd05/Sources/OpenCombine/Publishers/Publishers.Share.swift#L39">implementation of <code class="language-plaintext highlighter-rouge">Publisher.share</code></a>.</p>
<p>⇒ <em>Using Combine</em>’s <a href="https://heckj.github.io/swiftui-notes/#reference-multicast">multicasting chapter</a>.</p>
<p>⇒ For those who want more example operators that lean on <code class="language-plaintext highlighter-rouge">CombineExt.DemandBuffer</code>, Shai’s <a href="https://www.youtube.com/watch?v=Vnk4vFyuBGo">recorded live stream</a> where he implemented <code class="language-plaintext highlighter-rouge">Publishers.Amb</code> is fantastic.</p>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>The other two, at the time of writing, being the publishers returned from <a href="https://developer.apple.com/documentation/foundation/timer/3329589-publish"><code class="language-plaintext highlighter-rouge">Timer.publish(every:tolerance:on:in:options:)</code></a> and <a href="https://developer.apple.com/documentation/combine/publisher/3235803-makeconnectable"><code class="language-plaintext highlighter-rouge">Publisher.makeConnectable</code></a>. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>Other reactive libraries have a related notion, <a href="https://github.com/ReactiveX/RxSwift/blob/c1bd31b397d87a54467af4161dde9d6b27720c19/RxSwift/Observables/Multicast.swift#L99-L111"><code class="language-plaintext highlighter-rouge">refCount</code></a> or in <a href="https://github.com/tcldr/Entwine/blob/b839c9fcc7466878d6a823677ce608da998b95b9/Sources/Entwine/Operators/ReferenceCounted.swift#L115-L124">Entwine’s example, <code class="language-plaintext highlighter-rouge">referenceCounted</code></a>. The etymology here makes sense after some pause. Similar to ARC, <code class="language-plaintext highlighter-rouge">ref(erence)Count(ed)</code> connects to upstream after receiving the first subscriber and cancels when there aren’t any left (akin to the adding and subtracting strong references). <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>Tristan has a sketch of a similar type, <a href="https://github.com/tcldr/Entwine/blob/b839c9fcc7466878d6a823677ce608da998b95b9/Sources/Common/Utilities/SinkQueue.swift"><code class="language-plaintext highlighter-rouge">SinkQueue</code></a>, over in their Combine utilities library, Entwine. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
A materialization primer2020-04-09T00:00:00+00:00https://jasdev.me/materialization-primer<p>(Assumed audience: folks with a working knowledge of Combine and the <a href="https://developer.apple.com/documentation/combine/observableobject"><code class="language-plaintext highlighter-rouge">ObservableObject</code> protocol</a>.)</p>
<p>Materialization, like “<a href="/fusion-primer">fusion</a>” and “<a href="/duals">dual</a>,” is an intimidating term for a not-so-intimidating concept.</p>
<p>When I first encountered the word, I thought it was over the top. And, it kind of is. This entry tries to bring it down from abstraction and into Swift.</p>
<p>Let’s run into needing it by way of example, fetching integers from <a href="https://www.random.org/">random.org</a>.</p>
<h2 id="fetching-random-integers">Fetching random integers</h2>
<p>We’ll extend <a href="https://www.pointfree.co/episodes/ep42-the-many-faces-of-flat-map-part-1#t880">Point-Free’s pinging</a> of random.org for a random number.</p>
<script src="https://gist.github.com/jasdev/10432abe75d246f4b0dc3d9066f37287.js"></script>
<p>(<a href="https://gist.github.com/jasdev/10432abe75d246f4b0dc3d9066f37287">Gist permalink</a>.)</p>
<p>With <code class="language-plaintext highlighter-rouge">randomNumberPublisher</code> in hand, we can imagine a view model using it when responding to pings to fetch a number.</p>
<script src="https://gist.github.com/jasdev/b03a492f413d6da1f50bb6758870edbe.js"></script>
<p>(<a href="https://gist.github.com/jasdev/b03a492f413d6da1f50bb6758870edbe">Gist permalink</a>.)</p>
<p>Now’s let’s <code class="language-plaintext highlighter-rouge">send</code> on <code class="language-plaintext highlighter-rouge">randomNumberPing</code> a few times and see what happens.</p>
<script src="https://gist.github.com/jasdev/53ccd1da979ba488a083ea6a62580b7c.js"></script>
<p>(<a href="https://gist.github.com/jasdev/53ccd1da979ba488a083ea6a62580b7c">Gist permalink</a>.)</p>
<p>And while not ideal, it checks out that a failure in the first request would preclude attempting the second. The <code class="language-plaintext highlighter-rouge">Publisher</code> contract states that an error event means a publisher won’t further emit values, effectively rendering our <code class="language-plaintext highlighter-rouge">viewModel</code> inert after a failure.</p>
<p>It might be tempting to rework <code class="language-plaintext highlighter-rouge">RandomNumberViewModel.init</code> with error handling.</p>
<script src="https://gist.github.com/jasdev/d246ade5c20c6ad6a531fb3dbe42277e.js"></script>
<p>(<a href="https://gist.github.com/jasdev/d246ade5c20c6ad6a531fb3dbe42277e">Gist permalink</a>.)</p>
<p>Then, well, we run into another problem. We have no way of disambiguating between an initial and error state, since they both bear <code class="language-plaintext highlighter-rouge">nil</code> values. Moreover, if we had more errors than <code class="language-plaintext highlighter-rouge">RandomDotOrgError.failedRequest</code>, we’d lossily squash them.</p>
<p>We could lift <code class="language-plaintext highlighter-rouge">RandomNumberViewModel.randomNumber</code> <a href="https://developer.apple.com/documentation/swift/result">into a <code class="language-plaintext highlighter-rouge">Result</code></a>, but, doing so would put the burden on consumers to <code class="language-plaintext highlighter-rouge">switch</code> over <code class="language-plaintext highlighter-rouge">Result.success</code> and <code class="language-plaintext highlighter-rouge">.failure</code> when focusing in on the cases they’re concerned about (e.g. content views, the success case, and error views, the failure).</p>
<p>Materialization—as you probably guessed—has a say in this matter.</p>
<p>First, let’s upgrade our view model output from an <code class="language-plaintext highlighter-rouge">Int?</code> to a type that better relays error and loading states.</p>
<script src="https://gist.github.com/jasdev/432803d9002fec10d46272aad29e430a.js"></script>
<p>(<a href="https://gist.github.com/jasdev/432803d9002fec10d46272aad29e430a">Gist permalink</a>.)</p>
<p>And then, trying to wire everything up with <code class="language-plaintext highlighter-rouge">DataLoadState</code>.</p>
<script src="https://gist.github.com/jasdev/7736fe314960abdb90c5da539c477469.js"></script>
<p>(<a href="https://gist.github.com/jasdev/7736fe314960abdb90c5da539c477469#file-random_number_view_model_materialized-swift">Gist permalink</a>.)</p>
<p>Let’s take a breather. That was a ton of scaffolding to climb through, but, it’ll pay off—I promise!</p>
<p>Notes <code class="language-plaintext highlighter-rouge">(3)</code>, <code class="language-plaintext highlighter-rouge">(8)</code>, and <code class="language-plaintext highlighter-rouge">(10)</code> are gaps we’ll fill in with materialization.</p>
<h2 id="aliens-guyerrors-as-valuesaliens-guy"><<a href="https://knowyourmeme.com/memes/ancient-aliens">aliens-guy</a>>Errors as values</aliens-guy></h2>
<p>We’ll often need publishers to drive client-side errors views—which, seem at odds with Combine’s <code class="language-plaintext highlighter-rouge">Publisher</code> contract:</p>
<blockquote>
<p>[After sending a subscription to a subscriber and they request values,] the publisher is free to send that number of values or fewer to the subscriber.</p>
<p>…, if the publisher is finite, then it will eventually send a [<em>single</em>] completion or an error.</p>
</blockquote>
<p>—<a href="https://asciiwwdc.com/2019/sessions/722#t=408.346">WWDC ’19, Session 722 (timestamped transcript)</a></p>
<p>Receiving only a single <a href="https://developer.apple.com/documentation/combine/subscribers/completion/failure">failure</a> (or <a href="https://developer.apple.com/documentation/combine/subscribers/completion/finished">finished</a>) event makes user-initiated retries and displaying error views difficult. We’d have to peek into the <code class="language-plaintext highlighter-rouge">receiveCompletion</code> argument of either <a href="https://developer.apple.com/documentation/combine/publisher/3204713-handleevents"><code class="language-plaintext highlighter-rouge">Publisher.handleEvents</code></a> or <a href="https://developer.apple.com/documentation/combine/publisher/3343978-sink"><code class="language-plaintext highlighter-rouge">.sink</code></a> and then re-attach subscribers to new publisher instances.</p>
<p>Not ideal.</p>
<p>This not-ideal’ness stems from completion events being <em>terminal</em>. There’s sound practical (and theoretical<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>) reasons for this and we’ll need to bridge the gap by moving our involved <code class="language-plaintext highlighter-rouge">Publisher</code>’s <code class="language-plaintext highlighter-rouge">Failure</code> generic <em>into</em> its <code class="language-plaintext highlighter-rouge">Output</code> type.</p>
<p>“Into” in that sentence is a bit vague. Let’s pause with it.</p>
<p>Publishers emit either some (or none) <code class="language-plaintext highlighter-rouge">Output</code>-typed values or a single (or no) <code class="language-plaintext highlighter-rouge">Subscribers.Completion<Failure></code> terminating event.</p>
<p>So, we need a way of translating a publisher to one in this form (subbing in <code class="language-plaintext highlighter-rouge">AnyPublisher</code> as a placeholder),</p>
<p><code class="language-plaintext highlighter-rouge">AnyPublisher<Output, Failure> ⇒ AnyPublisher<Event<Output, Failure>, Never></code>.</p>
<p>It’s worth noting our old friend<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>, <code class="language-plaintext highlighter-rouge">Never</code> prevents the transformed publisher from error’ing out.</p>
<p>I’ll also need to properly introduce our new friend, <code class="language-plaintext highlighter-rouge">Event</code>. It’s an enumeration to represent the possible events a subscriber may receive.</p>
<script src="https://gist.github.com/jasdev/5afdb2544530745ea0749e9093dec063.js"></script>
<p>(<a href="https://gist.github.com/jasdev/5afdb2544530745ea0749e9093dec063">Gist permalink</a>.)</p>
<p>This is all sounds nice and well. But, what’s the point?</p>
<p>By materializing errors into a publisher’s output, we can then react to them in the same way we would handle ordinary values.</p>
<p>Or, rather, we’re considering errors as values.</p>
<h2 id="two-implementations">Two implementations</h2>
<p>We have two options: lean on existing operators or sketch out our own <code class="language-plaintext highlighter-rouge">Publisher</code> conformance. There’s tradeoffs for each and since there’s <a href="https://github.com/CombineCommunity/CombineExt/blob/83c71501d41e2f9e6ccc701032c3d6ee0b03c6fc/Sources/Operators/Materialize.swift#L52-L70">prior art</a> for the latter in <a href="https://github.com/CombineCommunity/CombineExt">CombineCommunity/CombineExt</a>, we’ll go with the former.</p>
<script src="https://gist.github.com/jasdev/b334cdb3c79efc50fef555b223df5cdd.js"></script>
<p>(<a href="https://gist.github.com/jasdev/b334cdb3c79efc50fef555b223df5cdd">Gist permalink</a>.)</p>
<p>Now, we can start chaining operators, case by <code class="language-plaintext highlighter-rouge">Event</code> case.</p>
<p>Values? We need to box ‘em into <code class="language-plaintext highlighter-rouge">Event.value</code>.</p>
<script src="https://gist.github.com/jasdev/cf5e768834e68c678585f6c8941d8c59.js"></script>
<p>(<a href="https://gist.github.com/jasdev/cf5e768834e68c678585f6c8941d8c59">Gist permalink</a>.)</p>
<p><code class="language-plaintext highlighter-rouge">Completion.finished</code>s? Well, we need a way of silencing upstream’s finished event and then turning around and publishing an <code class="language-plaintext highlighter-rouge">Event.completion(.finished)</code>. Thankfully, the framework ships with a few <a href="https://developer.apple.com/documentation/combine/publisher/3204683-append"><code class="language-plaintext highlighter-rouge">.append(_:)</code></a> overloads.</p>
<script src="https://gist.github.com/jasdev/5e6cbd30c78cbf5a9b5a83e4d822cc82.js"></script>
<p>(<a href="https://gist.github.com/jasdev/5e6cbd30c78cbf5a9b5a83e4d822cc82">Gist permalink</a>.)</p>
<p>And lastly, (and densely<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>), catching error events.</p>
<script src="https://gist.github.com/jasdev/b83638fe55a0ca4aebfa4a689fff71e6.js"></script>
<p>(<a href="https://gist.github.com/jasdev/b83638fe55a0ca4aebfa4a689fff71e6">Gist permalink</a>.)</p>
<p>That’s a lot to digest—let’s recap (and maybe take some Tums).</p>
<p>We’re doing three things.</p>
<ul>
<li>Lifting emitted values from upstream into the <code class="language-plaintext highlighter-rouge">Event.value</code> case.</li>
<li>Intercepting completions with the <code class="language-plaintext highlighter-rouge">append</code> operator and turning around and sending an <code class="language-plaintext highlighter-rouge">Event.completion(.finished)</code> to subscribers.</li>
<li>Catching any <code class="language-plaintext highlighter-rouge">error</code>s, boxing them up into a <code class="language-plaintext highlighter-rouge">Event.completion(.failure(error))</code>, and pass it along as a value event.</li>
</ul>
<h2 id="dematerializing">Dematerializing</h2>
<p>Materializing—in a sort of pseudo-Swift syntax—takes a <code class="language-plaintext highlighter-rouge">Publisher<Output, Failure></code> and lifts it to a <code class="language-plaintext highlighter-rouge">Publisher<Event<Output, Failure>, Never></code>, shifting errors to the value side.</p>
<p>However, consumers might only be concerned with specific <code class="language-plaintext highlighter-rouge">Event</code> cases. Think content views backed by <code class="language-plaintext highlighter-rouge">Output</code> instances or error views driven by <code class="language-plaintext highlighter-rouge">Failure</code> values.</p>
<p>We need a way of lowering a previously-materialized sequence, or as <a href="https://github.com/antitypical/Result/blob/c0838342cedfefc25f6dd4f95344d376bed582c7/Result/Result.swift#L84-L88">our wise elders</a> called it, “dematerializing”</p>
<p>To start, we might be tempted to extend <code class="language-plaintext highlighter-rouge">Publisher</code> with an <code class="language-plaintext highlighter-rouge">Output == Event<Output, Failure></code> constraint.</p>
<script src="https://gist.github.com/jasdev/a80b24f64cbc4c90e54d898bae9f02a7.js"></script>
<p>(<a href="https://gist.github.com/jasdev/a80b24f64cbc4c90e54d898bae9f02a7">Gist permalink</a>.)</p>
<p>Sadly, the compiler can’t reconcile the recursive constraint. We’ll need to introduce an intermediary protocol, <code class="language-plaintext highlighter-rouge">EventConvertible</code>, <a href="https://github.com/ReactiveX/RxSwift/blob/9b31a15520306b073cb9d46456f64826c1d6dcab/RxSwift/Event.swift#L92-L109">following Rx’s lead</a>.</p>
<script src="https://gist.github.com/jasdev/67dd1174dcc42cb7a18dcb1831451ef5.js"></script>
<p>(<a href="https://gist.github.com/jasdev/67dd1174dcc42cb7a18dcb1831451ef5">Gist permalink</a>.)</p>
<p>In both <code class="language-plaintext highlighter-rouge">values</code> and <code class="language-plaintext highlighter-rouge">errors</code> we’ll wanna focus in on upstream <code class="language-plaintext highlighter-rouge">Event.value</code> and <code class="language-plaintext highlighter-rouge">.completion(.failure(_))</code>s, respectively. There’s a couple of ways we could go about this—e.g. filtering cases or compacting associated values à la Point-Free’s <a href="https://www.pointfree.co/episodes/ep52-enum-properties">enumeration properties</a>.</p>
<p>The latter route is more fun—and of course, that doesn’t make the former any less valid.</p>
<p>To start, let’s add computed properties to <code class="language-plaintext highlighter-rouge">EventConvertible</code> to pluck out either an associated <code class="language-plaintext highlighter-rouge">Output</code> or <code class="language-plaintext highlighter-rouge">Failure</code> value.</p>
<script src="https://gist.github.com/jasdev/4269976ed2551c27639cbab081f921f5.js"></script>
<p>(<a href="https://gist.github.com/jasdev/4269976ed2551c27639cbab081f921f5">Gist permalink</a>.)</p>
<p>These optional properties tee up <code class="language-plaintext highlighter-rouge">Publisher.values</code> and <code class="language-plaintext highlighter-rouge">.errors</code> to <code class="language-plaintext highlighter-rouge">compactMap</code> them out.</p>
<script src="https://gist.github.com/jasdev/8cbb99bb5fca5b12dfae49ce3df44a32.js"></script>
<p>(<a href="https://gist.github.com/jasdev/8cbb99bb5fca5b12dfae49ce3df44a32">Gist permalink</a>.)</p>
<h2 id="all-together-now-materializing-and-dematerializing--the-beatlesi-think">“All together now (materializing and dematerializing).” — The Beatles…I think.</h2>
<p>The above snippets should all compile, again, and now we can sketch out a <code class="language-plaintext highlighter-rouge">ContentView</code> against <code class="language-plaintext highlighter-rouge">RandomNumberViewModel</code> with a button to fire off requests, honor loading states, and present a sheet with either a random number or error in under thirty lines.</p>
<script src="https://gist.github.com/jasdev/68a3968a5b96b471c7d1020dce9439cb.js"></script>
<p>(<a href="https://gist.github.com/jasdev/68a3968a5b96b471c7d1020dce9439cb">Gist permalink</a>.)</p>
<p>In GIF action,</p>
<blockquote class="imgur-embed-pub" lang="en" data-id="a/2ly2tf0" data-context="false"><a href="//imgur.com/a/2ly2tf0">Sample `ContentView` using `RandomNumberViewModel`.</a></blockquote>
<script async="" src="//s.imgur.com/min/embed.js" charset="utf-8"></script>
<p>(<a href="https://imgur.com/a/2ly2tf0">Image permalink</a>.)</p>
<h2 id="prior-art">Prior art</h2>
<p>You’re probably—and rightfully—thinking that performing the <code class="language-plaintext highlighter-rouge">materialize</code>-<code class="language-plaintext highlighter-rouge">share</code>-<code class="language-plaintext highlighter-rouge">values</code>-<code class="language-plaintext highlighter-rouge">errors</code> dance on every <code class="language-plaintext highlighter-rouge">ObservableObject</code> conformance is going to be…a lot.</p>
<p>The community agrees. Thankfully, we can stand on their shoulders. In the past, folks have abstracted away materialization under a higher-level type called <code class="language-plaintext highlighter-rouge">Action</code>.</p>
<p>It comes in a few flavorings:</p>
<ul>
<li><a href="https://github.com/ReactiveCocoa/ReactiveSwift/blob/e27ccdbf4ec36f154b60b91a0d7e0110c4e882cb/Documentation/FrameworkOverview.md#actions">ReactiveSwift’s</a>,</li>
<li><a href="https://github.com/RxSwiftCommunity/Action">Rx’s</a>,</li>
<li>and <a href="https://github.com/ReactiveCocoa/ReactiveObjC/blob/1af6617f007cae727dce48d2031cc1e66d06f04a/Documentation/FrameworkOverview.md#commands">ReactiveCocoa 2.0’s</a>.</li>
</ul>
<p>Each exposes observables and signals (synonyms for “publishers”) <a href="https://github.com/RxSwiftCommunity/Action/blob/d1d01df963d9704bc728e9d6ffa1f775d6ade168/Sources/Action/Action.swift#L33-L40">like <code class="language-plaintext highlighter-rouge">Action.errors</code>, <code class="language-plaintext highlighter-rouge">.elements</code>, and <code class="language-plaintext highlighter-rouge">.executing</code></a> corresponding to <code class="language-plaintext highlighter-rouge">Publisher.values</code> and <code class="language-plaintext highlighter-rouge">.errors</code>, and <code class="language-plaintext highlighter-rouge">DataLoadState.loading</code> above.</p>
<p>…is there a Combine <code class="language-plaintext highlighter-rouge">Action</code> analog?</p>
<p>Stay tuned, <a href="http://twitter.com/sharplet">Adam Sharp</a> has something in the works <a href="https://github.com/thoughtbot/CombineAction">over at <code class="language-plaintext highlighter-rouge">thoughtbot/CombineAction</code></a>.</p>
<p>■</p>
<hr />
<p>Special thanks to <a href="https://github.com/rvsrvs">Van</a> for feedback on an early draft of this entry.</p>
<h2 id="related-reading-and-footnotes">Related reading and footnotes</h2>
<p>⇒ Shai’s <a href="https://github.com/CombineCommunity/CombineExt/blob/f2346434e2cb5486ffcaf8c1f88285d3538ca5fd/Sources/Operators/Materialize.swift">implementation of materialization</a>.</p>
<p>⇒ The <a href="http://reactivex.io/documentation/operators/materialize-dematerialize.html">ReactiveX reference</a> for materialize and dematerialize.</p>
<p>⇒ “<a href="/notes/merge-many">TIL about <code class="language-plaintext highlighter-rouge">Publishers.MergeMany.init</code></a>”</p>
<p>⇒ “<a href="/notes/postfix-erasure">Postfix type erasure</a>”</p>
<p>⇒ “<a href="/notes/weak-assignment">Weak assignment in Combine</a>”</p>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>I’ll have more on this soon. Until then, Brian Beckman and Erik Meijer’s <a href="https://www.youtube.com/watch?v=looJcaeboBY">’09 Expert to Expert session</a> is worth the time and in prose form, my <a href="/duals">post on duals</a> translates <a href="https://www.caseyliss.com/2019/6/13/building-up-to-combine">Casey Liss’ from Rx</a>. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>No fret if you’re meeting <code class="language-plaintext highlighter-rouge">Never</code> for the first time. The NSHipster folks have <a href="https://nshipster.com/never/">a fantastic entry</a> on the type. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>If you’re open to operators, importing your Prelude of choice can rework the <code class="language-plaintext highlighter-rouge">catch</code> to <a href="https://gist.github.com/jasdev/2158a4605cf0d9df377a7602cc81a5ea#file-materialize_with_operators-swift-L38-L40">this form</a>. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
setFailureType, setOutputType, and initial objects2020-04-06T00:00:00+00:00https://jasdev.me/set-output-type<p>(Assumed audience: folks familiar with Combine.)</p>
<p><a href="https://developer.apple.com/documentation/combine/publisher/3204753-setfailuretype"><code class="language-plaintext highlighter-rouge">Publisher.setFailureType(to:)</code></a> is a head scratcher. At a glance, it seems to step on <a href="https://developer.apple.com/documentation/combine/publisher/3204719-maperror"><code class="language-plaintext highlighter-rouge">.mapError(_:)</code></a>’s toes. And, it sort of does!</p>
<p>The “available when <code class="language-plaintext highlighter-rouge">Failure</code> is <code class="language-plaintext highlighter-rouge">Never</code>” note in the documentation hints at why.</p>
<p>So, let’s spin up our favorite <code class="language-plaintext highlighter-rouge">Never</code>-erroring publisher, <a href="https://developer.apple.com/documentation/combine/just"><code class="language-plaintext highlighter-rouge">Just</code></a>, and map over its error type for a closer look.</p>
<script src="https://gist.github.com/jasdev/0f5f36ae0cbea851eb4ac37e9635e30e.js"></script>
<p>(<a href="https://gist.github.com/jasdev/0f5f36ae0cbea851eb4ac37e9635e30e">Gist permalink</a>.)</p>
<p>Hmm. We need to implement <code class="language-plaintext highlighter-rouge">(Never) -> OurError</code>, eh? Maybe we can signal an <code class="language-plaintext highlighter-rouge">OurError</code> return, ignore the input, and call it a day?</p>
<script src="https://gist.github.com/jasdev/dc5cd15980231022e94c29a03a239039.js"></script>
<p>(<a href="https://gist.github.com/jasdev/dc5cd15980231022e94c29a03a239039">Gist permalink</a>.)</p>
<p>This compiles‽</p>
<p>If you scratched your head at this, don’t worry. I did, too.</p>
<p>That’s the magic <code class="language-plaintext highlighter-rouge">setFailureType</code> generalizes and tucks away. In fact, we can <em>prove</em> that somewhere in Combine’s closed source, Apple calls <code class="language-plaintext highlighter-rouge">{ _ -> NewFailure in }</code> for some generic failure type, <code class="language-plaintext highlighter-rouge">NewFailure</code>.</p>
<p>Before we do, it’s natural to ask if <code class="language-plaintext highlighter-rouge">Publisher</code>’s other associated type, <a href="https://developer.apple.com/documentation/combine/publisher/3204681-output"><code class="language-plaintext highlighter-rouge">Output</code></a>, has the same affordance.</p>
<p><img src="/public/images/search_for_set_output_type.png" alt="" /></p>
<p>Whelp—time to write our own (!).</p>
<h2 id="publishersetoutputtypeto"><code class="language-plaintext highlighter-rouge">Publisher.setOutputType(to:)</code></h2>
<p>After learning about <code class="language-plaintext highlighter-rouge">setOutputType</code> from <a href="https://twitter.com/sharplet">Adam</a> and <a href="https://github.com/CombineCommunity/CombineExt/pull/15">PR’ing it</a> to <code class="language-plaintext highlighter-rouge">CombineExt</code>, a couple of folks asked about its value.</p>
<blockquote>
<p>[…], if the upstream is <code class="language-plaintext highlighter-rouge">Never</code>’d you’d probably <code class="language-plaintext highlighter-rouge">append</code> another publisher, which would change the output type to my understanding.</p>
</blockquote>
<p>—<a href="https://github.com/CombineCommunity/CombineExt/pull/15#pullrequestreview-387675862">CombineExt/pull/15</a></p>
<p>This is <em>almost</em> the case! Let’s dig further.</p>
<p>A way to <code class="language-plaintext highlighter-rouge">Never</code> out a publisher is <a href="https://developer.apple.com/documentation/combine/publisher/3204714-ignoreoutput">ignoring its output</a>. Doing so turns it into a “<a href="https://github.com/ReactiveX/RxSwift/blob/002d325b0bdee94e7882e1114af5ff4fe1e96afa/Documentation/Traits.md#completable">completable</a>” that only notifies downstream of <code class="language-plaintext highlighter-rouge">Subscribers.Completion<Failure>.finished</code> or <code class="language-plaintext highlighter-rouge">.failure</code> events.</p>
<script src="https://gist.github.com/jasdev/1e7d5fa1c097e001f206ae190c20915b.js"></script>
<p>(<a href="https://gist.github.com/jasdev/1e7d5fa1c097e001f206ae190c20915b">Gist permalink</a>.)</p>
<p>We did this often at Peloton.</p>
<p>Our API had a notion of “workout finalization,” after which, a workout’s metrics would be min-max’d, summed, averaged, and cleaned before reporting secondary statistics like achievements and streaks.</p>
<p>So, we’d ignore the output of the publisher backing the finalization request and then, if successful, concatenate another publisher—of a <em>different</em> output type—to fetch any finalization-dependent metadata.</p>
<p>Some issues shake out when mirroring this in the above gist by <code class="language-plaintext highlighter-rouge">append</code>ing another element.</p>
<script src="https://gist.github.com/jasdev/d88cc0d9154a4bcc4b5cc2444d9f4418.js"></script>
<p>(<a href="https://gist.github.com/jasdev/d88cc0d9154a4bcc4b5cc2444d9f4418">Gist permalink</a>.)</p>
<p>Downstream from the third line, <code class="language-plaintext highlighter-rouge">Output</code> is fixed to <code class="language-plaintext highlighter-rouge">Never</code>, forcing the <code class="language-plaintext highlighter-rouge">append</code> method to accept either a variadic number of <code class="language-plaintext highlighter-rouge">Never</code>s, a <code class="language-plaintext highlighter-rouge">Sequence</code> of ‘em, or another <code class="language-plaintext highlighter-rouge">Never</code>-outputting publisher—each corresponding to the three available overloads:</p>
<ul>
<li><a href="https://developer.apple.com/documentation/combine/publisher/3204683-append"><code class="language-plaintext highlighter-rouge">Publisher.append(_:)</code></a> (variadic).</li>
<li><a href="https://developer.apple.com/documentation/combine/publisher/3204684-append"><code class="language-plaintext highlighter-rouge">.append(_:)</code></a> (<code class="language-plaintext highlighter-rouge">Sequence</code>).</li>
<li><a href="https://developer.apple.com/documentation/combine/publisher/3204685-append"><code class="language-plaintext highlighter-rouge">.append(_:)</code></a> (<code class="language-plaintext highlighter-rouge">Publisher</code>).</li>
</ul>
<p>The first isn’t possible by extension of <code class="language-plaintext highlighter-rouge">Never</code> being uninhibited, the second only works with an empty <code class="language-plaintext highlighter-rouge">Never</code> sequence, and the third would mean chaining another “completable” (which, does have its uses).</p>
<p>We need to map <code class="language-plaintext highlighter-rouge">Never</code> into another type and that’s where <code class="language-plaintext highlighter-rouge">setOutputType</code> fits.</p>
<p>To start, let’s scope the implementation by providing a <code class="language-plaintext highlighter-rouge">(Never) -> String</code>.</p>
<script src="https://gist.github.com/jasdev/eb6aed4b28648890b42bac1695eed8ca.js"></script>
<p>(<a href="https://gist.github.com/jasdev/eb6aed4b28648890b42bac1695eed8ca">Gist permalink</a>.)</p>
<p>The <code class="language-plaintext highlighter-rouge">{ _ -> String in }</code> closure is a <a href="https://en.wikipedia.org/wiki/Vacuous_truth">vacuous transformation</a>. In that, since it’s never called, we can claim to return any type.</p>
<p>Requiring folks to roll their own <code class="language-plaintext highlighter-rouge">{ _ -> T in }</code> closures for each <code class="language-plaintext highlighter-rouge">T</code> isn’t ideal and that’s the cosmetic <code class="language-plaintext highlighter-rouge">setOutputType(to:)</code> and <code class="language-plaintext highlighter-rouge">setFailureType(to:)</code> provide.</p>
<script src="https://gist.github.com/jasdev/bdbfb245e2da525d16252310aaaab5ab.js"></script>
<p>(<a href="https://gist.github.com/jasdev/bdbfb245e2da525d16252310aaaab5ab">Gist permalink</a>.)</p>
<p>I should address the rogue type erasure after the <code class="language-plaintext highlighter-rouge">Just</code> publisher.</p>
<p>Turns out Apple buried some <del>Easter</del>lockdown eggs there—removing the erasure and <code class="language-plaintext highlighter-rouge">setOutputType</code> call still compiles.</p>
<script src="https://gist.github.com/jasdev/34948b0a2a1ede6356e3e96c9ff59a86.js"></script>
<p>(<a href="https://gist.github.com/jasdev/34948b0a2a1ede6356e3e96c9ff59a86">Gist permalink</a>.)</p>
<p>Combine ships with overloads of <code class="language-plaintext highlighter-rouge">ignoreOutput</code> for specific publishers to keep <code class="language-plaintext highlighter-rouge">Output</code> in tact, but that isn’t true across all publishers (and why I reached for erasure to demonstrate the subtlety).</p>
<h2 id="setfailuretypeto-and-initial-objects"><code class="language-plaintext highlighter-rouge">setFailureType(to:)</code> and initial objects</h2>
<p>You might’ve noticed—it’s okay, if you didn’t—that <code class="language-plaintext highlighter-rouge">setOutputType</code>’s implementation is kind of “forced” by the compiler to a lone <code class="language-plaintext highlighter-rouge">map</code> call (sans possibly sprinkling <code class="language-plaintext highlighter-rouge">print</code>s, <code class="language-plaintext highlighter-rouge">fatalError</code>s, or other statements around).</p>
<p>If we tried to implement our own <code class="language-plaintext highlighter-rouge">setFailureType</code> operator, we’d be similarly guided.</p>
<script src="https://gist.github.com/jasdev/85b093150c8e9399da7edc5fe62b2d00.js"></script>
<p>(<a href="https://gist.github.com/jasdev/85b093150c8e9399da7edc5fe62b2d00">Gist permalink</a>.)</p>
<p>There’s no other way to implement this function (with the noted exceptions).</p>
<p>And if you’re thinking that’s too much of a coincidence to not scratch a larger concept, you’re right (or also guessed from the section title)!</p>
<p>The concept is the <code class="language-plaintext highlighter-rouge">(Never) -> NewOutput</code>, <code class="language-plaintext highlighter-rouge">(Never) -> NewFailure</code>, or more generally, <code class="language-plaintext highlighter-rouge">(Never) -> T</code> closures for an arbitrary type, <code class="language-plaintext highlighter-rouge">T</code>. No matter which <code class="language-plaintext highlighter-rouge">T</code> you have in hand, the only way to map from <code class="language-plaintext highlighter-rouge">Never</code> to it is our new friend <code class="language-plaintext highlighter-rouge">{ _ -> T in }</code>.</p>
<p>Put another way, there is a <em>unique</em> <code class="language-plaintext highlighter-rouge">(Never) -> T</code> closure per <code class="language-plaintext highlighter-rouge">T</code> in Swift’s type system—making <code class="language-plaintext highlighter-rouge">Never</code> a “starting point” each type can be uniquely mapped from.</p>
<p>Mathematicians call such starting points <a href="https://en.wikipedia.org/wiki/Initial_and_terminal_objects">initial objects</a>.</p>
<p>In their words,</p>
<blockquote>
<p>An initial object of a category <em>C</em> is an object <em>I</em> in <em>C</em> such that for every object <em>X</em> in <em>C</em>, there exists precisely one morphism <em>I</em> → <em>X</em>.</p>
</blockquote>
<p>Replacing <em>C</em> with Swift<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>, <em>I</em> with <code class="language-plaintext highlighter-rouge">Never</code>, <em>X</em> with <code class="language-plaintext highlighter-rouge">T</code>, and “morphism” with “closure,” it’s fair to say <code class="language-plaintext highlighter-rouge">Never</code> is an initial object.</p>
<p>Further, since there is a unique closure from <code class="language-plaintext highlighter-rouge">Never</code> to any other type, that means Apple’s implementation of <code class="language-plaintext highlighter-rouge">setFailureType</code>—somewhere underneath the <a href="https://developer.apple.com/documentation/combine/publishers/setfailuretype"><code class="language-plaintext highlighter-rouge">Publishers.SetFailureType</code></a> hood—contains that <code class="language-plaintext highlighter-rouge">mapError { _ -> NewFailure in }</code> call.</p>
<p>Which is wicked because even though Combine is closed-source, initiality (i.e. <code class="language-plaintext highlighter-rouge">Never</code> being an initial object) forces the implementation.</p>
<p>⬦</p>
<p><code class="language-plaintext highlighter-rouge">setOutputType</code>’s single expression definition packs almost a thousand words-worth of detail behind its signature and usage.</p>
<p>Further, <code class="language-plaintext highlighter-rouge">Never</code> is only one side of the coin. Can you think of a type in the Standard Library that has unique closures <em>towards</em> it? That is, which type, <code class="language-plaintext highlighter-rouge">???</code>, in Swift has a unique function <code class="language-plaintext highlighter-rouge">(T) -> ???</code>, per type <code class="language-plaintext highlighter-rouge">T</code>?</p>
<p>It’s a fun exercise to think on.</p>
<p>Until next time.</p>
<p>■</p>
<hr />
<h2 id="footnotes">Footnotes</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>Calling Swift a category <a href="https://ro-che.info/articles/2016-08-07-hask-category">comes with asterisks</a>. Still, it’s a trove from which we can translate decades-worth of learnings from category theory into everyday programming. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
An operator fusion primer2020-04-01T00:00:00+00:00https://jasdev.me/fusion-primer<p>(Assumed audience: folks familiar with Combine and its operators.)</p>
<h3 id="updates">Updates:</h3>
<h4 id="4420">4/4/20:</h4>
<p><a href="https://twitter.com/tim_vermeulen">Tim</a> followed up and asked a question I should’ve covered more directly,</p>
<blockquote>
<p>I was hoping to also read <em>why</em> Combine fuses operators, any idea?</p>
<p>I get that it simplifies types, but I have no reason to assume that that gains you anything at runtime […]</p>
</blockquote>
<p>Sans working at Apple or poking at the framework with performance tests, it’s hard to tell. But, my guess is <a href="https://developer.apple.com/documentation/combine/processing_published_elements_with_subscribers">back pressure</a> handling. Each intermediary <code class="language-plaintext highlighter-rouge">Publisher</code> conformance likely has its own buffering mechanism and fusion removes redundant overhead in honoring downstream subscribers’ demand.</p>
<hr />
<p>Nuclear physicists 🤝 Engineers at Apple</p>
<p>…operator fusion.</p>
<p>Kidding. Well, only sort of.</p>
<p>“Fusion” is a term I’d see in the <a href="http://akarnokd.blogspot.com/2016/03/operator-fusion-part-1.html">Reactive Java</a> and <a href="https://github.com/fused-effects/fused-effects/blame/40cf33031576f179c9e4ba3f9e8a33ba642fcc91/README.md#L98-L106">effect system</a> communities and struggled to build intuition around until I read through Combine’s documentation.</p>
<p>If anything, the goal of this primer is to be the resource younger Jasdev wish he had.</p>
<p>⬦</p>
<p>Fusion—as the word hints—<em>fuses</em> two structures. Which, by extension, means “operator fusion” in the Combine context joins two operators to optimize away an intermediary type.</p>
<p>Publishers in the framework bear two names:</p>
<ul>
<li>Their <a href="https://developer.apple.com/documentation/combine/publishers/output/3241611-erasetoanypublisher">type-erased</a> nickname, <code class="language-plaintext highlighter-rouge">AnyPublisher<Output, Failure></code>.</li>
<li>Or, their fuller, non-erased names like <code class="language-plaintext highlighter-rouge">Publishers.Concatenate<Publishers.Map<Self, Sequence.Element>, Publishers.Sequence<Sequence, Failure>></code>.</li>
</ul>
<p>The erased and full names interact with chained operators in nuanced ways—e.g. how does <code class="language-plaintext highlighter-rouge">(2)</code> in the snippet below retain its type after mapping whereas <code class="language-plaintext highlighter-rouge">(1)</code> doesn’t?</p>
<script src="https://gist.github.com/jasdev/194004eaa530293f6322c75a8cea4830.js"></script>
<p>To illustrate the nuance and performance and aesthetic tradeoffs, let’s walk through an operator I’m <a href="https://github.com/CombineCommunity/CombineExt/pull/7">contributing</a> to CombineExt—a community overlay of Combine extensions—, <code class="language-plaintext highlighter-rouge">Publisher.removeKnownDuplicates</code><sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>.</p>
<h2 id="publisherremoveknownduplicates"><code class="language-plaintext highlighter-rouge">Publisher.removeKnownDuplicates</code></h2>
<p>Combine ships with a three deduplicating operators:</p>
<ul>
<li><a href="https://developer.apple.com/documentation/combine/publisher/3204745-removeduplicates"><code class="language-plaintext highlighter-rouge">Publisher.removeDuplicates</code></a></li>
<li><a href="https://developer.apple.com/documentation/combine/publisher/3204746-removeduplicates"><code class="language-plaintext highlighter-rouge">.removeDuplicates(by:)</code></a></li>
<li><a href="https://developer.apple.com/documentation/combine/publisher/3204777-tryremoveduplicates"><code class="language-plaintext highlighter-rouge">.tryRemoveDuplicates(by:)</code></a></li>
</ul>
<p>They all nix <em>pairwise</em> duplicates from an upstream publisher by comparing the previous and next value events along an <code class="language-plaintext highlighter-rouge">Equatable</code> conformance or (non-)throwing predicate. If they match, the value will be dropped, if not, it’ll be published downstream.</p>
<p>But, what if we need distinct values across <em>all</em> seen so far?</p>
<p>That’s <code class="language-plaintext highlighter-rouge">removeKnownDuplicates</code>’s goal.</p>
<p>Of course, this operator has a warning label. Deduplicating across known value events means the operator has to track distinct values in memory, which can be problematic for sequences that publish a high number of unique elements. The operator can only free resources after a <a href="https://developer.apple.com/documentation/combine/cancellable/3204391-cancel">cancellation</a> or <a href="https://developer.apple.com/documentation/combine/subscribers/completion">completion event</a>.</p>
<p>We’ll focus in on a specific implementation<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup> constraining <code class="language-plaintext highlighter-rouge">Publisher.Output</code> to <code class="language-plaintext highlighter-rouge">Hashable</code>.</p>
<script src="https://gist.github.com/jasdev/d241612e599842c3661bc26af28d3f3f.js"></script>
<p>First, we need to record incoming value events and suppress those we’ve seen already. Maybe we can lean on <code class="language-plaintext highlighter-rouge">Output</code>’s <code class="language-plaintext highlighter-rouge">Hashable</code> conformance?</p>
<p>We could store all value events in a <code class="language-plaintext highlighter-rouge">Set<Output></code>, say under the variable name <code class="language-plaintext highlighter-rouge">seen</code>, and determine uniqueness with <a href="https://developer.apple.com/documentation/swift/set/3128848-insert"><code class="language-plaintext highlighter-rouge">seen.insert(incoming).inserted</code></a> (for an <code class="language-plaintext highlighter-rouge">incoming</code> value).</p>
<script src="https://gist.github.com/jasdev/1c6f576a0b6ef709591c6ac96a04bb12.js"></script>
<p>If you’ve been compiling along, here’s the remaining error:</p>
<blockquote>
<p>Cannot convert return expression of type</p>
<p>“<code class="language-plaintext highlighter-rouge">Publishers.Filter<Self></code>”</p>
<p>to return type</p>
<p>“<code class="language-plaintext highlighter-rouge">AnyPublisher<Output, Failure></code>.”</p>
</blockquote>
<p>We’ve arrived at the “type erase?”, “fuse??”, or “<a href="https://knowyourmeme.com/memes/confused-nick-young">???</a>” juncture.</p>
<h2 id="to-return-the-full-type-erase-or-be-opaque-that-is-the-question">“To return the full type, erase, or be opaque? That is the question.”</h2>
<p>— Wayne Gretzky</p>
<p>— — <a href="https://www.reddit.com/r/OutOfTheLoop/comments/3kyz8p/why_do_people_always_quote_things_with_michael/">Michael Scott</a></p>
<p>There’s two-ish<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup> routes we can take (I’ll explain the “-ish” soon).</p>
<h3 id="return-the-full-type">Return the full type</h3>
<p>There’s no dancing around it. Returning <code class="language-plaintext highlighter-rouge">Publishers.Filter<Self></code> leaks implementation details. But, is that such a bad thing?</p>
<p>The only properties on <a href="https://developer.apple.com/documentation/combine/publishers/filter"><code class="language-plaintext highlighter-rouge">Publishers.Filter</code></a> (and likewise, for other publishers) is its <a href="https://developer.apple.com/documentation/combine/publishers/filter/3207691-upstream">upstream</a> and <a href="https://developer.apple.com/documentation/combine/publishers/filter/3207629-isincluded"><code class="language-plaintext highlighter-rouge">isIncluded</code> predicate</a>, both of which call sites can’t do much with—and if they do, they’d sidestep reaching for <code class="language-plaintext highlighter-rouge">removeKnownDuplicates</code> in the first place.</p>
<h3 id="maybe-an-opaque-result-type">Maybe an opaque result type?</h3>
<p>Could <a href="https://github.com/apple/swift-evolution/blob/53c2e80bdede03b99aee683fe6399b6e4cf0bf95/proposals/0244-opaque-result-types.md">SE-0244</a>’s opaque result types help?</p>
<p>Swapping out <code class="language-plaintext highlighter-rouge">AnyPublisher<Output, Failure></code> for <code class="language-plaintext highlighter-rouge">some Publisher</code></p>
<p>…compiles? That’s fishy—and understandably so. The issue shows up when we interface against the opaqueness.</p>
<script src="https://gist.github.com/jasdev/620784d991fa6c9653754daae80e6f10.js"></script>
<p>Opaque return types are a sort of “<a href="https://github.com/apple/swift-evolution/blame/53c2e80bdede03b99aee683fe6399b6e4cf0bf95/proposals/0244-opaque-result-types.md#L89-L107">reverse generics</a>,” in that the <em>callee</em> (<code class="language-plaintext highlighter-rouge">removeKnownDuplicate</code>’s implementation, in our case)—instead of the caller (<code class="language-plaintext highlighter-rouge">deduplicatedEvens</code>) determines the resulting type.</p>
<p>There’s a cycle though. Our operator is stating it’s in control of the resulting type, covered by <code class="language-plaintext highlighter-rouge">some Publisher</code>, yet the associated type for the <code class="language-plaintext highlighter-rouge">Publisher</code> conformance, <code class="language-plaintext highlighter-rouge">Output</code>, is chosen by the caller, causing a stalemate.</p>
<p>This exceptional case is <a href="https://github.com/apple/swift-evolution/blame/53c2e80bdede03b99aee683fe6399b6e4cf0bf95/proposals/0244-opaque-result-types.md#L387-L403">tucked away</a> in the backing evolution proposal. And that’s the “-ish” I hinted at above.</p>
<h3 id="erase">Erase</h3>
<p>We could stay course with erasure. There’s a tradeoff through, and that’s fusion.</p>
<p>(Sorry it took almost a thousands words to get here. Nuclear physics is no joke.)</p>
<p>Type erasure—as the name implies—removes detail from a specific protocol conformance, leaving behind a type that only covers the protocol’s requirements. The technique is used frequently: <a href="https://developer.apple.com/documentation/combine/anypublisher"><code class="language-plaintext highlighter-rouge">AnyPublisher</code></a>, <a href="https://developer.apple.com/documentation/swift/anysequence"><code class="language-plaintext highlighter-rouge">AnySequence</code></a>, <a href="https://developer.apple.com/documentation/swift/anyiterator"><code class="language-plaintext highlighter-rouge">AnyIterator</code></a>,<a href="https://developer.apple.com/documentation/swift/anykeypath"><code class="language-plaintext highlighter-rouge">AnyKeyPath</code></a>, <a href="https://developer.apple.com/documentation/swiftui/anyview"><code class="language-plaintext highlighter-rouge">AnyView</code></a>, and so on.</p>
<p>In Combine, we can ask if that removed detail is useful—to answer this, let’s imagine a consumer tacks on a <code class="language-plaintext highlighter-rouge">filter</code> call to both non-erased and erased forms of <code class="language-plaintext highlighter-rouge">removeKnownDuplicates</code>.</p>
<script src="https://gist.github.com/jasdev/5ed5183336a12dca288c4eeb8a4e97bd.js"></script>
<p>Woah. <code class="language-plaintext highlighter-rouge">filter</code> calls normally nest the upstream publisher a <code class="language-plaintext highlighter-rouge">Publishers.Filter</code> level. Yet, when filtering <code class="language-plaintext highlighter-rouge">deduplicated</code>, <code class="language-plaintext highlighter-rouge">deduplicatedEvens</code> preserved its type.</p>
<p>Fusion!</p>
<p>Since <code class="language-plaintext highlighter-rouge">erasedRemoveKnownDuplicates</code> wipes the type information, Combine has no other choice than to wrap it in another intermediary. On the other hand, <code class="language-plaintext highlighter-rouge">Publishers.Filter</code> can specialize <a href="https://developer.apple.com/documentation/combine/publishers/filter/3207623-filter">its <code class="language-plaintext highlighter-rouge">filter</code> implementation</a> by returning another instance of the same type, reducing overhead.</p>
<p>We can’t peek behind Apple’s closed source. But, we can guess at the implementation.</p>
<script src="https://gist.github.com/jasdev/b5619cd756e23248b84fc12e7d92b4d2.js"></script>
<h2 id="tradeoffs-rule-everything-around-me">“[Tradeoffs] rule everything around me.”</h2>
<p>Fusion is wicked. However, it’s not always needed. We have to think about our publisher’s intended use, or at least guess when exposing a public-facing API.</p>
<p>If the operator is meant to be used in between other, chained operators, then it’s better to return the full type and let fusion happen.</p>
<p>However, if the publisher is meant to be used wholesale—i.e. its implementation covers all expected transformations—, then the aesthetic benefit of shortening to an <code class="language-plaintext highlighter-rouge">AnyPublisher</code> (or if possible, an opaque return type) is probably the way to go.</p>
<script src="https://gist.github.com/jasdev/363cb7268e93f75d45dc09a065b76eb9.js"></script>
<p>⬦</p>
<p>There you have it. We covered building our own composed operator, type erasure, opaque return types, a few memes, and nuclear physics in under 1,200 words. Fusion always felt out of reach and writing this primer distilled it from orbit and into Combine and Swift for me.</p>
<p>I hope it does for others, too.</p>
<p>In short,</p>
<ul>
<li>if your operator is an intermediary, try to return its full type to benefit from fusion’s optimizations.</li>
<li>if your publisher ends a chain, lean on either an opaque return type or erasure, in that order.</li>
</ul>
<p>■</p>
<hr />
<p>Special thanks to <a href="https://github.com/peter-tomaselli">Peter</a> and <a href="https://twitter.com/WilsonCusack">Wilson</a> for feedback on an early draft of this entry.</p>
<h2 id="related-reading-hat-tips-and-footnotes">Related reading, hat tips, and footnotes</h2>
<p>⇒ Thomas Visser’s “<a href="https://www.thomasvisser.me/2019/07/04/combine-types/">Why Combine has so many <code class="language-plaintext highlighter-rouge">Publisher</code> types</a>.”</p>
<p>⇒ Tim Ekl wrote a wonderful post detailing <a href="https://www.timekl.com/blog/2019/04/14/swift-generics-evolution/">the evolution of Swift generics</a>.</p>
<p>⇒ A notebook entry on <a href="/notes/postfix-erasure">type erasure with a postfix operator</a>.</p>
<p>⇒ Kudos to <a href="https://twitter.com/sharplet">Adam Sharp</a> for <a href="https://iosdevelopers.slack.com/archives/C0AET0JQ5/p1585060545148100?thread_ts=1582657918.079500&cid=C0AET0JQ5">talking through the tradeoffs</a> between erasure and fusion with me in iOS Folks’ #reactive channel.</p>
<p>⇒ Point-Free subscribers might’ve noticed that <a href="https://www.pointfree.co/collections/map-zip-flat-map/map/ep13-the-many-faces-of-map#t180">Episode #13’s mention</a> that “composition of maps are equivalent to the map of the composition” seems similar to fusion. And in a sense, it is! That’s because the functors we encounter in programming are strictly endofunctors.</p>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>I also implemented a predicate-based version <code class="language-plaintext highlighter-rouge">Publisher.removeKnownDuplicates(by:)</code> for when constraining <code class="language-plaintext highlighter-rouge">Output</code> to either <code class="language-plaintext highlighter-rouge">Equatable</code> or <code class="language-plaintext highlighter-rouge">Hashable</code> isn’t feasible. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>Two alternative implementations: <a href="https://gist.github.com/jasdev/702d1e8fc7079ba42081b5d4f4868e20"><code class="language-plaintext highlighter-rouge">flatMap</code>-</a> and <a href="https://gist.github.com/jasdev/3b98310fe3ca6b32fea5bfed243f54e2"><code class="language-plaintext highlighter-rouge">scan</code>-based</a>. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>It could be argued that there are three-ish, if we introduce a <code class="language-plaintext highlighter-rouge">Publishers.RemoveKnownDuplicates</code> type. However, that’d preclude fusion from kicking in unless we repeated the work Apple did by extending our new type with fused operators. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Writing a Combine operator: variadic zipping2020-03-24T00:00:00+00:00https://jasdev.me/zip-many<p>(Assumed audience: folks familiar with Combine’s syntax. Prior knowledge of zipping isn’t necessary, since I’ll build intuition and link to resources—but, if you’re already familiar with the topic, feel free to skip to “<a href="#a-variadic-publisherzip">a variadic <code class="language-plaintext highlighter-rouge">Publisher.zip</code></a>.”)</p>
<h3 id="updates">Updates:</h3>
<h4 id="52320">5/23/20:</h4>
<p><a href="https://twitter.com/xcadaverx">Daniel Williams</a> and I stepped through a crash he ran into with this post’s implementation. Notes from the conversation can be found <a href="/notes/variadic-zip-cash">here</a>.</p>
<hr />
<p>Zipping is backed by a metaphor that fits quite naturally. If you’ve used a physical zipper, your intuition is already primed.</p>
<p>Specifically, when a type supports a <code class="language-plaintext highlighter-rouge">zip</code> implementation, two (or more) of its instances can be <em>paired</em> in the same way teeth on a zipper meet when closing. Let’s ground this with a Standard Library example, <code class="language-plaintext highlighter-rouge">Sequence</code>.</p>
<h2 id="two-ary-sequence-zipping-with-zip__">Two-ary <code class="language-plaintext highlighter-rouge">Sequence</code> zipping with <code class="language-plaintext highlighter-rouge">zip(_:_:)</code></h2>
<p>Sequences are lists of any size, including empty and possibly infinite lengths. And if we have two in hand, what’re some ways to pair them?</p>
<p><em>pausing, so you can brainstorm.</em></p>
<p>Maybe return all possible pairs, channeling an inner-<a href="https://en.wikipedia.org/wiki/Cartesian_product">Cartesian</a>?</p>
<script src="https://gist.github.com/jasdev/fd340dff1bd5809f0aadbbf64e4dfd48.js"></script>
<p>Maybe losing information and pairing the first with a specific member of the second?</p>
<script src="https://gist.github.com/jasdev/970c074f5686141fa0a178da05ada9c3.js"></script>
<p>Or, maybe walking across both in lockstep.</p>
<script src="https://gist.github.com/jasdev/62b08d4ce4f3be5bb487341f0088fb8e.js"></script>
<p>While each have merits, the Standard Library ships with the third variant under the name <a href="https://developer.apple.com/documentation/swift/1541125-zip"><code class="language-plaintext highlighter-rouge">zip(_:_:)</code></a>. It walks over two sequences, index-wise, and stops once it reaches the end of the shorter.</p>
<p><a href="https://developer.apple.com/documentation/swift/zip2sequence"><code class="language-plaintext highlighter-rouge">Zip2Sequence</code></a> bookkeeps this traversal (which is why I opted for <code class="language-plaintext highlighter-rouge">≈</code> in the snippets above to avoid <code class="language-plaintext highlighter-rouge">Array</code>-casting).</p>
<p>Let’s <a href="https://developer.apple.com/search/?q=zip">search for “zip” across Apple’s documentation</a>.</p>
<p>…aside from the above free function and results from Combine, there isn’t anything else.</p>
<p>And that’s where we’ll fill in some gaps to exercise our intuition. Starting with an <a href="https://jasdev.me/notes/parity-arity">arity</a> three <code class="language-plaintext highlighter-rouge">zip</code> overload—i.e. a <code class="language-plaintext highlighter-rouge">zip(_:_:_:)</code> that accepts three sequences (and for simplicity, returns an <code class="language-plaintext highlighter-rouge">AnySequence</code> of triples).</p>
<p>Here’s some scaffolding we’ll work on.</p>
<script src="https://gist.github.com/jasdev/5cbbf814e8f1bbd542dace0a59f5ead3.js"></script>
<p>If we apply Swift’s provided <code class="language-plaintext highlighter-rouge">zip(_:_:)</code> twice, we can start sketching an implementation.</p>
<script src="https://gist.github.com/jasdev/b65351418a8ce2fe31325bcef90318a9.js"></script>
<p>To transform the unflattened—un<a href="https://github.com/apple/swift-evolution/blame/53c2e80bdede03b99aee683fe6399b6e4cf0bf95/proposals/0029-remove-implicit-tuple-splat.md#L31">splatted</a>?—form into an <code class="language-plaintext highlighter-rouge">(Sequence1.Element, Sequence2.Element, Sequence3.Element)</code> we need to <code class="language-plaintext highlighter-rouge">map</code> before finally wrapping everything in an <code class="language-plaintext highlighter-rouge">AnySequence.init</code> call.</p>
<script src="https://gist.github.com/jasdev/eeeaccf991e4e94725651551c371dfb2.js"></script>
<p>The <code class="language-plaintext highlighter-rouge">($0.0, $0.1, $1)</code> expression is dense—don’t second-guess yourself if you need to squint at it for a while. I certainly did.</p>
<p>All right, roll call. We’ve read through an existing <code class="language-plaintext highlighter-rouge">zip</code> in the Standard library and extended it. What about writing our own? That’s next before crossing over to Combine.</p>
<h2 id="implementing-resultzip">Implementing <code class="language-plaintext highlighter-rouge">Result.zip</code></h2>
<p>Swift packs its own <a href="https://github.com/apple/swift-evolution/blob/53c2e80bdede03b99aee683fe6399b6e4cf0bf95/proposals/0235-add-result.md"><code class="language-plaintext highlighter-rouge">Result</code> type</a>.</p>
<p>(For the unfamiliar, the linked SE-0235 proposal is an introduction to comb through before reading on.)</p>
<p>If we have two <code class="language-plaintext highlighter-rouge">Result</code> values in hand, then, like we did with <code class="language-plaintext highlighter-rouge">Sequence</code>, we can consider “pairing” them. To begin, we have to pick a case, <code class="language-plaintext highlighter-rouge">Result.success</code> or <code class="language-plaintext highlighter-rouge">.failure</code>. Let’s consider the success path, since we often favor (and map over) it.</p>
<p>So, assuming we have a <code class="language-plaintext highlighter-rouge">Result<Success, Failure></code> and <code class="language-plaintext highlighter-rouge">Result<OtherSuccess, Failure></code> we need to return something in the form <code class="language-plaintext highlighter-rouge">Result<_, Failure></code>.</p>
<p>A tuple is a natural fit for the paired, success generic. That is, <code class="language-plaintext highlighter-rouge">Result.zip</code> (in method form) would have the following shape:</p>
<script src="https://gist.github.com/jasdev/8292cc02b465ded09dc602f7d07c365d.js"></script>
<p><code class="language-plaintext highlighter-rouge">switch</code>ing over <code class="language-plaintext highlighter-rouge">(self, other)</code> guides the implementation.</p>
<script src="https://gist.github.com/jasdev/1cde6ba9befb9e81e70315d55511429b.js"></script>
<p>Now we can pair up <code class="language-plaintext highlighter-rouge">Result</code>s when they need to align along <code class="language-plaintext highlighter-rouge">.success</code>—e.g. imagine we need to check both a username and password with two validation functions, say <code class="language-plaintext highlighter-rouge">checkUsername: (String) -> Result<String, CredentialError></code> and <code class="language-plaintext highlighter-rouge">checkPassword: (String) -> Result<String, CredentialError></code>. <code class="language-plaintext highlighter-rouge">Result.zip</code> lets us chain <code class="language-plaintext highlighter-rouge">checkUsername(someUsername).zip(checkPassword(somePassword))</code>, returning a <code class="language-plaintext highlighter-rouge">Result<(String, String), CredentialError></code>.</p>
<p>You may have noticed a downside to this approach (that’s also commented in (4)). We’re dropping <code class="language-plaintext highlighter-rouge">checkPassword</code>’s error, if <code class="language-plaintext highlighter-rouge">checkUsername</code> is also in the <code class="language-plaintext highlighter-rouge">.failure</code> case.</p>
<p>The Point-Free duo covers this in <a href="https://www.pointfree.co/episodes/ep24-the-many-faces-of-zip-part-2#t98">Episode #24</a> and presents a generalization of <code class="language-plaintext highlighter-rouge">Result</code>, <code class="language-plaintext highlighter-rouge">Validated</code>, that allows for errors to be accumulated across zippings.</p>
<h2 id="a-variadic-publisherzip">A variadic <code class="language-plaintext highlighter-rouge">Publisher.zip</code></h2>
<p>Roll call number two.</p>
<p>So far we’ve extended Swift’s free-function <code class="language-plaintext highlighter-rouge">zip(_:_:)</code> an argument-length to <code class="language-plaintext highlighter-rouge">zip(_:_:_:)</code> and implemented <code class="language-plaintext highlighter-rouge">zip</code> on <code class="language-plaintext highlighter-rouge">Result</code>.</p>
<p>Now we’re ready for some Combine.</p>
<p>Let’s see which overloads the framework ships with,</p>
<ul>
<li><a href="https://developer.apple.com/documentation/combine/publisher/3204779-zip"><code class="language-plaintext highlighter-rouge">Publisher.zip(_:)</code></a> (and a <a href="https://developer.apple.com/documentation/combine/publisher/3333685-zip">transform variant</a>).</li>
<li><a href="https://developer.apple.com/documentation/combine/publisher/3204780-zip"><code class="language-plaintext highlighter-rouge">Publisher.zip(_:_:)</code></a> (again, a <a href="https://developer.apple.com/documentation/combine/publisher/3333686-zip">transform variant</a>).</li>
<li><a href="https://developer.apple.com/documentation/combine/publisher/3204781-zip"><code class="language-plaintext highlighter-rouge">Publisher.zip(_:_:_:)</code></a> (<a href="https://developer.apple.com/documentation/combine/publisher/3333687-zip">you know the drill</a>).</li>
</ul>
<p>And this is a solid set! Having a few, finite <code class="language-plaintext highlighter-rouge">zip</code>s lets us create higher arity overloads and the transforming versions save us a <code class="language-plaintext highlighter-rouge">.map(/* (_, …, _) -> Transformed */)</code> call, while better colocating transformations.</p>
<p>Still, there’s a missing piece. What if we don’t know the publisher count ahead of time?</p>
<p>We ran into this quite often at Peloton, especially when dealing with older, strictly-RESTful APIs.</p>
<p>An endpoint would return a list of identifiers, and then <em>per</em> identifier, we’d have to fire off another request to fetch metadata.</p>
<p>We’d usually treat these requests as all-or-nothing (if one fails, we’d bottom out the attempt) to simplify loading views.</p>
<p>To start the implementation of a variadic <code class="language-plaintext highlighter-rouge">zip</code>, we’ll need to extend the <code class="language-plaintext highlighter-rouge">Publisher</code> namespace.</p>
<p>When adding Combine operators, we have two options: composing existing operators<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> or, when needed, filling out a <code class="language-plaintext highlighter-rouge">Publisher</code> conformance.</p>
<script src="https://gist.github.com/jasdev/b801901063d88bf91f01021d5b908726.js"></script>
<p>If we zoom in on the parameter and return types, clearing the syntactic fog around <code class="language-plaintext highlighter-rouge">AnyPublisher</code> and re-writing <code class="language-plaintext highlighter-rouge">Publisher</code>’s associated types as a kind of generic, here’s what we’re dealing with:</p>
<p><code class="language-plaintext highlighter-rouge">[Publisher<Output, Failure>] -> Publisher<[Output], Failure></code></p>
<p>An array of publishers to be zipped into a single publisher emitting an array of <code class="language-plaintext highlighter-rouge">Output</code>s.</p>
<p>Other communities call this “flipping of containers” or the <a href="https://hackage.haskell.org/package/base-4.12.0.0/docs/Prelude.html#v:sequence">Haskell synonym, <code class="language-plaintext highlighter-rouge">sequence</code></a> (no worries if that link is incomprehensible, I added it so you can recognize the term when working across languages).</p>
<p>We have an array of publishers and need to…(spoiler alert)…reduce down to a single publisher. <a href="https://developer.apple.com/documentation/swift/sequence/2907677-reduce"><code class="language-plaintext highlighter-rouge">Sequence.reduce</code></a> to the rescue!</p>
<p>The method takes both an <code class="language-plaintext highlighter-rouge">initialResult</code> and <code class="language-plaintext highlighter-rouge">nextPartialResult</code> argument which brings us to the next question, when zipping arbitrarily many publishers, what’s the “initial result?”</p>
<p>In our case, it’s <code class="language-plaintext highlighter-rouge">self</code> (with some form adjustment to match the return type).</p>
<script src="https://gist.github.com/jasdev/1a78e883cc8f8145dccf63d4d6a0002b.js"></script>
<p>Now to reduce the remaining publishers. In pseudocode shorthand, if we have <code class="language-plaintext highlighter-rouge">Publisher<[Output], Failure></code> and <code class="language-plaintext highlighter-rouge">Publisher<Output, Failure></code> arguments, we need to fold the lone publisher into the array-emitting publisher.</p>
<p>Maybe we can two-<code class="language-plaintext highlighter-rouge">zip</code>?</p>
<script src="https://gist.github.com/jasdev/d619c33f60a90db2834b68f4b62e86b6.js"></script>
<p>Almost there! There’s a wrinkle to iron out: the <code class="language-plaintext highlighter-rouge">AnyPublisher<([Output], Output), Failure></code> and <code class="language-plaintext highlighter-rouge">AnyPublisher<[Output], Failure></code> mismatch.</p>
<p>And it’s a <code class="language-plaintext highlighter-rouge">map</code> away.</p>
<script src="https://gist.github.com/jasdev/d80decb26421a7f343eda339fcf37f5c.js"></script>
<p>⌘ + B’ing should finally work again.</p>
<p>Time to recap the path we took.</p>
<ul>
<li>Combine’s <code class="language-plaintext highlighter-rouge">Publisher.zip</code>s top out at arity three. Which, works well when we know the publisher count at compile time.</li>
<li>When we don’t, we can lean on the existing overloads and reduce and flatten to build a variadic version.</li>
</ul>
<p>Now, how would call sites look? Starting with a fabricated—and then more practical—example.</p>
<p>Imagine we had five timer publishers, each emitting with intervals ranging from 0.1–0.5 seconds and we wanted to lockstep emissions to the longest interval<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>.</p>
<script src="https://gist.github.com/jasdev/0f505d027f029efd47e71b45aec9793c.js"></script>
<p>Even though the publishers in <code class="language-plaintext highlighter-rouge">others</code> are <em>faster</em> than <code class="language-plaintext highlighter-rouge">first</code>, zipping gates them to emit in tandem. Each array the sink receives will be <code class="language-plaintext highlighter-rouge">first</code> and <code class="language-plaintext highlighter-rouge">others</code>’s first, second, third, …, <em>n</em>th value events, respectively.</p>
<p>Or, less abstractly, assume we have an array of user identifiers and want to (in an all-or-nothing fashion) make an API request per ID then collect the results.</p>
<p>To make our overload more ergonomic, let’s add a <code class="language-plaintext highlighter-rouge">[Publisher].zipped</code> variant.</p>
<script src="https://gist.github.com/jasdev/93c5ff2fe56c8f48dd2b9edd1ff99e02.js"></script>
<h2 id="looking-forward">Looking forward</h2>
<p>“Now what?” - <a href="https://knowyourmeme.com/photos/1142233-finding-nemo">Those fish from <em>Finding Nemo</em>, after reading this far</a>.</p>
<p><a href="https://github.com/CombineCommunity/CombineExt/pull/6">I landed</a> these overloads in a community collection of Combine extensions, <code class="language-plaintext highlighter-rouge">CombineExt</code>. So, it’s a CocoaPod-, Carthage-, or SPM-installation away.</p>
<p>We can then turn towards related combining operators and ask if they have variadic overloads.</p>
<p><code class="language-plaintext highlighter-rouge">Publisher.merge</code>? Turns out it’s <a href="/notes/merge-many">tucked away in the <code class="language-plaintext highlighter-rouge">Publishers</code> namespace</a>.</p>
<p><code class="language-plaintext highlighter-rouge">Publisher.combineLatest</code>? Like <code class="language-plaintext highlighter-rouge">zip</code>, it has an arity three ceiling. But hang tight—that’s what I’m <a href="https://github.com/CombineCommunity/CombineExt/pull/10">working on next</a>!</p>
<p>■</p>
<hr />
<h2 id="footnotes-and-related-reading">Footnotes and related reading</h2>
<p>⇒ It’s no coincidence that <code class="language-plaintext highlighter-rouge">Sequence</code>, <code class="language-plaintext highlighter-rouge">Result</code>, and <code class="language-plaintext highlighter-rouge">Publisher</code> all support a <code class="language-plaintext highlighter-rouge">zip</code> operation. A type that supports it is an applicative functor. Here’s a few links on the topic, for the curious:</p>
<ul>
<li>Type Classes’ <em><a href="https://typeclasses.com/functortown/applicative">Functortown: Applicative</a></em> series</li>
<li>Julie Moronuki’s “<a href="https://argumatronic.com/posts/2017-03-08-applicative-instances.html">Applicatives are monoidal</a>”</li>
<li>“<a href="https://jasdev.me/notes/applicatives-monoidal">Why applicatives are monoidal</a>” notebook entry</li>
<li><a href="https://www.youtube.com/watch?v=P2uxVQSHIjQ&feature=youtu.be&t=243">Lecture 16</a> (timestamped) of <em>Programming with Categories</em></li>
</ul>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p><a href="https://twitter.com/mattneub">Matt Neuburg</a> aptly called this route “composed operators” in their recent book, <a href="https://www.apeth.com/UnderstandingCombine/operators/operatorscomposed.html"><em>Understanding Combine</em></a>. An added benefit of composing existing operators is that back pressure handling comes for free, since each inner operator respects the <a href="https://developer.apple.com/documentation/combine/publisher"><code class="language-plaintext highlighter-rouge">Publisher</code> contract</a>. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>Borrowing from Shai’s <a href="https://twitter.com/freak4pc/status/1241424650623688704">example usage</a>. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Conditional flatMap’ing2020-03-14T00:00:00+00:00https://jasdev.me/conditional-flatmap<p>(Assumed audience: folks with a working knowledge of reactive operators and type erasure. Familiarity with Combine isn’t needed to read along.)</p>
<p>Let’s warm up<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> with a shorter post, shall we?</p>
<p><a href="https://twitter.com/freak4pc">Shai</a> showed me how to rewrite awkward “conditional <a href="https://developer.apple.com/documentation/combine/publisher/3204712-flatmap"><code class="language-plaintext highlighter-rouge">flatMap</code></a>s”—or relatedly, “conditional <a href="https://developer.apple.com/documentation/combine/publisher/3204718-map"><code class="language-plaintext highlighter-rouge">map</code></a> and <a href="https://developer.apple.com/documentation/combine/publisher/3204759-switchtolatest"><code class="language-plaintext highlighter-rouge">switchToLatest</code></a>” sequences.</p>
<p>Less abstractly, when we find ourselves checking a condition on upstream’s values and then <code class="language-plaintext highlighter-rouge">flatMap</code>ing on matches.</p>
<p>I’ve ordered the comments below to read more easily.</p>
<script src="https://gist.github.com/jasdev/8afa204fe3a208e9bf4326473f6b8898.js"></script>
<p>If we squint at the chain long enough—feel free to do so, if you don’t want spoilers—, a reworking shakes out.</p>
<p>And that’s what Shai taught me.</p>
<p>The <code class="language-plaintext highlighter-rouge">else</code> clause is a reactive no-op we can replicate by <code class="language-plaintext highlighter-rouge">filter</code>ing out <code class="language-plaintext highlighter-rouge">false</code> conditional checks entirely.</p>
<script src="https://gist.github.com/jasdev/5c1316119e2f0f173dd0c7f68f3dba51.js"></script>
<p>*</p>
<p>A small trick to add to the tool belt.</p>
<p>If you find yourself <code class="language-plaintext highlighter-rouge">Empty</code>ing out a sequence, it might be better to filter in on the conditions you’re considering, letting downstream lean on the checked precondition.</p>
<p>This is the reactive parallel to <code class="language-plaintext highlighter-rouge">guard</code>ing out early.</p>
<hr />
<h2 id="footnotes">Footnotes</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>It’s been some time since I’ve written here—trying not to beat myself up over the hiatus because well, a lot of Life has happened since the <a href="https://jasdev.me/operators">last entry</a> and well, <em>gestures widely at the macro state of affairs</em>. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Operator etymology2019-11-02T00:00:00+00:00https://jasdev.me/operators<p>(<em>Assumed audience</em>: folks with a working knowledge of Swift and an openness towards functional programming.)</p>
<p>There’s no dancing around it. Custom operators are <code class="language-plaintext highlighter-rouge">#holy-war</code> territory in the Swift community. You see it subtly mentioned in style guides:</p>
<blockquote>
<p><a href="https://github.com/Lickability/swift-best-practices/blame/f52debe08841dfd00a68b8680a2ae6d879a69a7a/CustomOperators.md#L3">Custom operators should be avoided. Custom operators can reduce the readability of the code because there can be confusion around their functionality.</a></p>
</blockquote>
<p>sidestepped in blog posts:</p>
<blockquote>
<p><a href="https://nshipster.com/swift-operators/#defining-custom-operators">One of the most exciting features of Swift (though also controversial) is the ability to define custom operators.</a></p>
</blockquote>
<p>and even Point-Free, the functional programming in Swift series, makes sure each operator they introduce <a href="https://www.pointfree.co/episodes/ep1-functions#t394">checks three important boxes</a>:</p>
<blockquote>
<ol>
<li>We shouldn’t overload an operator with existing meaning with new meaning.</li>
<li>We should leverage prior art as much as possible and make sure our operator has a nice “shape” that evokes its semantics.</li>
<li>We shouldn’t be inventing operators to solve very domain-specific problems. We should only introduce operators that can be used and reused in very general ways.</li>
</ol>
</blockquote>
<p>The above concerns aside, I wonder if operator dislike stems from the fact that we rarely pause to outline their shape. Why was a pipe and right arrow, <code class="language-plaintext highlighter-rouge">|></code>, chosen for forward function application? <code class="language-plaintext highlighter-rouge">flatMap</code> is written as <code class="language-plaintext highlighter-rouge">>>=</code> (and called <code class="language-plaintext highlighter-rouge">bind</code>) in other languages? <code class="language-plaintext highlighter-rouge"><$></code> is <code class="language-plaintext highlighter-rouge">map</code>…wat?</p>
<p>It’s completely reasonable for folks to shy away from operators if they can’t build an intuition around their function without context on symbol choice and origin, or, as I’ll dive into, operator etymology.</p>
<p>The sections below cover what I could find about some more common operators’ roots and my interpretation of their form. I did my best to summarize the concepts backing each in-line, linking out to additional resources when I couldn’t. Feel free to skip around or even come back to this post if you stumble upon a rogue operator in the wild or for a discussion about operator ergonomics and when to introduce functional programming terminology, skip to the last section.</p>
<h2 id="forward-and-backward-function-application--">Forward and backward function application: <code class="language-plaintext highlighter-rouge">|></code>, <code class="language-plaintext highlighter-rouge"><|</code></h2>
<p>We’ll start with (forward) function application (and cover the backward direction in a second). You might be scratching your head right about now: “isn’t function application simply tacking on parentheses after a function’s name and calling it a day? Why’d you have to go and make things ˢᵒ ᶜᵒᵐᵖˡᶦᶜᵃᵗᵉᵈ?”</p>
<p>Plain ol’ function application reads like an onion. If we had a function, say <code class="language-plaintext highlighter-rouge">incremented</code>, and a value in hand, say <code class="language-plaintext highlighter-rouge">2</code> (my fave number, shoutout to the only even prime) and wanted to get <code class="language-plaintext highlighter-rouge">3</code> out—riveting, I know—we wouldn’t think twice:</p>
<p><code class="language-plaintext highlighter-rouge">incremented(2) // ⇒ 3</code></p>
<p>Let’s think twice though. Reading from <em>left to right</em> we have to first mentally parse the function’s name and <em>then</em> stumble upon the value we want to apply. The onion-ness becomes apparent with function composition—time to toss a function <code class="language-plaintext highlighter-rouge">squared</code> into the mix:</p>
<p><code class="language-plaintext highlighter-rouge">squared(incremented(2)) // ⇒ 9</code></p>
<p>“<code class="language-plaintext highlighter-rouge">squared</code>, okay what’re we squaring? <code class="language-plaintext highlighter-rouge">incremented</code>, huh, okay okay, what are we incrementing? Ah! I see, <code class="language-plaintext highlighter-rouge">2</code>, Jasdev’s favorite number. Sweet—an incremented <code class="language-plaintext highlighter-rouge">2</code> is <code class="language-plaintext highlighter-rouge">3</code> and a squared <code class="language-plaintext highlighter-rouge">3</code> is <code class="language-plaintext highlighter-rouge">9</code>.”</p>
<p>Notice how we had to read left to right and then <em>back</em> to the left, peeling off onion rings of parentheses.</p>
<p>This is what our dear friend, the forward application operator, solves—that’s right, y’all are BFFs now. Most folks call it “pipe forward,” for short and maybe to sound less official. Here’s how you define it and its sibling in Swift:</p>
<script src="https://gist.github.com/jasdev/4d8d7c5978fc048ca4de7cacea9bf448.js"></script>
<p>Don’t fret if you haven’t stumbled upon <code class="language-plaintext highlighter-rouge">precedencegroup</code> or <code class="language-plaintext highlighter-rouge">associativity</code> in everyday Swift (most don’t)—they’re ways of helping the compiler determine an order of operations when parentheses are omitted. And ’associativity’ gives directionality to how we <em>associate</em> multiple expressions, i.e. either from the left or from the right. And for the curious, when the association order doesn’t matter (think addition of plain ol’ <code class="language-plaintext highlighter-rouge">Int</code>s), that’s what mathematicians mean when they say an operation is ‘associative.’</p>
<p>Back to squaring incremented numbers.</p>
<script src="https://gist.github.com/jasdev/705a1f3b170258923e052211ce212211.js"></script>
<p>The above reads linearly instead of nested-ly (let’s pretend that’s a word): “got a <code class="language-plaintext highlighter-rouge">2</code>, rad, piping it forward to <code class="language-plaintext highlighter-rouge">incremented</code>, and then the result to <code class="language-plaintext highlighter-rouge">squared</code>.”</p>
<p>Backwards function application loses the left-to-right, top-to-bottom readability while still allowing for parentheses-free expression. Its usage is rare and usually shows up in cases where the <em>primary</em> focus is the transformation and then the value we’re applying to it as an afterthought.</p>
<p>The symbol choice for <code class="language-plaintext highlighter-rouge">|></code> has roots in F# and Scala in addition to practically self-describing its pipe forwarding role.</p>
<p>Now that we’re warmed up, let’s talk composition.</p>
<h2 id="forward-and-backward-function-composition--">Forward and backward function composition: <code class="language-plaintext highlighter-rouge">>>></code>, <code class="language-plaintext highlighter-rouge"><<<</code></h2>
<p>The snippets above left repeated usages of pipe forward on the table—that is, while we’re afforded parentheses-free, linear reading, it came at the cost of two <code class="language-plaintext highlighter-rouge">|></code>’s and, as you might have guessed since this is a post on operator etymology and not a plot-twisting thriller, composition saves the day.</p>
<p>Instead of applying <code class="language-plaintext highlighter-rouge">2</code> to <em>two</em> functions, we can apply it once to the composition of <code class="language-plaintext highlighter-rouge">incremented</code> and <code class="language-plaintext highlighter-rouge">squared</code>.</p>
<script src="https://gist.github.com/jasdev/f52012a1b60021d9654775c004b2fe55.js"></script>
<p>Three greater-than signs is a fairly self-describing symbol choice in that the operator sequences functions from left to right and reflects well with <code class="language-plaintext highlighter-rouge"><<<</code> taking things in the opposite direction, i.e. <code class="language-plaintext highlighter-rouge">firstToSecond</code> and <code class="language-plaintext highlighter-rouge">secondToThird</code> swap spots:</p>
<script src="https://gist.github.com/jasdev/89085277a8720f40882cd2463014a73d.js"></script>
<p>The backwards variant is used when composition, <a href="https://www.pointfree.co/episodes/ep6-functional-setters#t545">somewhat surprisingly</a>, goes in the other direction.</p>
<p>It’s worth noting that <a href="https://hackage.haskell.org/package/base-4.12.0.0/docs/Prelude.html#v:.">Haskell’s function composition operator</a>, <code class="language-plaintext highlighter-rouge">.</code>, also reads right to left and closely resembles <a href="https://en.wikipedia.org/wiki/Function_composition">its cousin in mathematics</a>, <code class="language-plaintext highlighter-rouge">∘</code>, defined by <em>(g ∘ f )(x) = g(f(x))</em> (verbally read “<em>g</em> after <em>f</em>”).</p>
<p>The Swift community’s choice of <code class="language-plaintext highlighter-rouge">>>></code> and <code class="language-plaintext highlighter-rouge"><<<</code> instead of <code class="language-plaintext highlighter-rouge">.</code> or <code class="language-plaintext highlighter-rouge">∘</code> is two-fold: the first being that periods are <a href="https://docs.swift.org/swift-book/ReferenceManual/LexicalStructure.html#ID418">reserved in the language’s lexical structure</a> and the second being that, well, non-Unicode keyboard layouts don’t cover <code class="language-plaintext highlighter-rouge">∘</code><sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>.</p>
<p>The symbol choice has the added benefit of indicating directionality and follows <a href="https://github.com/purescript/purescript-prelude/blob/e3504b2f9f9e27a39b6e25a27eb6bb6e9045ad10/src/Control/Semigroupoid.purs#L18-L24">PureScript’s example</a>.</p>
<h2 id="semigroups-">Semigroups: <code class="language-plaintext highlighter-rouge"><></code></h2>
<p>Continuing on the arrow theme, we have the semigroup operator, <code class="language-plaintext highlighter-rouge"><></code>. Don’t fret if you’re not familiar with semigroups, it’s a semi-over-the-top name for an only semi-complicated structure and I’ll more-than-semi step through its definition by your side.</p>
<p>Semigroups are plain ol’ sets (in our case, types) with an extra bit of structure—an associative operator that takes two instances of the type and returns another instance (operators with this property are generally called “closed”).</p>
<p>You’ve probably worked with semigroups without even realizing they’re your coworkers. To name a couple,</p>
<ul>
<li>The set of all <code class="language-plaintext highlighter-rouge">String</code>s with <code class="language-plaintext highlighter-rouge"><></code> as <a href="https://developer.apple.com/documentation/swift/string/2431569">concatenation</a>.</li>
<li><code class="language-plaintext highlighter-rouge">Int</code> values (ignoring overflow crashes) under addition.</li>
<li>Booleans with <code class="language-plaintext highlighter-rouge"><></code>’s role being played by either <code class="language-plaintext highlighter-rouge">&&</code> or <code class="language-plaintext highlighter-rouge">||</code>.</li>
</ul>
<p>Adjacent left and right arrows for a semigroup’s operator has prior art in both <a href="https://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Semigroup.html">Haskell</a> and <a href="https://github.com/purescript/purescript-prelude/blob/e3504b2f9f9e27a39b6e25a27eb6bb6e9045ad10/src/Data/Semigroup.purs#L14-L25">PureScript</a>. I couldn’t find the story behind the symbol choice, yet my intuition around it is that semigroups are the “smallest“ structures, <a href="https://argumatronic.com/posts/2019-06-21-algebra-cheatsheet.html">amongst others</a>, we commonly run into. Operations on more elaborate structures like functors and applicatives <em>nest</em> symbols inside the arrows, making <code class="language-plaintext highlighter-rouge"><></code> a kind of parenthesis-like schema for operators we’ll soon discuss.</p>
<h2 id="functor-map---">Functor Map: <code class="language-plaintext highlighter-rouge"><$></code>, <code class="language-plaintext highlighter-rouge"><¢></code>, <code class="language-plaintext highlighter-rouge"><^></code></h2>
<p><em>checks if the coast is clear</em>—“functor”…there, I said it. Phew.</p>
<p>Using widely accepted, colloquial terms instead of ad hoc, yet supposedly more approachable substitutes is another unfortunate holy war in the community. I’ll dig into why I’m bummed about this in the “Ergonomics and Terminology” section. For short, by making up terminology for use in a specific community, we make it harder for folks from other communities to join ours and on the flip side, we incur translation costs when reading the decades-worth of research done under the original terminology.</p>
<p>I’ll start with the full definition and try to bring it down from orbit, and into Swift.</p>
<p>A functor is a structure-preserving mapping between categories.</p>
<p>(Once more, functors are likely already in your Swift tool belt—<code class="language-plaintext highlighter-rouge">Optional</code>, <code class="language-plaintext highlighter-rouge">Sequence</code>, <code class="language-plaintext highlighter-rouge">Result</code>, and <code class="language-plaintext highlighter-rouge">Publisher</code> are all functors.)</p>
<p>Let’s break down this definition, starting with “categories.“</p>
<p>I’ve previously touched on introductory category theory <a href="/duals">in my post on duals</a> (⌘ + F for “categories have a few properties” and read from there) and for a more thorough treatment, check out the lectures <a href="https://youtu.be/I8LbkfSSR58">1.1.1</a>–<a href="https://www.youtube.com/watch?v=EO86S2EZssc&list=PLbgaMIhjbmEnaH_LTkxLI7FMa2HsnawM_">1.6.2</a> in Bartosz Milewski’s stellar three-part Category Theory for Programmers series.</p>
<p>Summarizing, a category must have the following properties:</p>
<ul>
<li>Identity arrows for each object.</li>
<li>Composition and associativity of arrows.</li>
</ul>
<p>Swift <a href="http://math.andrej.com/2016/08/06/hask-is-not-a-category/">isn’t quite a category</a>—“there are technical details which break this idea, so [we’ll] need to be a bit careful.” Still, we can use category-theoretic constructions to gain intuition around functional programming instead of blindly Memorizing Laws.</p>
<p>Onto the ‘mapping’ part.</p>
<p>You might hear folks toss around the phrase “<a href="https://wiki.haskell.org/Functor#Functor_Laws">functor laws</a>.” Well, they originate from the fact that a functors map categories in a <em>careful</em> way.</p>
<p>What’re the categories involved when discussing functors? If we consider types in Swift—<code class="language-plaintext highlighter-rouge">Result</code>, <code class="language-plaintext highlighter-rouge">Publisher</code>, tuples, you name it—to be objects and functions between any two to be arrows, we get a sort of category we can denote <strong>Swift</strong>. “But, Jasdev, functors are mappings between categories and <strong>Swift</strong> is only one.”</p>
<p>Great catch, jedi.</p>
<p>Turns out we can map categories into themselves (wild, I know) and such functors are called “endofunctors.”</p>
<p>(We usually drop the ‘endo’ prefix since functional programming exclusively deals with <strong>Swift</strong> ⇒ <strong>Swift</strong>, <strong>Hask</strong> ⇒ <strong>Hask</strong>, or <strong>[Your language of choice]</strong> ⇒ <strong>[Your language of choice]</strong>.)</p>
<p>Now, onto “structure-preserving.”</p>
<p>The choice word ‘structure,’ until recently, felt out of reach. I’d wonder, “Structure? How can a bunch of intangible objects and arrows have structure?”</p>
<p>It clicked when I started viewing arrows as scaffolding in a category, letting you climb from one object to another. And through that perspective, functors make sure the scaffolding, the structure, the arrows of the source category, are maintained across the mapping and into the target category.</p>
<p>Specifically, that identity arrows are mapped to identity arrows and that composition is preserved across the functor.</p>
<p><code class="language-plaintext highlighter-rouge">Optional<Wrapped></code> is a functor in that it maps types (the objects, in the Category Theoretic and not <a href="https://en.wikipedia.org/wiki/Object-oriented_programming">OOP</a>-sense) in <strong>Swift</strong> into other types in <strong>Swift</strong> and functions (the arrows) into functions acting on optionals.</p>
<p>Explicitly,</p>
<p><code class="language-plaintext highlighter-rouge">Optional<Wrapped></code> is a type-level function from <code class="language-plaintext highlighter-rouge">Wrapped -> Optional<Wrapped></code>.</p>
<p><code class="language-plaintext highlighter-rouge">Optional.map</code>, in its Standard Library form, is a <em>method</em> and has the signature <code class="language-plaintext highlighter-rouge">(Optional<Wrapped>) -> ((Wrapped) -> U) -> Optional<U></code><sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>. The ordering here makes it difficult to see how the <code class="language-plaintext highlighter-rouge">Optional</code> functor <em>lifts</em> functions into another context, so let’s flip the first two parameters: <code class="language-plaintext highlighter-rouge">((Wrapped) -> U) -> (Optional<Wrapped>) -> Optional<U></code>.</p>
<p>There we go.</p>
<p>Shuffling makes it clearer that <code class="language-plaintext highlighter-rouge">Optional</code> takes plain ol’ <code class="language-plaintext highlighter-rouge">(Wrapped) -> U</code> transformations and makes them <code class="language-plaintext highlighter-rouge">(Optional<Wrapped>) -> Optional<U></code>’s by way of <code class="language-plaintext highlighter-rouge">map</code>.</p>
<p>So, what’s the dollars, cents, and carets—<code class="language-plaintext highlighter-rouge"><$></code>, <code class="language-plaintext highlighter-rouge"><¢></code>, <code class="language-plaintext highlighter-rouge"><^></code>—section heading got to do with this?</p>
<p>It turns out Haskell’s <em>generalized</em> map, <code class="language-plaintext highlighter-rouge">fmap</code>, read “functor map” (across all functors, i.e. not specialized to types like <code class="language-plaintext highlighter-rouge">Optional</code>, <code class="language-plaintext highlighter-rouge">Sequence</code>, <code class="language-plaintext highlighter-rouge">Result</code>, and <code class="language-plaintext highlighter-rouge">Publisher</code>) has an operator synonym, <code class="language-plaintext highlighter-rouge"><$></code>. And the inner <code class="language-plaintext highlighter-rouge">$</code> comes from the language’s function application operator (Haskell’s analog to <code class="language-plaintext highlighter-rouge">|></code> we defined above)!</p>
<p>In the same way that Semigroup’s <code class="language-plaintext highlighter-rouge"><></code> schema creates a context in which we can perform actions, <code class="language-plaintext highlighter-rouge"><$></code> is function application <em>in</em> a functorial context.</p>
<p>I wish someone had told me this sooner, so I’m filling you in on the secret now.</p>
<p>Back to Swift.</p>
<p><code class="language-plaintext highlighter-rouge"><$></code> and the back tick-escaped form aren’t valid operator definitions in Swift—and, that’s finally where <code class="language-plaintext highlighter-rouge"><¢></code> and <code class="language-plaintext highlighter-rouge"><^></code> come in.</p>
<p>The two are Swift alternatives to <code class="language-plaintext highlighter-rouge"><$></code>.</p>
<p>(While <code class="language-plaintext highlighter-rouge"><|>></code> seems to better follow suit with symbol nesting inside, the imbalanced arrows probably make chains hard to read.)</p>
<p>Point-Free’s <a href="https://github.com/pointfreeco/swift-prelude/blob/b26e98e82c7598fd5f381b3a27b59e27e312eb58/Sources/Prelude/Operators.swift#L9">Prelude opted for <code class="language-plaintext highlighter-rouge"><¢></code></a> and <a href="https://github.com/typelift/Operadics/blob/ef9c00e70ac4e5fba046f4eae3bd4e4e8f058648/Sources/Operadics/Operators.swift#L81-L82">the TypeLift</a> and <a href="https://github.com/thoughtbot/Runes/blob/5395accc1dbe99fe457a14810c0fc20c7f298656/Sources/Runes/Runes.swift#L31-L37">Thoughtbot folks</a> opted for <code class="language-plaintext highlighter-rouge"><^></code>—with the cents symbol being a play on the dollar sign and the caret signaling “lifting” an ordinary function into a functor’s context.</p>
<p>And there you have it, make some noise for functors!</p>
<p>I’ll touch on more specific functors below, namely applicative and monads, but, if you want a larger view of the landscape, Type Class’ “<a href="https://typeclasses.com/functortown">Functortown</a>” series has you covered.</p>
<h2 id="applicatives-">Applicatives: <code class="language-plaintext highlighter-rouge"><*></code></h2>
<p>Applicatives warrant a post of their own—so, I’ll leave you with <a href="https://www.youtube.com/watch?v=Awva79gjoHY">a talk</a>, <a href="https://thoughtbot.com/blog/efficient-json-in-swift-with-functional-concepts-and-generics">some writing</a>, and a <a href="/notes/applicatives-monoidal">recent note</a>.</p>
<p>Though, I do want to cover the etymology behind <code class="language-plaintext highlighter-rouge"><*></code> (often called “the <a href="https://en.wikipedia.org/wiki/TIE_fighter">tie fighter</a> operator,” don’t worry I haven’t seen all of Star Wars, either).</p>
<p>You might often hear folks call applicatives, “<a href="https://argumatronic.com/posts/2017-03-08-applicative-instances.html">monoidal functors</a>.” Sounds intimidating, but, if you’ve been reading a long, the intuition is an arm-length away.</p>
<p>Functors? Check. We went over them above. And it’s okay if they haven’t quite clicked; it took a while for me.</p>
<p>Monoidal? That’s the adjective form of “monoid.” They’re semigroups with an <em>additional</em> requirement, a neutral element with respect to the binary operator. Let’s scaffold out two protocols to show the relationship here.</p>
<script src="https://gist.github.com/jasdev/962bc702f743ad2484004ddfb8a37223.js"></script>
<p>Don’t sweat <code class="language-plaintext highlighter-rouge">DefaultPrecedence</code> here, I used it as a placeholder to sketch things out (in a production setting, you’d need a more <a href="https://github.com/pointfreeco/swift-prelude/blob/b26e98e82c7598fd5f381b3a27b59e27e312eb58/Sources/Prelude/PrecedenceGroups.swift">explicit precedence group structure</a><sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>). Again, <code class="language-plaintext highlighter-rouge"><></code> needs to be associative and the closed requirement is handled by the <code class="language-plaintext highlighter-rouge">Self</code> return type matching the parameters’ types.</p>
<p><code class="language-plaintext highlighter-rouge">Monoid</code> inherits from <code class="language-plaintext highlighter-rouge">Semigroup</code> and tacks on the neutral element requirement, often called <code class="language-plaintext highlighter-rouge">empty</code> or <code class="language-plaintext highlighter-rouge">mempty</code>, for “monoid(al?) empty.” <code class="language-plaintext highlighter-rouge">.empty</code> must satisfy the following condition, for other instances of the conforming type (let’s call an arbitrary instance here <code class="language-plaintext highlighter-rouge">other</code>):</p>
<p><code class="language-plaintext highlighter-rouge">(other <> .empty) == (.empty <> other) == other</code></p>
<p>Now, if I were to guess (if you know the full backstory, please reach out), folks didn’t want to nest <code class="language-plaintext highlighter-rouge"><></code> inside of the semigroup <code class="language-plaintext highlighter-rouge"><_></code> format (“yo, dawg”), with <code class="language-plaintext highlighter-rouge"><<>></code> for applicatives and instead followed math’s suit in using <code class="language-plaintext highlighter-rouge">*</code> to signal the combining operator.</p>
<p>That is, applicatives—monoidal functors—take two values in a context and reduce it into a single value in the same context in the same way monoids reduce two values into one. Rephrased, if you can define <a href="https://developer.apple.com/documentation/swift/1541125-zip"><code class="language-plaintext highlighter-rouge">zip</code></a> on a functor, you have an applicative in hand.</p>
<h2 id="monadic-composition-and-binding--">Monadic composition and binding: <code class="language-plaintext highlighter-rouge">>=></code>, <code class="language-plaintext highlighter-rouge">>>=</code></h2>
<p>We’re over two thousands into this post and I’d probably need double that to cover monads. Thankfully, there is <a href="https://broomburgo.github.io/fun-ios/post/why-monads/">fantastic writing</a> (also in Swift) on the topic and, for the bird’s-eye, category theoretic view, Bartosz has you covered in <a href="https://youtu.be/gHiyzctYqZ0">lecture 1.10.1</a>.</p>
<p>Two operators you’ll come across when working with such instances are monadic compose—informally called the “fish operator,” or formally, the “Kleisli arrow”—and bind: <code class="language-plaintext highlighter-rouge">>=></code> and <code class="language-plaintext highlighter-rouge">>>=</code>, respectively.</p>
<p>Let’s start with composition.</p>
<p>(For a more thorough introduction, the Point-Free folks step though this in their <a href="https://www.pointfree.co/episodes/ep2-side-effects#t660">Side Effects</a> episode.)</p>
<p>The functors we mentioned earlier—<code class="language-plaintext highlighter-rouge">Optional</code>, <code class="language-plaintext highlighter-rouge">Sequence</code>, <code class="language-plaintext highlighter-rouge">Result</code>, and <code class="language-plaintext highlighter-rouge">Publisher</code>—are <em>also</em> monads. Now, imagine we wanted to compose two functions from a generic type into an <code class="language-plaintext highlighter-rouge">Optional</code>, for sake of example.</p>
<p><code class="language-plaintext highlighter-rouge">First -> Optional<Second></code> and <code class="language-plaintext highlighter-rouge">Second -> Optional<Third></code>.</p>
<p>Can we combine them to
create a <code class="language-plaintext highlighter-rouge">First -> Optional<Third></code> transformation?</p>
<p>Almost! Ordinary function composition, <code class="language-plaintext highlighter-rouge">>>></code>, doesn’t quite get us there since the output of the first function doesn’t match the input of the second.</p>
<p>And that’s what the swapping of the second “>” in <code class="language-plaintext highlighter-rouge">>>></code> for “=“ in <code class="language-plaintext highlighter-rouge">>=></code> tries to convey. To restore composition, we need to introduce an intermediary <em>binding</em> and, in other languages, the equals sign is often read as binding some value to a named variable (in the same way that <a href="https://alisoftware.github.io/swift/pattern-matching/2016/03/27/pattern-matching-1/">we <code class="language-plaintext highlighter-rouge">if</code>-<code class="language-plaintext highlighter-rouge">let</code>, <code class="language-plaintext highlighter-rouge">guard</code>-<code class="language-plaintext highlighter-rouge">let</code>, or <code class="language-plaintext highlighter-rouge">case</code>-<code class="language-plaintext highlighter-rouge">let</code> bind in Swift</a>).</p>
<p>To see why we need that intermediary binding when composing monads, here’s a sketch of <code class="language-plaintext highlighter-rouge">>=></code> for <code class="language-plaintext highlighter-rouge">Optional</code>s (again, using a default precedence to get it compiling).</p>
<script src="https://gist.github.com/jasdev/8bf1f0bacddda120cca066728c8d286f.js"></script>
<p>The <code class="language-plaintext highlighter-rouge">case</code>-<code class="language-plaintext highlighter-rouge">let</code> on line 13 is an example of the binding needed to complete the chain. That is, we need to bind the inner component of <code class="language-plaintext highlighter-rouge">firstToOptionalSecond</code>’s return value and pipe it into <code class="language-plaintext highlighter-rouge">secondToOptionalThird</code>.</p>
<p>Implementing <code class="language-plaintext highlighter-rouge">>=></code> for <code class="language-plaintext highlighter-rouge">Sequence</code>, <code class="language-plaintext highlighter-rouge">Result</code>, and <code class="language-plaintext highlighter-rouge">Publisher</code> also takes a similar shape in that the operator will need to bind to some inner part—intermediary sequences, a success case, or an element from a publisher—to forward along to the next transformation.</p>
<p>Onto bind.</p>
<p><code class="language-plaintext highlighter-rouge">>>=</code>’s shape is close to <code class="language-plaintext highlighter-rouge">|></code>’s (in pseudo-Swift and where <code class="language-plaintext highlighter-rouge">Monad</code> is your monad of choice).</p>
<script src="https://gist.github.com/jasdev/14110080702ddd226c4316de9b7d1438.js"></script>
<p>The intuition here is that if you have a monadic instance at hand and some transformation that acts on its component, you can then retrieve another instance containing the output of the transformation.</p>
<p>Or, as <a href="https://twitter.com/morabbin/status/1166630610900848641">@morabbin put it</a> in response to my since-deleted tweet:</p>
<blockquote>
<p>“Anyone know of (or have resources on) the story behind the <code class="language-plaintext highlighter-rouge">>>=</code> operator choice (guessing it’s a shuffling of the Kleisli arrow, <code class="language-plaintext highlighter-rouge">>=></code>)?”</p>
</blockquote>
<blockquote>
<p>@morabbin: “(Sequence <code class="language-plaintext highlighter-rouge">>></code>) + (binding <code class="language-plaintext highlighter-rouge">=</code>)”</p>
</blockquote>
<blockquote>
<p>@morabbin: “was around long before the Kleisli arrow symbol I think.”</p>
</blockquote>
<hr />
<h2 id="ergonomics-and-terminology">Ergonomics and Terminology</h2>
<p>Phew. That was a lot and if you read through the above in full or skipped around (either is fine), I’m going to close with a discussion that often shakes out when considering operators in a team setting—starting with ergonomics.</p>
<p>It’s true, we lose Xcode’s autocomplete when using operators over named functions.</p>
<p>Still, the tooling limitation likely isn’t insurmountable, since both are backed by function implementations.</p>
<p>Then, there’s readability.</p>
<p>Let’s compare <code class="language-plaintext highlighter-rouge">>>></code> and a named-version, <a href="https://github.com/pointfreeco/swift-overture/blob/e3e63876688ca8b6f327e5af7018f5e93e742367/Sources/Overture/Compose.swift#L2-L74"><code class="language-plaintext highlighter-rouge">compose</code></a>, when chaining along three functions, <code class="language-plaintext highlighter-rouge">firstToSecond</code>, <code class="language-plaintext highlighter-rouge">secondToThird</code>, and <code class="language-plaintext highlighter-rouge">thirdToFourth</code>.</p>
<script src="https://gist.github.com/jasdev/ba3d4d81a04b9274df674affe9b495ed.js"></script>
<p>Functions—in Swift—will always require parentheses when applied. And that’s unfortunate, because the operators we’ll often use are associative, allowing us to drop them in the first place.</p>
<p>Moreover, even though “compose” may feel more approachable than <code class="language-plaintext highlighter-rouge">>>></code>, we’ll likely <em>still</em> need to look up its signature when encountering it for the first time to understand exactly what it’s doing (i.e. “compose,” as a verb, has overloaded meaning in that it works with ordinary functions, over specific monads instances, and the like).</p>
<p>It makes me pause that, when encountering a new word in ordinary languages, most folks would be open to—and not think twice about—looking up the definition. Then, when it comes to operators, rule them out entirely since they might not be readable at <em>first glance</em>.</p>
<p>And that makes me sad.</p>
<p>It’s limiting to only work with terms, abstractions, and functions that are within reach of one’s existing knowledge. Rephrased, how can we expect to learn anything new if we don’t open ourselves up to new ideas and terminology?</p>
<p>Alexis King put this succinctly in “<a href="https://lexi-lambda.github.io/blog/2016/08/11/climbing-the-infinite-ladder-of-abstraction/">Climbing the infinite ladder of abstraction</a>”</p>
<blockquote>
<p>Programmers are professionals, and we work in a technical domain. I am absolutely of the belief that programming, like any other field, is not always about what comes easiest: sometimes it’s important to sit down and study for a while to grok a particularly complicated concept</p>
</blockquote>
<p>Just because operators aren’t familiar, doesn’t mean we should throw them out entirely.</p>
<p>The same is true for terminology and the concepts behind them.</p>
<p>I believe functional programming (and mathematics) is best taught when terminology introduced after a structure’s shape has been observed.</p>
<p>Lifting a function between two types to a function between them inside another context.</p>
<p>The flattening of nested types.</p>
<p>The ability to join two instances of the same type into another instance of that type.</p>
<p>Once a pattern has been understood, then it can more readily be collected under an abstraction and named—in the same way generics and protocols should generally be introduced <em>after</em> noticing repeated behavior and not the other way around.</p>
<p>Then, there’s the naming part.</p>
<p>It’s frequently argued that opting for original, mathematics-backed names for abstractions—functor, monads, et al—is a form “gatekeeping.”</p>
<p>And sure, when used excessively without proper introduction they can appear to be intellectual posturing.</p>
<p>But, that speaks more to a potential mismatch between educational material and the assumed audience or a lack of proper onboarding.</p>
<p>Because, <a href="https://www.parsonsmatt.org/2019/08/30/why_functor_doesnt_matter.html">as Matt Parsons mentions</a>:</p>
<blockquote>
<p>Functor is hard to learn. It is not hard to learn because it is named “functor.” If you renamed it to anything else, you’d have just as hard of a time, and you’d be cutting off your student from all of the resources and information currently using the word “functor” to refer to that concept.</p>
</blockquote>
<p>Imagine if our community sidestepped “functor,” for example, and instead went with an alternative like “mappable.”</p>
<p>Two things would likely happen.</p>
<p>We would confuse folks by tautologically pushing the definition of “to map” even further: “things that can be mapped are ‘mappable,’ wait, what <em>exactly</em> does mapping mean?”</p>
<p>Secondly,—and the counterintuitive bit—is that by not using commonplace terms, Swift would become an island relative to other communities and, in a sense, that’s gate-keeping others out while also cutting ourselves off from existing literature and adding translation overhead.</p>
<hr />
<p>The next decade of engineering in the Swift ecosystem will be dense with change.</p>
<p>SwiftUI will require us think declaratively, instead of imperatively (à la UIKit).</p>
<p>In such settings, data flow can only reasonably be managed with functional reactive programming and Combine is now the provided tool of choice.</p>
<p>As a community, these paradigms will require open-mindedness and following precedents set by decades-worth of research in the mathematics and functional programming. Still, their previous learnings will be better appreciated if we start with the etymology.</p>
<p>Then, down the line, we’ll eventually contribute back with new abstractions and help tell the story of their etymology to future engineers when joining our community.</p>
<hr />
<h2 id="related-reading-and-footnotes">Related reading and footnotes</h2>
<p>⇒ “<a href="https://www.parsonsmatt.org/2019/08/30/why_functor_doesnt_matter.html">Why ‘Functor’ Doesn’t Matter</a>.”</p>
<p>⇒ “<a href="https://github.com/hypotext/notation">Notation and Thought</a>.”</p>
<p>⇒ <a href="https://github.com/pointfreeco/swift-overture">Point-Free’s Overture</a>, an operator-free library for function composition.</p>
<p>⇒ “Algebraic Roots—<a href="http://devlinsangle.blogspot.com/2016/04/algebraic-roots-part-1.html">Part 1</a> and <a href="https://devlinsangle.blogspot.com/2016/05/what-does-it-mean-to-do-algebra-in-part.html">2</a>.”</p>
<p>⇒ The “A New Interface” chapter of Jeremy Kun’s “<a href="https://pimbook.org">A Programmer’s Introduction to Mathematics</a>.”</p>
<p>⇒ An operator-heavy topic is <a href="https://www.youtube.com/watch?v=sFzuu676pFs">optics</a>. Here are a couple of resources I’ve found that tour its history and etymology: <a href="https://medium.com/@russmatney/haskell-lens-operator-onboarding-a235481e8fac">Haskell Lens Operator Onboarding</a> and <a href="https://github.com/typelift/Focus#further-reading">Focus’ Further Reading section</a>.</p>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>I recently came across <a href="https://software.sil.org/ukelele/">Ukelele</a>, a Unicode keyboard layout editor, which could help with inputting more-apt, yet physically-absent symbols. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>Ole Begemann <a href="https://oleb.net/blog/2014/07/swift-instance-methods-curried-functions/">wrote a wonderful post</a> on how unapplied methods are “curried” functions in disguise. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>For another setup, here are <a href="https://github.com/thoughtbot/Runes/blob/5395accc1dbe99fe457a14810c0fc20c7f298656/Sources/Runes/Runes.swift">Rune’s precedence groups</a>. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Deriving reactive from imperative: an introduction to duals2019-07-26T00:00:00+00:00https://jasdev.me/duals<p>While <a href="https://developer.apple.com/videos/play/wwdc2019/722/">Apple’s Combine framework</a> didn’t take center stage, its implications on our community certainly will for the next decade. We now have a first-party toolkit for functional reactive programming (often abbreviated “FRP”) that packs an intimidating <a href="https://developer.apple.com/documentation/combine/publishers">namespace of operators</a>, <a href="https://developer.apple.com/documentation/combine/subject">opaque abstractions</a>, and features even Combine’s open-source counterparts omit like <a href="https://developer.apple.com/documentation/combine/subscribers/demand">backpressure</a>.</p>
<p>This post aims to unpack those choice adjectives, ‘opaque’ and ‘intimidating,’ that are often lobbed at FRP when folks retreat to its more familiar sibling, imperative programming. We’ll start by contrasting the approaches and covering some history before deriving Combine’s foundational types, <code class="language-plaintext highlighter-rouge">Subscriber</code> and <code class="language-plaintext highlighter-rouge">Publisher</code>.</p>
<h2 id="imperative-and-reactive-er-declarative">Imperative and Reactive (er, Declarative)</h2>
<p>FRP is a way of handling data flow in a declarative style of computation. At its core, declarative programming focuses on the <em>what</em> of our programs, whereas imperative is focused on <em>how</em> to achieve some end, down to the exact commands and control flow.</p>
<p>“An analytics event should only fire once on view appearance.” As opposed to “we’ll bookkeep that we’ve only fired the event once by adding a boolean to our view and making sure we only flip it, when appropriate.”</p>
<p>“The screen should remove its loading view only when these three API requests have returned successfully” over “here’s three semaphores that signal on each respective API request. Use them to know when to finally remove loading view.”</p>
<p>To further ground this distinction, here’s an example I often run into while working on <a href="https://apps.apple.com/app/apple-store/id792750948">Peloton Digital</a>, updating an already-rendered cell in response to a member’s action. For instance, when a member (un)bookmarks a show, we want to update the state of every cell corresponding to it. The first snippet takes a declarative approach and the latter, more imperatively. (Pardon the RxSwift—Combine’s iOS 13.0+ requirement prevents us from using it in that target…<a href="https://twitter.com/jasdev/status/1151823053552271360">we’re luckier in others</a>).</p>
<p><img src="/public/images/rx_bookmark.png" alt="Snippet showing how to reactively handle two `Notification.Name`’s: `.createdBookmark` and `.deletedBookmark`. And update the cell corresponding to the show’s bookmarked state." /></p>
<p>Now, imperatively:</p>
<p><img src="/public/images/imperative_bookmark.png" alt="Snippet showing how to imperatively handle two `Notification.Name`’s: `.createdBookmark` and `.deletedBookmark`. And update the cell corresponding to the show’s bookmarked state." /></p>
<p>Transformations and relationships are the focus of the former approach. “Listen to these notifications, filter in on the firings we want to consider, map them as booleans, merge the sequences, lift the boolean to the appropriate bookmarked state image, and bind it to our button. Then, dispose of the sequence’s lifecycle when <code class="language-plaintext highlighter-rouge">disposeBag</code> deinitializes.”</p>
<p>Imperatively, all of the above is considered to a mechanical level. Details like <code class="language-plaintext highlighter-rouge">guard</code>’ing out control flow, managing capture semantics, explicitly calling <code class="language-plaintext highlighter-rouge">UIButton.setImage(_:for:)</code>, and remembering to remove the observer farther away from these attachment points in source all have to be spelled out.</p>
<h2 id="history">History</h2>
<p>Reactive programming has <a href="https://www.cocoawithlove.com/blog/reactive-programming-what-and-why.html#appendix-a-little-bit-of-history">been around for some time</a>. It draws inspiration from “Alan Kay’s 1969 paper <a href="https://www.mprove.de/visionreality/media/kay69.html">The Reactive Engine</a>, [and] modern use of the term ‘reactive programming’ refers to ideas started in Conal Elliot and Paul Hudak’s 1997 paper <a href="http://conal.net/papers/icfp97/">Function Reactive Animation</a>.” However, the paradigm caught wind in late 2009 when Reactive Extensions (Rx) for .NET was released, outlining a specification for other languages to conform to and, by extension (pun intended), join the <a href="https://github.com/ReactiveX">Rx family of libraries</a>.</p>
<p>ReactiveCocoa (“RAC,” for short) provided an Objective-C implementation of reactive programming <a href="https://github.blog/2012-05-04-reactivecocoa-for-a-better-world/">in May 2012</a>—and was ported to Swift in its 3.x release and is now split into two repositories: one <a href="https://github.com/ReactiveCocoa/ReactiveCocoa">under the same name</a> and the other being <a href="https://github.com/ReactiveCocoa/ReactiveSwift">ReactiveSwift</a>. RxSwift followed suit with its first tagged release landing in May 2015. Comparing the two is <a href="https://ashfurrow.com/blog/reactivecocoa-vs-rxswift/">a post in and of itself</a>, yet, the abstraction behind the implementations is what matters. And sharing that abstraction allows different codebases to <a href="https://www.youtube.com/watch?v=_DuGaAkQSnM&t=710">build on the same foundation</a>.</p>
<p>How have (Reactive|Rx)Swift been around for at least four years and still not become the common tool of choice for building applications? The answer isn’t straightforward—still, we can list a few culprits:</p>
<ul>
<li>The frameworks, despite being open-source, are external dependencies and that’s a risk to consider when working in Apple’s ecosystem.</li>
<li>FRP often makes code easier to reason about in the long run, at the cost of a steep on-boarding curve and <a href="https://twitter.com/chriseidhof/status/1104045586737741826">having a team of peers who are well-versed and bought into the paradigm</a>.</li>
<li>Traditional education focuses primarily (if not exclusively) on imperative programming. So, while reactive programming is the other side of the same coin, its incidental complexity is only exacerbated by unfamiliarity.</li>
</ul>
<p>If I were to guess, the unfamiliarity with and months-long patience required to grok FRP keep folks at bay. Helping others climb reactive programming’s learning curve will involve re-thinking educational material on the topic. Thankfully, reactive programming has roots in mathematics we can rely on and deriving the core types in Combine involves “flipping the arrows” of imperative primitives, <code class="language-plaintext highlighter-rouge">IteratorProtocol</code> and <code class="language-plaintext highlighter-rouge">Sequence</code>, or as I’ll show, defining their duals.</p>
<h2 id="duals">Duals</h2>
<p>“Dual” is one of those five dollar words for a five cent idea. Chances are you’ve used a dual in everyday programming.</p>
<p>Moreover, I’d wager anyone who has been introduced to a mathematical function, i.e. a mapping from one set, say <code class="language-plaintext highlighter-rouge">A</code>, to another, say <code class="language-plaintext highlighter-rouge">B</code>, ran into their first dual. If we call such a function <code class="language-plaintext highlighter-rouge">f</code>, then <code class="language-plaintext highlighter-rouge">A</code> is its domain (the starting set) and <code class="language-plaintext highlighter-rouge">B</code> is its range (the target set). Or, you might’ve heard <code class="language-plaintext highlighter-rouge">B</code> referred to as the co-domain—and that “co-” prefix connotes a duality. If we “flipped the arrow” of <code class="language-plaintext highlighter-rouge">f</code> to instead start from <code class="language-plaintext highlighter-rouge">B</code> and point towards <code class="language-plaintext highlighter-rouge">A</code>, then <code class="language-plaintext highlighter-rouge">B</code> would be our domain.</p>
<p><img src="/public/images/domain_codomain.png" alt="A function `f` from set `A` to `B` and its dual going the other direction." /></p>
<p>Let’s formalize “flipping the arrows.”</p>
<p>Duals are most readily seen from a category-theoretic perspective. Category theory is the study of structure and composition and its fundamental unit is, you guessed it, the category. Categories have a few properties:</p>
<ul>
<li>It consists of objects…really, that’s all the detail we get about them. That’s also the beauty of it. Category theory erases the details that delineate branches of mathematics to show their similarities.</li>
<li>There are directed “arrows” between objects and those arrows must include one for each object to itself (an identity arrow) and compose <em>associatively</em> (denoted by <code class="language-plaintext highlighter-rouge">g</code> ∘ <code class="language-plaintext highlighter-rouge">f</code>, if <code class="language-plaintext highlighter-rouge">f</code> is applied first, followed by <code class="language-plaintext highlighter-rouge">g</code> or <code class="language-plaintext highlighter-rouge">g(f(x))</code> in a programming context). Put another way, an object, <code class="language-plaintext highlighter-rouge">A</code>, must have an arrow to itself, and if it has another pointing towards <code class="language-plaintext highlighter-rouge">B</code> and <code class="language-plaintext highlighter-rouge">B</code> has one towards <code class="language-plaintext highlighter-rouge">C</code>, then there exists an arrow from <code class="language-plaintext highlighter-rouge">A</code> to <code class="language-plaintext highlighter-rouge">C</code> representing the composition of those two arrows.</li>
</ul>
<p><img src="/public/images/category_definition.png" alt="Category with four objects, `A`, `B`, `C`, and `D` with morphisms between each in that order and also arrows for their compositions and identities." /></p>
<p>From this construction, category theory provides a template for a lot of mathematics. If we consider the objects to be the types in our programs and arrows to be functions between them, then we almost<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> have a category.</p>
<p>Now we can start flipping arrows. For every construction, we can reverse the arrows to construct a dual. Below is a sketch of defining a tuple in terms of objects and arrows and to its right is the dual, which happens to be <a href="https://github.com/pointfreeco/swift-prelude/blob/536b8856e38853854b5c5689e5b5d06da75c992e/Sources/Either/Either.swift#L3-L6">our dear friend, <code class="language-plaintext highlighter-rouge">Either</code></a>.</p>
<p><img src="/public/images/product_sum.png" alt="Constructing a tuple and `Either` from a category-theoretic perspective." /></p>
<h2 id="hiding-in-plain-sight">Hiding in Plain Sight</h2>
<p>With the definition of duals at hand, you start to notice them…everywhere.</p>
<h3 id="structs-and-enumerations">Structs and Enumerations</h3>
<p>The above sketch of <code class="language-plaintext highlighter-rouge">(A, B)</code> and <code class="language-plaintext highlighter-rouge">Either<A, B></code> (and generalizing to higher <a href="https://en.wikipedia.org/wiki/Arity">arities</a>) shows the duality between structs (named tuples) and enumerations.</p>
<p>A tuple is the “smallest” composite type of <code class="language-plaintext highlighter-rouge">A</code> and <code class="language-plaintext highlighter-rouge">B</code> such that there exists projections from <code class="language-plaintext highlighter-rouge">(A, B)</code> <em>back</em> towards <code class="language-plaintext highlighter-rouge">A</code> and <code class="language-plaintext highlighter-rouge">B</code>—the projections are most commonly called <code class="language-plaintext highlighter-rouge">first</code> and <code class="language-plaintext highlighter-rouge">second</code>.</p>
<p><img src="/public/images/first_second.png" alt="Projection functions `first` and `second`." /></p>
<p>Dualizing, <code class="language-plaintext highlighter-rouge">Either<A, B></code> is the smallest type parameterized on <code class="language-plaintext highlighter-rouge">A</code> and <code class="language-plaintext highlighter-rouge">B</code> such that there exists injections from <code class="language-plaintext highlighter-rouge">A</code> and <code class="language-plaintext highlighter-rouge">B</code> <em>into</em> the type. If we define <code class="language-plaintext highlighter-rouge">Either</code> as follows, Swift provides those injections for free:</p>
<p><img src="/public/images/either.png" alt="`Either` and a note on its injection functions." /></p>
<p>Projections <em>down towards</em> and injections <em>up from</em> a struct or enumeration’s component types are the arrows being flipped. This duality is often conveyed by calling structs product types and enumerations, co-products.</p>
<p>Even wilder is that, despite this duality, language design often has a bias towards one side of the dual. e.g. structs can be defined anonymously with tuples and enumerations…can’t. Properties of structs can be accessed by dot-syntax or key-paths, whereas associated values in enumerations <a href="https://www.pointfree.co/episodes/ep52-enum-properties">have to be pattern matched out</a>.</p>
<h3 id="side-effects-and-dependency-injection-co-effects">Side Effects and Dependency Injection (Co-Effects)</h3>
<p>Any function that makes an observable change on the outside world—writing to disk, firing an analytics event, logging—can be viewed as a function with a sort of hidden <em>output</em>. Time to flip the function arrow. What is an <em>input</em> from the outside world a function needs to execute (e.g. a shared singleton or the current system time)? Typically, we call this dependency injection—or, with our “co-“ prefix, <a href="https://youtu.be/A0VaIKK2ijM?t=394">they’re co-effects</a>!</p>
<h2 id="from-iteratorprotocol-and-sequence-to-subscriber-and-publisher">From <code class="language-plaintext highlighter-rouge">IteratorProtocol</code> and <code class="language-plaintext highlighter-rouge">Sequence</code> to <code class="language-plaintext highlighter-rouge">Subscriber</code> and <code class="language-plaintext highlighter-rouge">Publisher</code></h2>
<p>The duals we’ve covered—products and co-products and effects and co-effects—are primers in seeing the duality between imperative and reactive programming. To start, here are abbreviated definitions of the fundamental imperative types, <a href="https://developer.apple.com/documentation/swift/iteratorprotocol">IteratorProtocol</a> and <a href="https://developer.apple.com/documentation/swift/sequence">Sequence</a>:</p>
<p><img src="/public/images/iterator_sequence.png" alt="`IteratorProtocol` and `Sequence`’s abbreviated signatures." /></p>
<p>At its core, <code class="language-plaintext highlighter-rouge">IteratorProtocol.next</code> is a function that takes <code class="language-plaintext highlighter-rouge">Void</code> input and optionally returns an <code class="language-plaintext highlighter-rouge">IteratorProtocol.Element</code>—put another way, “pulling” the next element from the iterator.</p>
<p>The dual story is a function that takes an optional <code class="language-plaintext highlighter-rouge">IteratorProtocol.Element</code> and returns <code class="language-plaintext highlighter-rouge">Void</code>, with the <code class="language-plaintext highlighter-rouge">Void</code> return signaling either a no-op or side effects. Now for the two input cases. An <code class="language-plaintext highlighter-rouge">Optional.some(Element)</code> is akin to receiving the next element of a sequence and an <code class="language-plaintext highlighter-rouge">Optional<Element>.none</code> is a sort of terminal condition when the sequence completes.</p>
<p>Which, conveniently enough, mirror <a href="https://developer.apple.com/documentation/combine/subscriber/3213653-receive"><code class="language-plaintext highlighter-rouge">Subscriber.receive(_:)</code></a> and <a href="https://developer.apple.com/documentation/combine/subscriber/3213654-receive"><code class="language-plaintext highlighter-rouge">Subscriber.recieve(completion:)</code></a>, respectively (noting that <code class="language-plaintext highlighter-rouge">Subscriber</code> also packs the notion of backpressure through <a href="https://developer.apple.com/documentation/combine/subscribers/demand"><code class="language-plaintext highlighter-rouge">Subscribers.Demand</code></a> which isn’t reflected in the duality and error handling through <a href="https://developer.apple.com/documentation/combine/subscribers/completion/failure"><code class="language-plaintext highlighter-rouge">Subscribers.Completion.failure(_:)</code></a> that’s subtly implied by the possibility of <code class="language-plaintext highlighter-rouge">IteratorProtocol.next</code> crashing at runtime<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>). <code class="language-plaintext highlighter-rouge">Subscriber</code> is the dual image of <code class="language-plaintext highlighter-rouge">IteratorProtocol</code> in Apple’s Combine—and instead of “pulling” <code class="language-plaintext highlighter-rouge">IteratorProtocol.Element</code>’s, it’s a type that’s “pushed” <a href="https://developer.apple.com/documentation/combine/subscriber/3213652-input"><code class="language-plaintext highlighter-rouge">Subscriber.Input</code></a>’s</p>
<p><img src="/public/images/iterator_subscriber_dual.png" alt="Demonstrating how `Subscriber` is the dual of `IteratorProtocol`." /></p>
<p>Onto <code class="language-plaintext highlighter-rouge">Sequence</code>. <code class="language-plaintext highlighter-rouge">Sequence.makeIterator</code> is a function from <code class="language-plaintext highlighter-rouge">Void</code> to an <code class="language-plaintext highlighter-rouge">IteratorProtocol</code> and, in the paragraph above, we noted that an iterator is a function from <code class="language-plaintext highlighter-rouge">Void</code> to <code class="language-plaintext highlighter-rouge">Element?</code>. So, <code class="language-plaintext highlighter-rouge">Sequence</code> has the following shape: <code class="language-plaintext highlighter-rouge">() -> (() -> Element?)</code>.</p>
<p>“Stop! Dual time.” - MC Hammer, probably.</p>
<p>The dual shape is then <code class="language-plaintext highlighter-rouge">(Element? -> ()) -> ()</code> and substituting <code class="language-plaintext highlighter-rouge">Subscriber</code> for <code class="language-plaintext highlighter-rouge">Element? -> ()</code> we get a function that accepts a <code class="language-plaintext highlighter-rouge">Subscriber</code> and <code class="language-plaintext highlighter-rouge">Void</code> returns.</p>
<p>And that brings us to <a href="https://developer.apple.com/documentation/combine/publisher/3229093-receive"><code class="language-plaintext highlighter-rouge">Publisher.receive(subscriber:)</code></a>—the attachment point of a subscriber to a publisher.</p>
<p><img src="/public/images/sequence_publisher.png" alt="Demonstrating how `Publisher` is the dual of `Sequence`." /></p>
<p>What’s wicked about the duality between (<code class="language-plaintext highlighter-rouge">IteratorProtocol</code>, <code class="language-plaintext highlighter-rouge">Sequence</code>) and (<code class="language-plaintext highlighter-rouge">Subscriber</code>, <code class="language-plaintext highlighter-rouge">Publisher</code>) is that, well, it’s been hiding in plain sight. Apple didn’t cook up a new abstraction—they just flipped the arrows in the same way RAC did as Justin Spahr-Summers explains in <a href="https://youtu.be/ICNjRS2X8WM?t=358">his June 2014 talk on the future of the library</a>, Brian Beckman and Erik Meijer in a <a href="https://youtu.be/looJcaeboBY?t=2164">March 2014 Expert to Expert video</a>, and, most recently, as Casey Liss walked through in his post: “<a href="https://www.caseyliss.com/2019/6/13/building-up-to-combine">Building up to Combine</a>.”</p>
<p>Reactive programming isn’t intrinsically harder (or newer) than imperative—it’s the dual. What requires patience is building an intuition around it in undoing our industry and academic institutions’ bias towards the imperative side of the coin.</p>
<p>I hope this undoing brings us to a period captured by the title of Rob Rix’s 2013 CocoaConf Columbus presentation, <a href="https://github.com/robrix/Postmodern-Programming/blob/8c556489635dc6bcb764d1617414cd32583269f7/Postmodern%20Programming.md">Postmodern Programming</a>.</p>
<h2 id="postmodern-programming">Postmodern Programming</h2>
<p>Rix’s talk is worth multiple reads with multiple [beverage of choice]s and its core is this: declarative programming—and by extension, reactive handling of data flow—allows us to tell our programs “what” to do, instead of meticulously instructing them “how” to execute. We can then focus on the relationships between components and lean on runtimes and frameworks to determine the mechanics of how those relationships are honored.</p>
<p>This isn’t to say postmodern programming will be entirely declarative. There are times when imperativeness makes code more performant or incurs a smaller memory footprint, usually when the backing engine isn’t entirely sure how to optimize for a use case.</p>
<p>And that’s okay. If premodern programming leaned imperatively, then maybe postmodern programming is remembering the reactive dual and knowing when to lean in the other direction.</p>
<p>After all, Combine is just flipping the arrows.</p>
<hr />
<h2 id="footnotes-and-related-reading">Footnotes and related reading</h2>
<p>⇒ In an early draft, I included a subheading on key paths (and more generally, lenses) and prisms in the “Hiding in Plain Sight” section. I’ve tucked it below to keep the post concise, yet provide another example for the curious.</p>
<blockquote>
<h3 id="key-paths-and--lenses-and-prisms">Key Paths and ??? (Lenses and Prisms)</h3>
</blockquote>
<blockquote>
<p><a href="https://github.com/apple/swift-evolution/blob/b1caeccf5d273a1cc53b331d41ea169f77294272/proposals/0161-key-paths.md">SE-0161</a> brought key paths to Swift and with their addition, we can traverse into deeply nested types through a series of properties and subscripts. After a couple of <a href="https://github.com/apple/swift-evolution/blob/b1caeccf5d273a1cc53b331d41ea169f77294272/proposals/0249-key-path-literal-function-expressions.md">enhancement</a> <a href="https://github.com/apple/swift-evolution/blob/b1caeccf5d273a1cc53b331d41ea169f77294272/proposals/0227-identity-keypath.md">proposals</a>, key paths buy us a sizable chunk of the benefits we’re afforded by a construct more generally known as lenses.</p>
</blockquote>
<blockquote>
<p><em>camera pans to Swift’s enumeration type upset in the corner</em></p>
</blockquote>
<blockquote>
<p>Sadly, there’s no first-class affordance for the dual story of traversing into branches of a nested enumeration via associated values, transforming some target value, and gluing the branch back into the enumeration. We have to write these dual structures—prisms—by hand.</p>
</blockquote>
<blockquote>
<p>Lens, prisms, and, more broadly, “optics” (not in the physics sense) is a world in and of itself. Here’s a couple of resources:</p>
</blockquote>
<blockquote>
<ul>
<li>Brandon Williams’ <a href="https://www.youtube.com/watch?v=ofjehH9f-CU">talk on lenses</a> and <a href="https://youtu.be/QOIigosUNGU?t=1525">coverage of prisms</a> in his presentation on “Composable Reducers and Effect Systems.”</li>
<li>Pickering, Gibbons, and Wu’s <a href="http://www.cs.ox.ac.uk/people/jeremy.gibbons/publications/poptics.pdf">Profunctor Optics paper</a>.</li>
</ul>
</blockquote>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>“<a href="https://ro-che.info/articles/2016-08-07-hask-category">Does it matter if <code class="language-plaintext highlighter-rouge">Hask</code> is (not) a category?</a>” <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>The potential for a runtime crash is a <code class="language-plaintext highlighter-rouge">Never</code> return value in disguise. So, <code class="language-plaintext highlighter-rouge">Never</code> can be seen as a subtype of any other type in that, if an expression doesn’t return by crashing, then it doesn’t matter what the original return type was—making this universal bottom type property explicit was an alternative considered in <a href="https://github.com/apple/swift-evolution/blob/fe8f41794cb96bfd1942d7b7c1030851cc440ab5/proposals/0102-noreturn-bottom-type.md#never-as-a-universal-bottom-subtype">SE-0102</a>. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Introducing substrate2019-03-07T00:00:00+00:00https://jasdev.me/substrate<p>I joined Twitter on December 29th, 2011—2,627 days ago—and have tweeted 17,644 times since (~7 per day). My addiction aside, I point out that total, 17k, specifically. The service’s website, apps, and API only let me sift through the most recent 3,200 of that total, leaving behind an iceberg of 14k thoughts, memories, questions, and conversations. While I can <a href="https://help.twitter.com/en/managing-your-account/how-to-download-your-twitter-archive">download</a> and <a href="https://twitter.com/search-advanced">search through</a> an archive, the former is only available for my account and the latter requires knowing exactly <em>what</em> (down to the keyword or date range) I’m looking for—put another way, trying to cut through said iceberg with a laser pointer.</p>
<p>This isn’t to single out Twitter—a combination of technical complexity <a href="https://twitter.com/Shwinnabego/status/1033079723893706753">and product debt</a> is likely the culprit. Still, it’s concerning that the knowledge created on the networks we use is outpacing our (and future) generation’s collective ability to digest it. If we’re all busy adding to these networks, who are the historians?</p>
<p>The answer might be our long-forgotten friend, the link blog.</p>
<p>Link blogs are exactly what you’d guess—blogs which share URLs and occasionally commentary. My favorites—links to link blogs? <em>Meta</em>.—are:</p>
<ul>
<li><a href="https://us11.campaign-archive.com/home/?u=1d1358224a50ff90314612a19&id=3a5aa8c5f5">The Link</a>: “A Sunday newsletter for curious people” by Ben Vander. His data journalism picks keep me reading week over week.</li>
<li><a href="https://twitter.com/whatkunalreads">@whatkunalreads</a> and <a href="https://twitter.com/whatryanreads">@whatryanreads</a>: feeds of what two of my friends are reading around the Web.</li>
<li>The “Links” section in <a href="https://tinyletter.com/nayafia/archive">Nadia Eghbal’s newsletter</a> and “Reads” in <a href="https://us14.campaign-archive.com/home/?u=91deb2ba0855dfc2f57dcb840&id=ec78c575fd">Drew Austin’s</a>.</li>
<li><a href="https://joy.recurse.com">Joy of Computing</a>: A link blog including computer-generated art, games, tools, crafts, libraries, and more.</li>
</ul>
<p>and frankly, those are the only six I check with some frequency, which is telling given that I spend seven-tweets-worth-of-time on the Internet a day.</p>
<p>So, I’m going to surface the substrate I find while sifting through Twitter, RSS, and newsletters—with the feed’s name, Substrate, following suit. Links will vary widely. But, you can expect <a href="https://twitter.com/gerstenzang/status/597989205109649408">tweets I constantly remember</a>, <a href="https://twitter.com/automaticyes/status/1044635113437442048">conversations that call for pause</a>, and <a href="https://www.buzzfeednews.com/article/fancyfeast/sex-toys-education-consent-positivity-gender">essays that have impacted me</a>. I hope to make the collection worth your time and play historian when I’m not working on Distillations.</p>
<p>You can follow along via <a href="https://raindrop.io/collection/28137415/feed">RSS</a> or the backing <a href="https://raindrop.io/jasdev/substrate-28137415">Raindrop profile</a>. As always, below is some related reading…which makes me wonder; maybe this site’s footnotes have been an approximation of a link blog all along.</p>
<hr />
<h2 id="related-reading">Related reading</h2>
<p>⇒ “<a href="https://domhofmann.com/reposting.html">Some Problems with Reposting</a>”</p>
<p>⇒ “<a href="https://twitter.com/nayafia/status/1040277890674249728">More people will get paid to be archivists/historians over the next 3–5 years. Someone needs to digest all the knowledge we’ve created.</a>”</p>
Bodies of work2019-02-10T00:00:00+00:00https://jasdev.me/bodies-of-work<p>At XOXO ’18, Jennifer Lee <a href="https://youtu.be/Dj8oul-ZCh0?t=66">posed the following question</a>:</p>
<blockquote>
<p>“Who do you envy? [Who you envy] is a compass for what you care about.”</p>
</blockquote>
<p>The folks I envy all share a common trait: having a body of work I admire greatly. That phrase, “body of work,” has been on my mind since the conference.</p>
<p>It’s thrown around often. “Their writing is an impressive body of work.” “Her body of work is the result of a lifetime of effort.“ What if we took the phrase somewhat…literally? Treating the archive of our work as a sort of <em>body</em> we can nurture, strengthen, groom, or even find comfort with.</p>
<p><em>Some are tall.</em> <a href="https://nearthespeedoflight.com"><em>Speed of Light</em></a> by Jason Brennan has an <a href="https://nearthespeedoflight.com/all">archive</a> page that takes seconds to load. Don’t get me wrong, this isn’t meant to ding the site’s performance; but rather, a testament to how rendering the full text of each post he’s written onto a single page causes even modern browsers to hiccup. The blog covers books, cognition, software, and education. Yet, whatever the topic, Jason’s work always skips my reading queue in favor of reading on the spot.</p>
<p><em>Some have our initials tattooed on them.</em> Above & Beyond’s radio show, <a href="https://itunes.apple.com/us/podcast/above-beyond-group-therapy/id286889904?mt=2"><em>Group Therapy</em></a> (ABGT for short and formerly <a href="https://itunes.apple.com/us/podcast/above-beyond-trance-around-the-world/id993499023?mt=2"><em>Trance Around the World</em></a>), welcomes with open arms. It’s a two-hour weekly music podcast that’s been running for over 770 weeks—almost 15 years—straight, ambitiously counted its shows upwards from 001, 002, 003, …, and is en route to exhaust the possible digits of the numbering scheme. Each week, fan-submitted messages to friends and loved ones are read on the air. The ABGT body of work carries its admirers on its sleeve.</p>
<p><em>Some are a friend of sorts.</em> <a href="https://buttondown.email/jmduke/archive"><em>Minor Arcana</em></a> is a newsletter that’s felt more like a friend than a weekly email. It provides a glimpse into <a href="https://twitter.com/justinmduke">Justin Duke</a>’s life in Seattle. Funnily enough, despite reading his work and following him on Twitter for years, we <em>finally</em> met in-person at XOXO ’18 in Portland. Our hour-long conversation packed the density of a catchup between hundreds-of-hours-long friends. We got to questions we’ve been meaning to ask one another—“Do you ever see yourself moving into management?” “What would you work on, if finances weren’t a consideration?”—and looked forward to hanging out during his recent New York trip. In a sense, I met his body of work before, well, meeting him.</p>
<p><em>Some understand.</em> <a href="https://anxymag.com"><em>Anxy</em></a> has been through it, too. The publication sits at the intersection of mental health, writing, and art, steered by <a href="https://twitter.com/redindhi">Indhira Rojas</a>. Her team’s body of work, in the form of four issues covering anger, workaholism, boundaries, and masculinity has provided a material empathy. Meeting someone familiar with the series is often met with a confiding silence because they have either struggled or are familiar with the depths of mental health.</p>
<p><em>My body of work?</em></p>
<p>I’ve maintained this collection as a way to clarify my thinking and to remind others of our often similar invisible wounds.</p>
<p>My relationship with Distillations’ body has changed over the years. It was previously unnamed and sat under the “jasdev.me” domain. Renaming the site distanced my identity from it—which, in turn, helped me better process critical feedback. The rename also emphasized the ideas and narratives threaded into each piece. I recently dug up some old copy for the site which attempted to capture this:</p>
<blockquote>
<p>The subtitle, “forgetting words, remembering ideas,” is a nod to my background in mathematics. During lectures, I was constantly reminded that the details, mechanics, and “words” of math aren’t important. We would forget them in a few years anyway. Instead, the ideas were what mattered—proof techniques such as a contradiction, induction, and correspondences. The goal was to spend time in the trenches of problems sets to develop an intimacy with these concepts. Distillations seeks to mirror this approach. In a sense, by effortfully turning observations into words, I hope to forget those very words—distilling a tangible intuition of the ideas.</p>
</blockquote>
<p>Distillations—and my larger body of writing—is almost six years old. As it’s grown taller, like Jason’s <em>Speed of Light</em>, I’ve penciled in notches on the doorway by adding to the homepage’s archive list. As friends and loved ones have supported it, I’ve followed ABGT’s suit and tattooed their names on the <a href="/village">Village page</a>. As the body has become a surface for my inner self, it’s been a friend <a href="/emotional-ranges">who understands</a> as much as <em>Anxy</em> and is an <a href="/ambient-intimacy">ambient part</a> of others’ days in the way Justin’s <em>Minor Arcana</em> is in mine.</p>
<p>I hope Distillations’ body lives beyond my own—taking shelter in this little corner of the Internet. Maybe someone—a relative, great grandchild (fingers crossed), an old friend—will revisit it in eighty years time, or maybe not. Until then, I’ll keep nurturing this body of work.</p>
<p>■</p>
To my unread books2019-01-05T00:00:00+00:00https://jasdev.me/to-my-unread-books<p>To the books on my shelf I haven’t read,</p>
<p>You started as a stack only eight wide held upright by <a href="https://www.amazon.com/dp/B007X6CIAY">Portal-themed bookends</a> I’m way too proud of. Now, you stretch beyond my five-foot wide desk, total over sixty, and I’ve maybe read—or, if I’m being honest, paged through, skimmed, or abandoned—around…twenty.</p>
<p>The tendency to book hoard started by reaching for Amazon’s tactfully decorated 1-Click® Ordering button. “I’ll read this book eventually—might as well have it handy for when the time comes.” “If three friends recommend a title, I’ll buy it.” These were the private justifications I told myself. An “eventually” that has yet to arrive and countless recommendations have left me with an “antilibrary.” A collection of mostly unread books. Some <a href="https://fs.blog/2013/06/the-antilibrary/">tout the merits</a> of such a library, claiming it’s a “visual reminder of what [they] don’t know.” This might be effective in small dosages. But, beyond a point, continually buying more than I could read felt less like building commonplace serendipity and more like faux-productive spending.</p>
<p>I was fooling myself—and yet, your unread pages might hint at roads I, at one point, wanted to venture down.</p>
<p>I bought <a href="https://www.amazon.com/Time-Crunched-Cyclist-Race-Winning-Fitness-Athlete-ebook/dp/B06Y1VRC54"><em>The Time-Crunched Cyclist</em></a> after finishing dead last in my first <a href="https://www.instagram.com/p/BSGpGqegAwO/">cycling race</a> in ’17 (turns out training indoors all winter for an outdoor event was a rookie mistake). I probably won’t elevate cycling from a weekend hobby to a sport I compete in seriously—still, you’re a physical reminder of a dream from my past self.</p>
<p>I bought <a href="https://www.amazon.com/Learn-You-Haskell-Great-Good/dp/1593272839"><em>Learn You a Haskell for Great Good!</em></a>—the often-recommended introductory text for the purely functional programming language, Haskell—out of a pang of homesickness for my education in mathematics. At times, I get tired running on the treadmill that is software engineering, where knowledge I’ve collected, even a couple of years ago, is perpetually rendered obsolete in the next couple. I miss working in a more durable—albeit abstract—world that sits parallel to our own. I wonder if I’m keeping you around for if/when I eventually step off the treadmill.</p>
<p>I bought Amy Saul-Zerby’s <a href="https://www.amazon.com/gp/product/1948700050"><em>Deep Camouflage</em></a> after one of her pieces, <a href="https://www.dropbox.com/s/x77vgu0rzqg9u43/Screen%20Shot%202020-08-02%20at%201.33.12%20PM.png?dl=0"><em>do you ever</em></a>, was rotated in a <a href="https://tinyletter.com/pome">modern poetry newsletter</a>. The opening lines</p>
<blockquote>
<p>do you ever stop breathing</p>
</blockquote>
<blockquote>
<p>to see if you will notice</p>
</blockquote>
<p>washed away the <a href="https://twitter.com/mistermircea/status/921535212164648960">tired residue</a> that nonfiction has caked onto its treatment of the breath and breathing. Poetry, like mathematics, has always been a sort of “left field” I enjoy spending time in.</p>
<p>Thinking about it, you all—my unread tree alumni—might be more than merely impulse purchases. You’re breadcrumbs of impulses to create different versions of myself. One who’s an amateur cyclist. One who returns home to mathematics. One who gathers poetry’s adjectives and hides them in the corners of nonfiction. And for that, I’m glad you’re around. Maybe that’s the crux of why we gift books. Not so much with the expectation that our friend, loved one, or stranger reads it cover to cover. But rather, that they explore a different person we see in them.</p>
<p>Either way, I probably should disable 1-Click® Ordering.</p>
<p>With love,</p>
<p>Jas</p>
<p>■</p>
Invisible badges: handling long-term injuries2018-08-09T00:00:00+00:00https://jasdev.me/injuries<p>It’s safe to assume most would agree with the following statement: “We shouldn’t judge too quickly—everyone is fighting an internal war we know nothing about.” Yet, the principle is so rarely acted on. Its disregard comes in small doses. A group fitness instructor who asks the room if there are any injuries and—instead of coming over to discuss privately—forces you to publicize your situation. The double take from others who find out you hired movers, when, at a glance, you appear perfectly capable of doing so yourself.</p>
<p>Chronic pain and long-term injuries are two invisible badges many of us wear. A couple of years ago, I got surgery for a bilateral sports hernia and I’m still coming to grips with the recovery. I’ve never written about the injury in detail and hope doing so can provide some agency, if you’re quietly aching with the feeling that you’re not at—or seemingly will ever return to—“100%.”</p>
<h2 id="injured-identities">Injured Identities</h2>
<p>Those closest to me know that fitness is a large part of my identity. I work at <a href="http://onepeloton.com">Peloton</a>, started strength training almost a decade ago, and ran middle distance on my high school track team. Providing this context sets the stage for why injuries weigh on me so heavily. Yes, there’s the immediate, palpable pain. However, temporarily—or potentially permanently—losing a part of my identity ended up hurting more. Coming to terms with an injured identity is a slow process. The surgery was two years ago and subsequent waves of chronic pain continue to make my mind wander into dark corners.</p>
<p>“Injured identities” parallel the core concept covered in “<a href="/peeling-labels">Peeling Off Labels</a>.”</p>
<blockquote>
<p>“[Labels] often have implicit power in affecting internal dialogues. Ancestrally, humans rely on the security that static traits provide, especially with regard to identity and labels for a sense of control.”</p>
</blockquote>
<p>We can view identities as the sum of the labels we hold, both voluntarily and involuntarily. <em>Jasdev is an engineer, writer, and enjoys cycling and strength training</em>. Injuries can painfully rip off the bigger labels. <a href="https://twitter.com/mrmrs_">Adam Morse</a> described how quickly thoughts can snowball, when dealing with an <a href="https://medium.com/@mrmrs_/i-got-rsi-and-what-happened-next-wasnt-surprising-at-all-63ddb58b5e3f">extended period of RSI</a>:</p>
<blockquote>
<p>“My career was largely [centered on] designing through writing on a computer. My internal perceived value was tied to my ability to manipulate, produce, and delete code. Would I need to change careers? Could I use some dictation software to code? What am I going to do? What careers involve no computering? My head was filled with stressful questions.”</p>
</blockquote>
<p>I’ve coped in a handful of ways: remembering the incomparability between our current and past selves, feeling the texture of pain, and allowing forced seasonality to take course. Here’s what I’ve learned about each.</p>
<h2 id="incomparability">Incomparability</h2>
<p>A retroactively obvious, yet difficult-to-digest observation is that I’m a <em>different</em> person walking into each workout. Metrics create a false comparability. I inadvertently perpetuate this by working at Peloton—where stats such as output, distance, and cadence are the focus. I touched on comparability in a previous post, “<a href="/nostalgia">Nostalgia and Upper Bounds</a>” (emphasis added).</p>
<blockquote>
<p>“While reflection is healthy in small, periodic doses, it’s tempting to fall into a trap of comparing where you are now against where you were <em>n</em> years ago. This comparison is helpful in <em>concrete</em> domains such as personal fitness, but breaks down in subjective areas like relationships.”</p>
</blockquote>
<p>I was wrong—fitness isn’t a concrete domain. Compressing workouts to a handful of numbers (“I hit 631 kJ in 45 minutes today compared to 584 last week.”) makes them <em>comparable</em>, yet that comparison completely sidesteps the context of our situations<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. Did I get enough rest last night? Social exhaustion could be spilling into my training? Or, maybe my surgery incision sites might be sensitive at the moment, heightening my worry?</p>
<p>Falling into the metrics-focused trap caused me to internally create a “pre- and post-surgery Jasdev” divide. During recovery, I placed myself in the shadow of my former self. Every subsequent workout crawled me towards my <em>past</em> baseline. It would’ve been fruitful to instead lower that imaginary baseline back to my <em>current</em> reality and build from there. That subtle shift in perspective helped me erase the artificial rift I created in my personal history.</p>
<h2 id="the-texture-of-pain">The Texture of Pain</h2>
<p>Another difficulty has been remembering the non-binary nature of pain. I’d often start days in the following trance: wake up, exercise, notice lower abdominal discomfort (from the surgery), and mentally tally the day as one with physical pain. Over time, the trance manifested itself in <a href="https://www.ncbi.nlm.nih.gov/pmc/articles/PMC2683754/">dissociative tendencies</a>. Instead of sitting with the pain, I’d run from it, causing me to space out, go on autopilot, and not feel like my full self. I worked, week over week, on catching these tendencies in therapy. The efforts made me recognize the “texture” of chronic pain. I slowly reconditioned the “notice pain and dissociate” loop to “notice pain, <em>stay</em> with it, recognize its caliber, and work from there.” <a href="https://twitter.com/keavy">Keavy McMinn</a> phrased this texture as being able to simultaneously hold space for the good and bad in “<a href="https://keavy.com/sport/broken/">On Being Broken</a>.”</p>
<blockquote>
<p>“I can say ‘this hurts and it sucks’ and also be content with what I can do. I have space in my head for the good and bad.”</p>
</blockquote>
<h2 id="forced-seasonality">Forced Seasonality</h2>
<p>A source of agency I’ve kept close is the notion of forced seasonality. It’s comforting—maybe in a backwards way—knowing that an injury can open space to focus on things I wouldn’t normally have time for. For me, temporarily stepping away from training started a new “season“ during which I concentrated on Distillations and meditation. In doing so, a couple of nuances cropped up.</p>
<p>First is the pitfall of the belief “<em>X</em> is my meditation,” where <em>X</em> is an activity that might be ruled out when injured or handling chronic pain. Let’s take a commonly cited <em>X</em>, exercise, as an example. To be clear, I’m <em>not</em> claiming that exercise isn’t an effective source of stability—the thesis of one of my favorite books, <a href="https://www.amazon.com/Spark-Revolutionary-Science-Exercise-Brain-ebook/dp/B000SFD21Q">Spark</a>, covers this extensively—, but rather to gut check whether or not our support systems can withstand unexpected injuries. I learned this the hard way. Every bolded line on my post-hernia repair paperwork reiterated that lifting—even larger household items—was out of the picture for at least three months. In the absence of exercise, I needed a durable centering function and dialing in my meditation practice did just that. Not everyone’s season should be filled more frequent sits—there are plenty of ways to mirror the benefits of meditation without doing exactly so; still, it’s constructive to make sure the activities that ground you remain grounded themselves when the going isn’t easy.</p>
<p>Second, I needed to better recognize my habit of postponing contentedness until after these newly formed seasons. That is, I’d tell myself “it’ll be better after that three-month, no-exercise window.” While true, this line of thinking put my life on hold. Funnily enough, human behavior encourages the opposite when it comes to gratification—i.e. instant over delayed. I’ve gently tried to translate “I’ll be happier when I have <em>X</em>, gotten over <em>Y</em>, or accomplished <em>Z</em>.”<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>-like thoughts into reminders that I don’t have to wait to be content.</p>
<p>Forced seasonality caused by injuries may seem like time away from your “practice” (cycling, running, etc.). But, time away from the practice <em>is</em> the practice. We do the same on smaller timescales with rest days—an entire season of rest can provide a stronger foundation for your return.</p>
<hr />
<p>I’m having trouble finding a thread through injured identities, incomparability, pain’s texture, and forced seasonality—which might hint at the nature of invisible badges. They’re sewn onto our sleeves—sometimes permanently, in the form of chronic pain, or temporarily, with recoverable injuries. And there’s no distinct point in time when we’re A-okay with them. Rather, we slowly—read glacially—accept their presence. When that presence is taken a step further in making badges visible to others, a kind of counterintuitive reassurance is realized. We’re all veterans of some internal war—keeping that in mind could be the way to handle long-term injuries.</p>
<hr />
<p>Special thanks to <a href="https://twitter.com/jtaylorhodge">Taylor</a> and <a href="https://twitter.com/vbhvsgr">Vaibhav</a> for feedback on early drafts of this entry.</p>
<h2 id="related-reading-and-footnotes">Related reading and footnotes</h2>
<p>⇒ “<a href="http://davidmaisel.com/essays/infinite-exchange/">Infinite Exchange</a>” (specifically, the excerpt on advanced scurvy and wound reappearance).</p>
<p>⇒ <a href="https://twitter.com/BrettdeM">Brett deMarrais</a>’ <a href="https://uncappednotes.com/the-easiest-gainz-ca38d3a38436">post on handling Crohn’s</a></p>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>Tangential to this context is <a href="/thoughts/2017-4-25">remembering the fun</a> in activities (from a 4/25/2017 note). <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p><a href="https://twitter.com/jasdev/status/883497841519407106">Thread</a> covering the trap of deferred contentedness. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Slowing down the web2018-07-01T00:00:00+00:00https://jasdev.me/slowing-down-the-web<p>iOS 12’s “<a href="https://www.apple.com/newsroom/2018/06/ios-12-introduces-new-features-to-reduce-interruptions-and-manage-screen-time/">Screen Time</a>“ might be the biggest, recent step forward in humane product design.</p>
<p><img src="/public/images/screen_time.jpg" alt="Screenshot of the iOS Screen Time dashboard. Includes total time, longest session duration, after bedtime use, and application limits." /></p>
<p>It packs usage reports, device pickup counts, enhanced Do Not Disturb controls, and granular notification settings. However, as I read the press release, I couldn’t shake the feeling that the feature Band-Aids over the networks called out—namely, Instagram, Facebook, and Twitter.</p>
<p><img src="/public/images/app_usage.jpg" alt="Summary of most used applications in a session." /></p>
<p>Fixing our collective relationship with these networks won’t happen overnight and I’m not advocating for their absence. Still, they could benefit from replicating aspects of the “<a href="https://jackcheng.com/the-slow-web/">Slow Web</a>“—blogs, journaling, and RSS. Particularly, <em>decoupling</em> reading and writing, preventing engagement, and allowing users to siphon content into smaller “ponds.”</p>
<h2 id="decoupling-writing-and-reading">Decoupling Writing and Reading</h2>
<p>Most networks implicitly couple the act of reading and writing. Not only are we expected to read Twitter, Instagram, or Facebook’s feeds, they’re designed for us to write back to them in the form of tweets and posts.</p>
<p>What if we broke this assumption? Journaling does this for me. The habit is almost purely <em>write-only</em>. Of the 1,943 entries (five years of memories) in my journal, I’ve only read a couple hundred—nostalgically resembling the letters high school teachers made us write to our future, university selves.</p>
<p>It’s healthy to gut check whether or not to read <em>and</em> write to networks. The conjunction might not always make sense. Personally, I use Swarm and GitHub as write-only feeds. I’ll checkin and commit, respectively, but rarely browse their feeds. There may be an opportunity for a service to use this decoupling to its advantage, as I noted in “<a href="/nostalgia">Nostalgia and Upper Bounds</a>.“</p>
<blockquote>
<p>[I]magine if services like Twitter allowed tweets to be posted at any time, and only surfaced the feed during specific, limited hours of the day. That could do wonders in eradicating continuous partial attention [while capturing the joy of shared presence that permeates Twitter during live events].</p>
</blockquote>
<h2 id="eliminating-engagement">Eliminating “Engagement”</h2>
<p>Another trait of the Slow Web is preventing typical notions of “engagement“—likes, retweets, favorites, shares…the Lucky Charms of the Internet. RSS may be dead for all mainstream purposes (aside from quietly powering the podcast industry), but I’m a holdout. What’s special about RSS is its postal system-like nature. Feed entries arrive at a digestible pace and the protocol eliminates engagement entirely. Posts simply float out into the ether. Following my friends on both Twitter and RSS has been fascinating because of this. More specifically, when feed items aren’t cross-posted to normal social channels, it’s a small glimpse into their digital inner life.</p>
<p>I recently adopted the engagement-free approach for Distillations by stripping out Google Analytics. What keeps me going are those of you who have edited, provided feedback, and reached out when a post resonated. It doesn’t go unnoticed. I’ve actually <a href="/village">kept a log</a> of every person who has helped me over the years, no matter how small the interaction.</p>
<p>The Fast Web onsets engagement fatigue. Generations that have grown up <a href="https://twitter.com/eve_rebecca/status/978224331267432448">mainlining the Internet</a> are realizing this and asking themselves “What would network <em>X</em> look like without vanity metrics?“ Substituting Instagram for <em>X</em>, <a href="http://www.thisisinsider.com/photo-editing-app-vsco-x-tops-one-million-subscribers-2018-6">VSCO might be the answer</a>. Though, the absence of one-bit reactions means higher-energy interactions. A “like” can become a sentence—“I love this photo.”—which, in the oceans of feeds we swim through, would be tiring. Siphoning from these oceans into smaller ponds helps.</p>
<h2 id="siphoning-oceans">Siphoning Oceans</h2>
<p>The funny thing about <a href="https://en.wikipedia.org/wiki/Dunbar%27s_number">Dunbar’s Number</a> is its practically universal acceptance in physical reality, yet it’s thrown out the window in social networks<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. “If the human brain is only able to remember and manage [150] relationships at a time, <a href="https://twitter.com/whitney/status/1002951523247251458">no wonder our systems are fried and shorting out</a>.” I wholeheartedly believe there is a Dunbar’s Number for each of the services we use, based on its atomic unit for content. The tricky part is finding that number. For my usage of Twitter and Instagram, the limits hover at 300 and 150 accounts, respectively.</p>
<p>Where do the metaphorical siphon and ponds come into play? They embed themselves in tools like <a href="https://www.instapaper.com">Instapaper</a> and <a href="https://getpocket.com">Pocket</a>—allowing content to be siphoned into smaller ponds that we can swim through, when we’re <em>ready</em>. ‘Ready’ is key here. Just because a network is real-time, doesn’t mean our consumption of it has to be. This speaks a bit to my qualms around features like “Stories” in Snapchat and Instagram. It sounds obvious, but by <em>forcing</em> folks to become daily active users, companies are boosting short-term metrics at the cost of burning out users in the long run.</p>
<p>The Slow Web doesn’t prescribe lap lengths. Stumble across four blog posts you want to read while scrolling through Twitter? No sweat, save them to Instapaper and read (or don’t) when you can, without the worry of ephemerality or higher frequency feeds sending the posts into the void.</p>
<hr />
<p>Our relationship with technology is usually metered by “time spent.“ 30 minutes per day on Instagram, 20 on Twitter, 12 on Facebook. After walking through the Slow Web, I’m starting to think measuring wall time misses the point. How a service <em>punctuates</em> your day is more important. Those 30 minutes may seem innocuous. But, if it translates to 60 device pickups (~3 per hour), that’s the attention-deficit equivalent of using commas, after, every, single, word, in, a, day-long, sentence. The Slow Web focuses on the punctuation. We’re free to choose non-prescribed cadences and whether to read <em>or</em> write to feeds, not defaulting to both. Fewer mechanisms for “engagement“ enable us to interact more honestly through words, instead of their one-bit facades. Come to think of it, the frontrunners (walkers?) of the Slow web—blogs, RSS, journaling, et al—have been around since the early days. Maybe it’s time to turn around, rejoin them, and slow down the Web.</p>
<hr />
<p>Special thanks to <a href="http://twitter.com/jasonbrennan">Jason</a> for feedback on early drafts of this entry.</p>
<h2 id="related-reading-and-footnotes">Related reading and footnotes</h2>
<p>⇒ <a href="http://nearthespeedoflight.com/article/2018_06_04_a_ramble_about_the_slow_web">A Ramble About the Slow Web</a></p>
<p>⇒ <a href="http://alistapart.com/article/orbital-content">Orbital Content</a></p>
<p>⇒ <a href="https://www.jessesquires.com/blog/removing-google-analytics-too/">Removing Google Analytics, too</a></p>
<p>⇒ <a href="http://snarkmarket.com/2010/4890">Stock and Flow</a></p>
<p>⇒ <a href="/infinite-feeds">Finite Steps in Infinite Feeds</a></p>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>Sans <a href="https://path.com">Path</a>. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Ladder construction2018-05-28T00:00:00+00:00https://jasdev.me/ladder-construction<p>Were the four years I spent at university worth it? With 2018 marking a school-length after graduation, the question has been top of mind. My unmeditated answer is yes; <a href="http://www.virginia.edu">UVA</a> was incredibly formative. Still, I’ve just recently put a finger on why. Universities construct “ladders.” Just because coursework is increasingly becoming available online doesn’t imply we can make effective traversals of that content. Schooling provides said content <em>and</em> creates a ladder—allowing us to focus on climbing through the material, instead of trying to simultaneously climb a ladder we’re building.</p>
<p>School is one of the many forms ladders can take. My peers who made the least turbulent transitions after graduation were those who could continue to build ladders outside structured educational environments—knowing when to delegate construction externally versus internal construction.</p>
<h2 id="ladder-spectrum">Ladder Spectrum</h2>
<p>Ladders serve two functions: a guided traversal through a domain—think sport or subject matter—and a feedback loop in countering our tendency towards mean reversion. The spacing between the rungs on the ladder represent the resolution of the traversal. Are you being instructed from concept to concept or sporadically checking in for accountability? A feedback loop should occur at each rung, allowing for course correction.</p>
<p>Who (or what) builds these ladders for us? Under this lens, a spectrum of solutions comes into sight: groups, friends, and coaches/managers.</p>
<h3 id="groups">Groups</h3>
<p>On one end, we have groups. They can take shape as running clubs, traditional classroom settings, or meditation groups. The last example has been particularly impactful. I’ve used Headspace for the better part of the past five years. Still, I’d regularly hit “plateaus.” The periods are hard to describe—but, I felt it. Sits started resembling a chore more than a 20-minute enclave in my day. I needed a ladder and a now-defunct group in NYC, <a href="http://www.balanced.nyc">Balanced</a>, built one for me in climbing out of these plateaus.</p>
<p>Week over week, we’d meet in <a href="https://twitter.com/CaseyRosengren">Casey</a> and <a href="https://twitter.com/LeoWid">Leo</a>’s SoHo loft, sit for 20 minutes, sift through a passage on topics like transitions, burnout, “being enough,” and then wrap up with a group discussion. Even though the volume of my practice was roughly the same between Headspace and Balanced, translating it from a single-player journey to a ladder we all climbed, together, made the difference.</p>
<p>Two nuances crop up in group settings: whether or not a teacher or peer set provides the feedback loop and the fact that <em>larger</em> groups tend to construct ladders that don’t fit each individual well.</p>
<p>The first nuance reminds me of a thought from <a href="https://twitter.com/nikillinit">Nikhil</a>.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Because your peers didn't have any say in your grade, you just needed to learn the rules the teacher had and play by them properly</p>— Nikhil Krishnan (@nikillinit) <a href="https://twitter.com/nikillinit/status/914636773447421952?ref_src=twsrc%5Etfw">October 1, 2017</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>In ladders where evaluation comes from a single person, e.g. a teacher, climbing faster might mean learning their rules and playing by them properly. This is probably innocuous in most cases—but it becomes a problem when the teacher’s rules diverge from the course corrections you actually need to make. Evaluation by a peer set (or <a href="/seek-perspectives">multiple perspectives</a>) could be more effective. An issue with peer evaluation is that the set is usually small for in-person contexts—pointing to a hybrid approach between IRL and Internet peers as a remedy. Still, that approach causes the second nuance, larger groups tend towards building inaccurate ladders. There will be times where you want to climb faster or slower than the ladder—moving along the spectrum helps with this.</p>
<h3 id="friends"><a name="friends">Friends</a></h3>
<p>Friends land between groups and coaches/managers as a ladder constructors. My most-cherished friendships have had the trait of inciting sustained, personal growth—<a href="https://twitter.com/ShivaKilaru">Shiva</a> being the one I’m most thankful for.</p>
<p>Over the past year, we’ve added a rung between our <a href="/monthly-checkins">monthly checkins</a>: a weekly email recapping the progress we’ve made towards our respective goals. The format is dead simple.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Midterm Goals:
# Goal(s) for the week:
# What got done?
# Honorable Mentions:
# Goals for the upcoming week:
</code></pre></div></div>
<p>38 checkins later, I can honestly say this accountability ladder has single-handedly driven Distillations (this collection) closer to my longer-term goals.</p>
<p><img src="/public/images/checkin_log.png" alt="Email log of the checkins so far." /></p>
<p>The weekly cadence is key—long enough to make tangible progress, yet short enough to limit time lost from procrastinating. With <em>only</em> monthly calls, I would punt work to just before the checkin, losing a possible month of headway. Tightening the rungs of our ladder from four weeks to one let us course correct more frequently. There’s a downside though; by increasing the cadence, we often lost sight of the forest. So, we’ve kept our monthly FaceTime calls as an opportunity to zoom out and work through any concerns that shook out in the weeks beforehand.</p>
<h3 id="coaches-and-managers">Coaches and Managers</h3>
<p>Coaches and managers land on the other end of the spectrum. I used to wonder why world-class athletes and CEOs were <em>commonly</em> cited as roles that necessitated coaching, leaving a gap for the rest of us. However, if you squint hard enough, coaches and managers are practically the same thing. Effective managers not only serve as umbrellas for their team, but also provide reports with regular feedback loops and guidance in climbing a company’s (sometimes-existent) career ladder.</p>
<p>An added benefit of this one-to-one dynamic is specificity. Ladders are built to your trajectory, context, and preferences, instead of being muddied by group constraints. “Luck” is teased apart by appropriately spaced rungs—better correlating efforts and outcomes.</p>
<h2 id="intuition">Intuition</h2>
<p>There’s a leap to be made between recognizing the spectrum and knowing how to move along it. Deploying this intuition can prevent progress from reverting to the mean and be especially helpful in trying times. I learned this three years ago during an extended struggle with mental health.</p>
<p>To paint context, a major part of my life is strength training. I started back in 2009—researching workouts, form, and techniques. The activity quickly became “an anchor in uneasy times, helped me look in the mirror with a palpable sense of peace, and continues to [<a href="/time-under-tension">teach me lessons</a>].” Everything was incrementally smooth sailing until February of 2016, when I had a close call while deadlifting. I nearly passed out. The ensuing check-ups to rule out any potential injuries induced a constant, lingering worry in my mind. Two months of medical stress began affecting other areas of my life. Experiences I never thought twice about—exercise, flights, public speaking—became coated with anxiety.</p>
<p>Prior, I kept my mental and physical health in check through self-paced meditation and workouts—building my own ladders. Hitting rock bottom that winter forced me to admit I couldn’t do it all myself. Instead of flying solo, I decided to delegate ladder construction externally through therapy and hiring a coach. The two provided shorter feedback loops, gradually elevating my baselines. All I had to do was show up and put in the physical and emotional labor. Over the following six months, I weathered the collectively familiar, yet internal wars that are mental health struggles. If I hadn’t admitted I needed help and let others build ladders, that rut may have lasted longer.</p>
<hr />
<p>It’s easier to trust externally constructed ladders. The hardest part of building while climbing is critiquing your own strategy, because you’re doing three things simultaneously: building the ladder, working up it, and revising the entire process. But, this doesn’t mean we should shy away from internal construction—instead, forming an intuition of the ladder spectrum in different areas of our lives is key.</p>
<p>I’m shaping my intuition when it comes to this site. While I’ve kept pace with writing, I feel like I’m on a treadmill—churning out post after post, <em>locally</em> improving (hopefully) with each one, without a larger “mile marker” in sight. It might be time to let someone (or something) else build a ladder here—until then, I’ll keep climbing.</p>
<hr />
<p>Special thanks to <a href="https://twitter.com/purpleyay">Gwen</a> and <a href="http://twitter.com/ShivaKilaru">Shiva</a> for feedback on early drafts of this entry.</p>
<h2 id="related-reading">Related reading:</h2>
<p>⇒ “<a href="/earned-fatigue">Chasing Earned Fatigue</a>”</p>
<p>⇒ “<a href="https://docs.google.com/document/d/14oYTj9gn9_XVtcXnlbmOiCHPB7sm-IsC3yOcAer55i0/edit#heading=h.pzt11033qdtd">Find Tight, Fast Feedback Loops</a>”</p>
Seek perspectives, not mentors2018-05-01T00:00:00+00:00https://jasdev.me/seek-perspectives<p>At the moment, I have three “mentors,” all of varying ages. <a href="http://twitter.com/alicexyr">Alice</a> at 21, <a href="http://twitter.com/ShivaKilaru">Shiva</a> is 25, and <a href="http://twitter.com/jxxf">John</a> coming in at 33. Before diving in, let’s clear the air around the quotes surrounding “mentors.” I wholeheartedly believe mentorship is <em>bidirectional</em> and the set of folks you give this label to can, and should, <em>change</em> over time. This flies against the traditional notion of mentorship—making me question if it’s the right thing to seek in first place. The usual narrative claims we should find a mentor (singular) early in our careers and stay under their wing, indefinitely. In more <em>static</em> domains such as woodworking, that one-to-one, apprentice-like setup might be enough. However, in fields where knowledge has an incredibly short half-life—think medicine or engineering—time fogs the lens through which any one mentor can assess the context of your situation.</p>
<p>Perspectives are more effective and age is just one dimension to strive for variability. If we’re all in some <em>n</em>-dimensional space, where <em>n</em> is the number of axes that make us unique, then getting the most perspective on a decision will likely involve consulting folks <em>outside</em> your <em>n</em>-dimensional neighborhood.</p>
<p><img src="/public/images/age_perspective.png" alt="Axis with age increasing to the right. Three annotations are present: Alice at 21, Shiva at 25, Me (Jasdev) at 26, and John at 33. Surrounding each annotation is a colored “neighborhood,“ representing a perspective." /></p>
<p>Of course, there are two caveats here. First, constructing a peer set that covers every dimension is practically impossible. The intent is to get a large-enough span and then stand at the intersection of divergent influences. Second, this isn’t to disqualify traditional, long-term mentors—but rather to be honest in checking whether or not their experiences are relevant to your immediate situation. There are a couple of aspects to consider: knowledge half-life and longitudinal bias—we’ll dig into both and cover some ways to seek perspectives in practice.</p>
<h2 id="knowledge-half-life">Knowledge Half-Life</h2>
<p>Half-life is “the time required for any specified property (e.g., the concentration of a substance in the body) to decrease by half.” Applied to knowledge, we can guess how long the things we learn today will remain relevant. The encompassing domain is important. For instance, in mathematics, half-lives are durable over longer timescales, since each finding is a deduction from previous work and axioms<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. On the other hand, scientific fields like civil engineering and astrophysics move quickly, lowering half-lives. How does this tie back to mentorship? When seeking advice on a challenge you’re facing, it might be helpful to tease apart how much of the difficulty lies in knowledge with shorter half-lives versus relative invariants like emotional intelligence, architecture, and design patterns. Doing so could determine if an existing mentor or a new set of perspectives would shine more light.</p>
<h2 id="longitudinal-bias">Longitudinal Bias</h2>
<p>I’ve started questioning whether or not knowing someone, longitudinally, over the arc of their career means you’re better equipped to provide advice when needed. I used to think so—and I’m starting to change my mind. A way to describe the underlying problem is “longitudinal bias”: being familiar with someone’s past <em>subconsciously</em> molds how you see them in your mind’s eye and sets expectations on future behavior. The problem comes when their situation requires them to break out of this mold—showing the advantage of perspectives. By consulting <em>outside</em> perspectives on relevant situations, we can solicit guidance that isn’t biased by our past.</p>
<h2 id="in-practice">In Practice</h2>
<p>Assembling a set of perspectives while considering knowledge half-life and avoiding longitudinal bias involves putting yourself in the right “rooms“—both physically and digitally. I’m going to focus on the latter, as it’s often overlooked in mentorship discussions.</p>
<p>Extending a section from a previous entry, “<a href="/emotional-ranges">Emotional Ranges</a>,“ Twitter is an incredible tool for not only cultivating empathy, but also constructing a diverse peer set.</p>
<blockquote>
<p>While Twitter receives a lot of rightly-deserved flak for no longer harboring a safe space for those in the minority of the aforementioned dimensions, it has the power to enable users to be deliberate about the metaphorical “rooms“ they place themselves in, for better or for worse. On the worse end, the “room“ is filled to the brim with similar walks of life, reinforcing confirmation bias. On the better end, it’s a wonderful educational tool in cultivating empathy.</p>
</blockquote>
<p>Following folks from walks of life unlike your own lets you span a larger subset of the <em>n</em>-dimensional space we discussed earlier. An added benefit of placing your consciousness at the confluence of diverse streams of thought is that it makes independent thinking an act of discipline, rather than leaving it up to willpower.</p>
<p>I don’t mean to imply I’m perfect at this—my following graph is a continual work in progress. Yet, there are a couple of approaches that have helped.</p>
<p>First is a Twitter gender breakdown tool, <a href="https://www.proporti.onl">Proporti.onl</a>. The code is a bit dated, “[inaccurate,] and undercounts nonbinary folk, but it’s better than making no effort at all.” Proporti.onl has given me a frame of reference—albeit an approximation—in working towards increasing the variance of my feed.</p>
<p>Second is a subtle habit I’ve picked up since using Twitter over the years. Let’s assume there are two contributors, <em>A</em> and <em>B</em>, behind a major project. I’ll follow the one with <em>fewer</em> followers (say, <em>B</em>). Odds are <em>B</em> will bring a newer, humbler perspective into my feed and, by retweeting them, it’ll chip away at existing associations between the project and <em>A</em>—a reminder that our usual habits around attaching a single person to an effort inadvertently “others” contributors with a smaller online footprint. <a href="https://twitter.com/mcgeerosa">Rosa McGee</a> is not only a friend, but a prime example of someone who brings such a perspective to my timeline. She may not have the “largest following” amongst <a href="http://www.theskimm.com">theSkimm</a>’s team. Yet, I always pause to read her thoughts on Twitter.</p>
<p>This speaks a bit to my qualm around Twitter Lists and “you should follow <em>{Q, R, S, T…}</em>”-style <a href="https://twitter.com/bjc290/status/980332848023527424">tweets</a>. Having a surface area on Twitter <a href="https://twitter.com/nbashaw/status/810339855641223168">isn’t necessarily correlated with skill</a>.</p>
<hr />
<p>It’s healthy to challenge whether or not traditional, apprentice-style mentorship still works. The term “mentor“ can lead to uncomfortable and untrue pedestals—making me lean towards perspectives. By reaching out to people from walks of life unlike your own, you can illuminate a larger neighborhood of the <em>n</em>-dimensional space your career is navigating through. Digitally, we’re currently afforded this ability through Twitter. Instead of having a handful of lenses on the world with in-person mentorship, I have 311. You’d be surprised how willing people are to lend a hand. In fact, I’ve made some of my <a href="/ambient-intimacy">best friends</a> by seeking perspectives, not mentors.</p>
<hr />
<p>Special thanks to <a href="http://twitter.com/alicexyr">Alice</a>, <a href="http://twitter.com/ShivaKilaru">Shiva</a>, <a href="https://twitter.com/mcgeerosa">Rosa</a>, and <a href="http://twitter.com/jxxf">John</a> for feedback on early drafts of this entry.</p>
<h2 id="related-reading-and-footnotes">Related reading and footnotes:</h2>
<p>⇒ “<a href="https://medium.com/thelist/have-some-coffee-9e468d958e77">Have Some Coffee</a>”</p>
<p>⇒ “<a href="https://themanual.org/read/issues/4/diana-kimball/article">On Mentoring</a>”</p>
<p>⇒ “<a href="https://emptysqua.re/blog/gender-of-twitter-users-i-follow/">72% Of The People I Follow On Twitter Are Men</a>”</p>
<p>⇒ “<a href="https://cate.blog/2017/06/29/lists-of-women-dont-change-anything/">Lists of Women Don’t Change Anything</a>”</p>
<p>⇒ “<a href="https://speakerdeck.com/nikhilkrishnan/why-twitter-is-dope-and-how-to-use-it">Why Twitter Is Dope And How To Use It</a>”</p>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>In editing, <a href="http://twitter.com/jxxf">John</a> pointed out a nuance that knowledge in mathematics is rarely invalidated, but often <em>superseded</em>. The distinction is important. Unlike other fields in which prior knowledge is frequently proven incorrect, the structure of mathematics forces correctness through proofs. Yet, the techniques and approaches used can often be made obsolete in the future (e.g. integrals instead of Riemann sums). <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Slowing down time2018-04-03T00:00:00+00:00https://jasdev.me/slowing-down-time<p>There’s no dancing around it—we all have a finite number of heartbeats, breaths, and seconds on this planet (and maybe Mars). However, the curious thing about time is its elastic nature. Never-ending high school summers. Days so long they had seasons. One-on-ones that stall when you ask for a raise. Assuming we’re not moving near the speed of light, time marches forwards at a constant rate—it’s our <em>perceptions</em> of it that change. I’ve become fascinated with ways to slow down this perception.</p>
<p>We’ll cover three aspects that appear to be at play: new experiences, busy minds, and tempo.</p>
<h2 id="new-experiences">New Experiences</h2>
<p>When everyday routines aren’t punctuated by experiences like travel, meeting new people, or novel tasks, we shift into autopilot. We’ve all been there. A friend will ask how your week went and, after pausing to reflect, the past few days resemble a blurred stretch of time. This dynamic is typically attributed to spending time in “System One” of thought. Daniel Kahneman presents the duality between System One and Two in the notorious tome: “<a href="https://www.amazon.com/Thinking-Fast-Slow-Daniel-Kahneman/dp/0374533555">Thinking Fast and Slow</a>.” At a high level, the first mode is reserved for low-energy cognitive tasks; those that are “fast, automatic, frequent, emotional, stereotypic, or <em>unconscious</em>.” Examples include commutes, performing pre-computed arithmetic (<code class="language-plaintext highlighter-rouge">10 * 10 = ?</code>), or finishing trite phrases (“To be, or…”). The second mode is more effortful and <em>conscious</em>. Newer experiences, performing an unfamiliar exercise, or following a math proof all land in this bucket. These modes have a powerful effect on our perception of time. It seems to contract when the unconscious thoughts of System One string together; conversely, the neurogenerative nature of System Two makes us more aware of the gaps between thoughts, dilating that perception.</p>
<p>The awareness of gaps in thought brings us to the next topic—how busy our minds are.</p>
<h2 id="busy-minds">Busy Minds</h2>
<p>I firmly believe that a busy mind accelerates the passage of time. Constant context switching, obsessive rumination, and other forms of mental load not only shorten the space between thoughts, but also <a href="https://www.nytimes.com/2016/06/19/opinion/sunday/think-less-think-better.html">inhibit our capacity for originality</a>. How do we remedy this? The answer might involve allocating uninterrupted spans of time to experience boredom, let the dust in our minds settle, and get into flow.</p>
<p>Boredom hit hard during my first, week-long, silent retreat. I hesitate to include this anecdote, since describing the experience might alter my relationship with it. But, I aim to honestly distill it here in the hope that it helps folks like me, whose insatiable information diet has made boredom a privilege.</p>
<p>Seven days without reading, writing, speaking, or direct eye contact (one of the retreat rules) made me realize how rarely I get bored during everyday New York Life. It got to the point where I would take longer showers just to read the label on my shampoo bottle—yikes. Around the third day, I began shifting my appetite for mental stimulation towards paying attention to what was around me. I vividly remember spending 45 minutes staring at a tree on Wednesday evening. My focus wandered from root to leaves and back in some sort of mental Brownian motion. The remainder of the retreat contained the slowest days of my life, in the most palatable sense. I was reminded that time moves at a glacial pace when we’re present to soak in the world around us.</p>
<p>This <em>isn’t</em> to say we should be present at all times. Going on autopilot during commutes or chores lets us to deploy our attention towards experiences we want to magnify.</p>
<h2 id="tempo">Tempo</h2>
<p>Tempo has a relation to time in a couple of domains: music and clocks.</p>
<h3 id="music">Music</h3>
<p>Music backgrounds the entirety of my days. Between wearing headphones at work to turning on speakers at the apartment, I’m constantly listening to house and trance. These genres are marked by differences in beats per minute (bpm) ranges with house clocking in at 120–130 bpm and trance at 110–150. This got me wondering if bpm ranges impacted time perception. After <a href="https://twitter.com/jasdev/status/786961355962257409">asking on Twitter</a>, I was pointed to <a href="http://journals.sagepub.com/doi/abs/10.1177/0305735698261007">a study from 1998</a> on tempo and time perception in a gymnasium setting (emphasis mine).</p>
<blockquote>
<p>“Fast or slow music was played in [a] gymnasium [for] two consecutive days, and as [the participants] left, [they] were informed of their time of entry and asked to estimate the present time. The results showed that musical tempo <em>did not influence</em> time duration estimates in any one particular direction, but that slow music led to a greater degree of <em>inaccuracy</em> in estimations than did fast music.”</p>
</blockquote>
<p>Despite a lack of influence between tempo and time duration estimates, I’ve felt the observed inaccuracy when going to slower house music concerts. I’ll often find myself checking my watch not out of habit, but because my internal clock and wall time have fallen out of sync.</p>
<h3 id="clocks">Clocks</h3>
<p>Tempo is also embedded in the measurement of time itself. A few alternative approaches to clocks have side effects on time perception.</p>
<h4 id="chromachron">Chromachron</h4>
<p>The Chromachron is a physical manifestation of synesthesia. Opting for “Color Time”—a literal translation of Chromachron—the “anti-stress watch is <a href="https://www.hodinkee.com/articles/chromachron-a-radically-new-approach-to-time">praised for its inability to display the exact time</a>.”</p>
<p><img src="/public/images/chromachron.jpg" alt="“Two Chromachron watches over the 1976 booklet ‘The Story of Color Time.’” Image credit to HODINKEE." /></p>
<blockquote>
<p>“Two Chromachron watches over the 1976 booklet ‘The Story of Color Time.’” Image credit to <a href="https://www.hodinkee.com">HODINKEE</a>.</p>
</blockquote>
<p>The watch face is split into a dozen colors, proxying the “illumination and energy” of each hour. Its black center slows the tempo of our perception by providing an hour-wide slice into the current time. Instead of seeing the minutes or seconds pass by, we can only perceive subtler shifts in color—making me curious if these widened intervals trigger the tendency towards inaccurate time estimations shown in the gymnasium study.</p>
<p>Chromachrons are hard to come by. So, I’ve tried to approximate its relaxed approach by customizing the faces on watchOS. Explorer, Hermès, and Utility are some examples with configurable hour increments.</p>
<p><img src="/public/images/hermes.png" alt="Various Hermès watchOS face configurations with differing amount of hour markers." /></p>
<p>Just as avid enthusiasts wear a weekend watch, I’ll usually swipe over to a less fine-grained setting on Saturdays and Sundays—fading the precision of 1:24pm to “an afternoon coffee in Madison Square Park.”</p>
<h4 id="24-hour-time">24-Hour Time</h4>
<p>While the Chromachron has 12 colors, it still folds a calendar day into “a.m.” and “p.m.” I’ve <a href="/24-hour-time">tried switching to military time</a> and it allowed me to better grasp the length of a day. The division, modulo 12, provides a two-beat tempo for the hours in a day—which could shorten our time perception. There’s a palpable length of time that using “13” over “1 p.m.” conveys. The rest of the world probably made the right call on this format.</p>
<h4 id="without-time">Without Time</h4>
<p>What if we threw out clocks altogether? Doing so would certainly be utopian and make coordination nearly impossible. Still, I believe there’s a case for regimenting stretches of time to <em>not know</em> what time it is. I honestly can’t remember the last time I went an entire day without seeing a clock of some sort. David Cain meditated on this in his “<a href="http://www.raptitude.com/2017/09/the-case-for-not-knowing-what-time-it-is/">Case For Not Knowing What Time It Is</a>.”</p>
<blockquote>
<p>“There’s a […] psychological cost to constantly learning what time it is. Whenever we see the time, we often can’t help but remember our whole basket of obligations and goals, and wonder how we’re ever going to fit them all together. And how often do we feel completely confident in our ability to do that?”</p>
</blockquote>
<p>A perfect congruence between tempo—or lack thereof in Cain’s case—and our perception of time probably doesn’t exist. In fact, tempo is so important for humans that we <a href="https://twitter.com/mmay3r/status/934671385485381632">ritualized weekdays</a>. Yet, we can still be deliberate in picking intervals to maintain a healthy relationship with the abstract grid that overlays existence.</p>
<hr />
<p>Our tour of new experiences, busy minds, and tempo has shown that the escalator of time already moves slowly—we’re the ones sprinting up it. Perhaps, slowing down time means finding a tempo that provides just enough routine, while allowing us to notice the infinite, green glow between the gaps of that escalator. After all, our perception of time is the sum of these gaps.</p>
<hr />
<p>Special thanks to <a href="https://twitter.com/ShivaKilaru">Shiva</a> for feedback on early drafts of this entry.</p>
<h2 id="related-reading">Related reading:</h2>
<p>⇒ “<a href="http://nearthespeedoflight.com/article/2017_01_25_don___t_kill_time_2">Don’t Kill Time 2</a>”</p>
<p>⇒ “<a href="https://ktzine.com/walk-on-the-other-side-of-the-street-be803c8f55cb">Walk on the Other Side of the Street</a>”</p>
<p>⇒ “<a href="http://nautil.us/issue/53/monsters/what-boredom-does-to-you">What Boredom Does to You</a>”</p>
<p>⇒ “<a href="https://www.edge.org/conversation/brain-time">Brain Time</a>”</p>
Alone in parallel2018-03-21T00:00:00+00:00https://jasdev.me/alone-in-parallel<p>An often-forgotten aspect of “being alone” or “me time” is the backdrop in which it takes place. On one end of the spectrum, we have isolation—<a href="https://en.wikipedia.org/wiki/Walden">Walden</a>-esque retreats or withdrawing to our apartments after a full day; on the other end, there’s the intriguing dynamic of being alone <em>amongst</em> company.</p>
<p>The notion of simultaneously individual, yet shared experiences has many embeddings (don’t worry, we won’t be covering silent discos). Cities are incredible at fostering this. A favorite New York Saturday tradition of mine is floating from coffee shop to coffee shop. Time to be alone in parallel with the city.</p>
<p>I want to explore a few personal attempts at being alone, together and how they’ve been a litmus test for how close I am with someone.</p>
<h3 id="synchronized-workouts">Synchronized Workouts</h3>
<p>Last spring, <a href="https://twitter.com/ryandawidjan">Ryan</a> and I tried outdoor runs with synchronized audio by playing the same mix through each of our headphones. This relieved the pressure to sustain out-of-breath conversation, allowed us to keep pace, and provided for an ambient, parallel experience. There’s something about music’s ability to coordinate the biorhythms of those listening. I highly recommend trying it, even in non-fitness contexts—which segues to nicely to another instance of individual/shared experiences: remote coworking.</p>
<h3 id="remote-coworking">Remote Coworking</h3>
<p>A cherished habit amongst my friends is to make otherwise lonely evenings less so by remotely coworking together. We’ll hop on Skype, Hangouts, or FaceTime and tackle whatever we need to get done, occasionally distracting one another with memes or pointless digressions.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Round 2 w/ <a href="https://twitter.com/vvdandekar?ref_src=twsrc%5Etfw">@vvdandekar</a>, <a href="https://twitter.com/binroot?ref_src=twsrc%5Etfw">@binroot</a>, and <a href="https://twitter.com/aksimhal?ref_src=twsrc%5Etfw">@aksimhal</a> <a href="https://t.co/wNxdXRP1jR">pic.twitter.com/wNxdXRP1jR</a></p>— Jasdev Singh (@jasdev) <a href="https://twitter.com/jasdev/status/800469476600909824?ref_src=twsrc%5Etfw">November 20, 2016</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Given a large enough subgroup, it’s been possible to keep a room running for entire weekends at a time (which might point towards our level of sheltered nerdiness). Remote coworking feels like an approximation of a digital “<a href="https://www.wsj.com/articles/SB115885721556770219">third place</a>”—a designated retreat from the office and our homes.</p>
<h3 id="pseudo-book-club">Pseudo-Book Club</h3>
<p>My favorite example of being alone with others is what I unofficially refer to as the “Pseudo-Book Club.” The name stems from my gripe with traditional book clubs: that every member has to read the <em>same</em> thing. I’m a firm believer that literature should be digested when we’re best able to receive it (e.g. with age, certain experiences, or desired knowledge). Forcing everyone in the group to forego their personal trajectories never made sense to me. So, <a href="https://twitter.com/katelikestoread">Kate</a>, <a href="https://twitter.com/jasonbrennan">Jason</a>, <a href="https://twitter.com/mergesort">Joe</a> and I landed on a different approach. Each Tuesday morning from 9–10am, we meet at coffee shop with our <a href="http://instapaper.com">Instapaper</a><sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> queues or a book and read on our own, together. It’s an hour I look forward to week over week.</p>
<hr />
<p>Between synchronized workouts, remote coworking, and the book club, I used to worry about the potential for uncomfortable silences. But, the opposite ended up happening; the silence was heartening. A commonly cited indicator of a strong friendship is the ability to talk for hours. After this exploration, I’m beginning to think that’s not entirely accurate. Our closest friends might be the ones we can comfortably be alone, in parallel, with.</p>
<hr />
<p>Special thanks to <a href="https://twitter.com/ShivaKilaru">Shiva</a> for editing early drafts of this entry.</p>
<h2 id="footnotes">Footnotes</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>The coffee shop must serve tea and Joe uses <a href="https://getpocket.com">Pocket</a>, because he likes to make things complicated <3 <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Digital drift2018-03-05T00:00:00+00:00https://jasdev.me/digital-drift<p>Why do some networks—like Facebook’s graph—seem to <em>drift</em> into irrelevance over time? Putting the product’s (ever-crowded) feature set aside, I think there’s a fundamental assumption baked into services today causing this: edges (friendships, follows, subscriptions, etc.) between nodes existing <em>indefinitely</em>. Sure, you can always unfriend, unfollow, or unsubscribe from accounts that are no longer of interest. But these actions aren’t <em>encouraged</em>. This is counterintuitive. By dissuading users from pruning their graphs, services boost short-term “metrics,” at the risk of causing the network and reality to diverge—eventually leading to churn altogether. Concretely, the combination of my FB feed containing posts from folks I’ve been out of touch with and the <a href="/ambient-intimacy#network-nomenclature">weight of the verb</a> “unfriending” causes the network’s snapshot of my social reality to become increasingly inaccurate. It’s healthy for my inner circle to <em>change</em> over time; Facebook makes it feel unhealthy through the connotation of “unfriending.” Let’s call the gap between a system’s model of reality and reality itself “digital drift.”</p>
<p><img src="/public/images/digital_drift.png" alt="Diagram of social reality and its graph modeled by a system diverging" /></p>
<p>After giving the concept a name, I’ve noticed it crop up elsewhere. To-do apps are another instance. At their core, they attempt to model the state of the tasks we need to accomplish. However, without regular upkeep, they go stale in being able to answer the question “what should I be working on right now?“—causing a loss of trust in the system.</p>
<p>How can services mitigate digital drift? Two approaches come to mind: periodic “reality reconciliations” and malleability.</p>
<h2 id="reality-reconciliations">Reality Reconciliations</h2>
<p>Reality reconciliations are <em>deliberate</em> attempts to update a system’s model of the world. These attempts can be explicitly encouraged or initiated by the user. <a href="https://www.omnigroup.com/omnifocus">OmniFocus</a> does a wonderful job at encouraging the former. Each project within the task management system can be assigned a “review” cadence.</p>
<p><img src="/public/images/omni_review.png" alt="Review: Next Review: Feb 26, 2018, Review Every: 1 week, Last Reviewed: Feb 19, 2018" /></p>
<p>Reviews are prompted at the chosen intervals, allowing users to groom tasks within a project. By reconciling items in OmniFocus and reality, it’s easier to trust the system—opening the app is met with a snapshot that better approximates your life instead of irrelevant cruft.</p>
<p><a href="https://twitter.com/rosadona">Rosemary Donahue</a> (somewhat hilariously) provided an example of user-initiated reality reconciliation in her post “<a href="https://hellogiggles.com/love-sex/friends/love-facebook-notifications-unfriend/">The Strange Reason I Love Facebook Birthday Notifications</a>.” On birthdays of folks she no longer considers friends, she quietly unfriends them—a Facebook Irish Goodbye. This may seem harsh, but Rosemary elaborates on how it gives her more digital space.</p>
<blockquote>
<p>“It may sound heartless, to unfriend people on their birthday, but I like to think that they won’t notice I’ve done it on a day that other people—presumably, people they’re still in touch with—are showering their timeline with love, affection, and, of course, GIFs. While I know that I could simply unfollow rather than unfriend on Facebook…, I don’t understand the need to keep up digital pretenses…I prefer to leave people’s Internet lives the same way I leave parties: quietly, with no goodbyes, and perhaps with a few snacks in my pocket.”</p>
</blockquote>
<p>OmniFocus Reviews and Rosemary’s approach counteract digital drift. Still, these forms of reconciliation feel like half-steps and require non trivial effort, which brings us to a way around that: malleability.</p>
<h2 id="malleability">Malleability</h2>
<p>What if the networks (and apps, systems, etc.) we use were <em>malleable</em>? Social graphs would frictionlessly evolve as friends come in and out of our lives. Messaging platforms seem to poke at this by sorting threads by recency. Those we’re in touch with float to the top and everyone else gently lands a few scrolls away—adjusting our lens on the social graph of the platform along a single axis: time. Of course, there’s an inherent bias here, as it prioritizes recency over longer-term friends, loved ones, and relatives we may talk to on infrequent timescales.</p>
<p><img src="/public/images/messaging_graph_lens.png" alt="Illustration of messaging platforms shuffling the social graph by recency" /></p>
<p>Algorithmic feeds are another attempt at malleability. Unlike sorting, algorithmic feeds use multiple dimensions to shape our view of the graph. For example, engagement™ and pauses in scrolling on another user’s posts might cause them to show up more frequently in the feed. We can view this as the network updating the edges of your social graph in real time.</p>
<p><img src="/public/images/feed_lens.png" alt="Illustration of feeds creating a lens on a social graph" /></p>
<p>Sorting and algorithmic feeds have a major shortcoming: they can’t prune our graphs. Don’t get me wrong, I’m not advocating for cutting everyone outside of one’s inner circle from their life—those “older” edges in the graph might actually be ripe with <a href="http://danshipper.com/latent-serendipity">latent serendipity</a>. But, there is a cognitive overhead involved with keeping others in our lives, even digitally. We’ve observed this in the “real world” through <a href="https://en.wikipedia.org/wiki/Dunbar%27s_number">Dunbar’s Number</a>, which suggests that the “cognitive limit to the number of people with whom one can maintain stable social relationships” hovers around 150 people. I wonder if there’s a unique analog to Dunbar’s number for each of the services we use (dependent on the atomic units<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> of the network, emotional weight attached to edges, etc.).</p>
<p>A lightweight way to rein in digital drift could be edges with expiration dates. As an example, what if we had the ability to follow an account for <em>X</em> months. When <em>X</em> months comes around, the edge silently drops—letting our graph reconcile with reality. Functionality like this could be especially useful on Twitter. I’ll often follow someone when they’re covering an event or aligned with a temporary interest of mine. Despite the social awkwardness of “following someone for a finite amount of time,” it’s more honest to realize that not everyone you follow should continue to be in your graph <em>forever</em> (the default assumption of most services). The permanence of the Internet is simultaneously a strength and an Achilles’ heel. Rethinking the dynamic of edges between nodes is going to require a heavy dose of thoughtfulness. Although, I think the next generation of networks will need it to avoid inevitably drifting.</p>
<p>We’re approaching a stage where people’s main “life”—to an increasing extent—happens in digital realms. As such, the gap between reality and its snapshot embedded in systems affect our usage, trust, and relationship with them. The model of my social reality approximated by Myspace? Archaic. My Facebook graph? Becoming more irrelevant by the day. Perhaps the best services <em>mirror</em> reality and digital drift is a trailing indicator of that mirror being broken.</p>
<p>In meatspace, there are no reconciliations, they happen naturally. Edges fade by <em>default</em>; if you stop calling a friend, they’ll slowly vanish from your life. So, mitigating digital drift might be a lost cause, since digital worlds are inherently durable. What if we took the opposite approach—requiring maintenance to keep edges around and not requiring maintenance for them to drop? That’s how our world works after all.</p>
<hr />
<p>Special thanks to <a href="https://twitter.com/can">Can</a> and <a href="https://twitter.com/nikillinit">Nikhil</a> for feedback on early drafts of this entry.</p>
<h2 id="footnotes-and-related-reading">Footnotes and related reading:</h2>
<p>⇒ <a href="https://blog.pinboard.in/2011/11/the_social_graph_is_neither/">The Social Graph Is Neither</a></p>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>Twitter : tweets, Facebook : posts, Snapchat : snaps, … (if we’re considering the primary actions taken on each platform). <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Planting memories2018-02-19T00:00:00+00:00https://jasdev.me/planting-memories<p>Rounding out my third year in New York, I’ve started noticing something special. Memories have increasingly attached themselves to locations. Walking down 1st Avenue reminds me of summer dates, Madison Square Park conversations over Gasoline Alley coffee, meeting Dave and Ryan alongside Riverside Drive at ungodly hours for Saturday bike rides.</p>
<blockquote class="instagram-media" data-instgrm-captioned="" data-instgrm-permalink="https://www.instagram.com/p/BakSvTGAdVf/" data-instgrm-version="8" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);"><div style="padding:8px;"> <div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50.0% 0; text-align:center; width:100%;"> <div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAMUExURczMzPf399fX1+bm5mzY9AMAAADiSURBVDjLvZXbEsMgCES5/P8/t9FuRVCRmU73JWlzosgSIIZURCjo/ad+EQJJB4Hv8BFt+IDpQoCx1wjOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div></div> <p style=" margin:8px 0 0 0; padding:0 4px;"> <a href="https://www.instagram.com/p/BakSvTGAdVf/" style=" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;" target="_blank">weekend forty two</a></p> <p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;">A post shared by @<a href="https://www.instagram.com/jasdev/" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px;" target="_blank"> jasdev</a> on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2017-10-22T22:28:54+00:00">Oct 22, 2017 at 3:28pm PDT</time></p></div></blockquote>
<script async="" defer="" src="//www.instagram.com/embed.js"></script>
<blockquote class="twitter-video" data-lang="en"><p lang="en" dir="ltr">a first riding 9W: pacelining w. a speed skater, averaging around 23 mph w. <a href="https://twitter.com/jasdev?ref_src=twsrc%5Etfw">@jasdev</a> 💨<br /><br />(gimbal video crafted by <a href="https://twitter.com/ryandawidjan?ref_src=twsrc%5Etfw">@ryandawidjan</a>) <a href="https://t.co/EqY7sk5bnX">pic.twitter.com/EqY7sk5bnX</a></p>— Dave Ambrose (@daveambrose) <a href="https://twitter.com/daveambrose/status/873941633183735809?ref_src=twsrc%5Etfw">June 11, 2017</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>The density of the city allows me to <em>plant</em> memories and walk through the forest into which they grow. As I move from neighborhood to neighborhood, each one watermarks a chapter of my life. My <a href="https://twitter.com/jasdev/status/637401939555274752">first sublet</a> in the Lower East Side, being the seemingly only unmarried, non-guardian of a child and/or pet in Park Slope, and the reunion with my former <a href="https://www.instagram.com/p/BcKtUATA9YT">university roommates</a> here on 23rd Street and Avenue C. This got me thinking about other memory anchors and two came to mind: <a href="https://support.apple.com/en-us/HT207310">Live Photos</a> and music.</p>
<p>Live Photos are three-second clips <em>passively</em> recorded around every photo taken on iOS. They have the magical ability to take you back to the instant a picture was taken by capturing beginnings of smiles, background laughter, and other small details that would’ve otherwise been lost. These videos add a third dimension to the previously two-dimensional scrolls of the Camera Roll.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Today was a good day<br /><br />📸: <a href="https://twitter.com/ryandawidjan?ref_src=twsrc%5Etfw">@ryandawidjan</a> <a href="https://t.co/6pskWPzDKc">pic.twitter.com/6pskWPzDKc</a></p>— Jasdev Singh (@jasdev) <a href="https://twitter.com/jasdev/status/882764346505474049?ref_src=twsrc%5Etfw">July 6, 2017</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-lang="en"><p lang="und" dir="ltr"><a href="https://t.co/xdT1SoKBZ0">pic.twitter.com/xdT1SoKBZ0</a></p>— Jasdev Singh (@jasdev) <a href="https://twitter.com/jasdev/status/861745878951964672?ref_src=twsrc%5Etfw">May 9, 2017</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Music is another powerful anchor. It almost losslessly compresses entire seasons of our lives.</p>
<p>“<a href="https://soundcloud.com/quylong/dirty-south-joe-gil-your-heart">Your Heart</a>” fixed itself to my last undergraduate final. “<a href="https://soundcloud.com/jasdev-singh/dubvision-vs-east-young-redux">Redux Starting Again</a>” was on repeat as I wrote a friend’s 21st birthday card. “<a href="https://soundcloud.com/zerothree-music/tim-mason-chords-of-life-realprog-v3-out-friday">Chords of Life</a>” backdropped a commute one evening when New York started to <a href="https://twitter.com/jasdev/status/788543352736186368">feel like home</a>.</p>
<p>Batching these tracks <a href="https://soundcloud.com/jasdev-singh/sets">into playlists</a> creates an audiobook of my inner life. Maybe there was something to the old make-your-crush-a-mixtape sign of affection.</p>
<hr />
<p>The anchoring between memories and media is fascinating, whether physical (locations) or digital—with Live Photos and music. These anchors form a sort of forest. When revisiting old neighborhoods, opening the Camera Roll, or digging up old playlists, I’m walking through memories.</p>
<blockquote class="instagram-media" data-instgrm-captioned="" data-instgrm-permalink="https://www.instagram.com/p/44ybjVta49/" data-instgrm-version="8" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);"><div style="padding:8px;"> <div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50.0% 0; text-align:center; width:100%;"> <div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAMUExURczMzPf399fX1+bm5mzY9AMAAADiSURBVDjLvZXbEsMgCES5/P8/t9FuRVCRmU73JWlzosgSIIZURCjo/ad+EQJJB4Hv8BFt+IDpQoCx1wjOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div></div> <p style=" margin:8px 0 0 0; padding:0 4px;"> <a href="https://www.instagram.com/p/44ybjVta49/" style=" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;" target="_blank">Walking through memories</a></p> <p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;">A post shared by @<a href="https://www.instagram.com/jasdev/" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px;" target="_blank"> jasdev</a> on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2015-07-08T19:43:14+00:00">Jul 8, 2015 at 12:43pm PDT</time></p></div></blockquote>
<script async="" defer="" src="//www.instagram.com/embed.js"></script>
<hr />
<p>Special thanks to <a href="https://twitter.com/justinmduke">Justin</a> for feedback on early drafts of this post.</p>
<h2 id="related-reading">Related reading:</h2>
<ul>
<li><a href="https://medium.com/@kraykray/crying-in-public-f6e573a79fd1">Crying in Public</a></li>
<li><a href="https://512pixels.net/2016/07/photographic-memories/">Photographic Memories</a></li>
</ul>
Time under tension2018-02-12T20:00:00+00:00https://jasdev.me/time-under-tension<p>Strength training is a nonnegotiable part of my routine. It has been an anchor in uneasy times, helped me look in the mirror with a palpable sense of peace, and continues to teach lessons that have cognitive analogs. I want to hone in on a particular lesson that my trainer has relayed over the past couple of years: “Time Under Tension.“</p>
<p>Time Under Tension (TUT) originates from a style of lifting called hypertrophy training. Unlike traditional olympic variants, which focus primarily on setting personal bests, hypertrophy is about how long a muscle is under strain. This duration of stress—up to a limit—induces physical growth. In a similar vein, endurance athletes practice heart rate zone training to <a href="https://www.thecut.com/2016/06/how-exercise-shapes-you-far-beyond-the-gym.html">get comfortable being uncomfortable</a>. I wholeheartedly believe there is a cognitive parallel here. Some of my hardest days have involved staring at a problem for hours, clueless—a sort of mental time under tension.</p>
<p>I’m trying to better lean into these forms of tension. Stuck on a bug whose root cause is just out of reach? I should give it a fair shot before asking for help<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. “Productively” procrastinating on what I <em>should</em> be working on with low hanging fruit? Likely a sign that I’m avoiding the TUT associated with my most important task. Embracing this stress fosters an internal confidence. Physically, knowing that I’ve previously endured <em>X</em> minutes of tension across multiple sets lets me graduate to <em>X + 1</em> minutes. Mentally, it reassures me that—on a long-enough timescale—I’ll figure out whatever bug, feature, etc. I’m working on. This doesn’t imply blindly leaning into tension, knowing when to stop is <em>just as important</em>. Effectively distinguishing between healthy and damaging forms of tension is a skill in its own right.</p>
<h2 id="a-different-lens-on-top-performers">A Different Lens on Top Performers</h2>
<p>The concept of TUT reshaped how I view top performers in their respective fields. Maybe they’re not so much “gifted” as having higher physical/mental pain tolerances than the rest. The best mathematicians are <em>masters</em> at this. Let’s take Andrew Wiles for example. He spent seven years (!!!) proving Fermat’s Last Theorem, which can be stated in a mere three lines:</p>
<p><img src="/public/images/fermat.jpeg" alt="Fermat’s Last Theorem, formulated in 1637, states that no three distinct positive integers _a_, _b_, and _c_ can satisfy the equation _a_<sup>_n_</sup> + _b_<sup>_n_</sup> = _c_<sup>_n_</sup> if _n_ is an integer greater than two" /></p>
<p>Imagine “being stuck” for the better part of a century. In the trenches of Fermat’s conjecture, Wiles built the machinery (quite literally, the proof is 129 pages long) necessary to confirm an intuition from 1637. Wiles described coming to terms with this state in a <a href="https://mathwithbaddrawings.com/2017/09/20/the-state-of-being-stuck/">conversation with Ben Orlin</a>.</p>
<blockquote>
<p>“When it comes to math, Wiles said, people tend to believe ‘that there is something you’re born with, and either you have it or you don’t. But that’s not really the experience of mathematicians. We all find it difficult. It’s not that we’re any different from someone who struggles with maths problems in third grade…. We’re just prepared to handle that struggle on a much larger scale. We’ve built up resistance to those setbacks.’”</p>
</blockquote>
<blockquote>
<p>…Listening to Wiles, you feel this. Beneath his gentle poise, you can sense the ten-year-old boy, pouring hours into Fermat’s Last Theorem, undeterred by the centuries of failure that have come before, unafraid of the decades of work ahead.</p>
</blockquote>
<blockquote>
<p>If you hold one mental image of Wiles, he wants it to be this: not the triumphant scholar with the medal around his neck, but the child learning to glory in the state of being stuck.”</p>
</blockquote>
<hr />
<p>The tension that comes with challenging tasks is often avoided. But, that’s where physical and mental growth lies. By leaning into the time spent under tension, we raise our baseline pain tolerance. <a href="https://en.wikipedia.org/wiki/Greg_LeMond">Greg LeMond</a> captured this in his hallmark quote on cycling: “It doesn’t get any easier, you just get faster.”</p>
<hr />
<p>Special thanks to <a href="https://twitter.com/sravanti__">Sravanti</a> and <a href="https://twitter.com/rneppalli">Ravi</a> for feedback on early drafts of this post.</p>
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>There is no hard rule here on how much time constitutes a “fair shot.“ It’ll depend on the situation. However, 15 minutes feels like a solid minimum. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Ambient social intimacy2018-02-05T00:00:00+00:00https://jasdev.me/ambient-intimacy<p>“How’d you two become friends?” “…we met on Twitter!” This origin story has sparked a few of my dearest friendships, since making an account in 2011. What about <del>140</del> 280 characters fosters such strong connections? After letting the question distill for a year, a potential answer is starting to take shape.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">(Continuing on this tweet) Twitter has a way of fostering ambient intimacy, that I’ve only seen matched by podcasts<a href="https://t.co/7zPbTTf1hH">https://t.co/7zPbTTf1hH</a></p>— Jasdev Singh (@jasdev) <a href="https://twitter.com/jasdev/status/803793770370232320?ref_src=twsrc%5Etfw">November 30, 2016</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Twitter has the magical ability to cultivate ambient social intimacy. Each day, folks in your graph get a slice of your <a href="https://twitter.com/jasdev/status/811601905101180928">thoughts</a>, <a href="https://twitter.com/jasdev/status/533567863408189441">joys</a>, and <a href="https://twitter.com/jasdev/status/829004906170347520">embarrassing moments</a><sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. The “ambient” nature of this dynamic is crucial. A simple “follow”—say of user <em>X</em>—translates into <em>X</em>’s online consciousness being threaded into yours. Moreover, this intimacy can span <em>years</em>, even before meeting <em>X</em> in-person. One of my great friends, <a href="https://twitter.com/search?q=from%3Aryandawidjan+jasdev">Ryan</a>, followed me for multiple years prior to cold emailing after my move to the city.</p>
<p><img src="/public/images/ryan_cold_email.png" alt="Email from Ryan: “Hey Jasdev, Ryan (@ryandawidjan) here, hope you don't mind me reaching out. Welcome to NYC! Been here for a year and a half and love it.”" /></p>
<h2 id="deconstructing-ambient-social-intimacy">Deconstructing Ambient Social Intimacy</h2>
<p>Ambient intimacy appears to have a few attributes. Namely, effortlessness, a metaphorical surface area, cadence, and deliberate choices around nomenclature/context. Let’s step through each.</p>
<h3 id="effortlessness">Effortlessness</h3>
<p>To catch up with friends and loved ones, a common approach is to ask “how are things going?”-style questions. They’re <em>well-intentioned</em> gestures that are necessary on occasion. But, they have a sticking point: an asymmetric amount of “effort” involved between the asker and the recipient. Firing off a quick “how’s the week been?” message takes seconds. To answer it, one has to (lossily) translate a week’s worth of emotions, interactions, and thoughts into text, which takes a lot of emotional energy. While the asymmetry is resolved by reciprocating the question, the net effort for both parties is still high. Ambient intimacy navigates around this. The intent <em>isn’t</em> a replacement for genuine catch-ups, but rather a lightweight way to maintain the <em>pulse</em> of a relationship. Services attempt to “reduce“ this energy spend through specific cues. For instance, I’d often use <a href="https://timehop.com">Timehop</a> entries as the starting point in a conversation that would otherwise be awkward to resurface out of the blue. Another example is Apple’s Activity watchOS and iOS apps. Throughout the day, Activity will send notifications as friends complete workouts and close their <a href="https://www.apple.com/apple-watch-series-3/#activity-tracker">Move, Stand, and Exercise rings</a>.</p>
<p><img src="/public/images/activity_notification.png" alt="Activity notification that reads: “Sravanti finished a workout.”" /></p>
<p><img src="/public/images/activity_reply.png" alt="Reply dialog to the notification with two options currently in view: “💪💪💪” and “Ready or not…”" /></p>
<p>watchOS makes cheering on my friends easier by pre-populating replies. Further, there isn’t an extra, explicit step to trigger these notifications. They silently fan out to all shared contacts upon workout completion.</p>
<h3 id="surface-area">Surface Area</h3>
<p>Ambient intimacy isn’t a one-and-done checkbox; it takes a while to bake. I visualize this intimacy as a sort of surface area. By vulnerably sharing on mediums like Twitter, I’m able to increase this metaphorical surface area, which folks can use as a jumping off point in taking relationships online to offline. Tangentially, there’s something special about tweets having timestamps. They add a timescale to this surface area. Being able to look back on dated interactions adds depth to a relationship that “we’ve known one another for eight years!“ glosses over.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr"><a href="https://twitter.com/jasdev?ref_src=twsrc%5Etfw">@jasdev</a> congrats on the move. are you in NYC now man?</p>— ryan (@ryandawidjan) <a href="https://twitter.com/ryandawidjan/status/633777052957736960?ref_src=twsrc%5Etfw">August 18, 2015</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">its been a great year hangin</p>— ryan (@ryandawidjan) <a href="https://twitter.com/ryandawidjan/status/768482417200037890?ref_src=twsrc%5Etfw">August 24, 2016</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<h3 id="cadence">Cadence</h3>
<p>While ambient intimacy shouldn’t be strictly regimented, it tends to fall into a natural cadence. My Twitter following graph’s consciousness is in my pocket <em>every day</em>. That’s incredible to think about. As we’ll explore in a bit, different mediums cultivate intimacy at specific paces depending on the unit of consumption (e.g. tweets ⇒ daily, podcasts ⇒ weekly or monthly).</p>
<h3 id="network-nomenclature">Network Nomenclature and Context</h3>
<p>The final components are network nomenclature and context. First, the nouns and verbs a service uses influence the weight of a relationship. Nuanced differences between “subscriber,“ “follower,” “friend,” or “connection” shape our interpretation of the edges between nodes<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>. This is part of the reason why losing a Facebook <em>friend</em> stings a bit more than losing a Twitter <em>follower</em> (aside from bidirectionality requirement).</p>
<p>Second, <em>where</em> you consume content in the medium matters. Context can span a few dimensions:</p>
<ul>
<li>Public (e.g. (Instagram|Snap) Stories or tweets, sans protected accounts)</li>
<li>Private (Direct messages)</li>
<li>Semi-private (group threads—Snap does an amazing job at <a href="/group-dynamics#snap-threads">blurring the lines between public and semi-private</a>)</li>
<li>Adjacency
<ul>
<li>What units of the medium are “next to“ each other? For instance, sending a birthday wish over email can be a diamond in the rough for some, but <em>accidentally</em> induce an ounce of “work“ for those who strive for inbox zero. Similarly, sending <a href="https://twitter.com/search?l=&q=birthday%20from%3Ajasdev">birthday wishes over Twitter</a> is a small, public leap of faith. But, it’s sometimes hard to avoid comparing them to tweets about our current administration’s blunders being a scroll away.</li>
</ul>
</li>
</ul>
<p>Both nomenclature and context tie back into ambient intimacy. By carefully <a href="/peeling-labels">choosing the labels</a> we use in our products and the context in which they’re used, we can alter the nature of connections that are formed<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>.</p>
<h2 id="other-mediums">Other Mediums</h2>
<p>Twitter seems to foster ambient intimacy, but where else does it crop up? I’ve noticed it in three other places:</p>
<h3 id="podcasts">Podcasts</h3>
<p>Having someone’s voice in your head day over day, week over week, or month over month is powerful. On a long enough timescale, it almost feels like you know the host(s), even if you’ve never met them. Moreover, depending on the <a href="https://twitter.com/nbashaw/status/914839369831329794">context in which you consume podcasts</a>, the hosts may “anchor” themselves to parts of your routine—commuting, cleaning, cooking, etc. Personally, I noticed this with my friend <a href="https://twitter.com/soffes">Sam</a>. Prior to meeting in-person, I listened to a show he co-hosted, <a href="https://spec.fm/podcasts/immutable">Immutable</a>, for months (and even <a href="https://overcast.fm/+FGbTyte_o/06:44">submitted questions</a> to be discussed on the podcast). This made meeting him at a conference feel like catching up with an old friend.</p>
<h3 id="apple-watch-activity">Apple Watch Activity</h3>
<p>I’ve worn my Apple Watch every day since April 2015 (minus a week-long retreat). While my praise for the device—<a href="https://twitter.com/jasdev/status/815656034714996737">when used to check one’s phone <em>less frequently</em></a>—could be a post in its entirety, the Activity app deserves a special mention. Apple may have accidentally created a network here that furthers its staying power.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr"><a href="https://t.co/Dfc0udtvXE">https://t.co/Dfc0udtvXE</a> on Watch has a good chance to be another accidental social network that locks people into the Apple ecosystem.</p>— Avneesh Kohli (@avneeshk91) <a href="https://twitter.com/avneeshk91/status/871045112234692608?ref_src=twsrc%5Etfw">June 3, 2017</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>As mentioned in the “Effortlessness” section, Activity Sharing lets me <em>passively</em> broadcast my fitness milestones and workouts. On the receiving end, it feels—figuratively and literally—like a pulse that holds so much potential. I’m able to get a slice of my friends’ days, no matter the distance, delivered right to my wrist.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">The Activity app on (watch|i)OS has the same effect. Getting pushes to my wrist for each of my friends' workouts allows me to cheer them on</p>— Jasdev Singh (@jasdev) <a href="https://twitter.com/jasdev/status/841059908120412161?ref_src=twsrc%5Etfw">March 12, 2017</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<h3 id="rss">RSS</h3>
<p>RSS may be “dead“ for all <em>mainstream</em> purposes (aside from quietly powering the podcast industry), but I’m a holdout. The Web seems to oscillate between algorithmic and raw feeds (e.g., RSS) and we’re currently on the upswing of the former. However, what’s special about RSS is its postal system-like nature. Feed entries arrive at a digestible pace and the protocol <em>prevents</em> any typical notion of “engagement.“ No likes, RTs, or comments. Posts simply float out into the ether. Following my friends on both Twitter and RSS has been fascinating because of this. More specifically, when feed items <em>aren’t</em> cross-posted to normal social channels, it’s a small glimpse into their digital inner life.</p>
<hr />
<p>Ambient intimacy is an invisible foundation. Some of my favorite services, protocols, and cherished friendships are built upon it. I wholeheartedly believe there’s a spectrum here with gaps to be filled—which makes me wonder if next wave of social products will be those that foster this intimacy.</p>
<hr />
<p>Special thanks to <a href="https://twitter.com/jasonbrennan">Jason</a>, <a href="https://twitter.com/cgallello">Chris</a>, and <a href="https://twitter.com/ShivaKilaru">Shiva</a> for feedback on early drafts of this entry.</p>
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>This is predicated on the assumption that one’s “true” and “digital“ selves are <em>roughly</em> aligned. If they diverge (i.e. one acts differently online than in-person), the capacity for ambient intimacy that survives the online-offline jump suffers. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p><a href="https://twitter.com/willhoekenga">Will</a> and <a href="https://twitter.com/nbashaw">Nathan</a> touched on this in <a href="https://overcast.fm/+GsgNyxmCU/27:20">episode 17</a> of the (now defunct) Hardbound podcast (timestamped link). <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>More abstractly, it almost seems like the major networks we use are <a href="https://en.wikipedia.org/wiki/Focal_point_(game_theory)">Schelling Points</a> along the space spanned by contexts of consumption. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Handing off public extension2017-12-06T00:00:00+00:00https://jasdev.me/handing-off-public-extension<p>Firing off the first tweet for <a href="https://twitter.com/PublicExtension">@PublicExtension</a> on October 9th, 2015 was a rush. I was on the tail end of my batch at the <a href="http://recurse.com">Recurse Center</a>, after having spent the <a href="/one-month-at-recurse-center">previous quarter transitioning</a> from an backend engineer to writing Swift every day. The goal was to regularly post extensions I’ve come up with, stumbled upon, or received from the community. In the span of a year, I collected 89 extensions, had <a href="https://twitter.com/jasdev/status/767781639837609984">countless conversations</a> with the Core Team, and even <a href="https://www.instagram.com/p/BKIGsJvjg74">represented the account at XOXO Festival</a>.</p>
<p>However, I (accidentally) treated Public Extension like a “<a href="https://dianaberlin.com/posts/no-more-forever-projects">Forever Project</a>.” Without an end in sight, the weight of “do I just keep running this account ad infinitum” caused missed days to turn into weeks, and weeks into a year of hibernation. My energy drifted elsewhere. <a href="/">Writing</a>, <a href="/crafting-space">building out</a> Peloton’s iOS team, and <a href="https://www.instagram.com/p/BU5jrpRgmeW">crafting memories</a> with friends on two wheels. Until a couple of weeks ago, I had almost forgotten about the project when Joe—commonly aliased as <a href="https://twitter.com/mergesort">@mergesort</a>—expressed interest in taking the baton.</p>
<p>I couldn’t imagine a better fit. Not only is Joe a <a href="https://twitter.com/search?l=&q=from%3Ajasdev%20%40mergesort&src=typd">great friend</a> who is 1000% game to <a href="https://twitter.com/mergesort/status/776210727078092800">volley Swift puns</a>, but he also has a track record of helping, advising, and guiding folks in the iOS community. A lunch, <a href="https://github.com/mergesort/Public-Extension">repository transfer</a>, and a few iMessages later, Public Extension has a second wind. Joe and I have discussed some of his early plans for the account and I’m stoked. Please give him the same <a href="https://twitter.com/parrots/status/779014268905816064">support</a> and <a href="https://twitter.com/jakemarsh/status/652543263690199040">cheers</a> y’all have kindly given me along the first leg of PE’s relay. Below are some notes about the transition and aspects that will remain the same:</p>
<ul>
<li>To prevent old commit permalinks from breaking, we <a href="https://github.com/mergesort/Public-Extension">transferred the repository</a> to Joe’s GitHub account and <a href="https://github.com/Jasdev/Public-Extension">I forked it</a>. This means that all of the old tweets can safely be embedded.</li>
<li>The account wouldn’t have been possible without submissions. Going forward, all extensions will <em>continue</em> to link back to the original author, if applicable and with permission.</li>
<li>On the note of submissions, they will still be accepted 💙 Joe can provide more details on preferred ways to do so.</li>
</ul>
Emotional ranges: a way to think about empathy2017-11-13T00:00:00+00:00https://jasdev.me/emotional-ranges<p>Let’s break down empathy. At its core, it’s the ability to <em>truly</em> place yourself in someone else’s shoes. Not just imagining what it would be like. But actually lacing up their boots, finding an irritating rock in the left one, and then running three marathons in two days.</p>
<p>Given this lens, a way to “build” empathy is to wear as many shoes as possible. Or as I like to view it, expand my “emotional range.”</p>
<p><img src="/public/images/empathy.jpg" alt="A woman living life and experiencing three particular events: a championship win, a death of a loved one, and becoming a dog owner as points X, Y, and Z along an emotional range." /></p>
<p>Imagine plotting all of life’s events onto a range. A championship win might be on the far left, the heartache of losing a loved one towards the middle, and the nervousness and overwhelming excitement of becoming a dog owner farther down. Each point on this range is <em>incomparable</em>—<a href="/nostalgia">time may be linear, but our memories aren’t</a>—and serve as reminders of experiences previously weathered. The low points in life are hard, there’s no glossing over this. On top of that, the usual advice around the struggles we face is extremely <em>lossy</em> (especially related to mental health). Someone else’s experiences can rarely be communicated in enough detail to provide useful advice for another’s situation.</p>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="en"><p lang="en" dir="ltr">The thing is, lots of people have advice that worked for them. Even more have advice that they think might work for you. Only you are you.</p>— Rob Rix (@rob_rix) <a href="https://twitter.com/rob_rix/status/917914155256041472?ref_src=twsrc%5Etfw">October 11, 2017</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>We all carry similar invisible wounds and this model provides a natural metaphor for empathy as the <em>intersection</em> between people’s emotional ranges. Which begets the following questions: “How do we ‘reveal’ our ranges?” and “Aside from direct experience, is it possible to venture outside of our range?”</p>
<p>Before diving into intersections, I want to qualify<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> that experiencing an event, say <em>X</em>, <em>doesn’t imply</em> the ability to empathize with the <strong>actual</strong> experience of <em>X</em> in other people’s lives. It only constructs a frame of reference as a basis for connection. There will always be work required to meet one another in that frame. Put simply, covering the same “trail” doesn’t account for the journey beforehand, how much of a load we’re carrying, and how far away our respective destinations are. Context matters.</p>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="en"><p lang="en" dir="ltr">Limitation of empathy: by putting your (limited) perspective in someone’s shoes, you might arrogantly trample over their actual experience.</p>— sean 🌹 (@seanrose) <a href="https://twitter.com/seanrose/status/812750884987813888?ref_src=twsrc%5Etfw">December 24, 2016</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<h2 id="range-intersections">Range Intersections</h2>
<p>Is it possible to gauge the “similarity” of our emotional range to another’s? I believe so. While the motivation behind this question <em>isn’t</em> to produce a statistic, there is a useful analog in mathematics: <a href="https://en.wikipedia.org/wiki/Jaccard_index">Jaccard indices</a>.</p>
<p><img src="/public/images/jaccard_index.png" alt="The Jaccard Index between two sets, A and B, is the ratio of the intersection over the union." /></p>
<p>Given two sets of elements, their “similarity“ can be measured by the ratio of the intersection and the union. The more the two sets have in common, the more alike they are—which makes “Jaccard indices” sound like a ten dollar word for a ten cent idea. In terms of emotional ranges, the more experiences you have <em>in common</em> with another, the more likely you’ll have opportunity to hold space, understand their pains and joys, and <em>truly</em> meet them where they’re at. Ironically, this is usually the reason <a href="http://bigthink.com/ideafeed/extremely-positive-people-overestimate-their-ability-to-empathize">why the “most positive“ folks aren’t great at empathy</a><sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>.</p>
<p><img src="/public/images/crossing_paths.jpg" alt="A man and a woman and their associated emotional ranges, sharing a point marking the passing of a loved one." /></p>
<h2 id="revealing-ranges">“Revealing” Ranges</h2>
<p>Jaccard indices can measure how much we have in common. But, to make this possible, we must color in the invisible wounds to “reveal” our ranges. There are a few a ways to do this and vulnerability seems to be the frontrunner.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">A minute of vulnerability gains you more trust than years of competence</p>— Tiago Forte (@fortelabs) <a href="https://twitter.com/fortelabs/status/855496735749165057?ref_src=twsrc%5Etfw">April 21, 2017</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p><a href="/crafting-space">Crafting spaces</a> which foster vulnerability allow us to lower the guard on our emotional ranges. Mentioning that I <a href="/moving-too-fast">struggled to keep up</a> with work, <a href="/thoughts/2016-12-30">battled a major surgery</a>, and still second guess whether or not the words you’re reading on this site are even worth writing have all been leaps of faith that turned acquaintances into some of my closest friends.</p>
<p>Communities which <a href="https://www.recurse.com/manual">thoughtfully consider</a> psychological safety give rise to moments of vulnerability which, in turn, help reveal ranges. It’s no wonder that when you meet someone from such a group something <em>clicks</em>. For me, <a href="https://twitter.com/recursecenter">The Recurse Center</a>, <a href="https://twitter.com/aboveandbeyond">Above & Beyond</a>’s <a href="https://twitter.com/abgrouptherapy">Group Therapy</a>, and <a href="https://twitter.com/xoxo">XOXO</a> have all been examples. Multiple New Yorkers—we’re notorious for rarely deviating from our beelines—have gone out of their way to say “hi” when I wear clothing that bears RC, ABGT, or XOXO’s logos. These groups have provided me and countless others with precious, shared experiences, and it’s exciting to meet someone who <em>knows</em>.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">2/ So far, I’ve had this experience with the following groups:<br /><br />- <a href="https://twitter.com/recursecenter?ref_src=twsrc%5Etfw">@recursecenter</a><br />- <a href="https://twitter.com/abgrouptherapy?ref_src=twsrc%5Etfw">@abgrouptherapy</a><br />- <a href="https://twitter.com/xoxo?ref_src=twsrc%5Etfw">@xoxo</a></p>— Jasdev Singh (@jasdev) <a href="https://twitter.com/jasdev/status/777203237984280580?ref_src=twsrc%5Etfw">September 17, 2016</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>It’s important to remember that vulnerability, while tender and well-intentioned, comes with a bill of emotional labor for those holding space and revealing their range. I’ve seen this bill handled extremely well by two close friends, <a href="https://twitter.com/LeoWid">Leo</a> and <a href="https://twitter.com/CaseyRosengren">Casey</a>. During facilitated, group discussions, they would preface with the fact that just because someone feels comfortable sharing in a group setting, doesn’t mean they’ll feel the same one-on-one (and vice versa for those listening). It’s helpful to be mindful of these “vulnerability equilibriums” in conversations with others.</p>
<h2 id="venturing-beyond-your-range">Venturing Beyond Your Range</h2>
<p>After thinking about emotional ranges, their intersections, and how to reveal them, I’ve started to wonder about ways to venture beyond our ranges. There is no substitute to direct experience when it comes to empathy, but <a href="https://twitter.com/seanrose/status/893184609969610754">catalysts seem to exist</a>.</p>
<h3 id="fiction">Fiction</h3>
<p>Reading fiction can provide an unbiased lens into other walks of life by <em>bypassing</em> our preconceived notions. The main character in a fiction novel may face similar struggles to an analogous demographic in our world, but since they’re presented in a fabricated context, it’s less likely that our minds will jump ahead of the narrative and instead be present with the character page by page.</p>
<h3 id="localization-and-accessibility">Localization and Accessibility</h3>
<p>As <a href="https://twitter.com/Catfish_Man">David Smith</a>, neatly put it: “localization and accessibility […give] you a glimpse of the nigh-infinite variety of humans.”</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">One of the neat things about working on localization and accessibility is that it gives you a glimpse of the nigh-infinite variety of humans</p>— David Smith (@Catfish_Man) <a href="https://twitter.com/Catfish_Man/status/637294479892844544?ref_src=twsrc%5Etfw">August 28, 2015</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>The groundwork for these two aspects of product development are almost always worth <a href="https://twitter.com/jasdev/status/879705191058210817">baking in early</a>. Accessibility also has an added benefit of not only <a href="https://www.vincit.fi/en/blog/software-development-450-words-per-minute/">helping target populations</a>, but it often makes experiences better for our current and <a href="http://inessential.com/2017/10/03/accessibility">future selves</a>.</p>
<h3 id="assembling-a-diverse-set-of-colleagues-friends-and-mentors">Assembling a Diverse Set of Colleagues, Friends, and Mentors</h3>
<p>We can almost view the collective empathy of a group as the aggregate of its members’ emotional ranges. It immediately follows that more diverse teams build better products<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>. This can be applied on a personal level too. By surrounding yourself—both physically and digitally—with colleagues, friends, and mentors from a breadth of races, ethnicities, gender identities, gender expressions, sexual orientations, physical abilities, physical appearances, socioeconomic backgrounds, educational backgrounds, nationalities, ages, religions, and beliefs (to name a few), it’s possible to venture outside of your range. While Twitter receives a lot of rightly-deserved flak for no longer harboring a safe space for those in the minority of the aforementioned dimensions, it has the power to enable users to be deliberate about the metaphorical “rooms“ they place themselves in, for better or for worse. On the worse end, the “room“ is filled to the brim with similar walks of life, reinforcing confirmation bias. On the better end, it’s a wonderful educational tool in cultivating empathy.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Been thinking about this post all week. <a href="https://twitter.com/katelikestoread?ref_src=twsrc%5Etfw">@katelikestoread</a> and <a href="https://twitter.com/jasonbrennan?ref_src=twsrc%5Etfw">@jasonbrennan</a>’s reading rec.’s are stellar 💯<a href="https://t.co/9UHVEAXbOT">https://t.co/9UHVEAXbOT</a> <a href="https://t.co/YTsDSUeZIN">pic.twitter.com/YTsDSUeZIN</a></p>— Jasdev Singh (@jasdev) <a href="https://twitter.com/jasdev/status/906596468873728000?ref_src=twsrc%5Etfw">September 9, 2017</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>There’s a common adage stating “you’re the average of the <em>N</em> people you hang out with.” I think this should be refactored a bit:</p>
<blockquote>
<p>You’re the average of the <em>N</em> people you hang out with…and those <em>N</em> <em>should</em> span walks of life unlike your own.</p>
</blockquote>
<hr />
<p>Life has a funny way of interjecting the highs and lows. While social media encourages editing out the “lows,” there’s value in remembering that they contribute to our emotional range. Expansions of this range—and the basis for empathy it provides—have been the stems for some of my most-cherished friendships. Vulnerability can shed light here in uncovering intersections with one another. I think about this a lot. From time to time when riding the subway, I’ll look around and soak in the metropolitan silence. Each rider with an infinitely complex story and emotional range that has shaken out from it. We’re all veterans of our own lives, decorated with invisible badges of honor from the experiences we’ve endured. Maybe empathy is remembering that.</p>
<hr />
<p>Special thanks to <a href="https://twitter.com/emilywithcurls">Emily</a> for the illustrations and <a href="https://twitter.com/EricJorgenson">Eric</a>, <a href="http://twitter.com/ShivaKilaru">Shiva</a>, and <a href="https://twitter.com/jxxf">John</a> for feedback on early drafts of this entry.</p>
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p><a href="https://twitter.com/artypapers/status/788786532979052544">Everyone has their own internal intuitive definition of empathy</a>. The writing here isn’t meant to be prescription (or advice), but rather my own distillation. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>Found this from <a href="https://twitter.com/holman">Zach Holman</a>’s impactful <a href="https://zachholman.com/posts/the-depression-thing">post on depression</a>. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p><a href="https://www.imore.com/heres-why-apple-watch-does-not-play-nice-with-some-tattoos">Inaccuracies in the Apple Watch’s heart rate sensor for users with tattoos</a> is probably my favorite example of an issue a diverse team would have caught earlier. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Crafting space to be ourselves2017-10-20T00:00:00+00:00https://jasdev.me/crafting-space<p>My all-time favorite quote from <a href="https://en.wikipedia.org/wiki/Rainer_Maria_Rilke">Rainer Maria Rilke</a> is</p>
<blockquote>
<p>“Where I create, there I am <em>true</em>.”</p>
</blockquote>
<p>I certainly feel this way about writing. Committing thoughts to prose is easily one of the <a href="https://www.instagram.com/p/BPk28P5AIGj">most <em>vulnerable</em> and rewarding things I do</a><sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. Moreover, hammering away at my keyboard on <a href="https://www.instagram.com/p/BZ9El9pAVTj">idle Saturdays</a> gives me space to be my true self.</p>
<p>In a similar vein, I’ve been supporting two junior engineers on <a href="https://www.instagram.com/p/BYeoOX6HFwJ">my team</a>, which got me thinking about <em>creating</em> spaces that harbor <a href="https://en.wikipedia.org/wiki/Psychological_safety">psychological safety</a>. As one of the more senior engineers in the group, the onus is on me to make sure teammates can comfortably pose questions, ask for help, be their true selves, and feel welcome. Below are a few ways I try to accomplish this, capping off two small details I’ve found valuable during interviews and one-on-ones.</p>
<h2 id="not-being-afraid-to-say-i-dont-know">Not Being Afraid to Say “I Don’t Know“</h2>
<p>While interning, a reassuring statement to hear from my mentor was “I don’t know.” It reminded me that we’re all human and a competitive advantage for knowledge workers isn’t what you know <em>internally</em>, but rather the ability to sift through the noise in finding an answer. I’ve tried to do the same for my mentees<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>. A good suffix to this phrase is “let’s research this <strong>together</strong>.”</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">As a senior engineer or a technical team lead, it's perfectly okay to say "I don't know, let's research this together."</p>— Celestine Omin (@cyberomin) <a href="https://twitter.com/cyberomin/status/915222080647548928?ref_src=twsrc%5Etfw">October 3, 2017</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<h2 id="collective-code-ownership">Collective Code Ownership</h2>
<p><a href="https://twitter.com/sarahmei">Sarah Mei</a> wrote a <a href="https://twitter.com/sarahmei/status/892180578753142784">great thread</a> on our industry’s tendency to conflate code authorship and ownership (and ultimately responsibility). That is, if person <em>X</em> wrote the code that caused a production issue, the (internal) limelight is <em>often</em> placed on <em>X</em> instead of their larger team (in the absence of sound company values). Doing so is a failure to realize that “<a href="https://twitter.com/sarahmei/status/892158909107982337">most non-trivial issues come from a confluence of problems and usually are driven by misunderstandings at multiple points in the process</a>.” A way to mitigate this is being <a href="https://twitter.com/sarahmei/status/892162945538334721">explicit about collective ownership</a>. Just because <em>X</em> shows up in <a href="https://git-scm.com/docs/git-blame"><code class="language-plaintext highlighter-rouge">git blame</code></a> doesn’t mean the “blame”<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup> is on them. To reinforce a sense of group ownership, I’ve made it a point to prefix code review comments with “let’s”.</p>
<p><img src="/public/images/lets_1.png" alt="An example of a GitHub code review comment that uses “let’s”" /></p>
<p><img src="/public/images/lets_2.png" alt="An example of a GitHub code review comment that uses “let’s”" /></p>
<p><img src="/public/images/lets_3.png" alt="An example of a GitHub code review comment that uses “let’s”" /></p>
<p>“Let’s” is an acknowledgement that although I’m not committing the code under review, we’re crafting it <em>together</em> and should treat any downstream fallouts as such. Scoping out, this might hint at the unfortunate <a href="/peeling-labels">power of the phrase</a> “ownership” and connotation of <code class="language-plaintext highlighter-rouge">git blame</code>. I don’t seem to be the only one who feels this way.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Wish they had avoided “ownership” as the primary metaphor. Otherwise many thumbs up.</p>— Matthew Bischoff (@mb) <a href="https://twitter.com/mb/status/883515108927909889?ref_src=twsrc%5Etfw">July 8, 2017</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Steward, maintainer, yeah</p>— Matthew Bischoff (@mb) <a href="https://twitter.com/mb/status/883515605806198784?ref_src=twsrc%5Etfw">July 8, 2017</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<h2 id="asking-questions-publicly">Asking Questions Publicly</h2>
<p>I err on the side of asking questions in public channels, as opposed to direct messages. This normalizes asking for help, <em>regardless of seniority</em>. In addition, it allows knowledge to be open and easily sharable for others.</p>
<h2 id="being-transparent-about-goofiness">Being Transparent About Goofiness</h2>
<p>This is one of the best definitions of a senior engineer I’ve heard:</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">The best definition of "senior engineer" I've ever heard <a href="https://t.co/ewN7yz1xtn">pic.twitter.com/ewN7yz1xtn</a></p>— Jasdev Singh (@jasdev) <a href="https://twitter.com/jasdev/status/870339296649248768?ref_src=twsrc%5Etfw">June 1, 2017</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Just because we’re surrounded by skilled colleagues, doesn’t mean we have to <a href="/earned-fatigue#derpiness">appear “with it” all the time</a>. It’s alright to be <a href="https://twitter.com/iano/status/870333996366016515">open about the embarrassing moments</a>. We’re all <a href="/lessons-after-college#making">making it up as we go</a>. However, this <em>doesn’t imply</em> an obligation to be “goofy,“ which might be natural for some, but forced for others. Everyone has their own way of recharging after periods of intense focus which should be acknowledged, not prescribed.</p>
<h2 id="accounting-for-future-teammates">Accounting for Future Teammates</h2>
<p>At Peloton, we started having our client teams—iOS and Core Android—review <a href="https://swagger.io/introducing-the-open-api-initiative/">OpenAPI</a> docs, prior to server-side implementation. During these reviews, I’ve often found myself pushing to have the team write down our assumptions, avoid temporary (internal) codenames, or other hurdles that would make on-boarding difficult for <em>future</em> teammates. While the three engineers on the initial iOS team identify as male (we’ve since benefited from hiring more teammates of other genders), I’ve made nuanced efforts to ensure our culture and code are welcoming to everyone <em>by default</em>.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Gonna start approving all code reviews at Peloton with cycling emojis <a href="https://t.co/ZlAqjpdiPS">pic.twitter.com/ZlAqjpdiPS</a></p>— Jasdev Singh (@jasdev) <a href="https://twitter.com/jasdev/status/849648790307037185?ref_src=twsrc%5Etfw">April 5, 2017</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">My favorite type of comment <a href="https://t.co/v348yz9oyd">pic.twitter.com/v348yz9oyd</a></p>— Jasdev Singh (@jasdev) <a href="https://twitter.com/jasdev/status/883048264387506176?ref_src=twsrc%5Etfw">July 6, 2017</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="en"><p lang="en" dir="ltr">realtalk: we decided to call it appperson at work bc inclusivity</p>— laura (@eleichsea) <a href="https://twitter.com/eleichsea/status/883131483476688897?ref_src=twsrc%5Etfw">July 7, 2017</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="en"><p lang="en" dir="ltr">brb doing the same</p>— Jasdev Singh (@jasdev) <a href="https://twitter.com/jasdev/status/883131541450260480?ref_src=twsrc%5Etfw">July 7, 2017</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="en"><p lang="en" dir="ltr">Nixed all instances of it I could (in our source), thanks for mentioning this <3 <a href="https://t.co/hINAEDZRe0">pic.twitter.com/hINAEDZRe0</a></p>— Jasdev Singh (@jasdev) <a href="https://twitter.com/jasdev/status/883349603508211713?ref_src=twsrc%5Etfw">July 7, 2017</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>(Note: <code class="language-plaintext highlighter-rouge">Appboy</code> is an external SDK that we use in the app, which necessitated a thin veneer of types in our codebase. The company recently <a href="https://www.appboy.com/blog/rename-forester-wave-mea/">announced a re-brand</a>.)</p>
<h2 id="interview-development-environments">Interview Development Environments</h2>
<p>A seemingly unquestioned aspect of our industry’s default interview style is the use of online editors (e.g. <a href="https://coderpad.io">CoderPad</a> and <a href="https://www.hackerrank.com">HackerRank</a>). This <em>never</em> made sense to me. I spend all of my development hours in carefully configured IDEs and toolchains. Why should I expect candidates—who likely do the same—to feel comfortable in a minimal text editor they rarely use? To break down this mismatch, I rely on screen sharing. Doing so allows the potential hire to be in an environment they’re familiar with, so we can focus on letting them shine ✨</p>
<h2 id="walking-one-on-ones">Walking One-on-Ones</h2>
<p>A cue I’ve picked up from a couple of <a href="https://twitter.com/jxxf">my</a> <a href="https://twitter.com/briankassouf">mentors</a> is the walking one-on-one. Being adjacent to, instead of directly facing one another is subtle reminder that mentoring and management dynamics are <em>bidirectional</em>, not top-down. Additionally, walking outside—often in pursuit of coffee, tea, or juice (for the decaf folks)—is a <a href="https://twitter.com/PaolaNotPaolo/status/918613813443821568">refreshing retreat</a> from open offices and glass-walled conference rooms.</p>
<p>It’s important to remember that one-on-one’s are the (mentee|direct report)’s time and letting them drive the conversation. If the (mentor|manager) has anything to bring up, it should be deferred until after the former has had the opportunity to address anything on their end.</p>
<hr />
<p>A long-term goal of mine is to make the <a href="https://twitter.com/jasdev/status/803968637166034944">world better through people</a>. Crafting spaces for my teams to be their true selves has helped here, as the caliber of output when our work and personal-selves align is incredible. Here’s to turning “where <strong>I</strong> create, there <strong>I</strong> am true” into “where <strong>we</strong> create, <strong>we</strong> are true.”</p>
<hr />
<p>Special thanks to <a href="https://twitter.com/katelikestoread">Kate</a>, <a href="https://twitter.com/paulrehkugler">Paul</a>, and <a href="https://twitter.com/JulianRamirez">Julian</a> for reading early drafts of this entry and to <a href="https://twitter.com/ryan_nayr_">Ryan</a> for <a href="https://twitter.com/ryan_nayr_/status/888158199701848064">motivating me</a> to write about this topic.</p>
<h2 id="related-links-and-footnotes">Related Links and Footnotes:</h2>
<p>⇒ I was lucky enough to attend the <a href="https://www.recurse.com">Recurse Center</a> back in the fall of 2015. Many of the techniques mentioned in this entry are second-order effects of their fantastic <a href="https://www.recurse.com/manual#sub-sec-social-rules">social rules</a>. These carefully selected rules <a href="http://blog.annharter.com/2017/09/16/What-I-Learned-at-RC.html">make RC an escape</a> from the often-imperfect tech industry.</p>
<p><img src="/public/images/social_rules.jpg" alt="Poster with the following contents: “Social Rules: No Feigning Surprise, No Well-Actually’s, No Backseat Driving, No Subtle -isms”. Below the poster are signs that read “Help me!” and “BUSY, but I still love you <3”" /></p>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>Shout-out to <a href="https://twitter.com/fat">Jacob</a> for this phrasing in the context of <a href="https://medium.com/@fat/twitter-bootstrap-b95033c270af">building Bootstrap</a>. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>I use “mentor” and “mentee” in this entry <a href="https://twitter.com/jasdev/status/804002682969145344">in a bidirectional manner</a>. <a href="https://www.instagram.com/p/BY_iquag_oN">Knowledge transfer</a> isn’t exclusively top-down, it’s probably more like a graph. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>Handling retrospectives on production issues with care is crucial. Etsy does this extremely well with “<a href="https://codeascraft.com/2012/05/22/blameless-postmortems/">Blameless Postmortems</a>.” <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Peeling off labels2017-10-07T00:00:00+00:00https://jasdev.me/peeling-labels<p>With my first anniversary at <a href="http://onepeloton.com">Peloton</a> approaching, I’ve been thinking about how labels and titles shape internal narratives. Reflecting on my career and the various jobs I’ve held, I realized that the way the industry talks about jobs, and to some extent ourselves, needs to shift. For example, my online bios <em>used</em> to follow the cookie-cutter <a href="/x-at-y-previously-a-b-c">“X at Y. Previously A, B, and C.”</a> template, where <code class="language-plaintext highlighter-rouge">X</code> is some title, and <code class="language-plaintext highlighter-rouge">{Y, A, B, C}</code> are companies. This template has since been consciously refactored to <a href="http://spoonbill.io/datum/149497/">“<strong>building</strong> Y…“</a>. I have way more to offer than the ceiling under which “iOS Engineer” places me. The intent here isn’t external validation, but rather to be intellectually honest about whether the labels I hold—voluntarily or involuntarily—are the <em>right ones</em> and peeling off the ones that <em>don’t fit</em> anymore. Let’s explore this nuance a bit.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">2/ be more nuanced ways I can connect with others. It’s incredible how often the way we label things gives them power (in either direction).</p>— Jasdev Singh (@jasdev) <a href="https://twitter.com/jasdev/status/784511992618971136?ref_src=twsrc%5Etfw">October 7, 2016</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<h2 id="the-power-of-labels">The Power of Labels</h2>
<p>Why introspect labels in the first place? Because they often have implicit power in affecting <a href="https://www.theatlantic.com/science/archive/2016/11/figuring-out-how-and-why-we-talk-to-ourselves/508487/">internal dialogues</a>. Ancestrally, humans rely on the security that static traits provide, especially with regard to identity and labels for a sense of control. For instance, let’s break down the label of being introverted. “I am an introvert.“ “I’ve <em>always</em> been on the introverted side.” Typically, introversion comes across as something that one “has,” as if it were some sort of possession held <em>indefinitely</em><sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. I’ve tried to look at it from a <a href="https://twitter.com/jasdev/status/831682922704945152">slightly different lens</a>. “I (occasionally) need time to be on my own.” The latter is more <strong>malleable</strong> and hints at <a href="/thoughts/2017-1-2">introversion being a function of our social activity</a> instead of a <em>fixed</em> trait.</p>
<p>Moreover, a second-order effect of labels is narrowing/expanding expectations. Consider the earlier “iOS Engineer” example. While the “iOS“ prefix seems benign in the context of <a href="http://randsinrepose.com/archives/your-culture-is-rotting/">a</a> <a href="https://stripe.com/us/jobs/candidate-info-culture">great</a> <a href="https://jvns.ca/blog/2017/04/16/making-small-culture-changes/">culture</a>, it could <em>accidentally</em> carve an invisible sphere of expectation around an engineer <strong>in a company with mediocre values</strong>. If she wanted to contribute to the backend codebase, that team might gatekeep contributions with less-welcoming scrutiny. Make no mistake, the root issue at hand in this example is a <strong>lack of sound company values</strong>, but sticking with broader labels such as “engineer” can shatter these invisible spheres and encourage cross-team interaction and ownership.</p>
<h2 id="spotting-and-questioning-labels">Spotting (And Questioning) Labels</h2>
<p>I find that half the effort in adjusting labels, narratives, and beliefs I hold are <em>noticing them in the first place</em>. <a href="https://www.headspace.com/blog/2015/11/04/noting-technique-video/">Mental noting</a> has been vital here. At its core, noting involves gently<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup> applying a tag (e.g. “feeling”, “thinking“, etc.) to a thought when it arises in the mind. Doing so creates a sort of <em>distance</em> from it and can reveal patterns of thought. Taking this a step further, it’s been helpful to reflect on whether or not a pattern is doing harm. I’m not advocating judging one’s thoughts, but instead being truthful with oneself in questioning them.</p>
<p>A prompt I’ve used—which has lead to many pregnant pauses—is “who am I <em>without</em> this label, narrative, or belief?” As a concrete example, this question has helped me avoid conflating media narratives about 2017 with my own:</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="und" dir="ltr">2/ <a href="https://t.co/0QDlVkVHG3">https://t.co/0QDlVkVHG3</a></p>— Jasdev Singh (@jasdev) <a href="https://twitter.com/jasdev/status/892048117104857088?ref_src=twsrc%5Etfw">July 31, 2017</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="instagram-media" data-instgrm-captioned="" data-instgrm-version="7" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);"><div style="padding:8px;"> <div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50.0% 0; text-align:center; width:100%;"> <div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAMUExURczMzPf399fX1+bm5mzY9AMAAADiSURBVDjLvZXbEsMgCES5/P8/t9FuRVCRmU73JWlzosgSIIZURCjo/ad+EQJJB4Hv8BFt+IDpQoCx1wjOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div></div> <p style=" margin:8px 0 0 0; padding:0 4px;"> <a href="https://www.instagram.com/p/BOvpaAIjFCR/" style=" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;" target="_blank">A great year and pleasant reflection with close friends last night. If you're "troubled" by 2016, make sure you're not confusing media narratives with your own life. Here's to making 2017 better with my tribe 💪🏽</a></p> <p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;">A post shared by Arjun Balaji (@arjunblj) on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2017-01-02T02:01:53+00:00">Jan 1, 2017 at 6:01pm PST</time></p></div></blockquote>
<script async="" defer="" src="//platform.instagram.com/en_US/embeds.js"></script>
<h2 id="identity-minimization">Identity Minimization</h2>
<p>There’s a concept in physics that objects have a moment of inertia. An item’s “moment” is proportional to its particles’ mass and distance from an axis of rotation. The larger an object’s moment, the <em>harder</em> it is to rotate. This is why figure skaters bring their arms inward when going for faster spins; it <em>lowers</em> their moment of inertia. Why take this digression? Because I <em>think</em> there might be an analog here for the labels we hold. That is, I’ve noticed when I cling onto many labels it is <em>harder for me to change</em>. This lends itself to a sort of “identity minimization.” The fewer boxes I pack myself into, the more readily I’ll be able to unpack any situation that comes my way.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Why it pays to be nobody <a href="https://t.co/nqtodYw10o">pic.twitter.com/nqtodYw10o</a></p>— Patrick OShaughnessy (@patrick_oshag) <a href="https://twitter.com/patrick_oshag/status/896400267670937600?ref_src=twsrc%5Etfw">August 12, 2017</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<h2 id="if-im-more-than-an-ios-engineer-what-am-i">If I’m More Than an “iOS Engineer,” What Am I?</h2>
<p>I’m still sitting with this question. For now, I’m <em>personally</em> content with “Building Peloton.” But, this is super ambiguous in the workplace (and when it comes to pay grades). “Product engineer” could be fitting, given that I spend a lot of time in our users’ shoes (literally, I try to clip into the bike a couple of times a week to experience Peloton’s touch points).</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">My new hobby is to attend random group fitness classes in NYC to see how we can better improve Peloton 😏</p>— Jasdev Singh (@jasdev) <a href="https://twitter.com/jasdev/status/852130577461465089?ref_src=twsrc%5Etfw">April 12, 2017</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>But, I hope to eventually delineate our company’s strategy—at an executive level—to help users build better versions of themselves. So, I’ll likely have to reevaluate my upcoming label if/when I get there.</p>
<hr />
<p>Culturally, we tend to espouse “holding“ onto things, from labels, beliefs, narratives, and values. However, it’s been helpful for me to occasionally take a step back and remember that these shouldn’t be treated as possessions held indefinitely. Learning to shed those labels doing a disservice is almost as valuable as forming them in the first place. After all, we change year over year. Our personal frames of reference should too.</p>
<hr />
<p>Special thanks to <a href="https://twitter.com/katelikestoread">Kate</a>, <a href="https://twitter.com/jasonbrennan">Jason</a>, and <a href="https://twitter.com/ws">Will</a> for reading early drafts of this entry.</p>
<h2 id="related-links-and-footnotes">Related Links and Footnotes:</h2>
<p>⇒ Two additional nuanced examples demonstrating the power of labels/phrasing:</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">I don't love the phrase "empower women." We have innate power. The ? is: What are you doing to help counteract the way the world stifles it?</p>— Allison Esposito (@allisonveronica) <a href="https://twitter.com/allisonveronica/status/846362379516567554?ref_src=twsrc%5Etfw">March 27, 2017</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">I don’t like the noun "non-technical"<br /><br />It devalues sales, marketing, design, and other roles that are needed to build a great company <a href="https://t.co/z7hqk0PY9n">https://t.co/z7hqk0PY9n</a></p>— Ryan Hoover (@rrhoover) <a href="https://twitter.com/rrhoover/status/915771034606776320?ref_src=twsrc%5Etfw">October 5, 2017</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>The finiteness of (some) labels is often <em>forgotten</em>. This crops up a lot in mental health. Chemical forms of common conditions aside, it’s tempting to unquestioningly conclude “I <strong>have</strong> X or Y.” A more helpful narrative <em>might</em> be “I’m going through {a rough patch, a period of stress, etc.}.” This relinquishes the power of X/Y in being some condition held <em>ad infinitum</em> and can provide a much-needed ounce of agency. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>A tip that <a href="https://twitter.com/andypuddicombe">Andy Puddicombe</a> espouses in Headspace is that noting isn’t meant to be <em>exhaustive</em>. Racing to label every thought is a losing battle. It’s way easier for me to sit with the ones that stick around. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
No response needed2017-10-07T00:00:00+00:00https://jasdev.me/no-response-needed<p>When I got my first iPhone back in 2012, I was a devoutly against <a href="https://twitter.com/_Cooper/status/677163779373797376">read receipts</a>. I wanted the space to reply without the (false) worry that my friend on the other end is silently wondering “omg, Jasdev hasn’t replied in four minutes since seeing my message‽” Over the years (and after reading <a href="https://twitter.com/rosadona">Rosemary</a>’s <a href="http://hellogiggles.com/love-sex/friends/read-receipts-help-set-boundaries-relationships/">fantastic post on the topic</a>), <a href="https://twitter.com/jasdev/status/716810286980808704">I’ve changed my mind</a>. I now leave read receipts on by default<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. This change of heart got me thinking about how to better respect personal/friends’ energy levels in digital contexts.</p>
<h2 id="digital-contexts-cover-social-cues">Digital Contexts Cover Social Cues</h2>
<p>Messaging—both individually, and in groups—has allowed me to have <a href="/always-on-conversations">always-on conversations</a> with those close to me. However, unlike in-person conversations, it’s tough to pick up on social cues (e.g. being visibly tired or distracted). How do I relay to a friend that <a href="https://twitter.com/jasdev/status/893578963121041409">battling <code class="language-plaintext highlighter-rouge">UINavigationBar</code>’s API</a> all day might not leave me in a state to have a conversation on deep life topics™? How do I hint to a coworker that I’m exhausted and want to defer a discussion until tomorrow? Read receipts establish these boundaries. I’d rather signal that I’m ready to reply when I have the time and emotional energy to do so (critical and time-sensitive messages aside). And I want my friends to do the same! Nothing <a href="https://twitter.com/jasdev/status/883521405803016192">makes me happier</a> than seeing those around me prioritizing self care. Messaging when <em>ready</em> is an instance of this.</p>
<p>A way I’ve seen this nuance addressed is by deliberately <em>acknowledging</em> it, which brings me to the title of this post: “No Response Needed.”</p>
<h3 id="acknowledging-one-anothers-time-and-space">Acknowledging One Another’s Time and Space</h3>
<p>Two of my dearest friends, <a href="https://twitter.com/katelikestoread">Kate</a> and <a href="https://twitter.com/jasonbrennan/">Jason</a>, and I have an ongoing thread packed with <a href="https://twitter.com/exploreasyraf/status/884778801263136769">dank tweets</a>, <a href="https://www.instagram.com/p/BVMIFVvBzhe/">work from our favorite illustrators</a>, and <a href="https://twitter.com/jasdev/status/906596468873728000">posts that make us pause</a>. When sending a longer read to the group, we nix the ounce of stress associated with yet<sup>another<sup>item</sup></sup> on our reading lists by being open about personal time and space.</p>
<p><img src="/public/images/time_and_space_to_reply.png" alt="iMessage Conversation: Me: “(and, of course, I send that link with the intention of getting to it when y’all have the time and space to do so 🙌🏽)”, Me: “(no rush at all)”, Kate: “Jasdev you are always considerate of time ❤️ good friend”" /></p>
<h3 id="nrn">NRN</h3>
<p>I’ll often want to send a message to someone I’m thinking about without the imposed pressure to segue into an open-ended “Hey, how are things?”-conversation. <a href="https://twitter.com/ryandawidjan">Ryan</a> recently used an acronym that captures this nicely, “NRN.”</p>
<p><img src="/public/images/nrn.png" alt="iMessage Conversation: A: “nrn”, B: “?”, A: “No Response needed”, B: “TIL”" /></p>
<p>Ending thoughtful texts—“Hi, I miss your face <a href="https://www.instagram.com/toshinoguchi">Toshi</a>. Please move back to NYC.”—with “NRN” signals a digital <a href="https://twitter.com/jasdev/status/849637857169952768">pulse</a> <em>instead</em> of an obligation to reply immediately.</p>
<p>Thinking generally, <a href="https://en.wikipedia.org/wiki/AOL_Instant_Messenger">AIM</a> (RIP) Away Messages might’ve been onto something when it comes to online social cues. <a href="https://twitter.com/nbashaw/status/816779171472912384">Just because you’re online, doesn’t mean you have to respond.</a></p>
<h2 id="posting-is-different-than-conversing">Posting is Different than Conversing</h2>
<p>I no longer buy into the idea that one shouldn’t post online if they have haven’t replied to emails, texts, etc. Posting is different than conversing.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">if i haven't responded to your text and you've seen me post 50000 times since, i'm sorry but posting and talking are different energies</p>— Brain Mentality (@ByYourLogic) <a href="https://twitter.com/ByYourLogic/status/842611643909652481">March 17, 2017</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Tangentially, <a href="https://sivers.org/soso">Derek Sivers pointed out</a> how “digital talking” can ironically leave us fatigued to do the same in-person:</p>
<blockquote>
<p>I’ll sit alone in my little office for 8-10 hours, engaging with 100 people’s lives and questions for a few minutes each, and then go home.</p>
</blockquote>
<blockquote>
<p>Then when friends want to hang out with me, I say I need some alone time first.</p>
</blockquote>
<blockquote>
<p>They say I’ve been alone all day. So I explain that I’ve been massively social all day long, and feel spent, even though I’ve been sitting alone.</p>
</blockquote>
<blockquote>
<p>I like it, so I’m not complaining — just explaining. It’s unusual to be physically alone, but extremely social. A solitary socialite.</p>
</blockquote>
<hr />
<p>Funny how the above thoughts stemmed from a single setting on my phone. I’m beyond grateful to be <a href="/thoughts/2017-4-16">physically</a> and <a href="https://twitter.com/jasdev/status/891336723984244739">digitally</a> surrounded by friends and family. However, the lack of social cues in the latter context means that we should reply when we’re <em>ready</em>. Doing so helps both sides of the conversation.</p>
<hr />
<p>Special thanks to <a href="https://twitter.com/katelikestoread">Kate</a>, <a href="https://twitter.com/jasonbrennan">Jason</a>, and <a href="https://twitter.com/ws">Will</a> for reading early drafts of this entry.</p>
<h2 id="related-links-and-footnotes">Related Links and Footnotes:</h2>
<p>⇒ <a href="https://twitter.com/emilywithcurls/status/902162621033574401">The best: someone addressing all of a multipart text</a></p>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>This doesn’t have to be binary! iOS let’s you <a href="https://twitter.com/jasdev/status/787856251199905792">selectively enable receipts</a> for specific contacts. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Thinking about thinking2017-10-01T00:00:00+00:00https://jasdev.me/thinking-about-thinking<p>A common sticking point that meditators face when catching themselves lost in thought is <em>thinking about thinking</em>. For example:</p>
<ul>
<li>I’ll <a href="https://www.headspace.com/blog/2015/11/04/noting-technique-video/">mentally note</a> that I’m ruminating on a past experience.</li>
<li>Instead of returning to the present, I’ll accidentally <strong>think about</strong> the note I just made.
<ul>
<li>Getting lost in thought isn’t necessarily bad. However, it’s important to know when doing so is causing a disservice.</li>
</ul>
</li>
<li>Then, I’ll think about the aforementioned thinking.</li>
<li>The process usually recurses a few levels. That is, I’ll think about the thinking…about the thinking.</li>
</ul>
<p>“Thinking about thinking” throws off longtime meditators because their acute awareness can <em>sometimes</em> be a crutch in causing them to lean into thought. I encountered this during a <a href="https://twitter.com/jasdev/status/824997535483826176">retreat in May</a><sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. However, during office hours with <a href="http://jonathanfoust.com/about/">Jonathan</a>, we attempted to bucket the states our mind can be in, as a way to <em>flatten</em> any potential meta-thinking.</p>
<p><img src="/public/images/states_of_mind.png" alt="Two states of mind: thinking and being present" /></p>
<p>In the above division, one is either present or thinking<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>. If we assume that these two states are <em>mutually exclusive</em> (I’ll touch on the potential intersection in a moment), then “thinking about (thinking about…) thinking” gets reduced to, well, <em>just thinking</em>!</p>
<p>Can you think and be present concurrently? My gut says the intersection of these states is what we typically refer to as <a href="https://en.wikipedia.org/wiki/Flow_(psychology)">flow</a>. When you’re so in tune with a task at hand, that everything else takes the backseat.</p>
<p>It seems contradictory that I’m regarding these states as being mutually exclusive when I want to prevent “thinking about thinking,” but allowing them to intersect when it comes to flow. <a href="https://twitter.com/NickSzabo4">Nick Szabo</a> calls this holding of contradictory possibilities “<a href="http://unenumerated.blogspot.com/2012/07/more-short-takes.html">quantum thinking</a>” (fourth bullet point).</p>
<blockquote>
<p>[…] You should disagree with yourself. Totalitarian thought asks us to consider, much less accept, only one hypothesis at a time. By contrast quantum thought, […] demands that we [simultaneously] consider often mutually contradictory possibilities.</p>
</blockquote>
<p>The thinking/present distinction is the first instance of quantum thought I’ve come across, since reading their post. I’m still processing this thought. But, it’s been in my stream of consciousness for a couple of weeks now, so I wanted to commit it to prose.</p>
<hr />
<p>Special thanks to <a href="http://twitter.com/mergesort">Joe</a> for providing feedback on an early draft of this post.</p>
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>I took notes during the silent, week-long retreat. If you’d like posts on the major themes, let me know! <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>This isn’t meant to put thinking in a negative light! Without it, we wouldn’t be able to cogitate at all. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Chasing earned fatigue2017-09-09T00:00:00+00:00https://jasdev.me/earned-fatigue<p>I usually cringe at the trite question: “what does success mean to you?” But, <a href="https://en.wikipedia.org/wiki/Jodie_Foster">Jodie Foster</a>’s answer has stuck with me over the years.</p>
<blockquote>
<p>“In the end, winning is <em>sleeping better</em>.”</p>
</blockquote>
<p>When I first heard this, I immediately paused the podcast I was listening to and took note. Foster <a href="http://www.eugenewei.com/blog/2017/5/11/jpeg-your-ideas">powerfully compresses</a> so much into a handful of words.</p>
<p>Personally, sleeping <strong>better</strong> requires the sensation of <em>earning</em> my fatigue<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>, which usually arises from a day packed with total dedication to each task. One where I come home, drop my backpack, and crash-land onto my bed<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup> with my entire being filled with contentment. This past summer<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>, I’ve been chasing “earned fatigue.” In doing so, the following thoughts have shaken out: contrast (and how it sparks fatigue), defining “enough,” and <a href="https://twitter.com/jasdev/status/888200077046624256">toeing the line</a> with <a href="/thoughts/2017-7-25">overtraining</a>.</p>
<h2 id="contrast">Contrast</h2>
<p>If I stuck to the <em>minimum</em> <strong>physical activity</strong> levels that being an engineer—or generally, a knowledge worker—required, my days would culminate in a sort of “cupcake week.“ <a href="https://twitter.com/jasdev/status/883062810137243649">Sitting (or standing) at a desk</a> for multiple hours doesn’t provide the physical <em>contrast</em> needed to truly relax (acknowledging that this is a function of my age and capacities). If my working day is mostly spent sitting (near leisure), it’ll be hard <em>savor</em> it after-hours.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">People spend 80% of their lives in leisure or near-leisure then wonder why it’s so hard to relax.</p>— Michael (@mmay3r) <a href="https://twitter.com/mmay3r/status/841891545628479490">March 15, 2017</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>This “contrast“ seems to have two faces: <a href="https://twitter.com/ZackShapiro/status/892379653058564097">physical and mental</a>.</p>
<h3 id="physical">Physical</h3>
<p>How have I attempted to remedy this inactivity? By pushing my baseline as far as I can from relaxation. More tangibly, this involves <a href="http://nymag.com/scienceofus/2016/06/how-exercise-shapes-you-far-beyond-the-gym.html">practicing being uncomfortable</a> and <a href="/thoughts/2016-12-27">shocking the system</a> through humbling experiences<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup>.</p>
<p>Regimented discomfort has taken the form of training. Currently, I aim for three <a href="https://furthermore.equinox.com/articles/2012/05/are-you-training-too-much">key sessions</a> (around 60–70 minutes) a week—focusing on strength—and two supplementary workouts (endurance and yoga). By kicking off my days with intense, physical work (and the <a href="https://twitter.com/jewelia/status/888414514047180802">concentration it requires</a><sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">5</a></sup>), any bug or feature I tackle at the office almost pales in comparison.</p>
<p>“Shocking the system” refers to experiences that embody the <em>literal</em> definition of “humbling.” Challenges which knock me down, so I can make sure <em>I don’t forget how to stand up again</em>. An instance of this was my <a href="https://www.instagram.com/p/BSGpGqegAwO">first outdoor cycling race</a>. I trained indoors all winter…rookie mistake. Come race day, I got <a href="https://en.wikipedia.org/wiki/Glossary_of_cycling#D">dropped from the peloton</a> and spent the rest of the race rounding out the back of the field. I was in the peak physical condition, but failed to realize that conditioning can’t substitute time in the saddle, drafting skills, and timely gear shifting. The race helped me spot these unknown unknowns. Since then, I have been building my <a href="https://www.instagram.com/p/BUVA4ZRgLDK">endurance base</a> and sharing some <a href="https://www.instagram.com/p/BVK_u8UBll5">great memories (and laughs) along the ride</a>.</p>
<h3 id="mental">Mental</h3>
<p>The mental side of contrast mostly mirrors its physical counterpart. That is, <a href="https://twitter.com/sama/status/630869228137127936">submarine-like periods of attention</a> help me better enjoy the lighthearted moments. However, there are two nuances I want to explore: why some of my smartest friends are (often) the “goofiest” and how meditation compliments our <a href="http://nearthespeedoflight.com/article/2017_01_25_don___t_kill_time_2">default mode of <em>consuming</em> media all day</a>.</p>
<h3 id="raw-intellect--derpiness"><a name="derpiness">Raw Intellect <> “Derpiness”</a></h3>
<p>When I think about my friends<sup id="fnref:6" role="doc-noteref"><a href="#fn:6" class="footnote" rel="footnote">6</a></sup> who pack the most mental firepower<sup id="fnref:7" role="doc-noteref"><a href="#fn:7" class="footnote" rel="footnote">7</a></sup>, a common thread through all of them is that they <em>love</em> derping around. To provide some context, one of my closest friends, <a href="https://twitter.com/binroot">Nishant</a>, is easily one <a href="http://shukla.io">smartest people I know</a>. We’re so comfortable around one another to the point that we laugh at <a href="https://twitter.com/jasdev/status/843930159451426818">the most mindless things</a>. On many occasions, I’ve cried of laughter reading his messages. I’d <em>guess</em> that this dynamic occurs because Nishant is constantly surrounded by extraordinary colleagues, <em>implicitly</em> applying pressure to appear “with it.“ Goofing around after hours provides a refreshing contrast.</p>
<h3 id="meditation-as-mental-defragmentation">Meditation as Mental Defragmentation</h3>
<p>While engineering necessitates (relative) physical inactivity, the mental work involved lands on the opposite end of the spectrum. It’s very possible—if not common—to spend <em>all of my waking hours</em> consuming inputs (text, audio, video, etc.). Through this lens, mental contrast involves stepping away from these stimuli. Meditation has accomplished this for me. Over the past three years (and ~268 hours using <a href="https://www.headspace.com">Headspace</a>), the practice has become a sort of defragmentation for my mind. Time to just <strong>be with my own thoughts</strong>, process them (if needed), and <a href="https://twitter.com/mmay3r/status/841431966536814592">work through any difficult emotions</a>. Moreover, while meditation may come across as a purely mental effort, its effects have spilled over into my workouts<sup id="fnref:8" role="doc-noteref"><a href="#fn:8" class="footnote" rel="footnote">8</a></sup>.</p>
<h2 id="defining-enough">Defining “Enough”</h2>
<p>Now that we’ve covered contrast and its role in onsetting earned fatigue, let’s dive into something I struggled with. Defining how much fatigue is <em>enough</em>. I generally approach advice listicles with a ten-foot pole (<a href="https://twitter.com/sean_a_rose/status/758103984636764160">advice is extremely lossy</a>). But, entry #36 in <a href="https://thoughtcatalog.com/ryan-holiday/2017/06/how-to-live-a-full-life-and-leave-nothing-on-the-table-by-30/">Ryan Holiday’s recent post</a> captured the core of this difficulty:</p>
<blockquote>
<p>“Know What’s ‘Enough’ — If you don’t know what ‘enough’ is, then the default answer is always more.”</p>
</blockquote>
<p>Having more of a Type-A-leaning personality, I pushed my physical limits. I (foolishly) crammed weeks with multiple two-a-days, racking up six to seven workouts a week. As you can guess, I eventually hit a wall and became <a href="https://twitter.com/jasdev/status/853790810172772352">somewhat of a zombie</a> when I wasn’t caffeinated or in the gym. To break down this wall, I imposed structures in which “enough” was more concrete (or even defined <em>for me</em>). Physically, this involved <a href="https://twitter.com/jasdev/status/869938469153492999">working with a coach</a> who would plan our workouts, push me to safe failure, and check in to make sure I was resting enough (both in sleep volume and recovery days). There is a subtle—yet impactful—difference in having someone define the bounds of a workout. Let’s take the following superset as an example:</p>
<ul>
<li>Dumbbell pullovers at 35 pounds for 15 repetitions</li>
<li>Diamond push-ups for 15 repetitions</li>
<li>(Repeat the above for four rounds)</li>
</ul>
<p>I have done this circuit both with and without a coach. Despite the similar levels of effort in each context, completing it with someone else calling the shots <em>feels more rewarding</em>. It’s as if the rungs on your “ladder” are created for you, allowing you to focus on climbing; as opposed to trying to <em>simultaneously climb a ladder you’re building</em>. More generally, this delegation of “enough” might explain the (more recent) proliferation of group fitness classes. They’re instances of having someone construct ladders for us.</p>
<h2 id="rebounding-from-overtraining">Rebounding from Overtraining</h2>
<p>With a notion of <em>enough</em> fatigue, it’s normal to <em>accidentally</em> surpass it, or more-commonly: overtrain. There’s no beating around the bush here. Recovering from overtraining is hard. The following helped me immensely (and have cognitive analogs):</p>
<ul>
<li><strong>Seasonality</strong>: an (unfortunate) adage thrown around the fitness community is that one should “always be improving.“ I agree with the general notion here, but it glosses over a subtle nuance. Improvement can (and should) happen along <strong>multiple</strong> axes. More specifically, plateaus in one performance metric might be a sign to focus on another (potentially related) one. This happened with my cycling power output. I tended to <strong>compress</strong> how “effective“ a workout was solely based on my output, <em>trying to set a PR session after session</em>. When the wattage started to flatline, I redirected my attention to flexibility—through more consistent yoga—and it pushed the ceiling on my output within a month. Tying this back to seasonality, a useful mindset for handling overtraining is dividing efforts along seasons (e.g. strength, flexibility, endurance, etc.), instead of locking into the “always improving” framework along a single dimension.</li>
<li><strong>“Remember the fun“</strong>: I wholeheartedly believe that “absence makes the heart grow fonder” applies to training. This has held up for habits I’ve kept on longer timescales (the tail-end of habit formation isn’t discussed enough). By minimizing time in the gym, I <em>missed it</em> and reminded me of the <a href="/thoughts/2017-4-25">often-forgotten fun</a> involved in routine activities.</li>
<li><strong>Don’t chase the end</strong>: One of my favorite cues<sup id="fnref:9" role="doc-noteref"><a href="#fn:9" class="footnote" rel="footnote">9</a></sup> my coach has given me is “not to chase the end of a set.” Typically, this will happen when he throws out a target rep-range, say eight to ten, and notices that I mindlessly start rushing towards that mile marker. Taking a mental step back to focus on each repetition and time under tension helps make workouts more sustainable, as opposed to a series of dashes.</li>
</ul>
<hr />
<p>Earned fatigue—and the investigation of what induces it, defining “enough,” and inadvertently ignoring that definition—has made this summer a focused period of social, mental, and physical growth. I hope these ideas can help you grow in both expected and unexpected ways next season. “Sleeping better” is easier on the <strong>other side of tired</strong>.</p>
<hr />
<p>Special thanks to <a href="https://twitter.com/katelikestoread">Kate</a> and <a href="https://twitter.com/justinmduke">Justin</a> for reading early drafts of this entry.</p>
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p><a href="https://twitter.com/ryandawidjan">Ryan</a> commonly refers to this as the ”other side of tired.” Additionally, this is predicated on <a href="https://github.com/Jasdev/thoughts/blame/181dbeacaf083497ad10f080247a2e5b9b4af401/daily-list.md#L8">allocating enough time for sleep</a>. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p><a href="https://twitter.com/cgallello/status/841401466124349441">GIF equivalent of this</a> <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>In the absence of quarters and semesters that university provided, I’ve tried to impose structure on my years through seasons. It helps avoid “<a href="https://dianaberlin.com/posts/no-more-forever-projects">Forever Projects</a>” and allows efforts to be directed for finite periods of time. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:4" role="doc-endnote">
<p>I hope the <a href="http://www.dictionary.com/browse/humbling">literal definition</a> of “humbling” makes a comeback. Not the faux-bragging version that’s used in “I’m so humbled to receive this reward…” <a href="#fnref:4" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:5" role="doc-endnote">
<p>I’ve come around to better understanding why top-performers often <a href="https://twitter.com/jasdev/status/779000205815480321">don’t train with music</a>. When actually ensuring “time under tension” in strength work or keeping form during intervals, audio can be distracting (and even affect <a href="https://twitter.com/jxxf/status/817405605963038720">perception of time</a>). <a href="#fnref:5" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:6" role="doc-endnote">
<p>Of course, noting that this might not be an actual pattern, but instead a result of the self-selected pattern amongst my close friends. <a href="#fnref:6" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:7" role="doc-endnote">
<p>Nikhil <a href="https://twitter.com/nikillinit/status/820697243753840641">phrased this question</a> well in his thread on interview questions. <a href="#fnref:7" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:8" role="doc-endnote">
<p><a href="http://www.nature.com/tp/journal/v6/n2/full/tp2015225a.html">Further research on the combination</a> <a href="#fnref:8" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:9" role="doc-endnote">
<p>Amongst many. If you’re interested in a more-detailed post on his cues, let me know! <a href="#fnref:9" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
The interplay between high-cadence and well-formed thoughts2017-07-29T00:00:00+00:00https://jasdev.me/high-cadence-versus-well-formed<p>Back in January, I started a public, high-cadence log of <a href="/thoughts">what’s on my mind</a><sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. Along the spectrum between my <a href="http://twitter.com/jasdev">Twitter account</a> and this blog, the journal sits in-between. <em>Unpolished</em> thoughts, developed over time. Six months and 46 entries later, I wanted to take a moment to reflect on my experience so far.</p>
<h2 id="why-keep-one">Why Keep One?</h2>
<p>Two reasons. First, to create a distillery for my thoughts. By regularly thinking in public, I can better spot themes, <a href="/memory-reconsolidation">reconsolidate memories</a>, and <a href="/thoughts/2017-1-7">orbit around future posts</a>. Second, it felt like the most natural solution to the following question: “What single document should you read before meeting me to know what’s on my mind?” Or as <a href="http://twitter.com/ryandawidjan">Ryan</a> succinctly put it, a “one-way coffee with my brain.“</p>
<h2 id="what-went-well">What Went Well?</h2>
<p>One of the subtle—yet subconscious—lenses I <em>accidentally</em> use when thinking about my favorite creators (writers, artists, engineers, etc.) is through their volumes of work<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>. “She is the creator of A and B and wrote a wonderful post on C.” It’s <a href="/value-of-conferences#humanizing-heroes">too easy to forget</a> that <strong>we’re not our work</strong>.</p>
<p>This journal helped reveal my inner gears. More specifically, it allowed others to see beyond the blog posts, <a href="https://twitter.com/parrots/status/779014268905816064">side projects</a>, and <a href="https://twitter.com/jasdev/status/791701214664790016">(bad) puns</a>.</p>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="en"><p lang="en" dir="ltr">^ personally wish blogging was more about peeking behind the curtain into one's mind rather than shipping a polished contained unit</p>— ryan (@ryandawidjan) <a href="https://twitter.com/ryandawidjan/status/601601021471825920">May 22, 2015</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">This message absolutely made my day <3 <a href="https://t.co/BxWQMUI6oF">pic.twitter.com/BxWQMUI6oF</a></p>— Jasdev Singh (@jasdev) <a href="https://twitter.com/jasdev/status/854301253714759681">April 18, 2017</a></blockquote>
<h2 id="what-didnt-go-so-well">What Didn’t Go So Well?</h2>
<p>Counterintuitively, the journal almost acted like an <em>escape valve</em> for my ambition to write longer-form posts. Instead of taking the extra time to crystallize a cohesive thesis, I’d sometimes fire off an entry and call it a day<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>. Doing so caused me to miss out on arguably the most important part of writing: the final 20% involved in publishing. Editing, <em>n</em>-ary drafts, and trimming the cruft are the metaphorical <a href="https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4887540/">sticking points</a> where most of my cognitive growth happens.</p>
<p>A second, more concrete aspect that could have been improved was tooling. Below are the steps<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup>—read friction—around updating the thought journal:</p>
<ul>
<li>Open <a href="https://www.git-tower.com">Tower</a> and pull the latest changes</li>
<li>Open <code class="language-plaintext highlighter-rouge">thoughts.md</code> in <a href="https://twitter.com/jasdev/status/882070849389555714">iA Writer</a></li>
<li>“Spend some time writing new entry”</li>
<li>Make a commit in Tower and push the changes up to GitHub</li>
</ul>
<p>The fact that this process had four steps (and involved a <code class="language-plaintext highlighter-rouge">git</code> commit) impeded my cadence quite a bit. Ryan’s approach of deferring to <a href="http://quip.com/jgBUALiGBjwp">Quip</a> to focus on writing is better in this regard. However, I leaned towards a lack of tooling (and external hosting à la Quip) in an attempt to avoid the “configuration trap“ of <a href="http://jxf.me/entries/in-the-beginning/">writing software instead of prose</a> and to carve out <a href="http://blog.semilshah.com/2016/04/30/medium-rare/">my own little corner</a> of the Internet.</p>
<h2 id="whats-next">What’s Next?</h2>
<p>Moving forward, I’ve decided to make a few changes:</p>
<h4 id="thoughts-archive">Thoughts Archive</h4>
<p>The original version of the journal was simply one gigantic Markdown file. I’ve since migrated it to a Jekyll <a href="/thoughts">collection</a> with permalinks to each post. This will allow me to point others to specific entries and concretely trace themes across posts.</p>
<h4 id="feed">Feed</h4>
<p>While I thought the <em>lack</em> of notifications would be an asset<sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">5</a></sup>, it ended up adding friction for <a href="https://twitter.com/wahoo/status/854365871443107840">those who would otherwise love to read the journal</a>. As an initial option, I’ve opened up an <a href="/thoughts.xml">RSS feed</a> for my thoughts—which I might pipe to a separate Twitter account in the future.</p>
<h4 id="routine">Routine</h4>
<p>Inspired by <a href="https://twitter.com/justinmduke">Justin Duke</a>’s <a href="https://twitter.com/justinmduke/status/882434291258376193">keyboard shortcut to start new blog posts</a>, I made a port for my setup. Now, starting a new journal entry is as simple as ⌘⌥⌃T.</p>
<p><img src="/public/images/thoughts_workflow.png" alt="" /></p>
<p>Noticing that I <a href="https://github.com/Jasdev/jasdev.github.io/graphs/punch-card">wrote most of my entries in the evening</a>, public journaling started naturally capping off my days. <a href="https://twitter.com/jasdev/status/780141465917947904">Evening routines</a> warrant a post in itself. However, one habit that has stuck recently is <a href="https://twitter.com/jasdev/status/877709265070284801">being somewhat religious</a> about my <a href="http://irace.me/gtd#review-everything-every-week">OmniFocus Daily Reviews</a>. Looking ahead, I’m going use this existing momentum to <a href="http://jamesclear.com/habit-stacking">stack</a> my journal entries—public and <a href="/small-moments">private</a>—after the review.</p>
<p><img src="/public/images/daily_review.png" alt="" /></p>
<hr />
<p>This is all an experiment. So, I’ll check back in six month’s time. Until then, here’s what’s been <a href="/thoughts/2017-7-25">on my mind this past week</a>.</p>
<hr />
<p>Special thanks to <a href="https://twitter.com/_eeclaire">Claire</a>, <a href="https://twitter.com/jxxf">John</a>, <a href="https://twitter.com/justinmduke">Justin</a>, and <a href="https://twitter.com/krausefx">Felix</a> for reading early drafts of this entry.</p>
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>This experiment was inspired by Ryan’s attempt at this: <a href="http://quip.com/jgBUALiGBjwp">High-Cadence Thoughts</a>. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>On the note of “volumes of work,” an example that still blows me away is <a href="https://twitter.com/aboveandbeyond">Above & Beyond</a>’s radio show: <a href="https://itunes.apple.com/us/podcast/above-beyond-group-therapy/id286889904?mt=2">Group Therapy</a> (formerly <a href="https://itunes.apple.com/us/podcast/above-beyond-trance-around-the-world/id993499023?mt=2">Trance Around the World</a>). The group has run the podcast every week for 688 weeks (13+ years) so far! <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>I have noticed <a href="https://twitter.com/ryandawidjan/status/739490348553150464">this dynamic</a> with tweets too. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:4" role="doc-endnote">
<p>I’d repeat the same process on iOS with <a href="https://git2go.com">Git2Go</a> replacing Tower. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:5" role="doc-endnote">
<p>i.e. I <em>thought</em> the journal would be more effective when visits stemmed from curiosity, instead of pushes. <a href="#fnref:5" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Content givers2017-07-02T00:00:00+00:00https://jasdev.me/content-givers<p>Our routine is like clockwork. After each <a href="https://www.instagram.com/p/BUFscWDApi3/">hangout</a>, <a href="https://twitter.com/ryandawidjan">Ryan</a> and I will exchange <a href="https://twitter.com/jasdev/status/862051865529286656">photos</a> and videos to store in our <a href="/digital-attics">digital attics</a>. This habit sparked from the realization that although it’s <a href="http://ben-evans.com/benedictevans/2015/8/19/how-many-pictures">hard to approximate how many photos our generation takes each year</a>, a quick scroll of our Camera Rolls shows just how <em>rarely</em> we take photos of ourselves and others.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">The photos you’ll care about later will be the ones with people in them.</p>— Paul Graham (@paulg) <a href="https://twitter.com/paulg/status/421088648310706176">January 8, 2014</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>When was the last time someone took a great, candid photo of you? How special did it make you feel? When you do this for others, you probably won’t remember the slight faux-awkwardness involved and <strong>they’ll almost always be thankful for it</strong><sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. Ryan calls these folks “Content Givers.“ They’re a rare breed these days. Taking the extra moment to document <a href="/small-moments">small moments</a> and share timestamped assets can help increase the surface area of any friendship.</p>
<hr />
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>This, of course, goes without saying that the subject is comfortable having photos taken of them. Tangentially, I’ve seen this navigated extremely well by the wonderful folks over at <a href="http://xoxofest.com">XOXO</a>. They color coded conference lanyards to indicate levels at which attendees were alright with their photographs being used (i.e. not at all, taken with permission, public, etc.) <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Are we moving too fast?2017-07-01T00:00:00+00:00https://jasdev.me/moving-too-fast<p>I remember the sprint planning meeting so clearly. I had <a href="http://spoonbill.io/datum/81425/">joined Peloton</a> the week prior and the team was heads-down trying to launch a major refactor of the <a href="https://www.pelotoncycle.com/ios">iOS app</a>. After calculating how many story points we were going to tackle in the next sprint, our project manager, <a href="https://www.facebook.com/PelotonCycle/photos/a.436836049742545.1073741825.276454582447360/1024831050943039/?type=1&theater">Asma Khan</a>, asked the group: “are we moving too fast?”</p>
<p>I’ll never forget this question. In my three years in the industry so far (sans internships), this was the first time I was asked to reflect on our pace potentially being <em>too fast</em>. <a href="https://en.wikipedia.org/wiki/Agile_software_development">Agile Development</a> centers on a major interval training metaphor, namely <strong>sprints</strong>. But, Agile almost entirely misses the other side of the coin: regimented rest. We can’t expect teams to knock out a monotonically increasing amount of story points, sprint after sprint. Sometimes, you have to move slower now to move faster later.</p>
<p><a href="https://twitter.com/naval">Naval Ravikant</a> recently posted a great perspective on the Athlete ⇔ Knowledge Worker metaphor:</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Forty hour workweeks are a relic of the Industrial Age. Knowledge workers function like athletes - train and sprint, then rest and reassess.</p>— Naval Ravikant (@naval) <a href="https://twitter.com/naval/status/873624849230385152">June 10, 2017</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>I hope the next wave of development paradigms borrow from what we’ve learned in training world-class athletes<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. To list a few: <a href="/one-month-at-recurse-center#energy-management">sound bodies make for sound minds</a>, <a href="https://twitter.com/dhh/status/796846867971645440">sleep deprivation is a false badge of honor</a><sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>, <a href="https://www.nytimes.com/2016/11/20/jobs/quit-social-media-your-career-may-depend-on-it.html">information diets are akin to nutritional diets</a>, <a href="http://www.npr.org/sections/ed/2016/06/01/479335421/practice-makes-possible-what-we-learn-by-studying-amazing-kids">managers should also aim to be coaches (teachers)</a>, and <a href="http://nymag.com/scienceofus/2016/06/how-exercise-shapes-you-far-beyond-the-gym.html">being comfortable with being uncomfortable is a skill we can cultivate</a>.</p>
<hr />
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>And performers, in general <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p><a href="https://basecamp.com">Basecamp</a>’s cofounder, <a href="https://twitter.com/jasonfried">Jason Fried</a>, even posited the idea of <a href="https://overcast.fm/+GJ7jL2GIs/28:25">paying employees to sleep more</a> (timestamped link). <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
A couple of thoughts on groups2017-07-01T00:00:00+00:00https://jasdev.me/group-dynamics<p>In the moments after a small win, a handful of people come to mind for me in sharing it. My <a href="https://www.instagram.com/p/ST0WB3Na4y">Mom</a>, the “<a href="/always-on-conversations">Awesomest Turntable DJs</a>,” <a href="https://twitter.com/jasdev/status/869938469153492999">Ryan, Kunal</a>, <a href="/monthly-checkins">Shiva</a>, and my roommates to name a few. Thinking about this list of people, I wonder if it’s possible to <em>gracefully</em> scale this intimacy from a handful to a dozen. We tend to view scale and intimacy as an undisputed dichotomy.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">One thing I've learned to value in recent years is “intimacy over scale”</p>— jason kinda (@jasonbrennan) <a href="https://twitter.com/jasonbrennan/status/806177051661271040">December 6, 2016</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>While I believe this dichotomy is true on extreme ends of the spectrum (i.e. it’s difficult to have meaningful dialogues with hundreds of people), it’s fascinating to me how services like Snapchat affect intimacy through the features they do and do not offer. Moreover, this reminded me of a tangential topic: how groups partition over time.</p>
<h3 id="snapchat-threads"><a name="snap-threads">Snapchat Threads</a></h3>
<p>I imagine that Snapchat’s delay in releasing groups was somewhat intentional. The app cultivates closeness in both regular snaps (one user to many, in a <em>directed</em> fashion) or Stories (one user to many, <em>undirected</em>).</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Ability to share to many *individuals* as opposed to a group is one of Snapchats most underrated innovations <a href="https://t.co/TyoY7AAHTH">pic.twitter.com/TyoY7AAHTH</a></p>— Jonathan Greenglass (@jgreenglass) <a href="https://twitter.com/jgreenglass/status/799416249109254144">November 18, 2016</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>The beauty of this dynamic is that it allows individual recipients to maintain separate threads with the sender, as opposed to placing them all in a single, larger bucket<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. This ‘bucketing’ is essentially what groups do. While groups reduce friction when firing off messages to specific friends, it’s nice to know someone <a href="https://twitter.com/jasdev/status/757397911592394752">chose to individually share a moment with you</a><sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>. The latter almost feels like the digital equivalent of receiving a handwritten letter, especially when compared to the minimal effort required to toss a message into a bucket—whether named (group) or unnamed (Stories).</p>
<h3 id="group-dissolution-and-subgroups">Group Dissolution and Subgroups</h3>
<p>Another aspect of group dynamics that I’ve been mulling over is dissolution<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>. This stemmed from my interviews with <a href="https://twitter.com/chorus">Chorus</a> back in the fall of 2016. While Chorus focuses on small groups to help users achieve their health and fitness goals, I had asked the team if they had any ideas on how to <em>gently</em> disband groups (especially those centered on temporal goals, like training for a marathon). The interviewers and I felt that forced ‘closings’ weren’t the right answer and leaned towards dismissal by inactivity (i.e. users gradually leaving when groups idle, much like Slack channels today).</p>
<p>On a personal level, I’ve noticed group threads will often spawn subgroups (along characteristic or situational divisions). For example, <a href="/always-on-conversations">TNN</a> has a separate thread for the handful of us who are single to recap our—often hilarious—experiences dating, without sidetracking the original group. Moreover, a few friends and I use a <a href="https://twitter.com/jasdev/status/862696294770499584">Discord</a><sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup> thread (that forked from a larger server) to discuss dance music. On a larger scale, <a href="https://www.pelotoncycle.com/">Peloton</a>’s <a href="https://www.facebook.com/groups/pelotonriders">Facebook Group</a> also exhibited a similar dynamic by splitting into <a href="https://www.facebook.com/search/groups/?q=peloton">subgroups</a>. This make me wonder if there is sort-of “<a href="https://en.wikipedia.org/wiki/Dunbar%27s_number">Dunbar’s Number</a>” for groups, marking the point at which the size (and frequency of contact) of the group lends itself to subgroups forming.</p>
<hr />
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>Much like a form of “social <a href="https://en.wikipedia.org/wiki/Blind_carbon_copy">bcc</a>” when you think about it. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>Even though Twitter isn’t typically associated with groups, I’ve experimented with one-to-many, public, <em>directed</em> sharing in the form of an ongoing Corgi <a href="https://twitter.com/jasdev/status/799426899323670528">picture thread</a>. This approach sits between sharing with private groups or your entire following graph (i.e. one-to-many, public, <em>undirected</em>). Moreover, it has the added benefit of allowing the @-mentioned users to change over time. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>I briefly noted about group <em>formation</em> at the end of my <a href="https://medium.com/featuredfollow/featured-follow-jasdev-singh-2c5042abe3f6">Featured Follow post</a>. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:4" role="doc-endnote">
<p>Discord also facilitates channel trimming in an automated fashion with <a href="https://support.discordapp.com/hc/en-us/articles/213530048-Advanced-Community-Server-Setup">AFK Channels and Timeouts</a>. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Digital attics and service shutdowns2017-01-23T00:00:00+00:00https://jasdev.me/digital-attics<p>Electronic Dance Music is a <a href="http://www.seas.virginia.edu/pubs/unbound/spr14/singh.php">big part of my life</a><sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. Unlike other genres, tracks and remixes are released at a breakneck pace. Because of this, artists tend to gravitate towards Twitter-esque services for music like <a href="https://soundcloud.com/jasdev-singh">SoundCloud</a>, where (re)posting multiple songs a day is common. I’ve spent years curating tracks on the service to the point where my <em>local</em> music library is only a handful of albums—around four hours worth—that have <a href="https://twitter.com/jasdev/status/328698506397245440">attached themselves to memories</a>. Recent news that <a href="http://mixmag.net/read/soundcloud-may-run-out-of-cash-after-suffering-a-51-22m-loss-in-2015-news">SoundCloud posted a $53 million loss in 2015</a> caused me to reflect on how a potential shutdown would impact the dance community at large and <a href="https://twitter.com/jasdev/status/822180098954690560">me personally</a>. Due to the centralized tendencies of the web today, entire swaths of digital history<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup> can vanish from record.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Vine is the latest reminder that we are creating a Digital Dark Age w huge gaps in the historical record (via obsolescence or deletion) 😩</p>— jenny (@fvrmvn) <a href="https://twitter.com/fvrmvn/status/791857105040908288">October 28, 2016</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>I’ve been thinking about ways we can make digital “passings,” <a href="https://medium.com/@vine/important-news-about-vine-909c5f4ae7a7#.2er2e8gxk">like that of Vine</a>, less painful on both a macro and personal scale. Let’s start with the macro, by reflecting on the shutdown of our favorite six-second looping video service.</p>
<p>Under the safety net of Twitter, Vine opted to enter a <a href="https://support.twitter.com/articles/20175169">read-only mode, release a version of the app that only allows local/Twitter sharing, and provide downloadable archives</a>. Moreover, GIPHY pitched in and built a <a href="http://giphy.com/giphylovesvine">migration tool</a>.</p>
<p>This seems like the “right” way to do it. A <em>read-only</em> period gives users time to brace for the shutdown and a <em>write-only</em> tool grants creators the ability to produce atomic units of the service, despite its (future) absence (and hosting). When it comes down to platform migrations—no matter how well-intentioned—it’s hard to look past the fact that they’re essentially passings of the torch. Of course, with any hosted service, this process can (and will) continue indefinitely as they rise and fall. Third-party mirrors[^3, 4] (preferably those backed by enduring institutions) will probably be our best bet. The <a href="https://twitter.com/jack/status/814924317590962176">challenge of preserving public digital records</a> will be one I’m excited to see our generation(s) take on with the help of historians.</p>
<p>On a personal level, I’ve tried to approach backing up important files—photos, videos, notes, and journals—as creating a digital attic for my future kids to (potentially) climb into one day. Structuring the folders to be intuitive, yet allow for a meandering exploration of my digital life. My files range from the typical—<a href="https://twitter.com/jasdev/status/705791847319347200">screenshots</a>, camera uploads<sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">3</a></sup>, 9th grade Algebra 2 assignments, and <a href="/small-moments">journal entries</a>—to the intimate—<a href="https://twitter.com/maiab/status/751500059875782656">voicemails from my parents</a><sup id="fnref:6" role="doc-noteref"><a href="#fn:6" class="footnote" rel="footnote">4</a></sup>, notes on fun date spots/ideas, and failed attempts at a video diary. More concretely, I store these digital memories in formats that I <em>think</em> will stand the test of time—or at least be easily upconverted to their replacements. Plaintext (<a href="https://twitter.com/jxxf/status/789275320696406016">Markdown</a>), PNGs, MP4s, and MP3s have been some of my picks. As an example, instead of keeping <a href="https://twitter.com/jasdev/status/817769190920777728">first-person memories</a> generated by <a href="https://www.spectacles.com">Snap’s Spectacles</a> solely in their app, I’ll save each one to my Camera Roll—which, in turn, get backed up to my Dropbox<sup id="fnref:7" role="doc-noteref"><a href="#fn:7" class="footnote" rel="footnote">5</a></sup>.</p>
<p>On the note of backups, I’ve also thought about how to hand off digital assets (most importantly my journal) when I pass, or in the case of an emergency. The last thing I’d want for my loved ones is trouble in tracking down files and critical account credentials. This may sound grim, but it’s important to think about as more of our <em>possessions become purely digital</em>.</p>
<hr />
<p>People centuries from now will have the <em>potential</em> to tap into our global <a href="https://twitter.com/naval/status/604009689337044992">consciousness</a>, view the world as we saw it, and experience lossless versions of our greatest creative outputs. To preserve this potential, we should mindfully sunset our products. On a personal level, think about that digital attic you would like to create for your relatives or (future) kid(s). Save digital content that could help them get to know you in ways you wish you could know your ancestors. Or, at least, let them uncover the <a href="https://vine.co/v/iiWIUmAUAn6">dank memes you revined</a>.</p>
<hr />
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>So much so, that I wrote my <a href="https://github.com/jasdev/soundcloud-sentiment">thesis on analyzing data sets from the genre</a>. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p><a href="http://www.vox.com/2016/10/28/13439450/vine-shutdown-loss-to-black-culture">And culture</a>. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:5" role="doc-endnote">
<p>I’ve backed up every photo I’ve taken since 2011. <a href="#fnref:5" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:6" role="doc-endnote">
<p>Voice is underrated. It’s easily one of the most intimate mediums for me and Dropbox makes it <a href="/public/images/dropbox-voicemail.jpg">dead simple to export voicemails</a>. <a href="#fnref:6" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:7" role="doc-endnote">
<p>I also keep local copies and a mirror on Google Photos in an attempt to follow the <a href="http://blog.trendmicro.com/trendlabs-security-intelligence/world-backup-day-the-3-2-1-rule/">3-2-1 Rule</a>. <a href="#fnref:7" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Pouring my whole self into work2017-01-10T00:00:00+00:00https://jasdev.me/whole-self<p>Lately, I’ve had little desire to tackle side projects. This realization caught me off guard, especially as someone who participated in <a href="https://www.instagram.com/p/PnOAuvNa-Q">countless hackathons</a>, <a href="http://www.nbc29.com/story/25232742/uva-students-participate-in-36-hour-hack-a-thon">helped start one at their school</a>, and whose <a href="https://medium.com/@binroot/the-best-jokes-have-no-punchline-5f4713a963d6#.dbny4gut0">side project landed him and his friends a trip to visit Snapchat’s HQ</a>. Our industry tends to espouse the idea that you must always have a side project—often citing them as a factor in hiring decisions. Thinking more deeply about this change, I reflected on my <a href="http://spoonbill.io/datum/81425/">decision to join Peloton</a>, our industry’s assumption of the privilege to work on side projects, and how I’d like to incorporate my experience with side projects into my future management style.</p>
<p>Since joining <a href="http://pelotoncycle.com">Peloton</a>, I’ve almost had to throttle my drive for the product. I’m finally in a domain in which I could see myself working for the next 50+ years. I’m pouring my <strong>whole self</strong> into work, leaving little energy for side projects. The inverse of this pattern became evident when I looked back at my previous jobs, during which I juggled multiple side projects. I cared deeply about my time at <a href="http://imgur.com">Imgur</a> and <a href="https://www.tumblr.com">Tumblr</a>—and couldn’t be more thankful for how the people there molded me—but the vectors of the companies’ missions and my <a href="https://github.com/Jasdev/thoughts/blame/33113769819be196617b611387f51d8fc8b96af2/core-values.md#L6">personal one</a> weren’t completely aligned<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. Side projects made up for this. Generalizing, this could<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup> be a useful test for determining if you’ve diverged from your <a href="/lessons-after-college#trajectories">personal trajectory</a>.</p>
<p>When it comes to hiring, filtering based on side projects can accidentally rule out great candidates. <a href="https://twitter.com/_tessr">Tess Rinearson</a> captured this really well:</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Actually, I "get" it with students, since school doesn't necessarily teach you how to build. But otherwise it seems like a false filter.</p>— Tess Rinearson (@_tessr) <a href="https://twitter.com/_tessr/status/596142191002583040">May 7, 2015</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Having the time and energy to work on side projects is a form of privilege. “The people most likely to have spare time to do outside projects are people who don’t have families to take care of (kids, parents, whatever), who have a good, steady income (i.e. not learning while, say, balancing two part-time jobs), or who live close to work (minimizing a commute)<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>.” On the other hand, “some folks are permitted [and encouraged] to work on [side projects], as a part of their full-time work, so [we] shouldn’t [<em>completely</em>] ignore [them]<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup>.” It’s a balance. If a candidate has side projects that they’re open to talking about, fantastic! If not, that’s perfectly fine (and normal). Having hobbies and responsibilities outside of programming doesn’t mean they’re not a committed developer.</p>
<h3 id="how-this-could-apply-to-management">How This Could Apply to Management</h3>
<p>There might be an important management lesson buried in the concept of putting your “whole self” into work. When I’m a manager, I’d like to structure sprints with the most important <strong>and</strong> <em>most interesting</em> work. The latter shouldn’t be relegated to <a href="http://www.businessinsider.com/google-20-percent-time-policy-2015-4">20% time</a>. It should be a first-class citizen, as it affects the team’s momentum (and excitement) in tangible ways. Moreover, <a href="https://blog.curtisherbert.com/slopes-diaries-16-recovering-from-a-stall/">this is especially important for one-person shops</a>. A gauge to see if your engineers are disengaged from their current workload might be the presence of side projects (if they’re open to sharing and noting that this doesn’t paint a complete picture). If business needs make it impossible to harmonize the most important and the most interesting, I’d add items to be excited about in future sprints. Doing so generally builds anticipation, making the current sprint more tolerable.</p>
<hr />
<p>I’ll likely start a few side projects again at some point that are rooted in deeper motivations, as opposed to addressing a disconnect from my full-time work. The fact that I’m pouring all of my engineering love into work lets me wake up energized and go to sleep excited. For the first time in my life, I’m catching myself <a href="https://twitter.com/jasdev/status/804339211117666304">smiling on the way to work</a> ❤️</p>
<hr />
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>And that’s okay! Our personal missions and those of the companies we work for won’t always be. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>This test—like any—can lead to false positives. Some constantly build side projects <a href="https://twitter.com/deray/status/817219653386768384">for the thrill</a>. <a href="https://twitter.com/jazzychad">Chad Etzel</a> is <a href="https://twitter.com/jazzychad/timelines/773442085278085121">prime example of this</a>. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>From “<a href="http://belkadan.com/blog/2016/05/So-You-Want-To-Be-A-Compiler-Wizard/">So You Want to Be a (Compiler) Wizard</a>” <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:4" role="doc-endnote">
<p><a href="https://twitter.com/jdan">Jordan Scales</a>’ <a href="https://twitter.com/jdan/status/596149143959113728">addition to Tess’ thread</a> <a href="#fnref:4" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Why I’m glad Twitter doesn’t allow editing2017-01-01T00:00:00+00:00https://jasdev.me/memory-reconsolidation<p>Hear me out. While typos and grammar issues are inevitable in the absence of editing, there is a subtle way to use this as a lens into your thinking. I view tweets as <em>atomic units</em>. <strong>Immutable</strong> snapshots about your life that you can thread together with others over time.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Thought: Twitter is (potentially) an RSS-like feed for your life</p>— Jasdev Singh (@jasdev) <a href="https://twitter.com/jasdev/status/803791802918637568">November 30, 2016</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Twitter threads with other users are <a href="https://twitter.com/artypapers/status/632263932594421760">gold mines of insight</a>. But, there is untapped value in longitudinal threads—over time—with yourself<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. The lack of editing gives you the opportunity to posit something, sit with it, and revisit your stance at a later point<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>. If Twitter allowed editing with a revision history, the history would likely be tucked away in a submenu<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>, squandering its usefulness. The deltas revealed through threads gives us a peek into our thought processes, instead of focusing on editing a <strong>single</strong>, polished snapshot in-place.</p>
<p><a href="https://twitter.com/nikillinit">Nikhil Krishnan</a>’s tweets embody this approach. Below are some of his <em>truncated</em> threads<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup> (along with a personal example). Remember, what’s important here is that these threads evolve over time and aren’t simply <em>one-time bursts</em>.</p>
<h3 id="future-of-consumer-products-and-the-focused-product-ecosystem">Future of Consumer Products and the Focused Product Ecosystem</h3>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Wrote some stuff about the future of consumer products and the focused product ecosystem: <a href="http://t.co/5tW0qU48TC">http://t.co/5tW0qU48TC</a> <a href="http://t.co/2j2lPs0gJg">pic.twitter.com/2j2lPs0gJg</a></p>— Nikhil Krishnan (@nikillinit) <a href="https://twitter.com/nikillinit/status/638534045043945472">September 1, 2015</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="en"><p lang="en" dir="ltr">Like this post from <a href="https://twitter.com/robgo">@robgo</a> about non-recurring revenue businesses. Think a lot of consumer products fall into this: <a href="https://t.co/CLmE4OPgc3">https://t.co/CLmE4OPgc3</a></p>— Nikhil Krishnan (@nikillinit) <a href="https://twitter.com/nikillinit/status/739475938853945345">June 5, 2016</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="en"><p lang="en" dir="ltr">This point talks about the ecosystem aspect around a product essentially <a href="https://t.co/VvP2TjkVrw">pic.twitter.com/VvP2TjkVrw</a></p>— Nikhil Krishnan (@nikillinit) <a href="https://twitter.com/nikillinit/status/739476633795604481">June 5, 2016</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="en"><p lang="en" dir="ltr">Toothpaste choice paralysis in action <a href="https://t.co/lkiZrmvAMS">pic.twitter.com/lkiZrmvAMS</a></p>— Nikhil Krishnan (@nikillinit) <a href="https://twitter.com/nikillinit/status/746782872678326272">June 25, 2016</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="en"><p lang="en" dir="ltr">Allbirds is an interesting case study in the consumer product ecosystem. So far, it fits into my post<a href="https://t.co/yWsHB4ypYp">https://t.co/yWsHB4ypYp</a></p>— Nikhil Krishnan (@nikillinit) <a href="https://twitter.com/nikillinit/status/773544389213884416">September 7, 2016</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>(<a href="https://twitter.com/nikillinit/status/638534045043945472">Full Thread</a>)</p>
<h3 id="the-communication-of-tomorrow">The Communication of Tomorrow</h3>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">I wrote a post about what the communication of tomorrow might look like. Emojis and one-bit communication galore: <a href="http://t.co/hsDUN86iLV">http://t.co/hsDUN86iLV</a></p>— Nikhil Krishnan (@nikillinit) <a href="https://twitter.com/nikillinit/status/623323820162682880">July 21, 2015</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="en"><p lang="en" dir="ltr">Interesting use of one-bit communication. ReplyYes would remove choice paralysis + lower friction to buying: <a href="https://t.co/dV9kX4bBix">https://t.co/dV9kX4bBix</a></p>— Nikhil Krishnan (@nikillinit) <a href="https://twitter.com/nikillinit/status/721037680646168576">April 15, 2016</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="en"><p lang="en" dir="ltr">Targeting collectors is smart. Sticky+have disposable income+want to see good deals as fast as possible. Tough part is nailing down taste</p>— Nikhil Krishnan (@nikillinit) <a href="https://twitter.com/nikillinit/status/721038098457604096">April 15, 2016</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="en"><p lang="en" dir="ltr">This is a very relevant read to some of the science and social factors behind emojis: <a href="https://t.co/g9ISBlj5Hj">https://t.co/g9ISBlj5Hj</a></p>— Nikhil Krishnan (@nikillinit) <a href="https://twitter.com/nikillinit/status/722525207710318592">April 19, 2016</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="en"><p lang="en" dir="ltr">Was just reading this, good read about gifs and changing in communication and why that's important <a href="https://t.co/E6ySX9FvQm">https://t.co/E6ySX9FvQm</a></p>— Nikhil Krishnan (@nikillinit) <a href="https://twitter.com/nikillinit/status/729696920118145024">May 9, 2016</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>(<a href="https://twitter.com/nikillinit/status/623323820162682880">Full Thread</a>)</p>
<h2 id="thoughts-on-friendship">Thoughts on Friendship</h2>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">1/ A litmus test for how close you are w/ someone is the presence or absence of the urge to justify breaks in conversation …</p>— Jasdev Singh (@jasdev) <a href="https://twitter.com/jasdev/status/793162455149084672">October 31, 2016</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="en"><p lang="en" dir="ltr">2/ (i.e. “brb gotta get back to work”). The latter _usually_ implies that your conversation is ‘always-on,’ which, for me, is the sign of …</p>— Jasdev Singh (@jasdev) <a href="https://twitter.com/jasdev/status/793162461411217408">October 31, 2016</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="en"><p lang="en" dir="ltr">3/ a close friend. Unpacked this thought a bit more in an old post<a href="https://t.co/YGjNQoVeEc">https://t.co/YGjNQoVeEc</a></p>— Jasdev Singh (@jasdev) <a href="https://twitter.com/jasdev/status/793162466855354368">October 31, 2016</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="en"><p lang="en" dir="ltr">4/ Another signal is the lack of the "need for an excuse" to reach out. The ones you can message for the sake of it<a href="https://t.co/5Jh4hBjvJ9">https://t.co/5Jh4hBjvJ9</a> <a href="https://t.co/t7SfdwHfKz">pic.twitter.com/t7SfdwHfKz</a></p>— Jasdev Singh (@jasdev) <a href="https://twitter.com/jasdev/status/813561672770129920">December 27, 2016</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>(<a href="https://twitter.com/jasdev/status/793162455149084672">Full Thread</a>)</p>
<p>I’ve experimented with extending this approach to my (incomplete) collection of <a href="https://github.com/Jasdev/thoughts">personal documents</a>. By keeping an enumerated core values list, <a href="/daily-list">daily list</a>, and beliefs under version control, I can reflect on when and how they changed. The ability to edit in-place would erase this history.</p>
<p><img src="/public/images/thoughts_snapshot.png" alt="" /></p>
<p>Scoping out, there <em>might</em> be a deeper concept at play here. The revisiting of older tweets—through threads—almost mirrors what we do in our heads through a process known as <a href="https://en.wikipedia.org/wiki/Memory_consolidation#Reconsolidation">memory reconsolidation</a>. “Research has shown that every time we recall a memory, it undergoes reconsolidation, meaning we are able to add new information or a different interpretation to our remembrance, even turning fearful memories into fearless ones<sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">5</a></sup>.” It’s almost as if these threads are a <em>digital manifestation</em> of reconsolidation. Through it, we can let go of previously strongly-held opinions, find holes in old arguments, and incorporate new learnings.</p>
<p>So, while we may collectively <a href="https://twitter.com/AnthonyQuintano/status/814538412686311424">groan at the inability to edit tweets</a>, I’m thankful. The lack of editing forces us to think out loud with clear revisions over time. It’s as if we have <strong>backstage access</strong> to “some great [collective] super-genius for the ages.”</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Really grateful for Twitter. Seeing the tweets flow by feels like access to some great super-genius for the ages. (Quite serious.)</p>— michael_nielsen (@michael_nielsen) <a href="https://twitter.com/michael_nielsen/status/719606290696269824">April 11, 2016</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<hr />
<h2 id="related-thoughts-and-footnotes">Related Thoughts and Footnotes:</h2>
<p>⇒ <a href="https://www.headspace.com/blog/2015/11/04/noting-technique-video/">Mental noting</a> in meditation serves as a good first step in the ability to reconsolidate more effectively.</p>
<p>⇒ I wonder if there would be value in authors including drafts of their work, when purchasing a book. Much like open source repositories with a full revision history, drafts could give us insight into their process. Here are <a href="https://twitter.com/jasdev/status/809416165940490240">some thoughts on this</a> in the replies to a (deleted) tweet of mine.</p>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>Aside from the typical one-time threads that aren’t revisited later. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>I usually bookmark threads via <a href="http://instapaper.com">Instapaper</a> and <a href="https://pinboard.in/">Pinboard</a>, falling back to Twitter’s <a href="https://twitter.com/search-advanced">Advanced Search</a> if I have trouble finding a particular tweet. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>Much like Facebook’s approach to this <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:4" role="doc-endnote">
<p>Embedded from the root tweet downwards. The threads are truncated for brevity. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:5" role="doc-endnote">
<p><a href="http://nautil.us/issue/39/sport/the-strange-brain-of-the-worlds-greatest-solo-climber">The Strange Brain of the World’s Greatest Solo Climber</a> <a href="#fnref:5" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Utility apps2016-12-29T00:00:00+00:00https://jasdev.me/utility-apps<p>A couple of days ago, I made a series of tweets that highlighted <em>smaller utility</em><sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> applications that I’ve found recently.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">I love utility apps that solve a small need in an elegant way. Wanted to share three I’ve found recently (would love other recommendations)!</p>— Jasdev Singh (@jasdev) <a href="https://twitter.com/jasdev/status/814666130371604485">December 30, 2016</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="en"><p lang="en" dir="ltr">1/ Moon by <a href="https://twitter.com/charliedeets">@charliedeets</a>: a clean moon phase calendar with a beautiful Today Widget<a href="https://t.co/rPEop0dzcp">https://t.co/rPEop0dzcp</a></p>— Jasdev Singh (@jasdev) <a href="https://twitter.com/jasdev/status/814666137384484864">December 30, 2016</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="en"><p lang="en" dir="ltr">2/ Tailor by <a href="https://twitter.com/Foundry_63">@Foundry_63</a>: Allows you to stitch together iOS screenshots into one<a href="https://t.co/yQJbZ19v1A">https://t.co/yQJbZ19v1A</a> <a href="https://t.co/NKEXeKJNzI">pic.twitter.com/NKEXeKJNzI</a></p>— Jasdev Singh (@jasdev) <a href="https://twitter.com/jasdev/status/814666157013762049">December 30, 2016</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="en"><p lang="en" dir="ltr">3/ Spectator by <a href="https://twitter.com/timonus">@timonus</a>: View Spectacles videos outside of Snapchat with the same rotation gesture<a href="https://t.co/FnMjcMVzNR">https://t.co/FnMjcMVzNR</a></p>— Jasdev Singh (@jasdev) <a href="https://twitter.com/jasdev/status/814666167038177280">December 30, 2016</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>The thread hit home with others and served as a shout out to the developers behind these tools that impact my life. I wanted to aggregate the list here (along with replies to the thread), broken down by platform<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>. If you have any other suggestions, <a href="https://twitter.com/jasdev">please let me know</a>!</p>
<h2 id="ios">iOS:</h2>
<ul>
<li><a href="https://itunes.apple.com/us/app/moon-current-moon-phase/id660036257?mt=8">Moon</a> by <a href="https://twitter.com/charliedeets">Charlie Deets</a>: A Moon phase calendar with a beautiful Today Widget.</li>
<li><a href="https://itunes.apple.com/us/app/tailor-automatic-screenshot/id926653095?mt=8">Tailor</a> by <a href="https://twitter.com/Foundry_63">Foundry 63</a>: Allows you to stitch together iOS screenshots.</li>
<li><a href="https://itunes.apple.com/us/app/spectator-for-spectacles-videos/id1177861209?mt=8">Spectator</a> by <a href="https://twitter.com/timonus">Tim Johnsen</a>: View <a href="https://www.spectacles.com">Spectacles</a> videos outside of Snapchat with the same rotation gesture.</li>
<li><a href="https://itunes.apple.com/us/app/zero-fasting-tracker/id1168348542?mt=8">Zero</a> by <a href="https://twitter.com/kevinrose">Kevin Rose</a>, <a href="https://twitter.com/calebd">Caleb Davenport</a>, and <a href="https://twitter.com/dburka">Daniel Burka</a>: A simple app to track your fasting.</li>
<li><a href="https://itunes.apple.com/us/app/oneshot-for-screenshots/id953724147?mt=8">OneShot</a> by <a href="https://twitter.com/iano">Ian Ownbey</a> and <a href="https://twitter.com/goldman">Jason Goldman</a>: Easily highlight screenshots of text and share them to Twitter.</li>
<li><a href="https://itunes.apple.com/app/apple-store/id669858907?mt=8">Pinpoint</a> by <a href="https://twitter.com/lickability">Lickability</a>: My favorite tool to mark up screenshots.</li>
<li><a href="https://itunes.apple.com/app/captio-email-yourself-1-tap/id370899391?mt=8">Captio</a> by <a href="https://twitter.com/benlenarts">Ben Lenarts</a> and <a href="https://twitter.com/eelco">Eelco Lempsink</a>: The fastest way to send a note to your inbox.</li>
<li><a href="https://itunes.apple.com/us/app/deliveries-a-package-tracker/id290986013?mt=8">Deliveries</a> by <a href="https://twitter.com/robotspacer">Mike Piontek</a>: A delivery tracking app with an incredible attention to detail. Seriously, <a href="https://twitter.com/junecloud">Junecloud</a> sets the standard when it comes to new Apple API adoption.</li>
</ul>
<h2 id="macos">macOS:</h2>
<ul>
<li><a href="https://balancemy.money">Balance</a> by <a href="https://twitter.com/einsteinx2">Ben Baron</a>, <a href="https://twitter.com/chrisbaroni">Chris Baroni</a>, and <a href="https://twitter.com/ricburton">Richard Burton</a>: Ever since Mint <a href="https://mint.lc.intuit.com/questions/1244369-discontinuing-the-mac-quickview-app">deprecated their Mac app</a>, I’ve been looking for a replacement. Balance has helped fill this void.</li>
</ul>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr"><a href="https://twitter.com/jasdev">@jasdev</a> <a href="https://twitter.com/campedersen">@campedersen</a> that was a big motivation for us. If you have any feature requests or ideas, please do let us know.</p>— Balance (@balancemymoney) <a href="https://twitter.com/balancemymoney/status/775760491683516416">September 13, 2016</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<ul>
<li><a href="https://www.macbartender.com">Bartender</a> by <a href="http://www.surteesstudios.com">Surtees Studios</a>: Lets you organize your menu bar apps, by hiding them, rearranging them, or moving them to a separate bar.</li>
<li><a href="https://www.boastr.net">BetterTouchTool</a> by <a href="https://twitter.com/boastr_net">Andreas Hegenberg</a>: Takes macOS gestures and shortcuts to the next level. This is a must-install on all of my macOS devices.</li>
<li><a href="https://flexibits.com/chatology">Chatology</a> by <a href="https://twitter.com/macguitar">Michael Simmons</a> and <a href="https://twitter.com/ksuther">Kent Sutherland</a>: What ⌘ + F for Messages.app should be.</li>
<li><a href="https://itunes.apple.com/us/app/helium/id1054607607?mt=12">Helium</a> by <a href="https://twitter.com/jadengeller">Jaden Geller</a>: <a href="https://support.apple.com/en-us/HT206997">Apple’s PiP</a> on steroids.</li>
<li><a href="https://itunes.apple.com/us/app/boxy.-inbox-by-gmail-email/id1053031090?l=it&ls=1&mt=12">Boxy</a> by <a href="https://twitter.com/frankdilo">Francesco Di Lorenzo</a> and <a href="https://twitter.com/linuz90">Fabrizio Rinaldi</a>: An native wrapper for Google’s <a href="https://www.google.com/inbox/">Inbox</a>.</li>
<li><a href="https://fbmacmessenger.rsms.me">Messenger for Mac</a> by <a href="https://twitter.com/rsms">Rasmus Andersson</a>: A native wrapper for Facebook’s <a href="https://www.messenger.com">Messenger</a>.</li>
<li><a href="https://itunes.apple.com/app/redacted/id984968384?mt=12">Redacted</a> by <a href="https://twitter.com/soffes">Sam Soffes</a>: A simple photo redaction tool.</li>
<li><a href="https://itunes.apple.com/us/app/spectrum/id518156125?mt=12">Spectrum</a> by <a href="http://www.eigenlogik.com">Eigenlogik</a>: Easily manage your color palettes.</li>
<li><a href="https://itunes.apple.com/us/app/wallcat/id1000397973?mt=12">Wallcat</a> by <a href="https://twitter.com/dustin">Dustin Senos</a>: Wake up every morning to a new, hand-picked desktop image.</li>
</ul>
<h2 id="watchos">watchOS:</h2>
<p>While the following are <em>larger</em> apps, their watchOS apps make the device that much better.</p>
<ul>
<li><a href="https://itunes.apple.com/us/app/fantastical-2-for-iphone-calendar/id718043190?mt=8">Fantastical</a> by <a href="https://twitter.com/macguitar">Michael Simmons</a> and <a href="https://twitter.com/ksuther">Kent Sutherland</a>: Its complication gets a lot of details right that the default Calendar misses.</li>
<li><a href="https://itunes.apple.com/us/app/dark-sky-weather/id517329357?mt=8">Dark Sky</a>: The detailed, local weather notifications and complication have saved me so many times.</li>
<li><a href="https://itunes.apple.com/us/app/pcalc-the-best-calculator/id284666222?mt=8">PCalc</a> by <a href="https://twitter.com/jamesthomson">James Thomson</a>: The attention to detail put into this app is amazing. In the same league as <a href="https://twitter.com/junecloud">Junecloud</a>.</li>
</ul>
<h2 id="web-and-email">Web and Email</h2>
<ul>
<li><a href="http://spoonbill.io">Spoonbill</a> by <a href="https://twitter.com/justinmduke">Justin Duke</a>: Follow bio, location, and name changes in your Twitter following graph.</li>
<li><a href="https://github.com/jurre/switchr">Switchr</a> by <a href="https://twitter.com/jurretweet">Jurre</a>: Safari extension to quickly switch between <code class="language-plaintext highlighter-rouge">.h</code> and <code class="language-plaintext highlighter-rouge">.m</code> files on GitHub.</li>
<li><a href="https://github.com/Jasdev/DomainCloser">DomainCloser</a> (shameless plug 😆): Quickly close all tabs matching the domain of the currently active tab.</li>
</ul>
<hr />
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>I tried to highlight lesser-known utilities. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>Some of these are cross-platform <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Finite steps in infinite feeds: my story with information overload2016-10-23T00:00:00+00:00https://jasdev.me/infinite-feeds<p>Hi, I’m Jasdev, and I’m a recovering Twitter ‘completionist.’ I have an unusual relationship with the service. Aside from a handful of mute filters<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>, I read <em>every single tweet</em> in my timeline. By following ~400 accounts and assuming two tweets a day per account, I read about 800 tweets each day. As a result, I tended to fill emptier moments, like <a href="/commuting">waiting for the subway</a> or passing time when arriving early, with a clockwork routine. Sense boredom, pull out my phone, open <a href="http://tapbots.com/tweetbot/">Tweetbot</a>, pull to refresh, rinse and repeat. While Twitter excels at real-time content, I realized that my consumption of its feed <strong>shouldn’t be</strong>. At times, I would feel like a hamster running on a wheel and this struggle of “keeping up” forced me to reevaluate the role various feeds play in my life. I was trying to reach the ‘end’ of an infinite feed with finite steps.</p>
<p>Now, I just check Twitter a few times a day. I still read every tweet, but it feels healthier not to <a href="https://en.wikipedia.org/wiki/Continuous_partial_attention">continuously be paying partial attention</a> to everything. I sparked this shift in a few ways: labeling feeds as <strong>inboxes</strong> or <strong>streams</strong>, determining the importance of <strong>creating</strong> and <strong>consuming</strong> content in each one, reducing inbound sources, saving items to read later (at specific times), and <a href="https://www.headspace.com/blog/2015/11/04/noting-technique-video/">mental noting</a>.</p>
<h2 id="streams-and-inboxes">Streams and Inboxes</h2>
<p>It’s funny how the subtle labels we use for services affect our relationship with them<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>. E-mail is typically treated as an inbox, which <a href="https://overcast.fm/+GsgOnpEfc/30:40">induces the urge</a><sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup> to reach “Inbox 0,” or rather, ‘complete it.’ On the other hand, Twitter is generally viewed as a ’stream’ that you dip into from time to time, which makes my ‘completionist’ tendency somewhat rare. Extrapolating a bit, here is how I classify other feeds in my life:</p>
<ul>
<li>Streams
<ul>
<li><a href="https://soundcloud.com/jasdev-singh">SoundCloud</a></li>
<li><a href="https://www.instagram.com/jasdev">Instagram</a></li>
<li><a href="https://www.snapchat.com/add/justdavesingh">Snapchat</a>
<ul>
<li><a href="https://support.snapchat.com/en-US/about/stories">Stories</a>, specifically. Direct Messages are more inbox-like.</li>
</ul>
</li>
<li><a href="https://www.facebook.com/jasdev.singh">Facebook</a></li>
</ul>
</li>
<li>Inboxes
<ul>
<li><a href="https://m.me/jasdev.singh">Messenger</a>/<a href="sms://7035992135">Messages</a></li>
<li><a href="https://twitter.com/jasdev">Twitter</a></li>
<li><a href="https://www.instapaper.com/p/jasdev">Instapaper</a></li>
<li>RSS (blogs and podcasts)<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup></li>
</ul>
</li>
</ul>
<p>Taking the time to enumerate this classification has been helpful. By <em>explicitly</em> realizing that my usage of services like Instagram is more stream-like, I was able to quell the desire to view every post.</p>
<h2 id="importance-of-creation-and-consumption">Importance of Creation and Consumption</h2>
<p>An implicit assumption we make with feeds is that we have to both read <strong>and</strong> write (post) to it. However, this conjunction doesn’t always have to be true. After all, we’re the users of these services! Historically, Twitter has been criticized for its inability to convert <a href="https://en.wikipedia.org/wiki/Lurker">lurkers</a> into avid Tweeters. However, this can be a blessing in disguise, especially at a personal level. Reversing this, I use <a href="https://www.swarmapp.com">Swarm</a> and <a href="https://github.com/jasdev">GitHub</a> as write-only feeds. I’ll checkin and commit, respectively, but rarely browse their feeds. I believe there is an opportunity for a service to use this decoupling to its advantage, as I noted in my previous post, “<a href="/nostalgia">Nostalgia and Upper Bounds</a>.“</p>
<blockquote>
<p>[I]magine if services like Twitter allowed tweets to be posted at any time, and only surfaced the feed during specific, limited hours of the day. That could do wonders in eradicating <a href="https://en.wikipedia.org/wiki/Continuous_partial_attention">continuous partial attention</a>.</p>
</blockquote>
<h2 id="filtering-and-reading-later"><a name="inbound">Filtering and Reading Later</a></h2>
<ul>
<li>Reduce Inbound Information
<ul>
<li>Whether it’s your following or subscription count, the most reliable way to shrink your feed is to reduce the number of inbound sources. Like <a href="https://en.wikipedia.org/wiki/Dunbar%27s_number">Dunbar’s Number</a> for social relationships, I think there is an equivalent for each service, based on its atomic unit for content. The tricky part is finding that number. For my usage of Twitter, this limit hovers around 300 accounts<sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">5</a></sup>. Beyond that, my timeline escalates from a stream to a firehose. The following technique can help facilitate this filtration process.</li>
</ul>
</li>
<li>Saving for Later (at Specific Times)
<ul>
<li>When scrolling through a feed, people commonly defer content to apps like <a href="http://instapaper.com/">Instapaper</a>, <a href="https://getpocket.com">Pocket</a>, or <a href="https://pinboard.in">Pinboard</a> for later consumption. The resulting ‘queue,’ <a href="/managing-my-pocket-queue">when properly managed</a>, can give you a better perspective in assessing the value of specific content. For instance, a common trait amongst posts I archive is time-dependency, causing them to go ‘stale.’ Moreover, to avoid repeating the same mistake of constantly checking this queue, I’ll reserve specific hours in the day to go through it.</li>
</ul>
</li>
</ul>
<h2 id="mental-noting">Mental Noting</h2>
<p>As the number of feeds in our lives scale, it’s important that our consumption patterns evolve in lockstep<sup id="fnref:6" role="doc-noteref"><a href="#fn:6" class="footnote" rel="footnote">6</a></sup>. Ultimately, the handling of information overload will have to take place within our heads. This is where <a href="https://www.headspace.com/blog/2015/11/04/noting-technique-video/">mental noting</a> comes in. Common amongst meditators, noting involves recognizing when your attention has been swept away by a thought or feeling, gently labeling it—”oh, that was a thought about yesterday”—and returning to the breath. Applied to social media, this can help catch urges to check feeds and return to the present moment, when you find yourself mindlessly scrolling.</p>
<p>So, the next time you find yourself with an empty moment, urging to press ⌘ + T, typing ‘t’ or ‘f’, and pressing enter, try to be aware. Gently catch yourself. Do you really need the dopamine hit of a few tweets? Probably not. This is how I’ve recovered from information overload. Hopefully, it can help others too.</p>
<hr />
<h2 id="related-links-thoughts-and-footnotes">Related Links, Thoughts, and Footnotes:</h2>
<p>⇒ “<a href="https://hbr.org/2011/03/information-overloads-2300-yea.html">Information Overload’s 2,300-Year-Old History</a>”</p>
<p>⇒ <a href="https://twitter.com/andypuddicombe">Andy Puddicombe</a>’s <a href="https://overcast.fm/+BiIbfZewE/19:57">ice cream metaphor for social media</a> on Radio Headspace has stuck with me.</p>
<p>⇒ The infinitude of feeds is why I stay away from dating apps like <a href="https://www.gotinder.com">Tinder</a>. Instead, I prefer alternatives like <a href="https://coffeemeetsbagel.com">Coffee Meets Bagel</a>, which emphasize finite feeds spanning multiple days. However, I’m still collecting my thoughts around online dating, but that’s for another post.</p>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p><a href="http://tapbots.com/tweetbot/">Tweetbot</a>’s hallmark feature <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>Labels are almost concrete implementations of <a href="http://www.insightmeditationcenter.org/books-articles/articles/mental-noting/">mental noting</a>. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p><a href="http://twitter.com/nbashaw/">Nathan Bashaw</a> and <a href="https://twitter.com/willhoekenga">Will Hoekenga</a> touched on this in a recent episode of the <a href="https://overcast.fm/itunes1120232683/hardbound">Hardbound Podcast</a>. The link is timestamped and the relevant section ends at 31:47. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:4" role="doc-endnote">
<p><a href="https://feedwrangler.net/welcome.html">Feed Wrangler</a> for feed management paired with <a href="http://reederapp.com">Reeder</a> has worked well for me. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:5" role="doc-endnote">
<p>There is a delicate balance here. Too small of a number could introduce a filter bias. <a href="#fnref:5" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:6" role="doc-endnote">
<p><a href="https://twitter.com/buster">Buster Benson</a> noted that the source of this evolution will ultimately <a href="https://twitter.com/buster/status/773913867353141248">need to be internal</a>. <a href="#fnref:6" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
257 hours on the F Line2016-10-20T00:00:00+00:00https://jasdev.me/commuting<p>Since moving to New York City, I’ve spent ~257 hours commuting to and from work via <a href="https://en.wikipedia.org/wiki/F_(New_York_City_Subway_service)">the F line</a><sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. While I used to dread my commute, I’ve recently spent a lot of time thinking about how to make it more enjoyable. The goal <strong>wasn’t to optimize every minute of my commute</strong>, but rather to make it something I looked forward to, while aligning with my <a href="https://github.com/Jasdev/thoughts/blame/97c505fa1cd52e74060fc898d0f20f3206347164/core-values.md#L5">core value of always learning</a>.</p>
<p>The substrate for this reflection was <a href="https://twitter.com/uint_min">Jordan Rose</a>’s note about the privilege of spare time in open-source development at the end of “<a href="http://belkadan.com/blog/2016/05/So-You-Want-To-Be-A-Compiler-Wizard/">On Becoming a (Compiler) Wizard</a>.” The last point really hit home for me.</p>
<blockquote>
<p>I want to explicitly recognize this as a form of <strong>privilege</strong>. The people most likely to have spare time to do outside projects are people</p>
<ul>
<li>
<p>who don’t have families to take care of (kids, parents, whatever)</p>
</li>
<li>
<p>who have a good, steady income (i.e. not learning while, say, balancing two part-time jobs)</p>
</li>
<li>
<p>who live close to work (minimizing a commute)</p>
</li>
</ul>
</blockquote>
<p>Spending 70 minutes each day commuting doesn’t have to be a sunk cost. Below are some ways I’ve tried to view commutes through a more fruitful lens.</p>
<h2 id="disconnection">Disconnection</h2>
<p>One of my favorite hobbies is to observe subway riders instinctively pulling out their phones upon resurfacing or arriving at a stop. I realize that most of these impulses can be traced back to <a href="/always-on-conversations">messaging loved ones</a><sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>, but I find that a large percentage of my urges to check my phone<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup> are usually aimless. To combat this, I periodically challenge myself to keep my phone in my pocket (on Airplane Mode) for the entire ride. This seems trivial. But, it’s challenging when we condition ourselves to be heads down in Instapaper queues and Twitter timelines. Scoping out a bit, the notion of being idle for extended periods of time<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup> has become increasingly rare, as my friend <a href="http://twitter.com/ryandawidjan">Ryan</a> noted in his <a href="https://quip.com/jgBUALiGBjwp">thought journal</a>:</p>
<blockquote>
<p>It just occured to me that if someone is simply waiting for you and doing nothing [with] their phone/hands when you arrive, you either think they’re mad at you [or up to something].</p>
</blockquote>
<p>While underground routes facilitate disconnection from the Internet, there is a tangible dose of inspiration that comes with above-ground lines (especially in New York).</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">There is something so motivating about taking the subway across the Manhattan Bridge from Brooklyn and seeing the skyline 🚀</p>— Jasdev Singh (@jasdev) <a href="https://twitter.com/jasdev/status/685116226008395776">January 7, 2016</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-lang="en"><p lang="und" dir="ltr"><a href="https://t.co/6tSs0fZp7n">pic.twitter.com/6tSs0fZp7n</a></p>— Brian Gesiak (@modocache) <a href="https://twitter.com/modocache/status/769318275893358592">August 26, 2016</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<h2 id="small-gains-with-am-and-pm-splits">Small Gains with AM and PM “Splits”</h2>
<p>While working full-time affords you freedoms that are absent from schooling, an aspect I miss the most from university is the“mandatory [mental] exercise periods.”<sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">5</a></sup></p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">I hated school as a kid, but at 46, a disciplined study of multiple subjects with a mandatory exercise period sounds... kind of nice.</p>— John Carmack (@ID_AA_Carmack) <a href="https://twitter.com/ID_AA_Carmack/status/775310858058469379">September 12, 2016</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Because of this, I’ve tried to use my morning commute as dedicated time to improve myself in some way (reserving the evening for disconnection). This is a practice I <a href="https://github.com/Jasdev/thoughts/blame/7b2d5327bad2aeb3d7ac353356f9e2bddef68583/daily-list.md#L8">take to heart</a>. Every so often, I unfavorably fall into a routine of starting my days in a <em>reactive</em> manner<sup id="fnref:6" role="doc-noteref"><a href="#fn:6" class="footnote" rel="footnote">6</a></sup> (i.e. checking notifications upon waking up, not allowing enough time before work to write, read, etc.). This defers my personal ambitions until after work, which enables tiredness or social obligations to take priority. <strong>While these aren’t bad things</strong>, it causes a cycle in which no time is left for growth outside of the office.</p>
<p><a href="https://twitter.com/ayanonagon">Ayaka Nonaka</a>, my friend, encapsulated this idea in her <a href="http://blog.ayaka.me/post/137497360302/daily-routine">post on daily routines</a>:</p>
<blockquote>
<p>Setting aside time in the morning to work on something that is <em>just for me</em> has been the most impactful thing I’ve started doing for myself. <strong>I wake up for myself</strong>.</p>
</blockquote>
<p>Below are some things I do during my morning commute:</p>
<ul>
<li>Read my “Daily List”
<ul>
<li><a href="/daily-list">More on this habit</a> and my <a href="https://github.com/Jasdev/thoughts/blob/master/daily-list.md">current list</a></li>
</ul>
</li>
<li>Podcasts
<ul>
<li>I manage my subscriptions with <a href="https://overcast.fm/">Overcast</a>.</li>
</ul>
</li>
<li>Read
<ul>
<li>Usually a book or my <a href="http://instapaper.com/p/jasdev">Instapaper Queue</a><sup id="fnref:7" role="doc-noteref"><a href="#fn:7" class="footnote" rel="footnote">7</a></sup>. I’m trying to strike a better balance between short and long-form content. But, that’s for another post! <a href="https://twitter.com/scotthyoung/">Scott Young</a> has a fantastic <a href="https://www.scotthyoung.com/blog/2014/01/16/blogs-vs-books/">comparison of the two</a>.</li>
</ul>
</li>
<li>Meditation
<ul>
<li>I always feel a bit awkward meditating on the train, but <a href="https://www.headspace.com">Headspace</a>’s “Commuting” exercise is my go-to.</li>
</ul>
</li>
<li>Writing
<ul>
<li>Morning Pages
<ul>
<li><a href="https://twitter.com/chriswinfield">Chris Winfield</a> has a detailed <a href="http://www.chriswinfield.com/morning-pages/">overview of the practice</a>, but at a high-level, it entails writing ~750 words, usually by hand, without stopping. <strong>Think cache invalidation for your mind</strong>.</li>
</ul>
</li>
<li>Thank You Letters
<ul>
<li>To better <a href="https://github.com/Jasdev/thoughts/blame/97c505fa1cd52e74060fc898d0f20f3206347164/core-values.md#L8">practice going first</a>, I started randomly writing letters to my family, friends, mentors, and others who have impacted my life in some way, no matter how small. The reactions to them have been beyond heart warming. Moreover, <a href="https://twitter.com/patrickc">Patrick Collison</a> recently summarized his experience with this:</li>
</ul>
</li>
</ul>
</li>
</ul>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">It’s like a paper-based, unidirectional, asynchronous version of Twitter in which you only say nice things.</p>— Patrick Collison (@patrickc) <a href="https://twitter.com/patrickc/status/717086681719922688">April 4, 2016</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="instagram-media" data-instgrm-captioned="" data-instgrm-version="7" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);"><div style="padding:8px;"> <div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50.0% 0; text-align:center; width:100%;"> <div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAMUExURczMzPf399fX1+bm5mzY9AMAAADiSURBVDjLvZXbEsMgCES5/P8/t9FuRVCRmU73JWlzosgSIIZURCjo/ad+EQJJB4Hv8BFt+IDpQoCx1wjOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div></div> <p style=" margin:8px 0 0 0; padding:0 4px;"> <a href="https://www.instagram.com/p/BEyl6w4Na9w/" style=" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;" target="_blank">The start of a new tradition</a></p> <p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;">A photo posted by Jasdev Singh (@jasdev) on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2016-04-29T16:15:11+00:00">Apr 29, 2016 at 9:15am PDT</time></p></div></blockquote>
<script async="" defer="" src="//platform.instagram.com/en_US/embeds.js"></script>
<p><img src="http://i.imgur.com/a0r4B4M.jpg" alt="" /></p>
<h2 id="turning-off-autopilot">Turning off “Autopilot”</h2>
<p>We’ve all been there. After hopping on the train, beginning a walk, or driving a car, the actual act of traveling fades to the background, only snapping out of <em>autopilot</em> upon reaching your destination. This is typically referred to as a transition to <a href="https://en.wikipedia.org/wiki/Thinking,_Fast_and_Slow#Two_systems">System 1</a> of thought, which is primed for rote tasks like commuting. After reading <a href="https://twitter.com/ktzhu">Katie Zhu</a>’s post, “<a href="https://ktzine.com/walk-on-the-other-side-of-the-street-be803c8f55cb#.h11156bkk">Walk on the Other Side of the Street</a>,” I’ve tried to be more deliberate about anchoring my attention<sup id="fnref:8" role="doc-noteref"><a href="#fn:8" class="footnote" rel="footnote">8</a></sup>. She describes the wonder of <em>perceiving what you see</em>:</p>
<blockquote>
<p>There’s so much that we have to tune out in order to stay sane or be able to just, you know, generally function. But it’s a nice reminder every now and then to take the time to actually see, look at your surroundings, the routine, boring shit you write off because it’s not novel or exciting anymore. Sometimes, it’ll surprise you.</p>
</blockquote>
<p>Doing so has the side-effect of slowing down one’s perception of time<sup id="fnref:9" role="doc-noteref"><a href="#fn:9" class="footnote" rel="footnote">9</a></sup>.</p>
<hr />
<p>These are just a few of the thoughts that have bubbled up after commuting for the past year. Looking back, it’s hard to find a cohesive narrative that threads them together. But, maybe that’s the beauty of it. Commuting is an unstructured block in the day that one can mold to their liking.</p>
<hr />
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>11 months at Tumblr, commuting from <a href="https://en.wikipedia.org/wiki/Park_Slope">Park Slope</a> ⇒ <code class="language-plaintext highlighter-rouge">11*20*35*2/60</code> hours <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p><a href="https://twitter.com/ashfurrow">Ash Furrow</a> wrote a <a href="https://ashfurrow.com/blog/smartphones-and-augmenting-humanity/">great post</a> that elucidates the harm that othering people who rely on technology causes. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>Even throughout the day <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:4" role="doc-endnote">
<p>Aside from meditation, which is more intentional <a href="#fnref:4" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:5" role="doc-endnote">
<p>Or homework, projects, etc., for lack of a better phrase <a href="#fnref:5" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:6" role="doc-endnote">
<p>More on this in my previous <a href="/made-beds-and-cold-showers">post on morning routines</a> <a href="#fnref:6" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:7" role="doc-endnote">
<p>Instapaper profiles (currently) only surface favorites. <a href="#fnref:7" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:8" role="doc-endnote">
<p>I’d like to elaborate on this in a future post, but here is <a href="https://www.fastcompany.com/3061366/your-most-productive-self/your-brain-is-on-autopilot-more-than-you-think-heres-how-to-wake-i">more reading</a>, if you’re curious about the topic! <a href="#fnref:8" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:9" role="doc-endnote">
<p><a href="https://twitter.com/naval/">Naval Ravikant</a> put this <a href="https://twitter.com/naval/status/769745071675539456">very succinctly</a>. <a href="#fnref:9" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Monthly check-ins2016-08-18T00:00:00+00:00https://jasdev.me/monthly-checkins<p>Two days I look forward to each month are the 28th and the 30th<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. A couple of my closest friends, <a href="https://twitter.com/a_raj11">Ashoka</a> and <a href="https://twitter.com/ShivaKilaru">Shiva</a>, and I have established a ritual of checking in with one another on these days. The setup is simple. An hour-long, monthly calendar event where we work through problems we’re facing, wins for the past month, common themes in our <a href="/small-moments">journals</a>, and big questions we’d like to bounce off one another.</p>
<p><img src="http://i.imgur.com/sG6ImFA.png" alt="" /></p>
<p>The only regret I have about these checkins is not starting sooner. Despite being on opposite coasts, they have allowed us to dive deep into one another’s lives and skip basic questions like“how’re you doing?”,“work going well?”, and “dating anyone?” that often kickoff the rare times we meet in-person.</p>
<p><img src="http://i.imgur.com/FIW8eTq.jpg" alt="" /></p>
<p><img src="http://i.imgur.com/S3xHM8s.png" alt="" /></p>
<p>In a recent checkin, Shiva and I traded ideas on how to prioritize deep work, why first principles are often <a href="https://twitter.com/jasdev/status/760518965865119744">better than analogies</a>, long-term perspectives on our careers, and how to wake up earlier. Moreover, the discussion often permeates our <a href="/always-on-conversations">always-on conversations</a> in the form of motivational messages before interviews or on some idle Tuesday evening.</p>
<p><img src="http://i.imgur.com/FZa6L4h.png" alt="" /></p>
<p>This is one of the <a href="https://twitter.com/jasdev/status/748332315433144320">habits</a> that has kept me happy. Shiva and Ashoka, here’s to the next 1000 checkins!</p>
<p>If you’re reading this post and want to try monthly check-ins, let’s make it happen! 🚀</p>
<hr />
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>Adjusting on Februarys of course <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Case closed: a dive into open enumerations2016-08-13T00:00:00+00:00https://jasdev.me/open-enums<p>Time for a pop quiz. Given that <a href="https://developer.apple.com/reference/coredata/nsfetchedresultschangetype"><code class="language-plaintext highlighter-rouge">NSFetchedResultsChangeType</code></a> is defined as follows:</p>
<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">typedef</span> <span class="nf">NS_ENUM</span><span class="p">(</span><span class="n">NSUInteger</span><span class="p">,</span> <span class="n">NSFetchedResultsChangeType</span><span class="p">)</span> <span class="p">{</span>
<span class="n">NSFetchedResultsChangeInsert</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span>
<span class="n">NSFetchedResultsChangeDelete</span> <span class="o">=</span> <span class="mi">2</span><span class="p">,</span>
<span class="n">NSFetchedResultsChangeMove</span> <span class="o">=</span> <span class="mi">3</span><span class="p">,</span>
<span class="n">NSFetchedResultsChangeUpdate</span> <span class="o">=</span> <span class="mi">4</span>
<span class="p">}</span> <span class="n">NS_ENUM_AVAILABLE</span><span class="p">(</span><span class="n">NA</span><span class="p">,</span> <span class="mi">3</span><span class="n">_0</span><span class="p">);</span>
</code></pre></div></div>
<p>What is the value of <code class="language-plaintext highlighter-rouge">changeType</code> in the following expression (assuming Swift 3.x)?</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">changeType</span> <span class="o">=</span> <span class="kt">NSFetchedResultsChangeType</span><span class="p">(</span><span class="nv">rawValue</span><span class="p">:</span> <span class="mi">5</span><span class="p">)</span>
</code></pre></div></div>
<p><em>Hint: Enumerations with raw values in Swift implicitly conform to <a href="https://developer.apple.com/reference/swift/rawrepresentable"><code class="language-plaintext highlighter-rouge">RawRepresentable</code></a> (⇒ the presence of <code class="language-plaintext highlighter-rouge">init?(rawValue:)</code>).</em></p>
<p><strong>(Corgi for padding between question and answer)</strong></p>
<blockquote class="instagram-media" data-instgrm-version="7" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);"><div style="padding:8px;"> <div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:36.1111111111% 0; text-align:center; width:100%;"> <div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAMUExURczMzPf399fX1+bm5mzY9AMAAADiSURBVDjLvZXbEsMgCES5/P8/t9FuRVCRmU73JWlzosgSIIZURCjo/ad+EQJJB4Hv8BFt+IDpQoCx1wjOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div></div><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"><a href="https://www.instagram.com/p/BIJhXdhghyO/" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank">A photo posted by Zoey Corgella the corgi (@zoeydacorgi)</a> on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2016-07-22T03:32:07+00:00">Jul 21, 2016 at 8:32pm PDT</time></p></div></blockquote>
<script async="" defer="" src="//platform.instagram.com/en_US/embeds.js"></script>
<p>Turns out <code class="language-plaintext highlighter-rouge">changeType</code> is non-<code class="language-plaintext highlighter-rouge">nil</code>, <a href="https://twitter.com/jckarter/status/720413224110129152">works in a <code class="language-plaintext highlighter-rouge">switch</code> statement</a>, and has <a href="https://twitter.com/jckarter/status/720413571734118400">undefined behavior</a>!</p>
<p>To understand why this is the case<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>, we have to take a step back and define two terms: open and closed enumerations. <code class="language-plaintext highlighter-rouge">NSFetchedResultsChangeType</code> is an <code class="language-plaintext highlighter-rouge">NS_ENUM</code> and when bridged to Swift, it’s <a href="https://twitter.com/jckarter/status/720413830329663488">represented as an “open” enumeration</a>. The distinction between open and closed enumerations can be found in <a href="https://twitter.com/UINT_MIN">Jordan Rose</a>’s manifesto on Swift <a href="https://github.com/apple/swift/blob/26fcd8c1e2a716b1b695de39e9be470c2a1814ba/docs/LibraryEvolution.rst#enums">Library Evolution</a>. In summary, open enumerations will be the <a href="https://twitter.com/jckarter/status/720413830329663488"><em>default once ABI resilience ships</em></a> and <strong>allow</strong> the following changes between library versions <strong>without breaking binary compatibility</strong>:</p>
<blockquote>
<ul>
<li>Adding a new case<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup></li>
<li>Reordering existing cases is a binary-compatible source-breaking change. In particular, if an enum is <code class="language-plaintext highlighter-rouge">RawRepresentable</code>, changing the raw representations of cases may break existing clients who use them for serialization</li>
<li>Adding a raw type to an enum that does not have one</li>
<li>Removing a non-public, non-versioned case</li>
<li>Adding any other members</li>
<li>Removing any non-public, non-versioned members</li>
<li>Adding a new protocol conformance (with proper availability annotations)</li>
<li>Removing conformances to non-public protocols</li>
</ul>
</blockquote>
<p>On the flip side, <a href="https://github.com/apple/swift/blob/26fcd8c1e2a716b1b695de39e9be470c2a1814ba/docs/LibraryEvolution.rst#closed-enums">closed enumarations</a> must be explicitly marked with the <code class="language-plaintext highlighter-rouge">@closed</code> attribute. This attribute <strong>prevents</strong> the enumeration from“having any cases with less access than the enum itself<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup> and adding new cases in the future.” In addition to <strong>guaranteeing case exhaustion for clients</strong>, the attribute implies:</p>
<blockquote>
<ul>
<li>Adding new cases is not permitted</li>
<li>Reordering existing cases is not permitted.</li>
<li>Adding a raw type to an enum that does not have one is still permitted.</li>
<li>Removing a non-public case is not applicable.</li>
<li>Adding any other members is still permitted.</li>
<li>Removing any non-public, non-versioned members is still permitted.</li>
<li>Adding a new protocol conformance is still permitted.</li>
<li>Removing conformances to non-public protocols is still permitted.</li>
</ul>
</blockquote>
<p>Despite “open” being the default for enumerations moving forward, adding new cases (and consequences of this) can lead to gnarly bugs like the <a href="https://forums.developer.apple.com/thread/11662">one</a> my teammate, <a href="http://twitter.com/paulrehkugler">Paul</a>, and I found earlier this week.</p>
<h2 id="invalid-changes-sent-to-nsfetchedresultscontrollerdelegate">Invalid Changes Sent to <code class="language-plaintext highlighter-rouge">NSFetchedResultsControllerDelegate</code></h2>
<p><a href="https://developer.apple.com/reference/coredata/nsfetchedresultscontrollerdelegate"><code class="language-plaintext highlighter-rouge">NSFetchedResultsControllerDelegate</code></a> provides an <a href="https://developer.apple.com/reference/coredata/nsfetchedresultscontrollerdelegate/1622296-controller">optional function</a> to notify concrete implementations that a fetched object has been changed due to an add, remove, move, or update. During testing, we noticed“extra” calls<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup> to this function, which caused crashes via subsequent invalid table view updates. To set some context, our concrete implementation had the following structure:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">func</span> <span class="nf">controller</span><span class="p">(</span><span class="n">_</span> <span class="nv">controller</span><span class="p">:</span> <span class="kt">NSFetchedResultsController</span><span class="o"><</span><span class="kt">NSFetchRequestResult</span><span class="o">></span><span class="p">,</span>
<span class="n">didChange</span> <span class="nv">anObject</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">,</span>
<span class="n">at</span> <span class="nv">indexPath</span><span class="p">:</span> <span class="kt">IndexPath</span><span class="p">?,</span>
<span class="k">for</span> <span class="nv">type</span><span class="p">:</span> <span class="kt">NSFetchedResultsChangeType</span><span class="p">,</span>
<span class="nv">newIndexPath</span><span class="p">:</span> <span class="kt">IndexPath</span><span class="p">?)</span> <span class="p">{</span>
<span class="cm">/* Precondition checks */</span>
<span class="c1">// ...</span>
<span class="k">switch</span> <span class="n">type</span> <span class="p">{</span>
<span class="k">case</span> <span class="o">.</span><span class="nv">insert</span><span class="p">:</span>
<span class="cm">/* Insertion logic */</span>
<span class="k">case</span> <span class="o">.</span><span class="nv">delete</span><span class="p">:</span>
<span class="cm">/* Deletion logic */</span>
<span class="k">case</span> <span class="o">.</span><span class="nv">move</span><span class="p">:</span>
<span class="cm">/* Move logic */</span>
<span class="k">case</span> <span class="o">.</span><span class="nv">update</span><span class="p">:</span>
<span class="cm">/* Update logic */</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Breakpointing on calls to this function, we noticed something odd in the debug menu:</p>
<p><img src="https://i.imgur.com/XZbBx1a.png" alt="" /></p>
<p>Looks like Core Data is making calls to this delegate function with invalid <code class="language-plaintext highlighter-rouge">NSFetchedResultsChangeType</code>s! However, due to the nature of open enumerations, we have no way of disambiguating this from a scenario where a new case is added (i.e. <code class="language-plaintext highlighter-rouge">NSFetchedResultsChangeType(rawValue: x) != nil, ∀x ∈ {UInt(0), UInt(1), UInt(2), ... , UInt.max}</code>, due to the lack of guaranteed case exhaustion). To make this even trickier to find, the example case seemed to match against the first case, <code class="language-plaintext highlighter-rouge">.insert</code>, (as <a href="https://twitter.com/calebd">Caleb</a> noted <a href="https://twitter.com/calebd/status/720413415894683649">in his testing</a> as well). But, this <a href="https://twitter.com/jckarter/status/720413571734118400">behavior is technically undefined</a>.</p>
<h2 id="getting-around-this">Getting Around This</h2>
<p>To safeguard against this bug <a href="https://twitter.com/jckarter/status/723339235126849537">until a <code class="language-plaintext highlighter-rouge">default</code> case is required for open enumerations</a>, we came up with an <strong>imperfect</strong> workaround. In the delegate function body, we added a <code class="language-plaintext highlighter-rouge">guard</code> to ensure the presence of a valid raw value on the <code class="language-plaintext highlighter-rouge">type</code> parameter:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">guard</span> <span class="p">[</span>
<span class="kt">NSFetchedResultsChangeType</span><span class="o">.</span><span class="n">update</span><span class="o">.</span><span class="n">rawValue</span><span class="p">,</span>
<span class="kt">NSFetchedResultsChangeType</span><span class="o">.</span><span class="n">delete</span><span class="o">.</span><span class="n">rawValue</span><span class="p">,</span>
<span class="kt">NSFetchedResultsChangeType</span><span class="o">.</span><span class="n">insert</span><span class="o">.</span><span class="n">rawValue</span><span class="p">,</span>
<span class="kt">NSFetchedResultsChangeType</span><span class="o">.</span><span class="n">move</span><span class="o">.</span><span class="n">rawValue</span>
<span class="p">]</span><span class="o">.</span><span class="nf">contains</span><span class="p">(</span><span class="n">type</span><span class="o">.</span><span class="n">rawValue</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span>
<span class="cm">/* Note about the open enumeration issue we’ve described and logging to track this in production */</span>
<span class="k">return</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This keeps our code crash-free until ABI resilience alleviates the need for the <code class="language-plaintext highlighter-rouge">guard</code>. Of course, this doesn’t handle the scenario in which Apple adds a new case, but that’s why we perform logging in the <code class="language-plaintext highlighter-rouge">else</code> clause to keep an eye on any API updates. You can watch <a href="https://bugs.swift.org/browse/SR-1258">this issue</a> on Swift’s JIRA.</p>
<p>Hope this helps make open and closed enumerations more clear! Huge shoutout to <a href="http://twitter.com/jckarter">Joe Groff</a>, <a href="https://twitter.com/calebd">Caleb Davenport</a>, and <a href="https://twitter.com/UINT_MIN">Jordan Rose</a> for discussing this topic in the open on Twitter and in the <a href="https://github.com/apple/swift/tree/26fcd8c1e2a716b1b695de39e9be470c2a1814ba/docs">Swift repository docs</a>.</p>
<hr />
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>Pun intended 😁 <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>We’ll soon see why this feature causes <code class="language-plaintext highlighter-rouge">changeType</code> in the quiz to be non-<code class="language-plaintext highlighter-rouge">nil</code>. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p><a href="https://github.com/apple/swift/blame/26fcd8c1e2a716b1b695de39e9be470c2a1814ba/docs/LibraryEvolution.rst#L828">Non-<code class="language-plaintext highlighter-rouge">public</code> cases don’t exist at the moment</a>. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:4" role="doc-endnote">
<p>Masked as insertions due to this <a href="https://twitter.com/calebd/status/720413415894683649">undefined behavior</a>. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Nostalgia and upper bounds2016-08-08T00:00:00+00:00https://jasdev.me/nostalgia<p>Nostalgia products have made a comeback. Whether it’s <a href="https://timehop.com">Timehop</a>’s daily peek down memory lane, Facebook <a href="https://www.facebook.com/help/1422085768088554">inserting memories</a><sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> into your feed, or Apple tucking away old photos into iOS <a href="https://developer.apple.com/ios/human-interface-guidelines/extensions/home-screen-actions/">Quick Actions</a>, companies are trying to find meaningful ways to resurface the past.</p>
<p><img src="https://i.imgur.com/bsRwOVR.jpg" alt="" /></p>
<p>While I enjoy these features, I’d like to highlight a consequence many of them overlook: causing users to erroneously compare the present to the past. This is especially critical when you’re going through a tough time in life.</p>
<p>I came to this realization after rounding out my third year of <a href="/small-moments">journaling every day</a>. Before heading to bed each night, I’ll spend ten minutes recapping the day and picking a single photo to represent it<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>. This has allowed me to build a personal time capsule like no other. I’d often catch myself randomly recapping old memories while riding the subway home or in-between snoozes before getting out of bed. While reflection is healthy in small, periodic doses, it’s tempting to fall into a trap of comparing where you are now against where you were <em>n</em> years ago. This comparison is helpful in concrete domains such as personal fitness, but breaks down in subjective areas like relationships. For example, I’ve had to overcome the tendency of comparing my happiness while single to how I felt when in past relationships. These two phases are incomparable. Time may be linear, but experiences in my life aren’t. This took me a while to internalize.</p>
<p><img src="http://i.imgur.com/OrevQJp.jpg" alt="" /></p>
<p>A second-order effect of these unfair comparisons is the tendency to view favorable memories as ’upper bounds’ in life. Culturally, we often do this. Take the adage that your“college years will be the best of your life”, for example. <a href="https://github.com/Jasdev/jasdev.github.io/blame/c76d77c8cb92680a831f762284b80c4d6e8b418f/_posts/2015-08-31-lessons-after-school.md#L15">Believing this is shortchanging yourself</a>. Instead, I try to <a href="http://www.insightmeditationcenter.org/books-articles/articles/mental-noting/">catch myself</a> when I have these thoughts and invert them to lower bounds. Your past should serve as a springboard into the present and future. Another trick I’ve found useful is to check services like Timehop at night, as opposed to the morning. This allows me to tackle the day anew and prevent the aforementioned comparison.</p>
<p>A related (and more general) concept I’ve been thinking about is the notion of write-only feeds. What if we designed a journal that <em>only allowed</em> you to create entries<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>? This might allow one to experience the cathartic benefits of writing, while reducing the urge to compare distinct points in time. More generally, imagine if services like Twitter allowed tweets to be posted at any time, and only surfaced the feed during specific, limited hours of the day. That could do wonders in eradicating <a href="https://en.wikipedia.org/wiki/Continuous_partial_attention">continuous partial attention</a>. But, that’s for another post!</p>
<p>It’s never been easier to revisit old memories—but the repetitive juxtapositions in today’s nostalgia products make it easy to draw inequitable comparisons between who you are now and who you were in the past. The key is applying what you know from the past to make the present and future a beautiful place to be.</p>
<hr />
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>Also known as “On This Day” <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>I’ll often use screenshots! <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>Only allowing viewing on longer timescales (e.g. 5–10 years) <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Thoughts on WWDC2016-07-07T00:00:00+00:00https://jasdev.me/thoughts-on-wwdc<p>WWDC is a conference like no other. This past month, I was lucky enough to experience it as a part of Tumblr’s <a href="https://cocoa.tumblr.com/post/146758581738/wwdc-2016-has-come-and-passed-but-we-wanted-to">iOS cohort</a>. While my peers have done a fantastic job <a href="https://swiftnews.curated.co/issues/91#start">recapping the conference’s highlights</a>, I wanted to take a moment to reflect on the smaller details, things that went well, and things I wish I had done differently.</p>
<h2 id="recharge-days-and-exercise">Recharge Days and Exercise</h2>
<p>This is something I regret not doing during the week. At times, WWDC can feel like a marathon. It doesn’t have to be that way. Take a day to <a href="https://www.instagram.com/p/BG5jDpLNaw_">explore the city</a>. <a href="https://www.instagram.com/p/BGyVICpta3I">Catch up with old friends</a>. <a href="https://twitter.com/mb/status/743516855076171776">Squash lingering email</a>. <a href="https://twitter.com/wahoo/status/741405799344267265">Get a guest pass to a local gym</a>. Time off can help you <a href="https://twitter.com/Sommer/status/741899695492976641">recharge</a>.</p>
<h2 id="layers-2016"><a href="https://bringyourlayers.com">Layers 2016</a></h2>
<p>I truly believe the magic of WWDC week is in the people you meet, not the actual sessions themselves<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. If I were to do it all over again, I’d attend <a href="https://bringyourlayers.com">Layers</a>. Layers is a three-day design conference for the macOS and iOS community and in their words: a “party, but for learning.” Moreover, <a href="https://www.caseyliss.com/2016/6/17/layers-2016">Casey Liss’ post on his experience</a> convinced me to attend next year.</p>
<h2 id="attending-in-person-podcast-recordings">Attending In-Person Podcast Recordings</h2>
<p>Podcasts have become a huge part of my life, since moving to New York City. Commuting from Park Slope to the Flatiron District each day gives me about an hour<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup> of idle time to learn via auditory osmosis. During the week of WWDC, both <a href="https://www.relay.fm">RelayFM</a> and <a href="https://twitter.com/gruber">John Gruber</a> from <a href="https://overcast.fm/itunes528458508/the-talk-show-with-john-gruber">The Talk Show</a> hosted in-person recordings of episodes<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>! I definitely hope to attend some of these events next year to help put faces to the voices I listen to each day on the way to work.</p>
<h2 id="twitter">Twitter</h2>
<p>I recently uninstalled Twitter from my phone<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup>. However, during WWDC, I made an exception. While the service gets a lot of flak, it has the magical ability to foster community, <a href="https://twitter.com/ayanonagon/status/743827770401165313">coordinate spontaneous lunches</a>, and <a href="https://twitter.com/joshavant/status/743178026259615745">facilitate serendipity</a>. The iOS community on Twitter is stellar and it truly shines during that week.</p>
<h2 id="aggregating-team-questions-for-labs">Aggregating Team Questions for Labs</h2>
<p>Tumblr’s iOS app has ~20 contributors. At this scale, questions were bound to come up when reading through the <a href="https://developer.apple.com/library/prerelease/content/releasenotes/General/iOS10APIDiffs/">API diffs</a>. To coordinate and answer them for folks who couldn’t make it, we created a spreadsheet with links to <a href="https://github.com/paulrehkugler/OptionalityChanges">sample projects</a> and relevant <a href="https://openradar.appspot.com/page/1">Radars</a>. This allowed us to divvy up the questions and better relay them to Apple engineers during the labs.</p>
<h2 id="attend-a-random-session">Attend a Random Session</h2>
<p>When plotting out sessions I wanted attend, I decided to sprinkle in a random one for the fun of it. I chose “<a href="https://developer.apple.com/videos/play/wwdc2016/712/">Working with Wide Color</a>” and was blown away. Being a math major in university, I’ve always been fascinated by color theory. This session provided an in-depth walkthrough of the <a href="https://en.wikipedia.org/wiki/DCI-P3">P3 Color Space</a> and how it affects various color APIs on Apple’s platforms, something I would’ve easily missed otherwise!</p>
<h2 id="wear-your-companyapps-shirt">Wear Your Company/App’s Shirt</h2>
<p>I picked this tip up from an <a href="https://overcast.fm/+FgnZ09-xk">episode of Under the Radar</a>. Countless conversations throughout the week started by recognizing the logo of an app on someone’s shirt. It’s a simple way to facilitate introductions and thank the developers who work on your favorite products!</p>
<hr />
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>Especially since they’re <a href="https://developer.apple.com/videos/wwdc2016">recorded</a> <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>Round trip <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p><a href="http://www.extras.relay.fm/blog/2016/4/4/announcing-relaycon-wwdc-2016">RelayCon Announcement</a> and <a href="https://vimeo.com/171186055">The Talk Show 158</a> links <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:4" role="doc-endnote">
<p>More on this in a separate post (also take this approach to e-mail)! I’m definitely not perfect at this, but it helps avoid the trap of <a href="https://twitter.com/jasdev/status/742985718461571073">continuous partial attention</a>. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Swift’s nonmutating Keyword2016-05-01T00:00:00+00:00https://jasdev.me/nonmutating<p>In preparing my recent “<a href="https://speakerdeck.com/jasdev/hidden-gems-in-swift">Hidden Gems in Swift</a>” talk, I carefully combed through Swift’s “<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html">Lexical Structure</a>” section. One keyword that stuck out to me, but ultimately didn’t make it into the talk was <code class="language-plaintext highlighter-rouge">nonmutating</code>. Much like the <a href="/when-to-use-nonobjc"><code class="language-plaintext highlighter-rouge">@nonobjc</code></a> attribute, it surprised me that I hadn’t seen this keyword in the wild.</p>
<p>In searching for example uses, I stumbled upon this conversation between <a href="https://twitter.com/andy_matuschak/">Andy Matuschak</a> and <a href="https://twitter.com/Sidnicious">Sidney San Martín</a>:</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Interesting new Swift keyword I learned about today: "nonmutating."<br /><br />e.g. UnsafeMutablePointer's<br /><br />var memory: T { get nonmutating set }</p>— Andy Matuschak (@andy_matuschak) <a href="https://twitter.com/andy_matuschak/status/522529881717215232">October 15, 2014</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="en"><p lang="en" dir="ltr"><a href="https://twitter.com/andy_matuschak">@andy_matuschak</a> I just discovered this myself. I'm using an enum to manage app defaults, and now I can…<br /><br />AppDefault.Bounciness.intValue = 5</p>— Sidney (@Sidnicious) <a href="https://twitter.com/Sidnicious/status/653618390557413376">October 12, 2015</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="en"><p lang="en" dir="ltr"><a href="https://twitter.com/Sidnicious">@Sidnicious</a> Yikes. What does that mean? Have a gist?</p>— Andy Matuschak (@andy_matuschak) <a href="https://twitter.com/andy_matuschak/status/653618565271040000">October 12, 2015</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-conversation="none" data-cards="hidden" data-lang="en"><p lang="en" dir="ltr"><a href="https://twitter.com/andy_matuschak">@andy_matuschak</a> Think it's too much? <a href="https://t.co/WiK4Onj8B1">https://t.co/WiK4Onj8B1</a></p>— Sidney (@Sidnicious) <a href="https://twitter.com/Sidnicious/status/653619188494434304">October 12, 2015</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="en"><p lang="en" dir="ltr"><a href="https://twitter.com/Sidnicious">@Sidnicious</a> Yes. :)<br /><br />That setter *is* mutating. This will break compilation semantics.</p>— Andy Matuschak (@andy_matuschak) <a href="https://twitter.com/andy_matuschak/status/653619522843217920">October 12, 2015</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="en"><p lang="en" dir="ltr"><a href="https://twitter.com/andy_matuschak">@andy_matuschak</a> Hah! That's fair. I'd consider it nonmutating, though, because it doesn't change the instance itself, just global state. No?</p>— Sidney (@Sidnicious) <a href="https://twitter.com/Sidnicious/status/653620291629924352">October 12, 2015</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="en"><p lang="en" dir="ltr"><a href="https://twitter.com/Sidnicious">@Sidnicious</a> Hm. I think you’re right, actually!</p>— Andy Matuschak (@andy_matuschak) <a href="https://twitter.com/andy_matuschak/status/653620726801395712">October 12, 2015</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Stepping through Sidney’s example, we can see how <code class="language-plaintext highlighter-rouge">nonmutating</code> signals that a setter doesn’t modify the containing instance, but instead has global side effects.</p>
<script src="https://gist.github.com/Jasdev/cd96d61277a8b1c1e6f4b413d9ccc645.js"></script>
<p>Whether or not this is good practice is a larger question. But, it’s definitely worth adding to your tool belt!</p>
Hiding selector methods2016-03-22T00:00:00+00:00https://jasdev.me/private-selector-methods<p>On the <a href="https://www.tumblr.com">Tumblr</a> iOS team, we have a strict rule that everything with an <code class="language-plaintext highlighter-rouge">internal</code> or <code class="language-plaintext highlighter-rouge">public</code> visibility must be documented<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. This gets a bit awkward when using the <a href="https://developer.apple.com/library/ios/documentation/General/Conceptual/Devpedia-CocoaApp/TargetAction.html">Target-Action</a> pattern that <code class="language-plaintext highlighter-rouge">UIKit</code> often forces you into. When dealing with multiple action selectors, it makes sense to factor out the target into a separate object. However, we’ll often just need a one-off action selector to handle an event from a <code class="language-plaintext highlighter-rouge">UIControl</code> subclass. Previously, this would lead to the following scenario and awkward comment:</p>
<script src="https://gist.github.com/Jasdev/3338cda9d6c799323abe.js"></script>
<p>I was curious if we could make <code class="language-plaintext highlighter-rouge">SampleViewController.buttonTapped(_:)</code> <code class="language-plaintext highlighter-rouge">private</code>, thus avoiding the need to specify that the method <em>shouldn’t</em> be called externally, in its documentation. My teammate <a href="https://twitter.com/paulrehkugler">Paul</a> pointed out that this is actually possible! To do this, we simply have to expose the method to the Objective-C runtime and give it a <code class="language-plaintext highlighter-rouge">private</code> access modifier:</p>
<script src="https://gist.github.com/Jasdev/ea842f5a4527dae5d9e3.js"></script>
<p>This allows our action to be invoked from a selector, but prevents other files from accessing the method directly 🎉 Hope this helps better encapsulate your types!</p>
<hr />
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>To help with this, we use <a href="https://github.com/onevcat/VVDocumenter-Xcode">VVDocumenter</a>. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
TIL about @nonobjc2016-02-27T00:00:00+00:00https://jasdev.me/when-to-use-nonobjc<p>In my free time, I run a Twitter account called <a href="https://twitter.com/PublicExtension">Public Extension</a>, where I share handy Swift extensions I’ve found or come up with. A recent extension involved adding <a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Subscripts.html">custom subscripts</a> to <a href="https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSUserDefaults_Class/"><code class="language-plaintext highlighter-rouge">NSUserDefaults</code></a>.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">4️⃣6️⃣: Make NSUserDefaults a bit easier to work with 😃<a href="https://t.co/1eULFysnXb">https://t.co/1eULFysnXb</a> <a href="https://t.co/7127Y6PCB1">pic.twitter.com/7127Y6PCB1</a></p>— Public Extension (@PublicExtension) <a href="https://twitter.com/PublicExtension/status/702543100095426560">February 24, 2016</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>One attribute that sticks out is <code class="language-plaintext highlighter-rouge">@nonobjc</code>. Often times, you’ll need to use <code class="language-plaintext highlighter-rouge">@objc</code><sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> to expose your Swift classes that don’t derive from an Objective-C class to the Objective-C runtime. It’s rare to <em>explicitly</em> cut that interoperability. Let’s dig into why this attribute is necessitated here by dropping it and deconstructing the compilation error:</p>
<p><img src="/public/images/nonobjc_error.png" alt="" /></p>
<p>The full error reads “Subscript getter with Objective-C selector ’<code class="language-plaintext highlighter-rouge">objectForKeyedSubscript:</code>’ conflicts with previous declaration with the same Objective-C selector” (and the analog for the setter). Turns out that Objective-C also supports <a href="http://nshipster.com/object-subscripting/">custom subscripting</a> and Swift will automatically bridge them back! However, in our example, we’re overloading the subscript with the same key and different return types (also referred to as return type polymorphism), which Objective-C doesn’t support 😔. To fix this, we simply prevent the default bridging via <code class="language-plaintext highlighter-rouge">@nonobjc</code>.</p>
<p>So, there you have it! A real example of the <code class="language-plaintext highlighter-rouge">@nonobjc</code> attribute in action. Hope this post sheds some light on why it exists 😃</p>
<p>Shout-out to <a href="https://twitter.com/jckarter">Joe Groff</a> for the help and <a href="https://twitter.com/garricn">Garric Nahapetian</a> for motivating me to write this post!</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr"><a href="https://twitter.com/jasdev">@jasdev</a> Overloading will work if you mark one or both subscripts as <a href="https://twitter.com/nonobjc">@nonobjc</a>.</p>— Joe Groff (@jckarter) <a href="https://twitter.com/jckarter/status/701536484873019392">February 21, 2016</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr"><a href="https://twitter.com/PublicExtension">@PublicExtension</a> This looks really fun. I would love to hear more about this solution. Is there a blog post about it?</p>— Garric G. Nahapetian (@garricn) <a href="https://twitter.com/garricn/status/702979513970327552">February 25, 2016</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<hr />
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>Swift also allows for the <code class="language-plaintext highlighter-rouge">@objc(name)</code> variant, which uses <code class="language-plaintext highlighter-rouge">name</code> when exposing the target class to the Objective-C runtime. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
X at Y. Previously A, B, and C.2016-01-17T00:00:00+00:00https://jasdev.me/x-at-y-previously-a-b-c<p>You’ve seen this type of bio all over the web. “X at Y. Previously A, B, and C.” I’ve been guilty of having this kind of bio since joining Twitter back in December 2011. I would list the company I’m currently at and those I worked at previously, without realizing it covers up who I truly am. I am not my job and my job isn’t me. There is much more beyond my online bios that makes me tick. Moving forward, I want to be more authentic.</p>
<p>This thought popped into my head on my daily commute into Manhattan from Brooklyn. I was listening to <a href="https://twitter.com/marcoarment">Marco Arment</a>’s interview on the <a href="https://overcast.fm/itunes909109678/inquisitive">Inquisitive</a> podcast. The first question asked was <a href="https://overcast.fm/+Fg9IeSC2o/0:37">what he wanted to be known for</a><sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. His answer resonated with me. Marco would like to be known for his <strong>principles</strong>. Whether he is working on applications like <a href="http://instapaper.com">Instapaper</a> or <a href="http://overcast.fm">Overcast</a> is transient. The guiding principles with which he builds these projects don’t change. That’s how Marco would like to define himself.</p>
<p>This theme reminded of why I rarely follow brands on Twitter. I’d much rather follow the makers behind the products and services I use every day. I want to hear about their stories, lives beyond the office, and breakfast disasters.</p>
<blockquote class="twitter-tweet" lang="en"><p lang="en" dir="ltr">out of bowls, so this morning im having a plate of cheerios. 27 yrs old</p>— jacob <a href="https://twitter.com/fat/status/385409984638365696">October 2, 2013</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p><a href="https://twitter.com/_tessr">Tess Rinearson</a> sums up this trend of “personal branding” pretty well.</p>
<blockquote class="twitter-tweet" lang="en"><p lang="en" dir="ltr">twitter dot com, where people think they're brands and brands think they're people</p>— Tess Rinearson <a href="https://twitter.com/_tessr/status/636649966371344384">August 26, 2015</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>I don’t want to fit this mold. So, let’s get to know the <em>real</em> Jasdev. I’m constantly building <a href="https://www.coach.me/users/3140daf3d432d7f0065b/activity">habits</a>. My favorite emoji is 🚀. I used to compete on a <a href="https://www.youtube.com/watch?v=vXgF-Ezg78c&feature=youtu.be&t=15s">bhangra</a> team for three years. I probably follow more <a href="https://www.instagram.com/zoeydacorgi/">corgi accounts</a> on Instagram than human accounts. I’m currently making an effort to <a href="https://docs.google.com/spreadsheets/d/1_MuAkdaBHq5ib_fEJk6JBA4POMFk0BfIx8jRmbLXEmA/edit#gid=0">read</a> more books each year. <a href="https://twitter.com/jasdev/status/428517906813423616">Space will always fascinate me</a><sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>.</p>
<p>Now that you’ve learned a bit more about me, I want to hear about the real you online. That’s because beyond the “resume” lens of your past is an amazing person who deserves to be celebrated regularly 👏 Let’s sprinkle a bit more authenticity into the world.</p>
<hr />
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>Discussion ends around 4:25. The link is timestamped to the beginning of the question. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>I wanted to be an astronaut growing up! <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
One surprise from using the 24-hour format2015-12-31T00:00:00+00:00https://jasdev.me/24-hour-time<p>As an experiment, I switched the time format on my devices to 24-hour. The justification was my OCD with saving space on status bars and simpler time math.</p>
<p><img src="/public/images/watchos_face.png" alt="watchOS Watch Face" /></p>
<p><img src="/public/images/osx_status_bar.png" alt="OS X Menu Bar" /></p>
<p><img src="/public/images/ios_status_bar.png" alt="iOS Status Bar" /></p>
<p>However, the lack of AM and PM wasn’t the only benefit I noticed. My perception of time changed. When I glance at my watch in the afternoon and see 13:00, it’s much more <em>intimidating</em> than 1pm.</p>
<p>My brain now has this subconscious dialogue:</p>
<blockquote>
<p>“Holy crap! 13 hours of the day have gone by? Time to focus, Jasdev. You got this.”</p>
</blockquote>
<p>The 24-hour time format allows me to better grasp the length a calendar day, instead of splitting it up modulo 12. However, the tricky part in keeping this appreciation is that a large majority of clocks in U.S. default to 12-hour. In retrospect, it almost feels silly that we necessitate the use of AM and PM through the 12-hour format.</p>
The value of conferences2015-11-17T00:00:00+00:00https://jasdev.me/value-of-conferences<p>I’m a huge fan of <a href="https://twitter.com/seanarose">Sean Rose</a>. He is a forward-thinking mind in the world of product/platforms and is currently helping <a href="http://slackhq.com">Slack</a> on their mission to reinvent team messaging. One of his tweets made me think quite a bit about the value of conferences.</p>
<blockquote class="twitter-tweet" lang="en"><p lang="en" dir="ltr">In 99.9999% of cases, attending conferences in person is a waste of time.</p>— sean rose (@sean_a_rose) <a href="https://twitter.com/sean_a_rose/status/656895128573874176">October 21, 2015</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>I had recently been lucky enough to attend <a href="https://www.swiftsummit.com">Swift Summit</a>, a conference for <a href="https://developer.apple.com/swift/">Swift</a> developers in <a href="https://en.wikipedia.org/wiki/San_Francisco">San Francisco</a>. It was honestly one of the best decisions I’ve made as a young iOS developer. I wanted to offer a perspective on conferences that Sean’s tweet misses.</p>
<h2 id="humanizing-your-heroes"><a name="humanizing-heroes">Humanizing Your Heroes</a></h2>
<p>A quote I used to swear by is “keep working until your heroes become your peers.” I’ve have since modified my mindset towards this idea. Your heroes are really no different than you are. They may have a years of experience under their belts, <strong>but life is isn’t about Y-intercepts</strong>. <a href="https://twitter.com/sean_a_rose/status/631310981147271168">It’s all about your slope</a>. Conferences help remind me of this.</p>
<p>At Swift Summit, I met so many people I look up to including <a href="https://twitter.com/chriseidhof">Chris Eidhohf</a>, <a href="https://twitter.com/KrauseFx">Felix Krause</a>, <a href="https://twitter.com/soffes">Sam Soffes</a>, and <a href="https://twitter.com/chpwn">Grant Paul</a>. A few of us even got sushi together! It was like a Swift developer’s dream come true!</p>
<blockquote class="twitter-tweet" lang="en"><p lang="en" dir="ltr">Post-<a href="https://twitter.com/SwiftSummit">@SwiftSummit</a> sushi w/ <a href="https://twitter.com/chriseidhof">@chriseidhof</a>, <a href="https://twitter.com/KrauseFx">@KrauseFx</a>, and <a href="https://twitter.com/grp">@grp</a> 🙌 <a href="https://t.co/rI4GQqdB1s">pic.twitter.com/rI4GQqdB1s</a></p>— jasdev singh (@jasdev) <a href="https://twitter.com/jasdev/status/659934557484007424">October 30, 2015</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Meeting Chris, Felix, Sam, and Grant in person humanized my mental notion of them. Instead of only interacting with them on Twitter, I got to hear about their unfiltered day-to-day lives, fun hobbies, and work ethic. <a href="https://twitter.com/rrhoover">Ryan Hoover</a> recently relayed this idea in his <a href="https://medium.com/@rrhoover/everyone-is-human-d552a88e83f9">post</a> about the value of <a href="https://www.producthunt.com/live">Product Hunt LIVE Chats</a>.</p>
<blockquote>
<p>“Sometimes we’re given a chance to connect with our seemingly unreachable heroes thanks to the Internet, and recognize that they’re human, too.”</p>
<ul>
<li>Ryan Hoover</li>
</ul>
</blockquote>
<h2 id="fostering-community">Fostering Community</h2>
<p>Today most digital communities coalesce around forums, <a href="https://www.reddit.com/reddits/">subreddits</a>, <a href="http://www.chitchats.co">private Slack rooms</a>, and ad hoc Twitter circles. No matter how intimate these places are, nothing beats nerding out with a venue full of like-minded people. Every so often at Swift Summit, I’d look around and soak in the fact that I’m amongst a <em>portion</em> of the world’s Swift community and that I belong! This helps remedy the occasional loneliness that creeps up with being buried in <a href="https://developer.apple.com/xcode/">Xcode</a> all day.</p>
<p>These are just a few of the reasons I love attending conferences in person. While events like Swift Summit do a great job making all of the <a href="https://realm.io/news/swift-summit/">videos</a> accessible to those who cannot attend, I wish there was a way to lower cost as a barrier (potentially with attendee sponsorships). More frequent, local meet ups are also a way to tackle this :)</p>
Let’s get meta: Swift metatypes and cell reuse2015-11-16T00:00:00+00:00https://jasdev.me/lets-get-meta<p>For the past few weeks, I’ve been running a small project called <a href="https://twitter.com/publicextension">Public Extension</a>. Each weekday<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>, I come up with a handy Swift extension (to the <a href="http://swiftdoc.org">Standard Library</a>, UIKit, etc.) and tweet it out for everyone to follow along!</p>
<p>One particular extension has been on the back of my mind for a few days now. <a href="https://twitter.com/PublicExtension/status/656489984485146624">Public Extension #9</a> (initial draft below)</p>
<p><img src="http://i.imgur.com/ow2GPZp.png" alt="Reusable protocol and UITableView Extension" /></p>
<p>The goal was to make class registration for <code class="language-plaintext highlighter-rouge">UITableViewCell</code>s safer by avoiding a <code class="language-plaintext highlighter-rouge">String</code>ly-based API that requires code to be updated in multiple places, when reuse identifiers change. To tackle this, I took the following approach:</p>
<ul>
<li>Define a <code class="language-plaintext highlighter-rouge">Reusable</code> protocol that requires classes to have a static <code class="language-plaintext highlighter-rouge">reuseIdentifier</code> member</li>
<li>Wrap the traditional <code class="language-plaintext highlighter-rouge">registerClass(_:forCellReuseIdentifier:)</code> to accept a <code class="language-plaintext highlighter-rouge">Reusable.Type</code> metatype, which hides the previously-needed <code class="language-plaintext highlighter-rouge">forCellReuseIdentifier</code> parameter.</li>
<li>In my initial extension, I omitted the wrapper for <code class="language-plaintext highlighter-rouge">dequeueReusableCellWithIdentifier(_:)</code> and <code class="language-plaintext highlighter-rouge">dequeueReusableCellWithIdentifier(_:forIndexPath:)</code>, but we’ll discuss these soon!</li>
</ul>
<p><em>Note: This approach only works in code. Setting reuse identifiers in Storyboards requires you to manually enter a <code class="language-plaintext highlighter-rouge">String</code>.</em></p>
<p>Before diving in and refining the above approach, let’s take a step back and define what <code class="language-plaintext highlighter-rouge">Reusable.Type</code> and <code class="language-plaintext highlighter-rouge">CustomCell.self</code> mean in the snippet above. They’re making use of Swift’s <a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Types.html#//apple_ref/doc/uid/TP40014097-CH31-ID455">Metatype Type</a>.</p>
<h2 id="swift-metatype-type">Swift Metatype Type</h2>
<p>In <a href="https://itunes.apple.com/us/book/swift-programming-language/id881256329?mt=11">The Swift Programming Language</a> book, the metatype type is defined as follows:</p>
<blockquote>
<p>“A metatype type refers to the type of any type, including class types, structure types, enumeration types, and protocol types.”</p>
</blockquote>
<p>This is exactly what we need. Since our <code class="language-plaintext highlighter-rouge">Resuable</code> protocol defines <code class="language-plaintext highlighter-rouge">reuseIdentifier</code> as a <strong>static</strong> member, we can access it from the metatype of any class that conforms to it! To access a type as a value, we can use a postfix <code class="language-plaintext highlighter-rouge">self</code> expression (i.e. <code class="language-plaintext highlighter-rouge">.self</code>). For example, in our <code class="language-plaintext highlighter-rouge">CustomCell</code> class above, <code class="language-plaintext highlighter-rouge">CustomCell.self</code> returns <code class="language-plaintext highlighter-rouge">CustomCell.Type</code> not an instance of <code class="language-plaintext highlighter-rouge">CustomCell</code>.</p>
<h2 id="refining-the-approach">Refining the Approach</h2>
<p>Now that we’ve gone over metatypes, let’s make our approach to safe cell reuse better! We’ll keep the same <code class="language-plaintext highlighter-rouge">Reusable</code> protocol but redefine our <code class="language-plaintext highlighter-rouge">UITableView</code> extension as follows:</p>
<script src="https://gist.github.com/Jasdev/efe1feb9179fa1f8dd04.js?file=UITableView%2BReusable.swift"></script>
<p>While we could have used Swift’s error handling, cell misconfiguration is tricky to recover from. If misconfigured, it might be misleading to return a plain <code class="language-plaintext highlighter-rouge">UITableViewCell</code>. So, it’s probably better to lean towards a <code class="language-plaintext highlighter-rouge">fatalError</code> to catch these issues during development.</p>
<p>Note that the return types for the <code class="language-plaintext highlighter-rouge">dequeueReusable</code> overloads are non-optional. This is really handy because we no longer have to deal with awkward casts in <code class="language-plaintext highlighter-rouge">tableView(_:cellForRowAtIndexPath:)</code>. Here is how the call site might look:</p>
<script src="https://gist.github.com/Jasdev/efe1feb9179fa1f8dd04.js?file=SampleTableViewController.swift"></script>
<h3 id="extending-uicollectionview">Extending <code class="language-plaintext highlighter-rouge">UICollectionView</code></h3>
<p>We can apply this same pattern to <code class="language-plaintext highlighter-rouge">UICollectionView</code> as well:</p>
<script src="https://gist.github.com/Jasdev/efe1feb9179fa1f8dd04.js?file=UICollectionView%2BReusable.swift"></script>
<h2 id="a-note-on-cell-subclassing">A Note on Cell Subclassing</h2>
<p>One of my friends, <a href="https://twitter.com/Lumilux">Hans</a>, brought up a potential gotcha with regard to subclassing cells that implement <code class="language-plaintext highlighter-rouge">Reusable</code>.</p>
<blockquote class="twitter-tweet" data-conversation="none" lang="en"><p lang="en" dir="ltr"><a href="https://twitter.com/jasdev">@jasdev</a> <a href="https://twitter.com/PublicExtension">@PublicExtension</a> this doesn't allow for inheritance for classes that implement `Reusable`, right?</p>— Hans E Hyttinen (@Lumilux) <a href="https://twitter.com/Lumilux/status/662411481842065408">November 5, 2015</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>The <code class="language-plaintext highlighter-rouge">static</code> keyword in Swift translates to <code class="language-plaintext highlighter-rouge">class final</code><sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>, meaning that the property is attached to a class but cannot be overridden. However, when defining <code class="language-plaintext highlighter-rouge">Reusable</code>, we have to use the <code class="language-plaintext highlighter-rouge">static</code> keyword (even though we have the explicit <code class="language-plaintext highlighter-rouge">class</code> requirement on the protocol) because the class keyword is only allowed within <code class="language-plaintext highlighter-rouge">class</code> definitions. To allow for subclassing of cells that conform to <code class="language-plaintext highlighter-rouge">Reusable</code>, we can define <code class="language-plaintext highlighter-rouge">reuseIdentifier</code> as a computed<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup> <code class="language-plaintext highlighter-rouge">class var</code>.</p>
<script src="https://gist.github.com/Jasdev/efe1feb9179fa1f8dd04.js?file=CustomCell.swift"></script>
<p>It’s a little awkward that we can conform to a protocol (constrained to <code class="language-plaintext highlighter-rouge">class</code>) with a static requirement using a <code class="language-plaintext highlighter-rouge">class var</code>. Would love it if <a href="https://twitter.com/clattner_llvm">Chris Lattner</a> or <a href="https://twitter.com/jckarter">Joe Groff</a> could chime in on this!</p>
<h2 id="summary">Summary</h2>
<p>So, there we have it! We’ve abstracted away a <code class="language-plaintext highlighter-rouge">String</code>ly-based API for cell reuse in table and collection views. Now, if we ever need to change reuse identifiers, all we have to do is update the the cell definition. This benefits from code locality. I had also thought about an <code class="language-plaintext highlighter-rouge">enum</code>-based approach, but this would move reuse identifiers farther away from cell definitions, as <code class="language-plaintext highlighter-rouge">enum</code>s can’t be defined across multiple files. However, the <code class="language-plaintext highlighter-rouge">enum</code> approach has one advantage, which is guaranteeing unique reuse identifiers.</p>
<p>Hope this pattern is useful! If you have any feedback or suggestions, I’d love to <a href="https://twitter.com/jasdev">hear from you</a>!</p>
<hr />
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>Took a break between November 4th through 16th for job interviews :) <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>In the case of classes <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>Stored <code class="language-plaintext highlighter-rouge">class</code> properties aren’t supported yet. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Anchors2015-10-24T00:00:00+00:00https://jasdev.me/anchors<p>A mechanism for handling change is to focus on the constants in life. Let’s take a physical example of <a href="http://i.imgur.com/cwU2Iea.png">stretching your quadricep</a>, while standing on one leg. To keep your balance, it is helpful to focus on a single point in front of you. I have begun to notice this <em>anchor</em> pattern sprinkled throughout life.</p>
<ul>
<li>
<p>Difficult Decisions in the Workplace</p>
<p>When faced with a challenging decision at work, the core values upon which the company is built can help you reach the right choice. Care deeply about your users? Go with the option that puts them first instead of alternatives that increase runway at their inconvenience.</p>
</li>
<li>
<p>Meditation</p>
<p>A technique in meditation is to focus on one’s breath because it remains constant throughout the session. I try to imagine my mind as a highway, with each thought being a vehicle driving along it. Attempting to control a passing thought is like stopping traffic, which runs the risk of getting “hit.” Instead, I find it helpful to take a step back, label the thought for what it is (worry, stress, feeling), and return to my breath as an anchor.</p>
</li>
<li>
<p>Boredom</p>
<p>At its core, boredom is a dissatisfaction with the current state (surroundings, people, etc.), wishing to be in a more interesting one. Once I started viewing boredom through this lens, it became easier to address. When my mind wanders, I try to acknowledge the presence of boredom and return to the current state as a point of focus. If you want to learn more about this method, it is called <a href="http://www.insightmeditationcenter.org/books-articles/articles/mental-noting/">mental noting</a>.</p>
</li>
<li>
<p>Handling Habit Streaks</p>
<p>When I first <a href="/on-forming-habits">got into habit formation</a>, I <em>used</em> to focus on keeping “streaks.” This is known as the “<a href="http://jamesclear.com/stop-procrastinating-seinfeld-strategy">Seinfeld Strategy</a>,” where the fear of breaking a streak is <em>supposed</em> to motivate you to keep a habit. However, I found a big flaw with this approach. Breaking a streak can be tough to handle emotionally. Take exercise as an example. If I made it into the gym 20 days last month, it is tempting to focus on the days I missed going to the gym. To avoid this mental trap, I remind myself of the underlying goal that has not changed. My desire to build a better self. These goals serve as an anchor to handle the variability of life that may interrupt daily habits.</p>
</li>
</ul>
<p>It is fascinating how sometimes the best way to handle change is to focus on what stays the same. Notice this pattern in your life? <a href="https://twitter.com/jasdev">I would love to know</a>!</p>
Tracking as a catalyst for change2015-10-07T00:00:00+00:00https://jasdev.me/tracking-as-a-catalyst-for-change<p>When building a new habit, maintaining momentum is often the hardest part. Starting small helps, but I often need a larger impetus. To tackle this, I began tracking factors associated with each habit. For example, last year, I decided to spend less time on social media (primarily Twitter and Facebook). I did not take this habit seriously until I installed <a href="https://www.rescuetime.com">RescueTime</a> and saw how much time I was wasting. RescueTime is a small app that runs on your computer (and Android device), and tracks how much time you spend on specific applications and websites. Turns out, I was spending ~28 hours per month on social media alone! I was appalled by this realization. If this trend had continued, I would be wasting more than a day each month on social networks. I was driven to change.</p>
<p><img src="/public/images/rescuetime.png" alt="" />
<em>October 2014, so glad things have changed since then</em></p>
<p>Fast forward to today<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>, where I now average 11 hours on on social media per month (21 minutes a day). A 61% improvement over the past year! The key takeaway here is that although I did not have a target metric, the act of tracking itself persuaded me to change my habits.</p>
<p>Tracking has spilled into many other areas of my life:</p>
<ul>
<li>
<p>Mood</p>
<p>Over the summer, I was getting over a challenging breakup. To cope, I decided to actively track<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup> my mood using <a href="http://moodnotes.thriveport.com">Moodnotes</a>. Moodnotes is a thought journal that helps you log your mood and what influences it. This has allowed me to better be at ease with my emotions and prevent negative thoughts from snowballing.</p>
<p><img src="/public/images/moodnotes_trends.png" alt="" /></p>
</li>
<li>
<p>Food</p>
<p>I am consistent with exercise. However, my diet consists of an unclear strategy of picking “healthy” foods, without actually tracking macronutrients. To solidify this approach, I began tracking meals with <a href="https://www.myfitnesspal.com">MyFitnessPal</a>. Once my diet became balanced, I transitioned to using <a href="http://www.web.lark.com">Lark</a>. Lark is more personal, as it uses a virtual coach to cheer you on in making healthier choices.</p>
</li>
<li>
<p>Money</p>
<p><a href="https://itunes.apple.com/us/app/mint-quickview/id533491939?mt=12">Mint</a> will always be in my desktop menu bar. Having all financial accounts in a single dashboard is invaluable. For example, I quit coffee after realizing how much money I was wasting (~$100 a month), in addition to the benefits of a caffeine-free diet.</p>
</li>
<li>
<p>Age</p>
<p>I never truly appreciated how fast time moves until I installed Sam Soffes’ <a href="https://github.com/soffes/motivation">age ticker screensaver</a>. Watching my age tick upwards each time I opened my computer reminds me of life’s finiteness<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>. I even ported this concept to an <a href="https://github.com/jasdev/impulse">iOS app and Today Extension</a><sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup>!</p>
<p><img src="/public/images/age_ticker.gif" alt="" /></p>
</li>
</ul>
<h2 id="where-do-we-go-from-here">Where do we go from here?</h2>
<p>While tracking these domains is the first step, I believe the future of human improvement lies in aggregating them in a meaningful way. This is where we can make even deeper conclusions about our lives. Currently, my bet is with <a href="https://twitter.com/aprilzero">Anand Sharma</a> and the <a href="http://gyrosco.pe/">Gyroscope</a> team to solve this. Their site (and upcoming app) integrates with various services to present your life in a beautiful dashboard.</p>
<p><img src="/public/images/gyroscope_services.png" alt="" /></p>
<p>It is fascinating what you can do with all of this data in a <a href="https://gyrosco.pe/jasdev/">single place</a>. You can see <a href="https://gyrosco.pe/jasdev/explorer/">every place</a> I have been to since 2012<sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">5</a></sup> or check my most recent <a href="https://gyrosco.pe/jasdev/sport/">heart rate measurement</a>.</p>
<h2 id="final-thoughts">Final Thoughts</h2>
<p>While habit change often involves moving towards specific goals<sup id="fnref:6" role="doc-noteref"><a href="#fn:6" class="footnote" rel="footnote">6</a></sup>, tracking is often eye-opening enough to incite positive change. Insight on metrics I want to improve has enabled me to better coordinate my efforts. After all, you can’t reach your north star goals without establishing a frame of reference.</p>
<hr />
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>To accomplish this, I made heavy use of RescueTime’s push notifications (<a href="/public/images/rescuetime_alerts.png">enabling persistent alerts</a>) and used <a href="http://selfcontrolapp.com">SelfControl</a>. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>Tracking not only includes an overall mood but also details about recent events and tips on how to avoid common mental traps. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>I am a huge fan of <a href="https://twitter.com/jxxf">John Feminella</a>’s <a href="http://jxf.me/entries/time-passes/">post</a> on this subject. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:4" role="doc-endnote">
<p><a href="https://twitter.com/maccaw">Alex MacCaw</a> made a <a href="https://chrome.google.com/webstore/detail/motivation/ofdgfpchbidcgncgfpdlpclnpaemakoj">Chrome extension</a> for this concept as well! <a href="#fnref:4" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:5" role="doc-endnote">
<p>I started using <a href="https://www.swarmapp.com">Swarm</a> (previously bundled into <a href="https://foursquare.com">Foursquare</a>) in 2012. <a href="#fnref:5" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:6" role="doc-endnote">
<p>In these scenarios, I employ <a href="http://jamesclear.com/marginal-gains">Dave Brailsford’s 1% technique</a>. <a href="#fnref:6" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Made beds and cold showers2015-09-26T00:00:00+00:00https://jasdev.me/made-beds-and-cold-showers<p>A solid morning<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> routine is the secret weapon to making each day the best one yet. It can help set the tone for the rest of the day and allow you to hit the ground running. I wanted to talk about two parts of my morning routine that have made all the difference and an area I’m hoping to improve on.</p>
<h2 id="making-my-bed">Making My Bed</h2>
<p>Growing up, I never used to make my bed. My mom always nagged me about this, but I let this seemingly mundane task slide. That all changed when I watched Admiral William McRaven’s <a href="https://youtu.be/pxBQLFLei70?t=4m43s">commencement speech</a> at the University of Texas at Austin in 2014. He made two incredible points about taking this act seriously. First, it allows you to start your day with a small win. This momentum<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup> has helped me go on to tackle subsequent tasks. Secondly, no matter how “badly” your day goes, coming home to a bed that is made—that you made—gives you encouragement for tomorrow.</p>
<h2 id="cold-showers">Cold Showers</h2>
<p>Last week, I started a <a href="https://www.coach.me/plans/944-cold-shower">new goal</a> of taking a cold shower each day. While this may sound borderline crazy, it has helped in a couple of significant ways (in addition to its <a href="https://duckduckgo.com/?q=health+benefits+of+cold+showers">health benefits</a>). Cold showers allow me to conquer fear each day. There is no feeling quite like staring down your faucet on the coldest setting. Every instinct urging me to just cave in and revert to my warm shower days. That’s when I silence my inner fear and jump in. Moreover, cold showers wake you up like a double shot espresso without any of the calories or caffeine.</p>
<h2 id="room-for-improvement">Room for Improvement</h2>
<p>While making my bed and cold showers have helped bolster my morning routine, there is one aspect I would like to get better at. Not starting my day in a reactionary state. More specifically, I will often wake up, silence my alarm, and crawl back into bed checking Twitter and email. The problem with this is that it places my morning’s forcing function in the hands of others. Instead of starting the day on that project I’ve been working on, I might get derailed by replying to an email. A potential solution to this would be to set an iOS “Do Not Disturb” schedule that ends later in the morning. Allowing me to defer reactive activities until I have had time for myself. I am excited to make progress on this and will definitely write about what works in a future post!</p>
<p><img src="/public/images/dnd.png" alt="" /></p>
<p>The above habits are just a few<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup> that I have been doing and hope to improve on. Fine-tuning my morning routine has been worth its weight in gold. After all, the first step in building a better self is adjusting how you start each day.</p>
<p>Have aspects of your morning routine that you cannot live without? I’d love to <a href="https://twitter.com/jasdev">know about them</a>!</p>
<hr />
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>While “morning” generally means the first part of the day, I am using it to refer to the period when one wakes up. Everyone has their own sleep schedule that fits their needs. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>Momentum is a hell of a drug. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>My other morning habits include exercise, meditation, reading a <a href="/daily-list">daily list</a>, and reflecting on previous <a href="/small-moments">journal entries</a>. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
My favorite Xcode plugins and tweaks2015-09-23T00:00:00+00:00https://jasdev.me/xcode-plugins-and-tweaks<p><em>Updated 12/29/2015</em></p>
<p>Eight months ago, I switched to focusing on iOS and Swift development full-time. I’ve spent a lot of time trying out different plugins and tweaks to make Xcode feel like home. Below are a handful that I can’t live without:</p>
<h2 id="polychromatic">Polychromatic</h2>
<p><a href="https://github.com/kolinkrewinkel/Polychromatic">Polychromatic</a>, by <a href="https://twitter.com/kkrewink">@kkrewink</a>, takes Xcode coloring to the next logical level. Instead of assigning each token type with a fixed color, it “gives properties, ivars, and local variables each a unique, dynamic color, stripping away emphasis from types which don’t need it.”</p>
<p><em>Before</em></p>
<p><img src="/public/images/wo_polychromatic.png" alt="" /></p>
<p><em>After</em></p>
<p><img src="/public/images/w_polychromatic.png" alt="" /></p>
<h2 id="eppz-theme">eppz! Theme</h2>
<p>My favorite theme by far has to be <a href="https://github.com/eppz/iOS.Library.eppz_xCode">eppz</a>. It is easy on the eyes and looks beautiful (used in the “<em>Before</em>” screenshot above).</p>
<p>Tweaking eppz with Polychromatic was a bit tricky for me to get right. If you’re interested, <a href="/public/eppz!.dvtcolortheme">here</a> is my <code class="language-plaintext highlighter-rouge">.dvtcolortheme</code> file that you can import into Xcode by copying it to <code class="language-plaintext highlighter-rouge">~/Library/Developer/Xcode/UserData/FontAndColorThemes</code>.</p>
<h2 id="replacement-for-the-default-xcode-i-beam-cursor">Replacement for the Default Xcode I-Beam Cursor</h2>
<p>One of the problems with using darker Xcode themes is that the I-beam cursor is hard to find. <a href="https://twitter.com/stumbling_eric">@stumbling_eric</a> solved this. He made a <a href="https://github.com/egold/better-xcode-ibeam-cursor">replacement cursor</a> that is much easier to spot!</p>
<p><em>Before</em></p>
<p><img src="/public/images/dark_ibeam.png" alt="" /></p>
<p><em>After</em></p>
<p><img src="/public/images/light_ibeam.png" alt="" /></p>
<h2 id="xcode-todo-highlighting">Xcode TODO highlighting</h2>
<p>In-between commits, I often leave <code class="language-plaintext highlighter-rouge">TODO</code>s sprinkled in my code. To avoid letting them slip into the repo’s history, I prefer to have Xcode make them as noticeable as possible. Wouldn’t it be nice if Xcode highlighted them as warnings? <a href="https://twitter.com/jakemarsh">@jakemarsh</a> to the rescue! He wrote a <a href="https://deallocatedobjects.com/posts/show-todos-and-fixmes-as-warnings-in-xcode-4">post</a> on how to have Xcode highlight <code class="language-plaintext highlighter-rouge">TODO</code>s on a per-project basis.</p>
<p><img src="/public/images/todo_highlight.png" alt="" /></p>
<p><em>Note:</em></p>
<p>To make the script in the post work for Swift, you’ll want to add a <code class="language-plaintext highlighter-rouge">-or -name "*.swift"</code> clause in the <code class="language-plaintext highlighter-rouge">find</code> command.</p>
<h2 id="vvdocumenter">VVDocumenter</h2>
<p>I was recently introduced to <a href="https://github.com/onevcat/VVDocumenter-Xcode">VVDocumenter</a> and immediately fell in love. VVDocumenter generates documentation for functions (or any code) by simply typing <code class="language-plaintext highlighter-rouge">///</code>.</p>
<p><img src="http://i.imgur.com/S8zqem9.gif" alt="" /></p>
<h2 id="remap-show-document-items-shortcut">Remap “Show Document Items” Shortcut</h2>
<p><img src="/public/images/document_items.png" alt="" /></p>
<p>A wonderful feature of Xcode is the document outline that can be toggled from the toolbar or by pressing <code class="language-plaintext highlighter-rouge">⌃6</code>. However, the default shortcut is hard to reach, so I usually remap it to <code class="language-plaintext highlighter-rouge">⌃`</code>. This setting can be changed via Preferences > Key Bindings and then searching for“Show Document Items”.</p>
<p><img src="/public/images/document_items_setting.png" alt="" /></p>
<h2 id="custom-xcode-behavior-on-build-success-and-failure">Custom Xcode Behavior on Build Success and Failure</h2>
<p>Have a project with long compile times? Chances are you context switch while building. I’ve often caught myself getting distracted way after my build finishes. To prevent this, I added a custom Xcode behavior to make a sound when builds finish!</p>
<blockquote class="twitter-tweet" lang="en"><p lang="en" dir="ltr">Xcode behavior to play a sound when builds finish -> less wasted time when context switching during compilation <a href="https://t.co/9kXWuXjhQZ">pic.twitter.com/9kXWuXjhQZ</a></p>— jasdev singh (@jasdev) <a href="https://twitter.com/jasdev/status/675345062058921984">December 11, 2015</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Have any Xcode plugins or tweaks you love? <a href="https://twitter.com/jasdev">Let me know</a>!</p>
Always-on conversations2015-09-19T00:00:00+00:00https://jasdev.me/always-on-conversations<p>Growing up, I frequently participated in outdoor activities like sports and skateboarding. However, a large portion of my childhood was spent indoors in online communities with always-on conversations. Whether it was designing forum signatures for fellow members in the <a href="https://en.wikipedia.org/wiki/Halo_(series)">Halo</a> Forums or learning how to install <a href="https://en.wikipedia.org/wiki/Modchip">modchips</a> from Xbox modding threads, I was always fascinated by these online worlds. Whenever I felt lonely, I tried to turn things around by jumping into these conversations to learn and grow with my Internet friends.</p>
<p>Fast-forward to my time in college—I unfortunately lost touch with my online friends as life started moving faster. However, in February 2013, something amazing started. Eight close friends and I created a Facebook <a href="https://www.messenger.com">Messenger</a> thread called “Awesomest Turntable DJs”<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> (a reference to an old music streaming service, <a href="https://en.wikipedia.org/wiki/Turntable.fm">Turntable.fm</a>). Roughly three years later, we have sent over 166k messages (an average of 173 per day). While some may consider it distracting, this thread has helped me through the toughest times of my life so far.</p>
<p><img src="/public/images/tnn_members.png" alt="" /></p>
<p>Despite graduation and life moving us across the country<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>, this thread has kept us close. We have supported each other through breakups, family losses, near-tragic bike accidents, and the troubles that blindside you at <a href="http://www.davidpbrown.co.uk/poetry/mary-schmich.html">4pm on some idle Tuesday</a>. We are also a cheering squad for one another. There is no better feeling than sharing a win with your closest friends and being met with a flood of <a href="https://en.wikipedia.org/wiki/Minions_(film)">Minions</a> stickers. Below are a handful of moments I wanted to highlight from our thread:</p>
<hr />
<p><img src="/public/images/tnn_support.jpg" alt="" /></p>
<hr />
<p><img src="/public/images/tnn_support_2.png" alt="" /></p>
<hr />
<p><img src="/public/images/tnn_love.jpg" alt="" /></p>
<hr />
<p>Balance is key here. When the messages get too noisy, I take a step back and mute the thread for a few hours. This allows me to find my own headspace. That’s the beauty of asynchronous communication!</p>
<p>Always-on conversations make life better and I am beyond grateful for the close friends I have. While today’s generation is often portrayed with their heads down buried in their phones, it is helpful to reframe the picture. That person on the subway glued to their phone might be going through a rough time and is using this connectivity to stay close to those who aren’t physically present ❤️</p>
<hr />
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>We often refer to the thread as “TNN” (Turntable News Network) for short. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>Virginia, California, Oregon, Illinois, North Carolina, and New York <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
One month at Recurse Center2015-09-12T00:00:00+00:00https://jasdev.me/one-month-at-recurse-center<p>In a previous post on the <a href="/lessons-after-college/">lessons I’ve learned after college</a>, I mentioned time moves faster outside of school. This has held true for my first month at <a href="https://www.recurse.com">Recurse Center</a> (formerly known as <a href="https://www.recurse.com/blog/77-hacker-school-is-now-the-recurse-center">Hacker School</a>). For those not familiar with the program, it is a free, three-month, self-directed programming retreat in <a href="https://foursquare.com/v/recurse-center/4f3933eae4b017ad7cdce1fd">New York City</a>. Attendees are programmers looking to hone in on their skills, whether they have been coding for three decades or three months. I wanted to use this post to reflect on what I have learned so far, talk about the experience, and look ahead to my remaining months here.</p>
<h2 id="progress-so-far">Progress so Far</h2>
<h4 id="personal-projects">Personal Projects</h4>
<p>Instead of taking a new job after my time working at <a href="http://imgur.com">Imgur</a>, I decided to attend RC to tackle my growing list of side projects. So far, I’ve built two small apps in Swift 2.0. Now that Xcode 7 has hit its <a href="https://twitter.com/clattner_llvm/status/641697365003341824">GM release</a>, I’ll be submitting them to the store soon! The first app is called Rest. It is a simple bedtime calculator (much like <a href="http://sleepyti.me">sleepyti.me</a>) that helps you stay in sync with the recommended number of sleep cycles per night (5-6). The second app is called Trigger. Trigger is the habit reminder I’ve always wanted. After <a href="/on-forming-habits">using a habit tracker for the past three years</a>, I’ve learned that a key component of habit formation is the use of reminders to ’trigger’ specific actions. In my experience, a single scheduled reminder rarely suffices. This is where Trigger shines. Trigger allows you to set multiple reminders, scheduled or random, and even use geo-fences for location-based habits. It has been a blast building these two apps and they allowed me to dive deeper into some iOS frameworks I have always wanted to learn.</p>
<h4 id="swift-20">Swift 2.0</h4>
<p><a href="https://developer.apple.com/swift/blog/?id=29">It is an exciting time to be a Swift developer</a>. With the language, standard library, and compiler going open source (plus Linux compatibility) later this fall, I’m really eager to see how it shakes up the developer landscape over the next 10 years. The two apps I mentioned above were built entirely in Swift 2.0. This version of the language has brought many features <a href="https://twitter.com/jasdev/status/642051281952088064">I love</a> including <code class="language-plaintext highlighter-rouge">guard</code> and <code class="language-plaintext highlighter-rouge">defer</code> statements, protocol extensions, and better error handling. As cheesy as it sounds, Swift has rekindled my love for programming, and it seems like <a href="http://stackoverflow.com/research/developer-survey-2015#tech-super">others feel the same way</a>.</p>
<h4 id="recurse-to-fill-in-gaps">“Recurse” to Fill in Gaps</h4>
<p>Since RC is a self-directed retreat, it has given me time to fill in knowledge gaps. For example, some parts of the <a href="https://en.wikipedia.org/wiki/Git_(software)">git</a> workflow have always seemed magical to me. How does git actually implement merging? What does a ’detached <code class="language-plaintext highlighter-rouge">HEAD</code> state’ really mean and why does it sound so scary? These are all questions I’d feel embarrassed asking in a traditional work setting, but they’re highly encouraged at Recurse Center. In fact, one of the amazing facilitators, <a href="https://twitter.com/maryrosecook">Mary Rose Cook</a>, hosted a session on how git works under the hood and helped clear up any remaining questions I had!</p>
<h4 id="teaching-others">Teaching Others</h4>
<p>In my current batch, there are two other attendees (<a href="https://twitter.com/Caroline5">Caroline</a> and <a href="https://twitter.com/dalequi">David</a> 👋) writing Swift full-time. Although I started writing Swift about seven months ago, it’s been an amazing experience helping them get started with the language. Taking concepts that I’ve only internalized and explaining them via code snippets or diagrams has done wonders in helping me <a href="http://jamesclear.com/fundamentals">master the fundamentals</a>. Teaching, itself, is a great way to learn.</p>
<h2 id="intrinsic-motivation-and-work-cadence">Intrinsic Motivation and Work Cadence</h2>
<h4 id="recurse-center-doesnt-feel-like-traditional-work">Recurse Center Doesn’t Feel like “Traditional” Work</h4>
<p>Recurse Center places a heavy emphasis on intrinsic motivation. Want to learn that framework you keep hearing about? Go for it! Have you learned all you can from your current project? Move onto the next one! Without traditional driving factors like deadlines or user metrics, you begin to program for the sake of becoming a better programmer. This makes RC feel better than “traditional” work. I often find myself coming into the space at odd hours, simply because I am itching to get back to my projects.</p>
<h4 id="energy-management"><a name="energy-management">Energy Management</a></h4>
<p>Since the majority of my day is spent programming, paired with the fact that I <a href="https://www.coach.me/plans/1098-no-caffeine">eliminated caffeine</a> from my diet four months ago, I have learned a lot about managing my energy levels. <em>Actually</em> programming for eight hours <strong>straight</strong> each day is a myth and unsustainable. I’ve been able to refocus midday by taking breaks to work out and tweaking my sleep schedule (RC actually has both 10:30am and 1pm check-ins to cater to different routines). On the same note, one of my great friends, <a href="https://twitter.com/ZackShapiro">Zack Shapiro</a>, recently made a <a href="https://twitter.com/ZackShapiro/status/641425157425377280">tweet storm</a> about putting your health first and work second:</p>
<p><img src="/public/images/zack-tweet-storm.png" alt="" /></p>
<h2 id="community">Community</h2>
<h4 id="no-better-way-to-move-to-a-new-city">No Better Way to Move to a New City</h4>
<p>New cities are always daunting. Before making the move to <a href="https://en.wikipedia.org/wiki/Manhattan">Manhattan</a>, I grew up and went to school in <a href="https://en.wikipedia.org/wiki/Virginia">Virginia</a> and lived in <a href="https://en.wikipedia.org/wiki/San_Francisco">San Francisco</a> for a year and a half after graduating. Despite having a small friend group here, it’s really comforting to be making this adjustment with ~30 people in the same situation. I could not imagine a better group of people to move into the city with ❤️</p>
<h4 id="zulip-group">Zulip Group</h4>
<p>Internally, we use a chat program called <a href="https://zulip.com/hello/">Zulip</a>. Aside from solving a lot of the noise issues that <a href="http://www.chitchats.co">non-enterprise Slack channels</a> face, I’m continually impressed by how active the alumni community is. Whether you need help with the specifics of <a href="https://www.docker.com">Docker</a> or handling a tough situation at work, there is always someone online to help.</p>
<h4 id="a-slice-of-a-better-world">A Slice of a Better World</h4>
<p>Recurse Center’s <a href="https://www.recurse.com/manual#sub-sec-social-rules">social rules</a> have honestly made the experience infinitely better. At a high-level, the four rules are no feigning surprise, no well-actually’s, no back-seat driving, and no subtle -isms. The rules are “meant to be lightweight, and to make more <em>explicit</em> certain social norms that are normally <em>implicit</em>.” The result of these rules is an inclusive space where the top priority is learning. It has also been refreshing to see local meetups, like <a href="http://hackandtell.org">Hack && Tell</a>, adopt these rules in their Code of Conduct!</p>
<h2 id="looking-ahead">Looking Ahead</h2>
<p>Given my experience so far, there are two small adjustments I’d like to make.</p>
<h4 id="focus">Focus</h4>
<p>During my first month here in NYC, I’ve had trouble balancing social events with progress on my projects. While I absolutely love attending meetups and seeing what the city has to offer, I would like to strike more of a balance. This has been particularly draining because I tend to be more introverted. Moving forward, I’m going to spend more time with my <a href="https://twitter.com/sama/status/630869228137127936">head down</a> moving towards my goals, while maintaining a <em>healthier</em> amount of social activities.</p>
<h4 id="more-technical-writing">More Technical Writing</h4>
<p>Lastly, I’d like to do more technical writing. This past week, I wrote my first Swift post on <a href="/ios-interactive-notifications">interactive iOS notifications</a>, which truly helped me iron out my knowledge of the subject. To make things even better, one of my idols, <a href="https://twitter.com/natashatherobot">Natasha Murashev</a>, added the post to her <a href="https://swiftnews.curated.co/issues/52#start">weekly newsletter</a>!</p>
<hr />
<p>That’s all for this month’s checkin. I’ll be writing reflective posts like this one in October and November!</p>
A tour of iOS interactive notifications2015-09-05T00:00:00+00:00https://jasdev.me/ios-interactive-notifications<p>Starting in iOS 8, Apple opened up APIs to allow actions to be embedded in notifications. Moreover, in the upcoming release of iOS 9, these actions can now accept text input as opposed to simply being buttons (this had previously been restricted to system apps, like Messages). In a recent project of mine at <a href="https://www.recurse.com">Recurse Center</a>, I got the chance to use these notification actions and wanted to create a post on how to incorporate them into your existing apps!</p>
<p><img src="/public/images/reply.png" alt="" />
<img src="/public/images/textinput.png" alt="" /></p>
<p><em>Note: The example below uses local notifications, but there are analogs for their remote counterparts.</em></p>
<h2 id="request-permissions">Request Permissions</h2>
<p>These actions must be registered upfront alongside the normal notifications permissions. For the sake of this example, I’m going to place this logic in my <code class="language-plaintext highlighter-rouge">AppDelegate</code>. However, in a larger project, I’d recommend factoring this out into a Notification helper class.</p>
<p>Let’s start by building two instances of <code class="language-plaintext highlighter-rouge">UIMutableUserNotificationAction</code> and attaching them to a <code class="language-plaintext highlighter-rouge">UIMutableUserNotificationCategory</code>. To allow text input on the <code class="language-plaintext highlighter-rouge">replyAction</code>, note that the <code class="language-plaintext highlighter-rouge">behavior</code> property is set to <code class="language-plaintext highlighter-rouge">.TextInput</code>.</p>
<script src="https://gist.github.com/Jasdev/8eae09fb1efcb79019b7.js?file=AppDelegate.swift"></script>
<p>That’s all you need for permissions! Let’s schedule a sample notification to see the results and implement the appropriate <code class="language-plaintext highlighter-rouge">UIApplicationDelegate</code> methods to handle them. I make use of the handy Pod, <a href="https://github.com/naoty/Timepiece">Timepiece</a>, which adds syntactic sugar to <code class="language-plaintext highlighter-rouge">NSDate</code>.</p>
<script src="https://gist.github.com/Jasdev/8eae09fb1efcb79019b7.js?file=NotificationScheduler.swift"></script>
<p>Swiping down on the notification when the app is backgrounded.</p>
<p><img src="/public/images/actions.png" alt="" /></p>
<p>To handle notifications when the app is foregrounded, implement the <a href="https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/index.html#//apple_ref/occ/intfm/UIApplicationDelegate/application:didReceiveLocalNotification:"><code class="language-plaintext highlighter-rouge">application(_:didReceiveLocalNotification:)</code></a> method in the <code class="language-plaintext highlighter-rouge">UIApplicationDelegate</code> protocol).</p>
<p>UI for text input reply flow
<img src="/public/images/replyaction.png" alt="" /></p>
<h2 id="handling-notification-actions">Handling Notification Actions</h2>
<p>Next, we’ll implement the <code class="language-plaintext highlighter-rouge">UIApplicationDelegate</code> protocol method for handling notification actions. To access the user-inputted text, we key into <code class="language-plaintext highlighter-rouge">responseInfo</code> with the <code class="language-plaintext highlighter-rouge">UIUserNotificationActionResponseTypedTextKey</code> constant. Also, the <code class="language-plaintext highlighter-rouge">Notifications.Actions</code> enum we defined earlier combined with a guard at the top of the function makes the subsequent <code class="language-plaintext highlighter-rouge">switch</code> very clean!</p>
<script src="https://gist.github.com/Jasdev/8eae09fb1efcb79019b7.js?file=AppDelegateContinued.swift"></script>
<p>That’s it! As an additional note, if you happened to set your <code class="language-plaintext highlighter-rouge">UIMutableUserNotificationAction</code>’s <code class="language-plaintext highlighter-rouge">activationMode</code> to <code class="language-plaintext highlighter-rouge">.Foreground</code>, the way to handle this flow on application launch is as follows:</p>
<script src="https://gist.github.com/Jasdev/8eae09fb1efcb79019b7.js?file=AppDelegateAside.swift"></script>
<p>You can reference the full source of this post <a href="https://gist.github.com/Jasdev/8eae09fb1efcb79019b7">here</a> and for more info, check out the <a href="https://developer.apple.com/videos/wwdc/2015/?id=720">“What’s New in Notifications” WWDC 2015</a> session.</p>
<p>Enjoyed this post? <a href="https://twitter.com/intent/tweet?text=A%20Tour%20of%20iOS%20Interactive%20Notifications%20by%20%40jasdev%20-%20http%3A%2F%2Fjasdev.me%2Fios-interactive-notifications%2F">Let me know</a>!</p>
Lessons after college2015-08-31T00:00:00+00:00https://jasdev.me/lessons-after-college<p>1 year, 3 months, and 13 days ago I graduated from college. I feel the transition from high school to college is openly discussed, but the following transition into the real world is not. Life after school has taught me a lot about myself and I am writing this post as a reflection on some of the lessons I have learned (a few the hard way).</p>
<h2 id="everyone-is-making-it-up-as-they-go"><a name="making">Everyone Is Making It up as They Go</a></h2>
<p>There is no manual to life and how you should live it. Your parents, idols, mentors, managers, and favorite celebrities are all making it up as they go. That is the beauty of it! It is yours to define. All of your past experiences, successes, and failures have brought you to this exact moment.</p>
<h2 id="time-moves-way-faster-after-school">Time Moves Way Faster After School</h2>
<p>It seriously does. People claim that college will be the best four years of your life and that it moves quickly. I have two reactions to this. First, why upper bound your “best years?” College is an amazing time, but it should also be a launchpad into your best years down the road. Secondly, as the president of <a href="http://www.ycombinator.com">Y Combinator</a>, <a href="https://twitter.com/sama">Sam Altman</a>, mentioned in his <a href="http://blog.samaltman.com/the-days-are-long-but-the-decades-are-short">post on turning 30</a>: “the days are long but the decades are short.” Years following school fly by way faster than those in school.</p>
<h2 id="mental-health-is-just-as-important-as-physical-health">Mental Health Is Just as Important as Physical Health</h2>
<p>We tend to obsess over eating the right things, working out, and making healthier physical decisions. We need to do the same for our minds. Life after college is challenging. Find ways to be at ease with your mind and thoughts. <a href="https://www.headspace.com">Meditate</a>. <a href="http://moodnotes.thriveport.com">Track your mood</a>. Call an old friend who moved far away after graduation. FaceTime your parents and siblings. Smile while commuting to work. Say hi to a stranger. Mental health is much easier to handle in a preventive fashion. It is not weird or a sign of failure to do these things. By learning to manage thoughts and emotions, mindfulness is worth its weight in gold.</p>
<p>In a previous <a href="http://jasdev.me/remote-work-and-loneliness">post about loneliness</a>. I failed to mention how normal these feelings are. This is especially true after graduation. You will be lonely at times in this new chapter. However, the irony of loneliness is that we all share it from time to time.</p>
<h2 id="burnout-is-real">Burnout Is Real</h2>
<p>Related to mental health, make sure to pace yourself in your work. There are no speed limits after exiting the highway that is college (more on this metaphor in a bit). Anyone who tells you that you should be firing on all cylinders 24/7 is on track for burnout. Make sure to plan time for fun. Shut down your computer after work. Revisit an old video game you loved as a kid. While we often praise the hard worker who stays in the office until midnight, we overlook the fact that having a proper work cadence is the key to a long-lasting career.</p>
<h2 id="optimize-for-learning">Optimize for Learning</h2>
<p>After college, you might not exactly know your niche. That is perfectly fine. In this case, I tend to optimize for learning. Will that job at a startup pay less than the 800-pound gorilla but teach you more? Go with the startup. Your first job won’t define your entire career. Lean towards opportunities that push you outside your comfort zone.</p>
<h2 id="your-peers-are-all-on-their-own-trajectories"><a name="trajectories">Your Peers Are All on Their Own Trajectories</a></h2>
<p>In school, it’s too easy to fall into the trap of <strong>falsely</strong> comparing yourself to others. Whether it’s by GPA or courses taken, these metrics are not clean-cut. Moreover, after school, everyone embarks on their own trajectories and you need to make sure not to juxtapose them. Life is a race against yourself.</p>
<p>Additionally, just because someone has years of prior experience doesn’t mean they can’t one day be your peer. “<a href="https://twitter.com/sean_a_rose/status/631310981147271168">A little bit of slope makes up for a lot of y-intercept</a>.”</p>
<p><img src="/public/images/slope.png" alt="" /></p>
<p><sup><em>From a lecture by Professor John Ousterhout</em></sup></p>
<h2 id="establish-keystone-habits">Establish Keystone Habits</h2>
<p><a href="http://jamesclear.com/keystone-habits">Keystone habits</a> are those that keep you due north, when everything else in life may appear to be going south. A common example is exercise. When you exercise regularly, you tend to sleep, eat, and feel better. Some of my keystone habits include <a href="https://www.coach.me/plans/1090-write-three-positive-things-about-today">writing three positive things about each day</a> (which later turned into a <a href="http://jasdev.me/small-moments">habit of journaling</a>), meditation, and reading.</p>
<h2 id="no-matter-what-happens-itll-be-just-right">No Matter What Happens, It’ll Be Just Right</h2>
<p>Transitioning from college to the real world often leaves some aspects of your life in a difficult situation. Will you still be close to friends who are moving to a different city? Should you try a long-distance relationship with your significant other? I have coped with these difficult questions by being at ease with all possible outcomes. Some friends will naturally fall out of touch and that’s perfectly fine. Hold onto those that stay close. Long-distance relationships can work, but if they don’t, you’ll know right away. No matter what happens, it’ll be just right.</p>
<h2 id="take-time-to-reflect">Take Time to Reflect</h2>
<p>You know that <a href="http://thinkexist.com/quotation/the_best_time_to_plant_a_tree_is_twenty_years_ago/254949.html">quote about the second best time to plant a tree being now</a>? The same applies to journaling your thoughts, feelings, and smaller moments in life. The only regret I have about actively <a href="http://dayoneapp.com">journaling</a> is the fact that I didn’t start earlier. Make sure to also reflect on your previous entries. It can often be a great reminder of how far you’ve come.</p>
<h2 id="there-are-no-guardrails">There Are No Guardrails</h2>
<p>I like to think of college as a highway that you and your peers are driving along. You have the safe comfort of guardrails to keep you on track. Graduation is much like taking an exit in this regard. No one will tell you what you should do, when you should do it, or how to approach it. You are own your own now. Maybe you’ll blaze a new trail. Maybe you’ll find yourself at an intersection with an old friend. In either case, take care of your vehicle, visit rest stops, and enjoy the journey!</p>
Remote work and loneliness2015-08-22T00:00:00+00:00https://jasdev.me/remote-work-and-loneliness<p>From a previous internship and my most recent job, I have been fortunate enough to be allowed to work remotely for extended periods of time. While the benefits of remote work are widely discussed, I wanted to talk about one of the downsides I struggled with the most: loneliness. This is a really personal topic and definitely hard to write about. However, I hope the lessons I have learned can help others who are dealing with (or are worried about) loneliness in a remote setting.</p>
<h2 id="not-being-around-others">Not Being Around Others</h2>
<p>One of the most immediate hurdles is the fact that you will no longer be around your teammates. This can be especially difficult if a remote culture isn’t baked into your company, where you might feel like an outlier during in-person events such as all-hands meetings or social gatherings. To remedy this, I’ve tried a few things:</p>
<ul>
<li>
<p>Use <a href="https://www.sqwiggle.com">Sqwiggle</a></p>
<p>One of the easiest ways to make your teammates see the person behind your avatar in chat is to establish a method of ambient presence. This doesn’t have to be as daunting as a live stream of your workspace. <a href="https://www.sqwiggle.com">Sqwiggle</a> shines in this regard. The app takes pictures of you at regular intervals (one or five minutes, with manual override) to help your team feel more connected. I can’t recommend it enough. It is amazing how much of an effect seeing your coworkers faces each day has on your overall mood.</p>
<p><img src="/public/images/sqwiggle.jpg" alt="" /></p>
</li>
<li>
<p>Weekly Chats</p>
<p>I actually borrowed this technique from the wonderful folks over at <a href="https://blog.bufferapp.com">Buffer</a>. Every week, I would set up 15-20 minute calls with a specific teammate. A strict rule during these calls is that we would discuss anything except work. Not only did this help tackle loneliness, it allowed me to get to know my teammates on a more personal level (works well with meeting new hires too).</p>
</li>
<li>
<p>Meals With Friends</p>
<p>Another way to get out of your typical workspace is to schedule meals with friends in the area. This is perfect for catching up with old friends and helped me avoid the trap of simply eating lunch at my desk.</p>
</li>
<li>
<p>Coworking Spaces</p>
<p>Nearby coworking spaces can be a silver bullet in combatting loneliness. This is one resource I wish I had used more during my time working remotely.</p>
</li>
</ul>
<h2 id="missing-out-on-offline-communication-channels">Missing out on Offline Communication Channels</h2>
<p>In both of my remote experiences, the companies were onsite by default. This leads to challenges regarding offline communication (in-person chats, impromptu meetings, etc.) that must be overcome. In handling these challenges, the goal should be to default to online communication and reach a level where there is no advantage to being in the office. Not only does this help remote employees, it helps those onsite by making all communication searchable and documented in a manner accessible to all employees. One specific difficulty I wanted to highlight is accidentally not being called into meetings. To solve this, my previous team added Google Hangout links to all calendar invites, which worked really well. Additionally, I made sure to be vocal if I wasn’t called into a meeting before it started. Don’t be shy. You won’t be rude by doing so and it will help make calling in remote employees more habitual for those in the office.</p>
<h2 id="cabin-fever-and-burnout">Cabin Fever and Burnout</h2>
<p>Mental health is critical, especially when working remotely, and I wish it was discussed more openly. To help stay in check, I have used meditation, mood logging, and hour tracking.</p>
<ul>
<li>
<p>Meditation</p>
<p>After struggling to meditate regularly for the past couple of years, I finally hit my rhythm with <a href="https://www.headspace.com">Headspace</a>. It is an amazing app that guides you through meditation and presents it in a non-spiritual manner that is approachable. Starting to meditate has honestly been one of the best decisions of my life. It has allowed me to start each day with clarity and benefitted my personal relationships in ways I couldn’t even imagine. Relating to remote work, this will help prevent your emotions from snowballing internally, in the absence of others being around you for emotional support.</p>
</li>
<li>
<p>Mood Logging</p>
<p>One of the lessons in Headspace is that you should only treat thoughts for what they are, thoughts! In practice, this means that you should try<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> not to let thoughts become larger than they really are. To do this, it is helpful to label thoughts when they arise. For example, is the thought a worry about the future, a feeling, or just thinking? After labeling the thought, I try to imagine the thought as being a glass ball floating in space. In labeling it, I’m gently pushing it away with a feather. This metaphor serves as a way to mentally separate myself from my thoughts. I recently discovered an app that encapsulates this process called <a href="http://moodnotes.thriveport.com">Moodnotes</a>. After a week of use, it has already earned a spot on my home row!</p>
<p><img src="/public/images/moodnotes.png" alt="" /></p>
</li>
<li>
<p>Shutting Down My Computer</p>
<p>One thing that surprised me when I initially started working remotely was that I actually worked more than I previously did onsite. I would often let work-related tasks slip into my personal life (exaggerated by time zone differences). Answering that one email after hours or getting extra development time at night. While these small occurrences may seem harmless, they quickly add up and contribute to burnout. This is when I started to become strict about shutting down my computer after work. I wouldn’t reply to non-urgent messages after signing off either. While this may require an adjustment period, it helped my previous teams rely on asynchronous communication, which plays well with time zones and honoring people’s periods of productivity.</p>
</li>
</ul>
<p>All in all, I am a huge fan of remote work. It affords you and your team many benefits that just aren’t possible when requiring everyone to be under the same roof. However, loneliness is a hurdle that needs to be discussed openly and overcome. I hope some of the lessons and techniques I learned can help in your journey. Have any that I missed? <a href="https://twitter.com/jasdev">Let me know!</a></p>
<hr />
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>Easier said than done, of course! <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Managing my Pocket queue2015-08-09T00:00:00+00:00https://jasdev.me/managing-my-pocket-queue<p>Starting in 2015, I made a <a href="https://www.coach.me/plans/904-read">goal</a> to actively read more. I approached this in two directions: traditional books<sup>1</sup> and trekking through my <a href="https://getpocket.com">Pocket</a> queue. For those who aren’t familiar with Pocket, it’s an app that allows you to save online articles and videos for later consumption (even offline). Moreover, it has an amazing interface which makes reading web content feel comparable to an eBook.</p>
<p>One issue I have always faced with Pocket is the fact that deferring content to read later<sup>2</sup> often meant that I never got to it or read it <em>much</em> later, when no longer relevant. In the past few weeks, I have been able to take my Pocket queue from well over 400 items to 100. Here are some techniques that might help in your quest to “Pocket Zero”!</p>
<h3 id="aim-for-a-decreasing-number-of-items-each-day">Aim for a Decreasing Number of Items Each Day</h3>
<p>In any given day, I try to read more items than I save. A simple rule for accomplishing this is “for every item you save to the queue, read two.”</p>
<h3 id="use-badge-counts">Use Badge Counts</h3>
<p><img src="/public/images/pocket-badge.png" alt="" /></p>
<p>Badge counts help me keep track of my queue totals (and the above technique). However, I always have to remind myself that this number shouldn’t be discouraging. It’s just a mile marker. For each decrement, I’m one item closer to my goal.</p>
<h3 id="sort-by-oldest-first-to-trim-irrelevant-items">Sort by Oldest First to Trim Irrelevant Items</h3>
<p><img src="/public/images/pocket-sort.png" alt="" /></p>
<p>Every so often, I’ll reverse the sort order of my queue to trim older or irrelevant items. No longer interested in the topic? Archive. Out of date? Archive. Broken URL? Archive.</p>
<h3 id="watch-video-content-on-commutes-and-workouts">Watch Video Content on Commutes and Workouts</h3>
<p>Unfortunately, I get motion sick when reading in moving vehicles. So, I’ve often resorted to watching videos during commutes<sup>3</sup>. Another great time I’ve found for knocking out video content is during my cardio workouts (on stationary machines). It helps the time pass so quickly!</p>
<h3 id="use-the-quick-reads-feature-in-the-app">Use the “Quick Reads” Feature in the App</h3>
<p><img src="/public/images/pocket-quick-reads.png" alt="" /></p>
<p>When the day is coming to an end and I haven’t gotten any reading in, this feature is a lifesaver. As the name implies, it’s a collection of the shortest items in your queue. Any progress is better than no progress.</p>
<h3 id="reflect-on-what-you-have-read-for-the-day">Reflect on What You Have Read for the Day</h3>
<p>Before going to bed each night, I’ve started to look back at my most recently archived items. Reviewing items helps with <a href="https://en.wikipedia.org/wiki/Spaced_repetition">spaced repetition</a> of their core ideas and better persists them into memory. Moreover, this fits snugly next to my habit of <a href="/small-moments">journaling</a>.</p>
<h3 id="you-dont-have-to-read-everything-in-your-queue-life-goes-on">You Don’t Have to Read Everything in Your Queue. Life Goes On!</h3>
<p>At the end of the day, a Pocket queue is exactly that, a queue! It’ll be there whenever you have an idle moment or an itch to read. Don’t put life on pause just to clear Pocket, it’s there for you to read <em>later</em> :)</p>
<p>Hope these techniques help you get the most out of Pocket! Have any that you use personally? <a href="https://twitter.com/intent/tweet?text=Hi%20@jasdev!">I’d love to hear about them</a>!</p>
<hr />
<p><sup>1</sup>: My current preference for reading is with <a href="https://www.apple.com/ibooks/">iBooks</a>. This allows me to seamlessly transition across all of my devices (including bookmarks, notes, and hightlighting). I have given the <a href="https://www.amazon.com/gp/digital/fiona/kcp-landing-page">Kindle</a> app a try, but I preferred the visual aesthetics of iBooks over it.</p>
<p><sup>2</sup>: Subtle pun since Pocket <a href="https://en.wikipedia.org/wiki/Pocket_(application)">used to be called <em>Read It Later</em></a></p>
<p><sup>3</sup>: Not driving of course!</p>
Tips for pair programming2015-08-02T00:00:00+00:00https://jasdev.me/tips-for-pair-programming<p>This past Spring, I transitioned to the iOS team at <a href="http://imgur.com">Imgur</a>, after having worked on their API for the previous year. The motivation for this transition was my attendance of <a href="https://codepath.com">CodePath</a>’s Winter cohort. CodePath is an eight week program that teaches experienced web engineers either iOS or Android<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. After completing the course, I was eager to bridge our <a href="https://itunes.apple.com/us/app/imgur/id639881495?mt=8">existing Objective-C app</a> to <a href="https://developer.apple.com/swift/">Swift</a><sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup> and help in any capacity that I could.</p>
<p>To ramp up on the codebase, the mobile team uses pair programming extensively. This worked out really well because I would transfer knowledge about the intricacies of Swift to the more senior engineers, while they would teach me the best practices of Cocoa Touch! We’ve learned a lot about what makes pairing work more effectively and even got the opportunity to try remote pairing. Most recently, we used pair programming (almost exclusively) in adding upload functionality to the app. This helped catch a ton of bugs! Below are some tips we highly recommend for pairing:</p>
<ul>
<li>Switching drivers is a lot easier when using two keyboards hooked up to the same computer.</li>
<li>For remote pairing, we’ve been using <a href="https://screenhero.com">Screenhero</a><sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>.
<ul>
<li>For Xcode pairing specifically, <a href="http://feinstruktur.com/copilot/">CoPilot</a> looks promising. However, we haven’t tried it yet; so, we can’t vouch for how well it works.</li>
</ul>
</li>
<li>Switch drivers after completing major logical chunks. This allows the driver to get into the code without having to be interrupted by hard time limits.
<ul>
<li>Both partners should also agree on a cadence for switching before they start pairing.</li>
</ul>
</li>
<li>It’s sometimes helpful to have a second computer nearby to reference docs, as needed.</li>
<li>If you’re not currently driving and are having trouble following, speak up. You’ll often catch bugs by having the driver explain their thought process aloud (similar to the benefit of <a href="http://en.wikipedia.org/wiki/Rubber_duck_debugging">Rubber Duck Debugging</a>).</li>
<li>Get snacks and drinks before you start pairing to minimize distractions once you’ve gotten in the zone.</li>
<li><a href="https://en.wikipedia.org/wiki/Test-driven_development">TDD</a> and pairing go really well together. One person writes the tests and the other writes the corresponding implementation.</li>
<li>Shut down anything that can generate notifications. OS X has a “Do Not Disturb” mode available in the Notification Center.</li>
<li>Make sure to take breathers and have fun!</li>
</ul>
<p>Hope these help make your pairing sessions better! Have any other tips that I forgot to mention? Let me know on <a href="https://twitter.com/jasdev">Twitter</a>!</p>
<hr />
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>I was lucky enough to be in one of the first few batches that used Swift. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>Post about the lessons we learned coming soon! <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>They have closed new registrations, post-Slack acquisition. So, you’ll need an existing user to <a href="http://blog.screenhero.com/post/110852538851/already-a-screenhero-user-heres-how-to-invite">invite you</a>! <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Intercepting iOS network traffic2015-07-11T00:00:00+00:00https://jasdev.me/intercepting-ios-traffic<p>Ever wanted to write a small wrapper for an iOS app that has an undocumented API? Itching to reverse engineer how it communicates with its backend<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>? You’re in luck! 😎 In the past, my friends and I have used this technique to figure out how to programmatically send and receive Snapchats, months after the application’s release. This lead to one of the <a href="https://medium.com/@binroot/the-best-jokes-have-no-punchline-5f4713a963d6">greatest stories</a> we tell today.</p>
<p>To sniff an application’s network traffic, we’re going to use <a href="http://mitmproxy.org">mitmproxy</a>. It’s a powerful man-in-the-middle proxy that allows you to intercept, modify, replay, and save HTTP/S traffic.</p>
<h3 id="installing-mitmproxy-and-ca-certificate">Installing mitmproxy and CA certificate</h3>
<p>First, let’s install mitmproxy (I use <code class="language-plaintext highlighter-rouge">pip</code> in this example but there are other install methods in their <a href="http://mitmproxy.org/doc/install.html">docs</a>)</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ pip install mitmproxy
</code></pre></div></div>
<p>Next, we’ll need to alter the proxy settings on our iOS device to point to the machine running mitmproxy:</p>
<ul>
<li>Determine your computer’s IP on the network
<ul>
<li>System preferences > Network > Advanced > TCP/IP<br />
<img src="/public/images/osxip.png" alt="" /></li>
</ul>
</li>
<li>Launch mitmproxy
<ul>
<li><code class="language-plaintext highlighter-rouge">$ mitmproxy</code></li>
</ul>
</li>
<li>Set iOS manual proxy settings
<ul>
<li>Settings > Wi-Fi > [select current network]<br />
<img src="/public/images/iosproxy.png" alt="" /></li>
</ul>
</li>
<li>Install CA certificate on device
<ul>
<li>Open Safari and go to <code class="language-plaintext highlighter-rouge">mitm.it</code>, if everything is setup correctly, you should see this screen below. Install the Apple certificate.<br />
<img src="/public/images/mitmit.png" alt="" /></li>
</ul>
</li>
</ul>
<h3 id="viewing-network-requests">Viewing Network Requests</h3>
<p>Setup is done! Now the fun begins. You may have noticed the output pouring in from of the <code class="language-plaintext highlighter-rouge">mitmproxy</code> command you ran earlier. Those are all of the network requests your device is making! You can navigate through the requests with the arrow keys and press enter on any request to inspect it (once in the inspection mode, use <code class="language-plaintext highlighter-rouge">h</code> and <code class="language-plaintext highlighter-rouge">l</code> keys to switch tabs). If you need any additional help with possible commands, just type <code class="language-plaintext highlighter-rouge">?</code>.</p>
<p>You’re all set! Inspect away and go make that unofficial API wrapper you always wanted 🚀</p>
<hr />
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>If you find any security issues, do the right thing and let the developers know. It shows character and may even land you an interview with the company 😊 <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Daily list2014-10-03T00:00:00+00:00https://jasdev.me/daily-list<p>Inspired by Jack Dorsey’s habit of reading a daily list, I decided to create my own <a href="https://www.coach.me/dashboard">Coach.me</a> <a href="https://www.coach.me/plans/285255-read-daily-list">plan</a> to achieve this goal.</p>
<p><a href="http://www.youtube.com/watch?v=wEQawgkCMOU&t=1122"><img src="http://img.youtube.com/vi/wEQawgkCMOU/0.jpg" alt="" /></a></p>
<p>Below is the list I read every morning as I leave my apartment. (I’ve added comments to provide context around a few points.)</p>
<hr />
<ul>
<li>“How <em>lucky</em> am I to have something that makes saying goodbye so hard.”</li>
<li>Do the most <strong>important</strong> thing first each day.</li>
<li>You’re <strong>worth it</strong> and you can do it.</li>
<li>Imagine your desired outcome and work backwards to figure out immediate steps.</li>
<li>Meditate on this list<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>.</li>
<li><strong>Meditate</strong></li>
<li>Replace the word problem with <strong>opportunity</strong>.</li>
<li>Everything is worth it.</li>
<li>Love is giving someone the power to destroy you and trusting them not to.</li>
<li>Take a deep breath, remember your <em>practice</em>, you’ll be fine.</li>
<li>Practice mental noting<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>.</li>
<li><strong>Live on</strong>.</li>
<li>“Pass the coat.”</li>
<li>Do small things with <strong>great love</strong>.</li>
<li>“I’m glad you exist.”<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup></li>
<li>If you’re not failing, you’re not reaching high enough. Stay hungry. Be true. <strong>Dream big</strong>.</li>
<li>Every decision, experience, success, and failure in life has brought me to <strong>this moment</strong>.</li>
<li>The price of discipline is less than the <strong>price of regret</strong>.</li>
<li>“I never saved anything for the swim back.”<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup></li>
<li>Don’t put off until tomorrow, what can be done today. Seriously.</li>
<li>Take time to reflect.</li>
<li>“Nobody cares about what you didn’t accomplish.”</li>
<li>“When you go to work and aren’t a bit nervous that you can’t possibly do your job, find a more challenging one.”</li>
</ul>
<hr />
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>Meta, but allows you to reevaluate your list every to determine what’s important. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>More info on this <a href="http://www.insightmeditationcenter.org/books-articles/articles/mental-noting/">technique</a>. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>The best compliment I’ve ever received. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:4" role="doc-endnote">
<p><a href="https://www.youtube.com/watch?v=yRynilqRXwI">Reference</a> <a href="#fnref:4" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Small moments2014-07-19T00:00:00+00:00https://jasdev.me/small-moments<p>A <a href="/on-forming-habits">year ago</a> I started a routine of writing down three positive things that happened every day. It didn’t matter how small they were, as long as I hit a minimum of three. A year and over a 1,000 moments later, I ventured to expanding this habit into a daily journal.</p>
<hr />
<p>Photos have played a big part in my life. So, the journaling tool needed to allow entries to work seamlessly with images during my trips down memory lane. This is where <a href="http://dayoneapp.com">Day One</a> comes in. The app is dense with features including Markdown support, locations, reminders, tags, and iCloud/Dropbox integration just to name a few.</p>
<p><img src="/public/images/day-one.png" alt="" /></p>
<p>I also set out with the goal of attaching an image to each day that passed. While this seems hard, I’ve found that screenshots<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> are an easy way accomplish this. Small digital moments make great memories. Examples I’ve found in my journal are random “I love you!” messages from my family, hilarious conversations on my team’s chat room, or even getting a pull request merged on day 1 of work.</p>
<p><img src="/public/images/imgur-pr.png" alt="" /></p>
<p>Over the course of six months, I’ve managed to save 830 (~250MB) of these electronic moments that I would’ve otherwise forgotten. To make this even easier, I’ve <a href="http://osxdaily.com/2011/01/26/change-the-screenshot-save-file-location-in-mac-os-x/">redirected all my screenshots</a> to a Dropbox folder for automatic backup<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>.</p>
<p><img src="/public/images/day-one-photos.png" alt="" /></p>
<blockquote>
<p>Day One photo stream</p>
</blockquote>
<p>I now spend the last 10 minutes of each night writing my heart out and picking an image for the day. Imagine creating a slideshow with these images. You could summarize a year’s worth of memories in under 20 minutes<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>!</p>
<p>Come to think of it, I’ll make sure to write that down in today’s entry :)</p>
<hr />
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>My friend, <a href="https://twitter.com/octopi">@octopi</a>, wrote a great post on the <a href="http://words.davidhu.me/screenshots/">value of screenshots</a>. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>I backup traditional photos via Dropbox and their most recent release, <a href="https://www.carousel.com/">Carousel</a>. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>(assuming 3 second transitions) <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Growing up with Facebook2013-09-03T00:00:00+00:00https://jasdev.me/growing-up-with-facebook<h3 id="and-how-im-starting-to-grow-out-of-it">and how I’m starting to grow out of it</h3>
<p>October 30, 2007, I joined Facebook. I was 15 then, a high school sophomore. At the time, everyone was coming up with goofy statuses, poking each other, and joining funny groups. Every conversation ended with, “add me on Facebook!”</p>
<p>Fast forward to 2013. After multiple updates, feature additions, and interface changes, the service has become infinitely better (timeline, gifts, read-receipts, chat-heads, cover photo, etc). But, network fatigue is a core issue, e.g. seeing posts from a cousin of a friend of a friend who I met at a summer camp that I haven’t seen since. You can unsubscribe and mute certain activity from your feed, but this drives me (and my peers) towards other services.</p>
<p>A vehicle for discovering posts on services that I use in addition to Facebook is an app called <a href="http://timehop.com">Timehop</a>.</p>
<p><img src="/public/images/timehop.png" alt="" /></p>
<p>Timehop is simple. Every morning is a stroll through memory lane: the app recovers and shares what you did on this exact day in history across Facebook, Twitter, Instagram, Foursquare, Flickr, and your camera roll. With this perspective, you begin to realize which posts are important and which ones were just noise. Personally, what has stuck out are the places I’ve been, thoughts, pictures, and messaging. Below are these moments<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>—one by one:</p>
<p><img src="/public/images/foursquare.png" alt="" /></p>
<p>When it comes to the places you’ve been, Foursquare still does it best. I joined the service in Summer 2012 and have since checked in over 2,500 times<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>.</p>
<p>There is a certain feeling attached to eating at a restaurant where your friend has been that no Yelp review could ever replace. Also, there are two features that are hard to live without: tips and lists. The Foursquare community fosters leaving tips at venues. If you don’t know what to order, a reliable approach is to check the tips and see what other users enjoy (even Steve Wozniak leaves <a href="https://foursquare.com/stevewoz">tips</a>). Lists, on the other hand,help users share their local favorites or even tell you which <a href="https://foursquare.com/tek/list/free-wifi--power-in-san-francisco">coffee shops have power and wifi</a>.</p>
<p><img src="/public/images/instagram.jpg" alt="Crucial acquisition." /></p>
<p>Instagram wins me over with its simplicity and attention to <a href="https://twitter.com/levie/status/374326435227250688">detail</a>. One of the smartest design decisions is the absence of albums. This forces users to really think about which photos capture moments they care about, creating a feed where liking 10+ photos feels <em>natural</em>. Also, with its <a href="http://blog.instagram.com/post/29555443184/instagram-3-0-photo-maps-more-weve-been">photo maps</a><sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>, I occasionally find myself scrolling over Miami to relive precious moments such as <a href="https://www.youtube.com/watch?v=caYdSWljo1g">Ultra 2013</a>.</p>
<p><img src="/public/images/twitter.png" alt="" /></p>
<p>Twitter’s big three: thoughts, conversations, and real-time. People often have the intention of starting a blog or journal, but it never comes to fruition. Tweets serve as a nice intermediary. By putting thoughts and experiences out there, you’re essentially creating a <a href="https://www.atlassian.com/git/tutorials/setting-up-a-repository/"><em>git commit log</em></a> for your life. Conversations are also extremely rich on Twitter. Since users are on the same playing field, it’s easy for things like athletes reaching out to fans, musicians running Q&A’s, and airlines addressing customer issues to happen organically. On the other hand, it’s been noted that this open sandbox nature of the service leads to a confusing new user experience. While this may be true, the openness leads to awesome accounts like <a href="https://twitter.com/MagicRecs">@MagicRecs</a>, <a href="https://twitter.com/yourawaymessage">@YourAwayMessage</a>, and <a href="https://twitter.com/EmrgencyKittens">@EmergencyKittens</a>. Lastly, and most notably, there are certain moments that just happen first on Twitter. Some examples from my graph include <a href="https://twitter.com/R00tTh3B0x/status/323979355577524225">a hacker defacing our University’s homepage</a>, getting on stage with our favorite DJ, and presenting my final <a href="https://twitter.com/jasdev/status/363439854585016320">intern project</a>.</p>
<p><img src="/public/images/messaging.png" alt="" /></p>
<p>VC firms aren’t making a mistake when they <a href="http://thenextweb.com/insider/2013/03/19/one-week-live-messageme-crests-1-million-users-lands-1-9-million-in-seed-fundingfrom-a17z-others/">invest</a> in the future of messaging. Over the past year, I’ve noticed my friends and I staying in touch through the use of group threads (iMessage for those w/ iOS and FB Messenger for groups with a mix of mobile operating systems). In fact, I use these apps so frequently that receiving an SMS almost feels <em>archaic</em>. Features like read receipts, quick file sharing, and quite frankly, stickers make group threads way more efficient and entertaining. Next, I want to make a quick note on the effect Snapchat has caused on my generation. Sitting in on a talk with Mary Meeker this past summer, while she gave a recap of her <a href="http://www.kpcb.com/blog/2013-internet-trends">2013 Internet Trends</a> report, I couldn’t help but pause on the following slide.</p>
<p><img src="/public/images/concert-phones.png" alt="" /></p>
<p>Snapchat has essentially reduced the amount of time we spend trying to capture a moment, so we can start enjoying them again.</p>
<hr />
<p>This post isn’t meant to serve as my departure from Facebook. I’ll still have an an account for some time since Facebook events, messaging, and groups are still leaders in the space.</p>
<p>However, much like your hometown, you eventually want to explore more cities. For sharing moments, segmented networks in tandem are better than centralization.</p>
<hr />
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>Thinking about it, Path may have been right in its focus on moments. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p><a href="https://foursquare.com/timemachine">https://foursquare.com/timemachine</a> for some awesome stats on your account. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>Funny how Foursquare <a href="http://thenextweb.com/socialmedia/2014/05/03/instagrams-move-replace-foursquare-facebook-places-premature/">used</a> to power all Instagram geo data. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
On forming habits2013-08-13T00:00:00+00:00https://jasdev.me/on-forming-habits<p>Sports involving competing against oneself have always been my fuel. Whether it was running cross country in high school or making the decision to start weight training upon entering university, I love being able to do something today that I wasn’t capable of the day before.</p>
<p>About two years ago, I wanted to formally take this approach with my personal habits. Initially, I used a combination of daily visits to <a href="http://www.reddit.com/r/GetMotivated">r/GetMotivated</a> and the web app, <a href="https://chains.cc">chains.cc</a>.</p>
<p><img src="/public/images/chainscc.png" alt="" /></p>
<p>The GetMotivated subreddit is a fantastic collection of inspirational posts<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> and is a factor that has remained constant throughout my journey of building a better self.</p>
<p>But, chains.cc actually served as more of a crutch than a boost.</p>
<p>chains.cc allows you to enter various habits and each day that you complete them, you add a day to your chain. The problem lies in its emphasis on forming long chains. Sure, not breaking a long chain is motivation to keep up a habit, but it also serves as a huge downer when you break it. So, instead of focusing on the fact that I was able to get into the gym for 20 days last month, I’m left with a stark reminder that I broke a daily streak I was on. Clearly, this perspective on habits wasn’t going to work in the long term.</p>
<p>That’s when I found Lift (now rebranded as <a href="https://coach.me">Coach.me</a>). An iOS and web app that takes a more solid approach to habits.</p>
<p><img src="/public/images/lift.png" alt="Lift will always be on my home row." /></p>
<p>Like chains.cc, Lift allows you to add specific habits and track them from day to day. However, in creating a better self, you are not alone. Lift buckets you with other people striving towards the same goals.</p>
<p><img src="/public/images/lift-habits.png" alt="" /></p>
<p>So, after checking off a habit, I often get random ‘props’ from people<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>. While this may seem silly, the feedback loop is amazing. It’s like people giving you high fives along the marathon we call life.</p>
<p>Two other key features that set Lift apart are reminders and a focus on milestones. Reminders make perfect sense<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>.</p>
<p>In the morning, I get pushed a reminder to use a standing desk, midday, a reminder to talk to a stranger, and at night, I wrap up with writing three positives things about today. Unlike chains.cc, milestones help alleviate the stress of breaking chains. Instead, you’re rewarded when you hit specific phases in a habit (e.g. tenth checkin or mini streaks).</p>
<p>Out of the habits I’ve started forming with Lift, I’d like to point out one in particular. Writing three positive things about today<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup>.</p>
<p>40 days into it, this habit has allowed me to have a stream of consciousness like never before. Whether I just feel great after a workout or make an awesome first impression, I constantly look for the gems in life. Imagine looking at this log just a year from now, 1095 things that made your days amazing.</p>
<hr />
<p>These are a few of the tools I use to build a better self. I’m still learning, tweaking, and testing my approach. But, that’s the beauty of it. Becoming better than you were yesterday is not merely a habit, it’s a way to live life<sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">5</a></sup>.</p>
<hr />
<h2 id="footnotes">Footnotes:</h2>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>“People often say that motivation doesn’t last. Well, neither does bathing—that’s why we recommend it daily.” <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p><a href="https://www.fitocracy.com">Fitocracy</a> is another great example of this prop behavior being used well. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>Reminders can be set to day/time granularity. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:4" role="doc-endnote">
<p>Excellent <a href="http://blog.lift.do/post/54035976743/habit-of-the-day-write-three-positive-things-about">stats</a> on this habit. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:5" role="doc-endnote">
<p>Who knows, this may be my first post towards a habit of writing? <a href="#fnref:5" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>