Jasdev’s technical notebook 2026-04-13T00:43:30+00:00 https://jasdev.me Jasdev Singh jasdev@jasdev.me A bug with Swift shorthand arguments 2022-02-20T00:00:00+00:00 https://jasdev.me/notes/merging-dictionaries-anonymous-arguments <p>A common kickflip in <a href="https://thebrowser.company/">The Browser Company’s</a> codebase is merging two dictionaries. Take two groups of tabs, each keyed on tab IDs with a corresponding <code class="language-plaintext highlighter-rouge">Tab</code> instance for values.</p> <script src="https://gist.github.com/jasdev/6711e2d8651c5be1603d87a2086d56fb.js"></script> <p>(<a href="https://gist.github.com/jasdev/6711e2d8651c5be1603d87a2086d56fb">Gist permalink</a>.)</p> <p>When merging <code class="language-plaintext highlighter-rouge">tabGroup1</code> and <code class="language-plaintext highlighter-rouge">tabGroup2</code>, we need to consider the possibility — albeit rare with <code class="language-plaintext highlighter-rouge">UUID</code>s — of key collisions. Do we unique duplicate keys with an existing value in the dictionary? Overwrite it? Or compute some new value based on the two? Thankfully Swift provides an affordance to handle this with <a href="https://developer.apple.com/documentation/swift/dictionary/3127173-merging"><code class="language-plaintext highlighter-rouge">Dictionary.merging(_:uniquingKeysWith:)</code></a><sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>.</p> <p>Since computing a <em>new</em> value for duplicate keys doesn’t quite make sense here, let’s try both keeping existing and overwriting values wholesale.</p> <script src="https://gist.github.com/jasdev/2abea571b37be824f2cac5c03fb3c353.js"></script> <p>(<a href="https://gist.github.com/jasdev/2abea571b37be824f2cac5c03fb3c353">Gist permalink</a>.)</p> <p>And that’s the note’s title bug in action. When using shorthand names, the last argument must be referenced or you have to fall back to naming or ignoring every argument (i.e. <code class="language-plaintext highlighter-rouge">{ first, _ in first }</code> above).</p> <p><a href="https://bugs.swift.org/browse/SR-1528">The bug</a> has been outstanding for six years (!) and folks like John McCall <a href="https://forums.swift.org/t/removing-in-from-empty-closures/2604/14">described it</a> as an issue “several people have made efforts to fix” and “it’s not easy.” Yet, there was a call for next steps in a thread David James <a href="https://forums.swift.org/t/lift-limitation-that-closures-must-use-last-shorthand-argument/43953/9">revived just last month</a>.</p> <hr /> <div class="footnotes" role="doc-endnotes"> <ol> <li id="fn:1" role="doc-endnote"> <p>And a <a href="https://developer.apple.com/documentation/swift/dictionary/3127169-merge">mutating variant</a>. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> </ol> </div> Two implementations of delaySubscription 2021-04-17T00:00:00+00:00 https://jasdev.me/notes/implementations-of-delay-subscription <p><a href="http://jack-stone.surge.sh">Jack Stone</a>’s <a href="https://github.com/CombineCommunity/CombineExt/issues/80">proposal</a> and <a href="https://github.com/CombineCommunity/CombineExt/pull/81">implementation of</a> <code class="language-plaintext highlighter-rouge">Publisher.delaySubscription</code> was a TIL for me. I also didn’t know there was <a href="https://github.com/ReactiveX/RxSwift/blob/1a1fa37b0d08e0f99ffa41f98f340e8bc60c35c4/RxSwift/Observables/DelaySubscription.swift#L9-L24">prior art for the operator in Rx</a>.</p> <p>It’s different than <a href="https://developer.apple.com/documentation/combine/publisher/delay(for:tolerance:scheduler:options:)"><code class="language-plaintext highlighter-rouge">Publisher.delay</code></a> in that it shifts the point of <em>subscription</em> forward in time instead of emissions (à la <code class="language-plaintext highlighter-rouge">delay</code>).</p> <p>The primary step is <a href="https://github.com/CombineCommunity/CombineExt/pull/81/files#diff-77f7f894ef8425a690a91b1d1ad0a4eee576579c375c9eed2b4ae87fed47f684R110-R114">holding a received subscription for the specified delay before forwarding it downstream</a>. Which begs my favorite question, can this be implemented as composed operator? It can! An approach <a href="https://github.com/pointfreeco/isowords/pull/94">is tucked away</a> in isowords’ repo under the <code class="language-plaintext highlighter-rouge">Effect.deferred</code> naming, where the Point-Free team uses it delay a <a href="https://github.com/pointfreeco/isowords/blob/c2793f4aacbbe5273062ab2a064bb743b74bf9de/Sources/AudioPlayerClient/Client.swift#L6"><code class="language-plaintext highlighter-rouge">Never-Never</code> sound effect</a> until after a game cube is removed.</p> Free variables in TCA dependencies 2021-04-11T00:00:00+00:00 https://jasdev.me/notes/free-variables-in-tca-dependencies <h3 id="updates">Updates:</h3> <h4 id="41721">4/17/21:</h4> <p>Friend of the blog, <a href="https://twitter.com/dannyhertz">Danny Hertz</a>, brought up a good point: “one thing I’m curious about is how they make sure those globals are mutated on same thread.” <a href="https://twitter.com/mbrandonw">Brandon</a> replied with “it’s the responsibility of whoever makes the client — every spot <code class="language-plaintext highlighter-rouge">dependencies</code> is referenced, we know it’s on the main queue and if it wasn’t that’d be our error.”</p> <hr /> <p><a href="https://github.com/pointfreeco/swift-composable-architecture">TCA</a> completely rewired how I think about dependencies (and, well, how non-TCA code I wrote in the past worked with so much <a href="https://twitter.com/aaron_daub/status/1372892233196785664">implicit state</a>). A loose end in my understanding of the <a href="https://www.pointfree.co/collections/dependencies">designing dependencies</a> series has been those mutable free variables hanging out alongside for cancellation handling. e.g. <a href="https://github.com/pointfreeco/swift-composable-architecture/blob/4ea3dfed61f7e60859b888050233dac4243715e0/Examples/SpeechRecognition/SpeechRecognition/SpeechClient/Live.swift#L117"><code class="language-plaintext highlighter-rouge">dependencies</code> here in the SpeechRecognition case study</a> (or at the bottom of the abbreviated version, below).</p> <script src="https://gist.github.com/jasdev/e1575711753dd693cccab84f58fc3a1b.js"></script> <p>(<a href="https://gist.github.com/jasdev/e1575711753dd693cccab84f58fc3a1b">Gist permalink</a>.)</p> <p>Van (a regular <a href="https://github.com/jasdev/nyc-point-free-study-group">study group</a> pal) and I paused with the lifecycle of this variable — is it initialized on app. launch? Lazily loaded?</p> <p><a href="https://books.apple.com/us/book/the-swift-programming-language-swift-5-3/id881256329">The Swift Programming Language</a> book had our back with the answer in the Properties section of the Language Guide chapter:</p> <blockquote> <p>Global constants and variables are always computed lazily, in a similar manner to <code class="language-plaintext highlighter-rouge">lazy</code> stored properties. Unlike <code class="language-plaintext highlighter-rouge">lazy</code> stored properties, global constants and variables do not need to be marked with the <code class="language-plaintext highlighter-rouge">lazy</code> modifier.</p> </blockquote> <p>Aha! So, <code class="language-plaintext highlighter-rouge">dependencies</code> is initialized as needed, even without a <code class="language-plaintext highlighter-rouge">lazy</code> tacked on. TIL!</p> withLatestFrom as a composed operator 2021-04-10T00:00:00+00:00 https://jasdev.me/notes/with-latest-from <p>I kind of took CombineExt’s — and the <a href="https://rxmarbles.com/#withLatestFrom">ReactiveX specification</a> of — <a href="https://github.com/CombineCommunity/CombineExt/blob/8a070deab37e903490286f627bc9ecd078b65736/Sources/Operators/WithLatestFrom.swift"><code class="language-plaintext highlighter-rouge">Publisher.withLatestFrom</code></a> for granted. “It probably subscribes to both upstream and the provided sequence, suspending output from upstream until the other emits, and subsequently forwards the latest values down from the latter when the former emits” was my reading of the filled out <code class="language-plaintext highlighter-rouge">Publisher</code> conformance. Which made me assume this dance wasn’t possible as a <a href="https://www.apeth.com/UnderstandingCombine/operators/operatorscomposed.html">composed operator</a>.</p> <p>And the other day <a href="https://twitter.com/IanKay">Ian</a> showed me the way.</p> <p>Here’s a sketch of the approach (with a selector variant):</p> <script src="https://gist.github.com/jasdev/9503435250951e051b7dd0cca471b2e1.js"></script> <p>(<a href="https://gist.github.com/jasdev/9503435250951e051b7dd0cca471b2e1">Gist permalink</a>.)</p> <p>If this isn’t the Combine equivalent of a <a href="https://twitter.com/tonyhawk/status/1290472128723087361">kickflip</a> — I don’t know what is.</p> <p>The implicit capture of the upstream <code class="language-plaintext highlighter-rouge">self</code> in the <code class="language-plaintext highlighter-rouge">other.map { … }</code> closure is worth checking in on. Maybe we can write <code class="language-plaintext highlighter-rouge">AnyObject</code>-constrained overloads that <code class="language-plaintext highlighter-rouge">weak</code>ly capture <code class="language-plaintext highlighter-rouge">class</code>-bound publishers? Turns out all but three conformances in the <a href="https://developer.apple.com/documentation/combine/publishers"><code class="language-plaintext highlighter-rouge">Publishers</code></a> namespace are structs<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>, so let’s account for those.</p> <p><img src="/public/images/class_publishers.png" alt="" /></p> <script src="https://gist.github.com/jasdev/5ffeafb41b933aba8bb0740384715db5.js"></script> <p>(<a href="https://gist.github.com/jasdev/5ffeafb41b933aba8bb0740384715db5">Gist permalink</a>.)</p> <p>What’s wild is, at this point, we can sub in this implementation for CombineExt’s and the test suite still passes (!). Let’s check our work when it comes to terminal events, though.</p> <p>Failures?</p> <p>Failure events from either upstream or <code class="language-plaintext highlighter-rouge">other</code> are propagated down. Check.</p> <script src="https://gist.github.com/jasdev/7e865df7cfd0ef98f7376dfb1968861a.js"></script> <p>(<a href="https://gist.github.com/jasdev/7e865df7cfd0ef98f7376dfb1968861a">Gist permalink</a>.)</p> <p>Completions? This event type isn’t as intuitive. Should the argument’s completions be forwarded downstream? Let’s import <code class="language-plaintext highlighter-rouge">CombineExt</code> to see how the non-composed implementation handles this.</p> <script src="https://gist.github.com/jasdev/cef6a2abecb8da784288e5c12017c64d.js"></script> <p>(<a href="https://gist.github.com/jasdev/cef6a2abecb8da784288e5c12017c64d">Gist permalink</a>.)</p> <p>Hmm, alright, I can buy that <code class="language-plaintext highlighter-rouge">second</code>’s completions shouldn’t be sent downstream since <code class="language-plaintext highlighter-rouge">withLatestFrom</code> is essentially polling it for value events, caching the latest.</p> <p>Now let’s nix the CombineExt import and see how our operator handles this.</p> <script src="https://gist.github.com/jasdev/df46641f402eeef9a8a660dbb56c9e1b.js"></script> <p>(<a href="https://gist.github.com/jasdev/df46641f402eeef9a8a660dbb56c9e1b">Gist permalink</a>.)</p> <p>…neither scenario completes? Oof — and this checks out because the implementation’s <code class="language-plaintext highlighter-rouge">map</code>-<code class="language-plaintext highlighter-rouge">switchToLatest</code> dance only completes when upstream <em>and</em> all of the projected sequences complete<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup> (i.e. the scenarios in the snippet finish if you tack on <code class="language-plaintext highlighter-rouge">first.send(completion: .finished)</code> and <code class="language-plaintext highlighter-rouge">second.send(completion: .finished)</code> to each, respectively).</p> <p>But wait, didn’t Ext’s test suite pass with this implementation? It did, because at the time of writing (commit <a href="https://github.com/CombineCommunity/CombineExt/blob/8a070deab37e903490286f627bc9ecd078b65736/Tests/WithLatestFromTests.swift"><code class="language-plaintext highlighter-rouge">8a070de</code></a>) every test case in <code class="language-plaintext highlighter-rouge">WithLatestFromTests.swift</code> checks for <code class="language-plaintext highlighter-rouge">withLatestFrom</code>’s completion only after every argument and upstream has completed (missing the cases where only upstream finishes or the arguments do, but not both).</p> <p>Here’s Rx’s handling of the parenthesized cases:</p> <script src="https://gist.github.com/jasdev/0fb13c397c1203f5880fa903c776928b.js"></script> <p>(<a href="https://gist.github.com/jasdev/0fb13c397c1203f5880fa903c776928b">Gist permalink</a>.)</p> <p><em>Back to the drawing board.</em></p> <p>To recap, our implementation handles value and error events to spec. and needs to be reworked to finish when upstream does, even if the operator’s argument doesn’t.</p> <p>We can pull this off by using a note I wrote about on <a href="/notes/zip-completions"><code class="language-plaintext highlighter-rouge">Publisher.zip</code> completions</a> — specifically, that if any one of the publishers in a zip completes, the entire zipped sequence completes.</p> <script src="https://gist.github.com/jasdev/5b2d9d16ed7d14ba9232020e3432d4ab.js"></script> <p>(<a href="https://gist.github.com/jasdev/5b2d9d16ed7d14ba9232020e3432d4ab">Gist permalink</a>.)</p> <p>Which begs the question, if our initial, non-<code class="language-plaintext highlighter-rouge">zip</code>ped implementation passes CombineExt’s test suite? Does its implementation handle lone upstream completions? Let’s add a test case and take a look:</p> <script src="https://gist.github.com/jasdev/b4b20ef46af35669961f214f26a7fe0d.js"></script> <p>(<a href="https://gist.github.com/jasdev/b4b20ef46af35669961f214f26a7fe0d">Gist permalink</a>.)</p> <p>Shoot. Ext’s implementation doesn’t handle this.</p> <p>So, we have two options: either rework Ext to handle this case or sub in our composed variant which does. To kick off the discussion, I wrote an issue over at <a href="https://github.com/CombineCommunity/CombineExt/issues/87">CombineCommunity/CombineExt/87</a> with a sketch of how a PR for the latter approach could look.</p> <p>⬦</p> <p>The tl;dr is it’s possible to pull off <code class="language-plaintext highlighter-rouge">Publisher.withLatestFrom</code> as a composed operator! And while a full <code class="language-plaintext highlighter-rouge">Publisher</code> conformance can be more idiomatic, it’s fun to think on what it means to factor the operator’s behavior into the composition of ones that ship with the framework.<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup></p> <hr /> <div class="footnotes" role="doc-endnotes"> <ol> <li id="fn:1" role="doc-endnote"> <p><a href="https://developer.apple.com/documentation/combine/subject"><code class="language-plaintext highlighter-rouge">Subject</code></a> conformances are also generally reference types. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> <li id="fn:2" role="doc-endnote"> <p><a href="https://forums.swift.org/t/confused-about-behaviour-of-switchtolatest-in-combine/29914/27">Some chatter</a> about this on the Swift Forums when <code class="language-plaintext highlighter-rouge">map</code> plus <code class="language-plaintext highlighter-rouge">switchToLatest</code> didn’t quite match <a href="https://github.com/ReactiveX/RxSwift/blob/c4e4c1bd1c098ab942f0dc42b4fd66ab62e1bf9e/RxCocoa/Traits/SharedSequence/SharedSequence%2BOperators.swift#L84-L102">Rx’s <code class="language-plaintext highlighter-rouge">flatMapLatest</code></a>. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> <li id="fn:3" role="doc-endnote"> <p>It’s worth calling out though that composed operators <em>can</em> break under pressure compared to their <code class="language-plaintext highlighter-rouge">Publisher</code>-conformance counterparts. e.g. variadic zipping <a href="/notes/variadic-zip-cash">might crash on the order of hundreds of arguments</a>. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> </ol> </div> Iverson brackets and SwiftUI modifiers 2021-03-21T00:00:00+00:00 https://jasdev.me/notes/iverson-brackets-swiftui-modifiers <p>I love noticing when an everyday engineering practice has a named analog in mathematics. This time around, it was <a href="https://en.wikipedia.org/wiki/Iverson_bracket">Iverson brackets</a>. The Wikipedia page is…a lot, so no frets if it’s intimidating — the non-mathematician’s reading of it is the expression $[P]$ is equal to $1$ if $P$ is true or $0$, if false, where $P$ is some <a href="https://en.wikipedia.org/wiki/Predicate_(mathematical_logic)">predicate</a><del>true-false statement</del>.</p> <p>In Swift speak, a function from <code class="language-plaintext highlighter-rouge">Bool</code> to <code class="language-plaintext highlighter-rouge">Int</code><sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>.</p> <p>In SwiftUI speak, conditionally setting a modifier’s value<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>. Most commonly with <a href="https://developer.apple.com/documentation/swiftui/view/opacity(_:)"><code class="language-plaintext highlighter-rouge">opacity(_:)</code></a>,</p> <p><code class="language-plaintext highlighter-rouge">someView.opacity(isShown ? 1 : 0)</code>.</p> <p>And implicitly with others like <a href="https://developer.apple.com/documentation/swiftui/view/rotationeffect(_:anchor:)"><code class="language-plaintext highlighter-rouge">rotationEffect(_:anchor:)</code></a>,</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">someView</span> <span class="o">.</span><span class="nf">rotationEffect</span><span class="p">(</span><span class="o">.</span><span class="nf">degrees</span><span class="p">(</span><span class="n">isRotated</span> <span class="p">?</span> <span class="mi">90</span> <span class="p">:</span> <span class="mi">0</span><span class="p">))</span> <span class="c1">// which expands out to,</span> <span class="n">someView</span> <span class="o">.</span><span class="nf">rotationEffect</span><span class="p">(</span><span class="o">.</span><span class="nf">degrees</span><span class="p">(</span><span class="mi">90</span> <span class="o">*</span> <span class="p">(</span><span class="n">isRotated</span> <span class="p">?</span> <span class="mi">1</span> <span class="p">:</span> <span class="mi">0</span><span class="p">)))</span> </code></pre></div></div> <p>The <code class="language-plaintext highlighter-rouge">isShown ? 1 : 0</code> and <code class="language-plaintext highlighter-rouge">isRotated ? 1 : 0</code> ternaries are Iverson brackets in disguise. Kinda nifty to see another domain’s language around this type of expression. I came across the notation in <a href="https://math.stackexchange.com/a/4063297/13686">an answer</a> to the question of “What is the sum of <em>number of digits</em> of the numbers $2^{2001}$ and $5^{2001}$?” asked over on Math Stack Exchange.</p> <p>The next note will likely pencil in the intermediary steps of that solution.</p> <hr /> <div class="footnotes" role="doc-endnotes"> <ol> <li id="fn:1" role="doc-endnote"> <p>Or, a <a href="https://developer.apple.com/documentation/swift/binaryinteger"><code class="language-plaintext highlighter-rouge">BinaryInteger</code></a> conformance for the nerds. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> <li id="fn:2" role="doc-endnote"> <p>Harlan posted a thread on why this approach is preferred over <code class="language-plaintext highlighter-rouge">if</code>-<code class="language-plaintext highlighter-rouge">else</code>ing in <a href="https://developer.apple.com/documentation/swiftui/viewbuilder"><code class="language-plaintext highlighter-rouge">ViewBuilder</code></a>s.</p> <blockquote class="twitter-tweet" data-conversation="none" data-dnt="true" data-theme="light"><p lang="en" dir="ltr">The way to better express this is to create modifiers that take in values to determine how their effects get applied. Nearly every SwiftUI modifier takes in a value, so you can conditionalize it.</p>&mdash; Harlan Haskins (@harlanhaskins) <a href="https://twitter.com/harlanhaskins/status/1321614269994004481?ref_src=twsrc%5Etfw">October 29, 2020</a></blockquote> <script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> <p><a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> </ol> </div> Party Parrot waves and Collection rotations 2021-03-14T00:00:00+00:00 https://jasdev.me/notes/collection-rotation <h3 id="updates">Updates:</h3> <h4 id="31421">3/14/21:</h4> <p><a href="https://twitter.com/khanlou">Soroush</a> messaged that using <code class="language-plaintext highlighter-rouge">RangeReplaceableCollection</code>’s <code class="language-plaintext highlighter-rouge">+</code> overload unfortunately incurs a copy. I’ve added a footnote<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> with his workaround that coincidentally uses John’s hint at the end about leaning on the square’s symmetry across the diagonal.</p> <hr /> <p><a href="https://github.com/spadafiva">Joe Spadafora</a> sent an emoji masterpiece in our team’s Slack the other day that doubled as a code golf.</p> <p><img src="/public/images/party-parrot-wave.gif" alt="" /></p> <p>It’s a fun problem to think on: given some set of (successively delayed) party parrot emojis — say <code class="language-plaintext highlighter-rouge">:wave1parrot:</code>, <code class="language-plaintext highlighter-rouge">:wave2parrot:</code>, …, <code class="language-plaintext highlighter-rouge">:wave8parrot:</code> — , write a snippet to generate an 8×8 square of them doing <a href="https://en.wikipedia.org/wiki/Wave_(audience)">the wave</a>.</p> <p>That is, write a script with the following output:</p> <script src="https://gist.github.com/jasdev/83fd8eec22e50569bab3a2dda8b6c8e3.js"></script> <p>(<a href="https://gist.github.com/jasdev/83fd8eec22e50569bab3a2dda8b6c8e3">Gist permalink</a>.)</p> <p>(<em>…now’s your chance to draft an approach before the spoiler below.</em>)</p> <p>(<em>Padding.</em>)</p> <p>(<em>Padding.</em>)</p> <p>(<em>Padding.</em>)</p> <p>(<em>Padding.</em>)</p> <p>Here’s what I came up with in Swift:</p> <script src="https://gist.github.com/jasdev/25871cf89de515edc69c4e6e66a5ba59.js"></script> <p>(<a href="https://gist.github.com/jasdev/25871cf89de515edc69c4e6e66a5ba59">Gist permalink</a>.)</p> <p>The <code class="language-plaintext highlighter-rouge">parrotEmojis[$0...] + parrotEmojis[..&lt;$0]</code> pivoting on the fifth line stuck with me — it’s a trick I borrowed from Leo Dabus’ Stack Overflow <a href="https://stackoverflow.com/a/43772545">answer on <code class="language-plaintext highlighter-rouge">Array</code> rotations</a>.</p> <p>Which got me wondering, does the Standard Library provide an affordance for rotating its <a href="https://www.objc.io/blog/2019/03/26/collection-protocols/">hierarchy of <code class="language-plaintext highlighter-rouge">Collection</code> protocols</a>?</p> <p>Turns out the Standard Library itself doesn’t, yet the <code class="language-plaintext highlighter-rouge">swift-algorithms</code> package has us covered (the real MVP) with a mutating variant under the <a href="https://github.com/apple/swift-algorithms/blob/ed38a2dac4bd6fb46054c5cbfad0c178775e24f0/Guides/Rotate.md"><code class="language-plaintext highlighter-rouge">MutableCollection.rotate(toStartAt:)</code></a> method name. While we could modify the package’s implementation to be non-mutating, I wanted to see how broadly we could apply the <code class="language-plaintext highlighter-rouge">someCollection[$0...] + someCollection[..&lt;$0]</code> approach across the <code class="language-plaintext highlighter-rouge">Collection</code> protocols.</p> <p><code class="language-plaintext highlighter-rouge">Range</code> <a href="https://developer.apple.com/documentation/swift/collection/2945250-subscript">subscripting</a> is defined on <code class="language-plaintext highlighter-rouge">Collection</code>, returning a <code class="language-plaintext highlighter-rouge">Subsequence</code>, so we can start there (subbing in <code class="language-plaintext highlighter-rouge">AnyCollection&lt;Element&gt;</code> for the return type, which we’ll address next).</p> <script src="https://gist.github.com/jasdev/7568288f7a7721bdadf95f022da44ece.js"></script> <p>(<a href="https://gist.github.com/jasdev/7568288f7a7721bdadf95f022da44ece">Gist permalink</a>.)</p> <p>The second error points to the <code class="language-plaintext highlighter-rouge">RangeReplaceableCollection</code> overload of <a href="https://developer.apple.com/documentation/swift/rangereplaceablecollection/2965327"><code class="language-plaintext highlighter-rouge">+(_:_:)</code></a> being selected, which means we can quickly resolve the compilation error by following its guidance and returning a <code class="language-plaintext highlighter-rouge">Subsequence</code> out (since concatenating two <code class="language-plaintext highlighter-rouge">Subsequence</code>s yields a <code class="language-plaintext highlighter-rouge">Subsequence</code>).</p> <script src="https://gist.github.com/jasdev/35802ac2b5681d9b6c5ca2ee6928fd7b.js"></script> <p>(<a href="https://gist.github.com/jasdev/35802ac2b5681d9b6c5ca2ee6928fd7b">Gist permalink</a>.)</p> <p>The <code class="language-plaintext highlighter-rouge">extension Collection where SubSequence: RangeReplaceableCollection { /* … */ }</code> constraint is worth pausing on. Are there any <code class="language-plaintext highlighter-rouge">Collection</code>s out there whose subsequences are range-replaceable (RR, for short), yet the parent collection <em>isn’t</em>? I asked some folks over in an iOS Slack and <a href="https://twitter.com/tim_vermeulen">Tim Vermeulen</a> reminded me that since <code class="language-plaintext highlighter-rouge">self[...]</code> is a subsequence of the entire collection, any collection with RR subsequences can be made RR itself “by delegating all RR logic to <code class="language-plaintext highlighter-rouge">self[...]</code>.”</p> <blockquote> <p>You could in theory create your own non-RR collection type for which the subsequence is RR, but it’s guaranteed that there exist no cases of this because such a base collection always can be RR by delegating all RR logic to <code class="language-plaintext highlighter-rouge">self[...]</code>.</p> </blockquote> <p>— Tim V.</p> <p>Which means the extension’s constraint is functionally equivalent to extending <code class="language-plaintext highlighter-rouge">RangeReplaceableCollection</code> itself.</p> <script src="https://gist.github.com/jasdev/cfbb1a2a57d9fa40af68a51caa189a6f.js"></script> <p>(<a href="https://gist.github.com/jasdev/cfbb1a2a57d9fa40af68a51caa189a6f">Gist permalink</a>.)</p> <p>⬦</p> <p>I could’ve called it here, but there was <a href="https://github.com/apple/swift-algorithms/blame/ed38a2dac4bd6fb46054c5cbfad0c178775e24f0/Guides/Rotate.md#L66-L69">a note</a> in <code class="language-plaintext highlighter-rouge">swift-algorithm</code>’s docs. that got me wondering even further.</p> <blockquote> <p>[In Ruby,] you <a href="https://ruby-doc.org/core-2.6.1/Array.html#method-i-rotate">can rotate the elements of an array</a> by a number of positions, either forward <em>or backward</em> (by passing a negative number).</p> </blockquote> <p>So, let’s try to write a <code class="language-plaintext highlighter-rouge">rotated(by:)</code> overload, shall we?</p> <p>To start, the following equality <em>should</em> hold (if we assume positive <code class="language-plaintext highlighter-rouge">by</code>s rotate to the left and negative, to the right):</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">oneTwoThree</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span> <span class="n">oneTwoThree</span><span class="o">.</span><span class="nf">rotated</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="o">==</span> <span class="n">oneTwoThree</span><span class="o">.</span><span class="nf">rotated</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">oneTwoThree</span><span class="o">.</span><span class="n">count</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="c1">// ⇒ `true`</span> </code></pre></div></div> <p>The <code class="language-plaintext highlighter-rouge">-1</code> to <code class="language-plaintext highlighter-rouge">oneTwoThree.count - 1</code> wraparound hints at our old pal, <a href="https://developer.apple.com/documentation/swift/binaryinteger/2885003">the modulus operator</a>. Maybe we can modulo out the <code class="language-plaintext highlighter-rouge">by</code> parameter and mirror <code class="language-plaintext highlighter-rouge">rotated(toStartAt:)</code> above (with an empty collection check for safe measure)?</p> <script src="https://gist.github.com/jasdev/2ad9c9d27929fd842fcfa01bcd1f079f.js"></script> <p>(<a href="https://gist.github.com/jasdev/2ad9c9d27929fd842fcfa01bcd1f079f">Gist permalink</a>.)</p> <p>Taking a test drive with the <code class="language-plaintext highlighter-rouge">oneTwoThree</code> example above…crashes…?</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">oneTwoThree</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span> <span class="n">oneTwoThree</span><span class="o">.</span><span class="nf">rotated</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="o">==</span> <span class="c1">// ❌ “Fatal error: Negative `Array` index is out of range.”</span> <span class="n">oneTwoThree</span><span class="o">.</span><span class="nf">rotated</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="n">oneTwoThree</span><span class="o">.</span><span class="n">count</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="c1">// ⇒ `true`</span> </code></pre></div></div> <p>Hmmm, weird, that means the <code class="language-plaintext highlighter-rouge">shiftPoint</code> calculation — <code class="language-plaintext highlighter-rouge">-1 % 3</code> — is <code class="language-plaintext highlighter-rouge">-1</code>? Usually modulo $n$ operations land squarely in $\left[0, n\right)$ (excluded on the right). <code class="language-plaintext highlighter-rouge">%</code>’s docs. call out why this happens:</p> <blockquote> <p>The result of the remainder operator has the same sign as <code class="language-plaintext highlighter-rouge">lhs</code> [(<code class="language-plaintext highlighter-rouge">-1</code> in our example)] and has a magnitude less than <code class="language-plaintext highlighter-rouge">rhs.magnitude</code>.</p> </blockquote> <p>Alas. The more mathematical form of the operator <a href="https://forums.swift.org/t/double-modulo-operator/2718">was pitched</a> (and <a href="https://forums.swift.org/t/double-modulo-operator/2718/25">even considered</a>) on the Swift Forums before the thread lost steam back in July ’18. Still, it linked out to Rust’s <a href="https://github.com/rust-lang/rfcs/blob/e512fddd037b2cc32f9545994655bbd0de3d1199/text/2169-euclidean-modulo.md">RFC for their analog</a> that I translated into Swift.</p> <script src="https://gist.github.com/jasdev/ec91fd5245c5aabe7a807b661f0ea2df.js"></script> <p>(<a href="https://gist.github.com/jasdev/ec91fd5245c5aabe7a807b661f0ea2df">Gist permalink</a>.)</p> <p>With that, the <code class="language-plaintext highlighter-rouge">oneTwoThree</code> example is back in non-crashing order and we can finally re-work the original party parrot wave.</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">parrotEmojis</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="o">...</span><span class="mi">8</span><span class="p">)</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="s">":wave</span><span class="se">\(</span><span class="nv">$0</span><span class="se">)</span><span class="s">parrot:"</span> <span class="p">}</span> <span class="n">parrotEmojis</span><span class="o">.</span><span class="n">indices</span> <span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="n">parrotEmojis</span><span class="o">.</span><span class="nf">rotated</span><span class="p">(</span><span class="nv">by</span><span class="p">:</span> <span class="nv">$0</span><span class="p">)</span><span class="o">.</span><span class="nf">joined</span><span class="p">()</span> <span class="p">}</span> <span class="o">.</span><span class="nf">joined</span><span class="p">(</span><span class="nv">separator</span><span class="p">:</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span> </code></pre></div></div> <p>⬦</p> <p>I’ll stop here because I didn’t imagine I’d write — <em>checks word count</em> — 650+ words on rotating Party Parrot emojis. I gotta admit though, entries like this have been my favorites as of late.</p> <p>Please reach out if you have another approach! A good friend <a href="http://twitter.com/jxxf">John Feminella</a> mentioned that there might be a way to “exploit the square symmetry somehow.” That is,</p> <blockquote> <p>…by “exploit,” I mean that you are making a row of $N$ things, $N$ times, where the $i$th row is rotated by $i$ elements, so instead of making it once and rotating $N$ times, you could just make it $N$ times.</p> </blockquote> <p>— John F.</p> <p>Consider this an exercise for the dedicated reader.</p> <hr /> <div class="footnotes" role="doc-endnotes"> <ol> <li id="fn:1" role="doc-endnote"> <p>Soroush’s approach that avoids intermediary copies while also using John’s hint, above.</p> <script src="https://gist.github.com/jasdev/a2d84f06c43fd8892f0faa2815952b34.js"></script> <p>(<a href="https://gist.github.com/jasdev/a2d84f06c43fd8892f0faa2815952b34">Gist permalink</a>.) <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> </ol> </div> Implementing AnyEquatable 2021-03-04T00:00:00+00:00 https://jasdev.me/notes/anyequatable <p>Point-Free <a href="https://www.pointfree.co/episodes/ep133-concise-forms-bye-bye-boilerplate#exercise-1">#133.1</a> is a hidden gem<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. Outside the episode’s <a href="https://twitter.com/pointfreeco/status/1357770820270104576"><del><code class="language-plaintext highlighter-rouge">FormAction</code></del> <code class="language-plaintext highlighter-rouge">BindingAction</code></a> context, it asks the viewer to implement a type eraser on the <a href="https://developer.apple.com/documentation/swift/equatable"><code class="language-plaintext highlighter-rouge">Equatable</code></a> protocol. Here’s a quick note on the implementation because I’m still thinking about how wicked it is a month later — first, some scaffolding we’ll work under.</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">AnyEquatable</span><span class="p">:</span> <span class="kt">Equatable</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">value</span><span class="p">:</span> <span class="c1">// ???</span> <span class="kd">init</span><span class="o">&lt;</span><span class="kt">Value</span><span class="p">:</span> <span class="kt">Equatable</span><span class="o">&gt;</span><span class="p">(</span><span class="n">_</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">Value</span><span class="p">)</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="c1">// ???</span> <span class="p">}</span> <span class="kd">static</span> <span class="kd">func</span> <span class="o">==</span> <span class="p">(</span><span class="nv">lhs</span><span class="p">:</span> <span class="kt">AnyEquatable</span><span class="p">,</span> <span class="nv">rhs</span><span class="p">:</span> <span class="kt">AnyEquatable</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Bool</span> <span class="p">{</span> <span class="c1">// ???</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>Presumably we’d want to expose <code class="language-plaintext highlighter-rouge">AnyEquatable.value</code> — but, we can’t make it of type <code class="language-plaintext highlighter-rouge">Value</code>, since that’d require threading the underlying generic at the type level, defeating the purpose of erasure in the first place. Maybe…<code class="language-plaintext highlighter-rouge">Any</code>? That’d further mean, <code class="language-plaintext highlighter-rouge">==</code>’s implementation needs to remember <code class="language-plaintext highlighter-rouge">value</code>’s <code class="language-plaintext highlighter-rouge">Value</code>-ness if it has any hope of comparing it against <code class="language-plaintext highlighter-rouge">rhs.value</code> (since it’s another <code class="language-plaintext highlighter-rouge">Any</code>). So maybe we hold onto an <code class="language-plaintext highlighter-rouge">Any</code>-based predicate‽ It’s kinda wild, yet this seems to be the only implementation we can pull off sans compiler-generated magic like <a href="https://developer.apple.com/documentation/swift/anyhashable"><code class="language-plaintext highlighter-rouge">AnyHashable</code></a><sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>.</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">AnyEquatable</span><span class="p">:</span> <span class="kt">Equatable</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">Any</span> <span class="kd">private</span> <span class="k">let</span> <span class="nv">valueIsEqualTo</span><span class="p">:</span> <span class="p">(</span><span class="kt">Any</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Bool</span> <span class="kd">init</span><span class="o">&lt;</span><span class="kt">Value</span><span class="p">:</span> <span class="kt">Equatable</span><span class="o">&gt;</span><span class="p">(</span><span class="n">_</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">Value</span><span class="p">)</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">value</span> <span class="n">valueIsEqualTo</span> <span class="o">=</span> <span class="p">{</span> <span class="n">other</span> <span class="k">in</span> <span class="n">other</span> <span class="k">as?</span> <span class="kt">Value</span> <span class="o">==</span> <span class="n">value</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">static</span> <span class="kd">func</span> <span class="o">==</span> <span class="p">(</span><span class="nv">lhs</span><span class="p">:</span> <span class="kt">AnyEquatable</span><span class="p">,</span> <span class="nv">rhs</span><span class="p">:</span> <span class="kt">AnyEquatable</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Bool</span> <span class="p">{</span> <span class="n">lhs</span><span class="o">.</span><span class="nf">valueIsEqualTo</span><span class="p">(</span><span class="n">rhs</span><span class="o">.</span><span class="n">value</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>(!).</p> <p>Giving it a spin lets us mix and match erased values of the same or different underlying types.</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">AnyEquatable</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="o">==</span> <span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span> <span class="c1">// ⇒ false</span> <span class="kt">AnyEquatable</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="o">==</span> <span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="c1">// ⇒ true</span> <span class="kt">AnyEquatable</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="o">==</span> <span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="s">"Five"</span><span class="p">)</span> <span class="c1">// ⇒ false</span> </code></pre></div></div> <p>Er wait, hmm — I looked into why this isn’t already built into the language (à la <code class="language-plaintext highlighter-rouge">AnyHashable</code>) and it turns out this <a href="https://forums.swift.org/t/adding-a-polymorphic-equatable/12034/12">implementation breaks with classes and subclassing</a>. Maybe TCA gets away with this since it’s recommended that the <code class="language-plaintext highlighter-rouge">State</code> graph be composed entirely of value types:</p> <blockquote> <p><a href="https://github.com/pointfreeco/swift-composable-architecture/issues/230#issuecomment-665796295">In general, we think that state needs to consist of simple value types, and as soon as you introduce classes and objects you expose yourself to a lot of undefined behavior […]. If you need parts of these classes in your state we recommend defining structs that hold just the value-based [representations].</a></p> </blockquote> <p>The library also fences off misusing <code class="language-plaintext highlighter-rouge">AnyEquatable</code> by embedding <code class="language-plaintext highlighter-rouge">value</code> and <code class="language-plaintext highlighter-rouge">valueIsEqualTo</code> <a href="https://github.com/pointfreeco/swift-composable-architecture/blob/f8608c7421183d93ca246aae1e8656fb42b117f5/Sources/ComposableArchitecture/SwiftUI/Binding.swift#L126-L179">into <code class="language-plaintext highlighter-rouge">BindingAction</code>, directly</a>.</p> <p>And as a final tangent, SwiftUI prevents the similar problem of conforming reference types to <code class="language-plaintext highlighter-rouge">View</code>, <code class="language-plaintext highlighter-rouge">ButtonStyle</code>, et al by trigging runtime assertions.</p> <blockquote> <p><a href="https://developer.apple.com/documentation/watchos-release-notes/watchos-7_4-beta-release-notes#SwiftUI">Types conforming to any style protocol, such as <code class="language-plaintext highlighter-rouge">ButtonStyle</code>, <code class="language-plaintext highlighter-rouge">ToggleStyle</code>, are now enforced to be value types. Styles must be structures or enumerations, not classes, and conforming a class to a style protocol may trigger an assertion. This is the same restriction that the system has always enforced on types conforming to <code class="language-plaintext highlighter-rouge">View</code>. (62886135)</a></p> </blockquote> <hr /> <div class="footnotes" role="doc-endnotes"> <ol> <li id="fn:1" role="doc-endnote"> <p>Another recent one was <a href="https://twitter.com/jasdev/status/1364238350077599748">extending #136.2</a> for learning’s sake. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> <li id="fn:2" role="doc-endnote"> <p>Which arguably covers a decent chunk of <code class="language-plaintext highlighter-rouge">AnyEquatable</code> usages, since <code class="language-plaintext highlighter-rouge">Hashable</code> inherits from <code class="language-plaintext highlighter-rouge">Equatable</code>. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> </ol> </div> atan versus atan2 2021-03-01T00:00:00+00:00 https://jasdev.me/notes/atan-versus-atan2 <p>I never really thought twice — let alone since high school pre-calculus — about the <code class="language-plaintext highlighter-rouge">atan</code> function (shorthand for arctangent)<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. And it wasn’t until Richard Borcherds pointed out the difference between <a href="https://developer.apple.com/documentation/coregraphics/1454322-atan"><code class="language-plaintext highlighter-rouge">atan</code></a> and <a href="https://developer.apple.com/documentation/coregraphics/1455332-atan2"><code class="language-plaintext highlighter-rouge">atan2</code></a> (the <a href="/notes/parity-arity">arity</a> two form) in a <a href="https://youtu.be/JcatDvcV-fU?t=779">recent complex analysis lecture</a>, that I realized I also haven’t paused with why there’s two variants.</p> <p>The two-argument form is needed because the single-argument function <em>loses</em> the individual signs (positive or negative) of triangle side lengths whereas <code class="language-plaintext highlighter-rouge">atan2</code> keeps them in tact. Why is this important? Well, if we swing out $\frac{\pi}{4}$ along the unit circle in the anti-clockwise direction (with unit side lengths of the triangle formed) or diametrically by $-\frac{3\pi}{4}$ (with negative unit triangle side lengths), <code class="language-plaintext highlighter-rouge">atan</code> will give the same answer for both $\frac{y}{x}$ ratios — whoops. Here’s that subtle gotcha in Swift with a <code class="language-plaintext highlighter-rouge">CoreGraphics</code> import.</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">import</span> <span class="kt">CoreGraphics</span> <span class="nf">atan</span><span class="p">(</span><span class="kt">CGFloat</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="o">/</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="o">==</span> <span class="nf">atan</span><span class="p">(</span><span class="kt">CGFloat</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="o">/</span> <span class="mi">1</span><span class="p">)</span> <span class="c1">// ⇒ true</span> <span class="nf">atan</span><span class="p">(</span><span class="kt">CGFloat</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="o">/</span> <span class="mi">1</span><span class="p">)</span> <span class="o">==</span> <span class="o">.</span><span class="n">pi</span> <span class="o">/</span> <span class="mi">4</span> <span class="c1">// ⇒ true</span> </code></pre></div></div> <p><code class="language-plaintext highlighter-rouge">atan2</code> resolves this collision by separating out the lone $\frac{y}{x}$ <code class="language-plaintext highlighter-rouge">CGFloat</code> passed to <code class="language-plaintext highlighter-rouge">atan</code> into two arguments in $y$-first-then-$x$ order.</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Along the unit circle, anti-clockwise (positive radian direction).</span> <span class="nf">atan2</span><span class="p">(</span><span class="kt">CGFloat</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span> <span class="mi">1</span><span class="p">)</span> <span class="o">==</span> <span class="o">.</span><span class="n">pi</span> <span class="o">/</span> <span class="mi">4</span> <span class="c1">// ⇒ true</span> <span class="nf">atan2</span><span class="p">(</span><span class="kt">CGFloat</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="o">==</span> <span class="mi">3</span> <span class="o">*</span> <span class="o">.</span><span class="n">pi</span> <span class="o">/</span> <span class="mi">4</span> <span class="c1">// ⇒ true</span> <span class="nf">atan2</span><span class="p">(</span><span class="kt">CGFloat</span><span class="p">(</span><span class="mi">0</span><span class="p">),</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="o">==</span> <span class="o">.</span><span class="n">pi</span> <span class="c1">// ⇒ true</span> <span class="c1">// Along the unit circle, clockwise (negative radian direction).</span> <span class="nf">atan2</span><span class="p">(</span><span class="kt">CGFloat</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">),</span> <span class="mi">1</span><span class="p">)</span> <span class="o">==</span> <span class="o">-.</span><span class="n">pi</span> <span class="o">/</span> <span class="mi">4</span> <span class="c1">// ⇒ true</span> <span class="nf">atan2</span><span class="p">(</span><span class="kt">CGFloat</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">),</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="o">==</span> <span class="o">-</span><span class="mi">3</span> <span class="o">*</span> <span class="o">.</span><span class="n">pi</span> <span class="o">/</span> <span class="mi">4</span> <span class="c1">// ⇒ true</span> </code></pre></div></div> <p>Moreover, TIL the phrasing around resolving collisions from integer multiples of $2\pi$ being added to any angle without changing the resulting $x$ or $y$: “restricting to <a href="https://en.wikipedia.org/wiki/Principal_value">principal values</a>.”</p> <p><code class="language-plaintext highlighter-rouge">atan</code> is restricted to $\left(-\frac{\pi}{2}, \frac{\pi}{2}\right)$ principal values — to account for the domain spanning from $\left(\texttt{-CGFloat.infinity}, \texttt{.infinity}\right)$ — and <code class="language-plaintext highlighter-rouge">atan2</code> to the range $\left(-\pi, \pi\right]$.</p> <hr /> <div class="footnotes" role="doc-endnotes"> <ol> <li id="fn:1" role="doc-endnote"> <p>Looked into the etymology of the “arc” prefix while writing this — it’s from the fact that, in radians, the inverse trigonometric functions return angle values that correspond to the arc lengths they trace along the unit circle (!). Arturo Magidin put this well in <a href="https://math.stackexchange.com/a/33176">their Mathematics Stack Exchange answer</a>:</p> <blockquote> <p>When measuring in radians, an angle of $\theta$ radians will correspond to an arc whose length is $r\theta$, where $r$ is the radius of the circle.</p> <p>Thus, in the unit circle, “the arc whose cosine is $x$” is the same as “the angle whose cosine is $x$,” because the measurement of the length of the arc of the circle is the same as the measurement of the angle in radians.</p> </blockquote> <p><a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> </ol> </div> Modifying the pairwise operator with a duplicated start 2021-02-11T00:00:00+00:00 https://jasdev.me/notes/pairwise-with-duplicated-start <p>Bas van Kuijck <a href="https://github.com/CombineCommunity/CombineExt/pull/48">PR’d <code class="language-plaintext highlighter-rouge">Publisher.pairwise()</code> (and <code class="language-plaintext highlighter-rouge">.nwise(_:)</code>)</a> to CombineExt back in August and the operator came up in iOS Folks’ #reactive channel the other day. <a href="https://twitter.com/jshier">Jon Shier</a> asked:</p> <blockquote> <p>I need to compare changes to values within a sequence, they’re non-optional already, so I need a way to see the previous value and the current value. I could probably figure that part out, but what about the first pair? I can’t wait until I get two values and I’d rather not introduce optionals, but what if I duplicated the first value that came down the stream but otherwise always had the previous and current values?</p> </blockquote> <p>Unfortunately, <code class="language-plaintext highlighter-rouge">pairwise</code> — which is an overlay on <code class="language-plaintext highlighter-rouge">nwise(2)</code> — will only send values downstream after <a href="https://github.com/CombineCommunity/CombineExt/blob/2bb2b5ac35847f253b5024d22d8a7704abd8d24a/Sources/Operators/Nwise.swift#L27">two are received from its upstream</a>. So, it doesn’t meet the “I can’t wait until I get two values” requirement.</p> <p>And further, the “rather not introduce optionals” mention makes things trickier. Let’s take a look. An initial approach we could take is <code class="language-plaintext highlighter-rouge">share</code>ing upstream, spitting it off into two —  one left as is and another that’s <code class="language-plaintext highlighter-rouge">first</code>’d — and then connecting the split sequences with a <code class="language-plaintext highlighter-rouge">flatMap</code> that duplicates the first value and then finally <code class="language-plaintext highlighter-rouge">pairwise</code>ing the result (…that probably made zero sense in prose, here’s a sketch hah).</p> <script src="https://gist.github.com/jasdev/f406332b87651f1919dfb8aec9c34f89.js"></script> <p>(<a href="https://gist.github.com/jasdev/f406332b87651f1919dfb8aec9c34f89">Gist permalink</a>.)</p> <p>The repeat <code class="language-plaintext highlighter-rouge">(1, 1)</code> surprised me — I had assumed <code class="language-plaintext highlighter-rouge">share</code>ing upstream would ensure that the resubscribe in the <code class="language-plaintext highlighter-rouge">flatMap</code> would carry on with <code class="language-plaintext highlighter-rouge">2</code>. I scratched my head and dug further into this bit.</p> <script src="https://gist.github.com/jasdev/8c3f8e80cfc8dfa73121b2da65a96643.js"></script> <p>(<a href="https://gist.github.com/jasdev/8c3f8e80cfc8dfa73121b2da65a96643">Gist permalink</a>.)</p> <p>The second subscription to <code class="language-plaintext highlighter-rouge">upstream</code> in the <code class="language-plaintext highlighter-rouge">flatMap</code> seems to synchronously win out over the lingering <code class="language-plaintext highlighter-rouge">2</code> from the first subscription (notice it comes in at the bottom of the logs, yet never makes it to the <code class="language-plaintext highlighter-rouge">Post-share</code> <code class="language-plaintext highlighter-rouge">print</code> operator). We can further suss out this race condition by <code class="language-plaintext highlighter-rouge">delay</code>ing the first value event and then noticing the second subscription missed its chance entirely.</p> <script src="https://gist.github.com/jasdev/3657ba19d3fb5617efb344f6eda6c5ff.js"></script> <p>(<a href="https://gist.github.com/jasdev/3657ba19d3fb5617efb344f6eda6c5ff">Gist permalink</a>.)</p> <p>Stepping back, there miiight be a way to make a <code class="language-plaintext highlighter-rouge">share</code> based approach without race conditions like this while also accounting for <code class="language-plaintext highlighter-rouge">upstream</code>’s <a href="/publisher-temperature-primer">temperature</a> (please reach out, if you know how (!)), but at this point, I decided to ease the “rather not introduce optionals” requirement and use a <code class="language-plaintext highlighter-rouge">scan</code>-based approach to work around these concerns.</p> <script src="https://gist.github.com/jasdev/00ef4983faf79aa82a2bd3136cc0e2ff.js"></script> <p>(<a href="https://gist.github.com/jasdev/00ef4983faf79aa82a2bd3136cc0e2ff">Gist permalink</a>.)</p> <p>The core of the implementation is at <code class="language-plaintext highlighter-rouge">(1)</code> — to kick off the scan, we coalesce the initial <code class="language-plaintext highlighter-rouge">(nil, nil)</code> to <code class="language-plaintext highlighter-rouge">(next, next)</code> for the <em>first</em> upstream value. From then on, we pair à la <code class="language-plaintext highlighter-rouge">pairwise</code> since subsequent calls to <code class="language-plaintext highlighter-rouge">optionalZip</code> will have a non-<code class="language-plaintext highlighter-rouge">nil</code> return value. It’s a bit unfortunate that the local, free-function <code class="language-plaintext highlighter-rouge">optionalZip</code> couldn’t simply be named <code class="language-plaintext highlighter-rouge">zip</code>, since that would collide with <code class="language-plaintext highlighter-rouge">Publisher.zip</code> in the implementation. <a href="https://twitter.com/sharplet">Adam</a> mentioned he runs into this often with <a href="https://developer.apple.com/documentation/swift/1541053-print"><code class="language-plaintext highlighter-rouge">Swift.print</code></a> and <a href="https://developer.apple.com/documentation/combine/publisher/print(_:to:)"><code class="language-plaintext highlighter-rouge">Publisher.print</code></a>, too. Wonder if there’s any reason Swift can’t have an inverse to the <a href="https://fivestars.blog/swift/disfavoredOverload.html"><code class="language-plaintext highlighter-rouge">@_disfavoredOverload</code></a> annotation — maybe called <code class="language-plaintext highlighter-rouge">@_preferredOverload</code> — to nudge the compiler here? We could prefix with the current module’s name in most cases, but I was working within a Playground (and I’m guessing they have generated module names that are out of reach at compile time).</p> AnyCancellable.store(in:) thread safety 2021-01-31T00:00:00+00:00 https://jasdev.me/notes/anycancellable-storage-thread-safety <p>Thready safety has been a weak point for me (my university’s OS class also being taught in pre-<code class="language-plaintext highlighter-rouge">1.x</code> Rust didn’t help much, either hah); but, I’m <a href="https://github.com/CombineCommunity/CombineExt/pull/74">slowly working on it</a>.</p> <p>Kyle Bashour’s <a href="https://twitter.com/kylebshr/status/1355944018555797505">reply</a> to Curtis Herbert’s (Twitter) thread on the thread safety of <a href="https://developer.apple.com/documentation/combine/anycancellable/store(in:)-3hyxs"><code class="language-plaintext highlighter-rouge">AnyCancellable.store(in:)</code></a> (or analogously, for the <a href="https://developer.apple.com/documentation/combine/anycancellable/store(in:)-6cr9i"><code class="language-plaintext highlighter-rouge">RangeReplaceableCollection</code> overload</a>) reminded me of a Combine gotcha. Swift’s Standard Library data structures <em>aren’t</em> thread-safe out of the box — which, in turn means we need to be extra careful when storing cancellation tokens across threads. Let’s tee up an example to see why.</p> <script src="https://gist.github.com/jasdev/3390c43d90521009c04d17c96f346b05.js"></script> <p>(<a href="https://gist.github.com/jasdev/3390c43d90521009c04d17c96f346b05">Gist permalink</a>.)</p> <p>If <code class="language-plaintext highlighter-rouge">fetchCount</code> is called across multiple threads, <code class="language-plaintext highlighter-rouge">.store(in:)</code> will concurrently modify <code class="language-plaintext highlighter-rouge">cancellables</code>, possibly leading to race conditions. There’s even a <a href="https://github.com/OpenCombine/OpenCombine/issues/200">related issue</a> about this over on OpenCombine’s repository. So, we’ll need to lock around the <code class="language-plaintext highlighter-rouge">store</code> call and while we could do the usual <a href="https://developer.apple.com/documentation/foundation/nslocking/1416318-lock"><code class="language-plaintext highlighter-rouge">NSLocking.lock</code></a> and <a href="https://developer.apple.com/documentation/foundation/nslocking/1418241-unlock"><code class="language-plaintext highlighter-rouge">.unlock</code></a> dance, I looked around to see if we could do better. And I found a small helper over <a href="https://github.com/pointfreeco/swift-composable-architecture/blob/b692a6deaf8210da25385c9ae073f4ccbf2309f8/Sources/ComposableArchitecture/Internal/Locking.swift#L12-L19">in TCA</a> and <a href="https://github.com/ReactiveX/RxSwift/blob/0efa6d1482ddaea12d63f4f17567daa4744c7420/RxSwift/Concurrency/Lock.swift#L17-L23">in RxSwift</a>.</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">NSRecursiveLock</span> <span class="p">{</span> <span class="kd">@inlinable</span> <span class="kd">@discardableResult</span> <span class="kd">func</span> <span class="n">sync</span><span class="o">&lt;</span><span class="kt">Value</span><span class="o">&gt;</span><span class="p">(</span><span class="n">_</span> <span class="nv">work</span><span class="p">:</span> <span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">Value</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Value</span> <span class="p">{</span> <span class="nf">lock</span><span class="p">()</span> <span class="k">defer</span> <span class="p">{</span> <span class="nf">unlock</span><span class="p">()</span> <span class="p">}</span> <span class="k">return</span> <span class="nf">work</span><span class="p">()</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>We can then lean on this extension in our earlier example.</p> <script src="https://gist.github.com/jasdev/9748045de5e11ec14b200827bbf981cf.js"></script> <p>(<a href="https://gist.github.com/jasdev/9748045de5e11ec14b200827bbf981cf">Gist permalink</a>.)</p> <p>Prior art for this helper seems to be <code class="language-plaintext highlighter-rouge">DispatchQueue</code>’s <a href="https://developer.apple.com/documentation/dispatch/dispatchqueue/2016081-sync"><code class="language-plaintext highlighter-rouge">sync(execute:)</code></a> method. It might be tempting to further roll this logic up into an <a href="https://github.com/apple/swift-evolution/blob/5752a43ba5ddd6c53a5fd48d1551aa89864df827/proposals/0258-property-wrappers.md#atomic"><code class="language-plaintext highlighter-rouge">@Atomic</code> property wrapper</a>, yet it unfortunately won’t help when fencing off collection types — like <code class="language-plaintext highlighter-rouge">Set</code> in our case — because <a href="https://twitter.com/chunkyguy/status/1251119623233310720">each thread would operate on its own copy</a> of the data structure. Donny Wals walks through this in detail in <a href="https://www.donnywals.com/why-your-atomic-property-wrapper-doesnt-work-for-collection-types/">a post on the topic</a>.</p> Mapping over the range and pattern matching operators 2021-01-30T00:00:00+00:00 https://jasdev.me/notes/mapping-over-operators <p>Two tricks for the operator and <a href="https://en.wikipedia.org/wiki/Tacit_programming">point-free</a>-leaning crowd:</p> <ol> <li> <p>To quickly form <a href="https://developer.apple.com/documentation/swift/rangeexpression">ranges</a> from a sequence of <code class="language-plaintext highlighter-rouge">indices</code>, you can use <code class="language-plaintext highlighter-rouge">zip(indices, indices.dropFirst()).map(..&lt;)</code> (or subbing in the <a href="https://docs.swift.org/swift-book/LanguageGuide/BasicOperators.html#ID74">closed range operator</a> — the <a href="https://docs.swift.org/swift-book/LanguageGuide/BasicOperators.html#ID562">one-sided variants</a> are ambiguous in the point-free style)<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>.</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">let</span> <span class="nv">odds</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">7</span><span class="p">]</span> <span class="nf">zip</span><span class="p">(</span><span class="n">odds</span><span class="p">,</span> <span class="n">odds</span><span class="o">.</span><span class="nf">dropFirst</span><span class="p">())</span><span class="o">.</span><span class="nf">map</span><span class="p">(</span><span class="o">..&lt;</span><span class="p">)</span> <span class="c1">// ⇒ [(1..&lt;3), (3..&lt;5), (5..&lt;7)]</span> </code></pre></div> </div> </li> <li> <p>And tangentially, to test if parallel sequences of ranges and values <a href="https://developer.apple.com/documentation/swift/rangeexpression/2892547#">match against one another</a>.</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="c1">// (…continuing from above.)</span> <span class="k">let</span> <span class="nv">odds</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">7</span><span class="p">]</span> <span class="k">let</span> <span class="nv">oddRanges</span> <span class="o">=</span> <span class="nf">zip</span><span class="p">(</span><span class="n">odds</span><span class="p">,</span> <span class="n">odds</span><span class="o">.</span><span class="nf">dropFirst</span><span class="p">())</span><span class="o">.</span><span class="nf">map</span><span class="p">(</span><span class="o">..&lt;</span><span class="p">)</span> <span class="k">let</span> <span class="nv">evens</span> <span class="o">=</span> <span class="p">[</span><span class="mi">2</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">6</span><span class="p">]</span> <span class="nf">zip</span><span class="p">(</span><span class="n">oddRanges</span><span class="p">,</span> <span class="n">evens</span><span class="p">)</span><span class="o">.</span><span class="nf">map</span><span class="p">(</span><span class="o">~=</span><span class="p">)</span> <span class="c1">// ⇒ [true, true, true]</span> </code></pre></div> </div> </li> </ol> <hr /> <div class="footnotes" role="doc-endnotes"> <ol> <li id="fn:1" role="doc-endnote"> <p>Ended up using this in a <a href="https://jasdev.me/notes/buffer-and-batch-subscriptions">note on <code class="language-plaintext highlighter-rouge">Collection.batchedSubscribe(by:)</code></a>. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> </ol> </div> Constant bindings as placeholders 2021-01-27T00:00:00+00:00 https://jasdev.me/notes/constant-bindings <p>When building layouts in SwiftUI, I’ll often sketch out the views — ignoring data flow — and <em>then</em> turn my attention towards state management. However, since many view initializers require bindings (e.g. <a href="https://developer.apple.com/documentation/swiftui/textfield/init(_:text:oneditingchanged:oncommit:)-588cl"><code class="language-plaintext highlighter-rouge">TextField.init</code></a> or <a href="https://developer.apple.com/documentation/swiftui/toggle/init(ison:label:)"><code class="language-plaintext highlighter-rouge">Toggle.init</code></a>), that puts things in an awkward spot.</p> <p>What do I slot in if I haven’t determined whether to back the view with <a href="https://twitter.com/mbrandonw/status/1280452109931069441"><code class="language-plaintext highlighter-rouge">@State</code>, <code class="language-plaintext highlighter-rouge">@ObservedObject</code>, another <code class="language-plaintext highlighter-rouge">@Binding</code>, TCA-backed state, or the like</a>?</p> <p>Is there a placeholder I can use in the same way <code class="language-plaintext highlighter-rouge">fatalError</code> appeases the compiler while I think through an implementation?<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup></p> <p>Brandon and Stephen showed a technique <a href="https://www.pointfree.co/episodes/ep131-concise-forms-swiftui#t121">early in episode #131</a> (timestamped).</p> <blockquote> <p>For now, we’ll use a constant binding because we don’t have anywhere to send the user’s changes, but we will get to that soon.</p> </blockquote> <p>Aha! That’s a solid trick — here’s a <code class="language-plaintext highlighter-rouge">Toggle</code> example with its binding fixed to <code class="language-plaintext highlighter-rouge">false</code>.</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">import</span> <span class="kt">SwiftUI</span> <span class="kd">struct</span> <span class="kt">SampleView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span> <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span> <span class="kt">Toggle</span><span class="p">(</span><span class="s">"A toggle"</span><span class="p">,</span> <span class="nv">isOn</span><span class="p">:</span> <span class="o">.</span><span class="nf">constant</span><span class="p">(</span><span class="kc">false</span><span class="p">))</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <hr /> <div class="footnotes" role="doc-endnotes"> <ol> <li id="fn:1" role="doc-endnote"> <p><a href="https://github.com/apple/swift-evolution/blob/fe8f41794cb96bfd1942d7b7c1030851cc440ab5/proposals/0102-noreturn-bottom-type.md#never-as-a-universal-bottom-subtype"><code class="language-plaintext highlighter-rouge">Never</code> as a universal “bottom” subtype</a> was unfortunately not pursued in SE-0102. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> </ol> </div> Publisher.zip completions 2021-01-23T00:00:00+00:00 https://jasdev.me/notes/zip-completions <p>Zipping — in general — is a pairwise affair. <a href="https://github.com/pointfreeco/episode-code-samples/blob/b0aef80cf7688bd18c29509c38f06de1235936c8/0023-zip-pt1/Zip.playground/Pages/01-episode.xcplaygroundpage/Contents.swift#L92-L95"><code class="language-plaintext highlighter-rouge">Optional</code>’s <code class="language-plaintext highlighter-rouge">zip</code></a> is non-<code class="language-plaintext highlighter-rouge">nil</code> if both arguments are. Similarly for <a href="https://github.com/pointfreeco/episode-code-samples/blob/b0aef80cf7688bd18c29509c38f06de1235936c8/0024-zip-pt2/Zip.playground/Pages/01-episode.xcplaygroundpage/Contents.swift#L69-L82"><code class="language-plaintext highlighter-rouge">Result</code>’s <code class="language-plaintext highlighter-rouge">zip</code></a> along the <code class="language-plaintext highlighter-rouge">.success</code> case. <a href="https://developer.apple.com/documentation/swift/1541125-zip"><code class="language-plaintext highlighter-rouge">Swift.zip</code></a> pairs until it runs off the shorter of the sequence arguments. <a href="https://github.com/pointfreeco/swift-parsing/blob/dd2e6f2e8e7ca0211e81bf790044a314602fd7dd/Sources/Parsing/Parsers/Zip.swift#L88-L98"><code class="language-plaintext highlighter-rouge">Parsers.Take2</code></a> (another name for zipped parsing) succeeds if both parsers involved do.</p> <p>While <a href="https://developer.apple.com/documentation/combine/publisher/zip(_:)"><code class="language-plaintext highlighter-rouge">Publisher.zip</code></a> and its higher-<a href="/notes/parity-arity">arity</a> overloads follow suit for value events, there’s a subtle gotcha for <code class="language-plaintext highlighter-rouge">.finished</code> events (failures are immediately passed downstream).</p> <p>A zipped publisher <em>can complete</em> even if all of its inner publishers don’t.</p> <script src="https://gist.github.com/jasdev/88dc714541995b37504615d13856960f.js"></script> <p>(<a href="https://gist.github.com/jasdev/88dc714541995b37504615d13856960f">Gist permalink</a>.)</p> <p>This checks out after a pause — since <code class="language-plaintext highlighter-rouge">second</code> completes after the first <code class="language-plaintext highlighter-rouge">(1, 2)</code> pair comes through, there’s no chance it’ll pair with any future value events from <code class="language-plaintext highlighter-rouge">first</code>. Hence the completion. So, even though zipping is usually synonymous with “pairing” in my head, I’ll need to remember that doesn’t necessarily extend to completion events.</p> Buffered and batched subscriptions in Combine 2021-01-23T00:00:00+00:00 https://jasdev.me/notes/buffer-and-batch-subscriptions <h3 id="updates">Updates:</h3> <h4 id="12320">1/23/20:</h4> <p><a href="https://github.com/CombineCommunity/CombineExt/pull/73">PR’d <code class="language-plaintext highlighter-rouge">Collection.batchedSubscribe(by:)</code></a> to CombineExt.</p> <hr /> <p><a href="https://twitter.com/yar1vn">Yariv</a> asked a great question in iOS Folks’ #reactive channel the other day:</p> <blockquote> <p>I’m creating an array of <a href="https://developer.apple.com/documentation/foundation/urlsession/datataskpublisher"><code class="language-plaintext highlighter-rouge">URLSession.DataTaskPublisher</code></a>s — is there a way to perform them fifty at a time?</p> <p>…I think <a href="https://developer.apple.com/documentation/combine/publisher/collect(_:)"><code class="language-plaintext highlighter-rouge">Publisher.collect(50)</code></a> will cache the responses and return them in batches, but they’ll be subscribed to all at once.</p> </blockquote> <p>They’re spot on about <code class="language-plaintext highlighter-rouge">Publisher.collect(_:)</code>’s behavior — if we <code class="language-plaintext highlighter-rouge">flatMap</code> a sequence of publishers and then <code class="language-plaintext highlighter-rouge">collect</code>, the operator will subscribe to <em>all</em> of the upstream publishers and then emit their outputs in batches. Here’s a condensed example.</p> <script src="https://gist.github.com/jasdev/e1f35b0de714de9281d2241f449c0579.js"></script> <p>(<a href="https://gist.github.com/jasdev/e1f35b0de714de9281d2241f449c0579">Gist permalink</a>.)</p> <p>Ideally, we’d subscribe to upstream publishers and output in batches, bailing out if any publisher fails along the way. <a href="https://twitter.com/sharplet">Adam</a> quickly chimed in with a solution and <a href="https://twitter.com/nnnnnnnn">Nate</a>, <a href="https://twitter.com/freak4pc">Shai</a>, and I worked on another that also guarantees ordering within each batch.</p> <blockquote> <p>…throw a <a href="https://developer.apple.com/documentation/combine/publisher/buffer(size:prefetch:whenfull:)"><code class="language-plaintext highlighter-rouge">Publisher.buffer(size:prefetch:whenFull:)</code></a> before the <code class="language-plaintext highlighter-rouge">flatMap</code> and then use its optional <code class="language-plaintext highlighter-rouge">maxPublishers</code> argument to limit the number of concurrent requests.</p> </blockquote> <p>— Adam</p> <p>Translating this into a constrained extension on <code class="language-plaintext highlighter-rouge">Sequence</code>:</p> <script src="https://gist.github.com/jasdev/4e417aded551fafd37e8159066aa10b0.js"></script> <p>(<a href="https://gist.github.com/jasdev/4e417aded551fafd37e8159066aa10b0">Gist permalink</a>.)</p> <p>There’s three bits to note. First, <code class="language-plaintext highlighter-rouge">flatMap</code> will subscribe to (up to) <code class="language-plaintext highlighter-rouge">size</code> many publishers and let their outputs come in as is, not guaranteeing ordering <em>within</em> each batch. We can randomly tweak the delay in the earlier example to show this:</p> <script src="https://gist.github.com/jasdev/48a2ff15df057a05a8afd17507f36e37.js"></script> <p>(<a href="https://gist.github.com/jasdev/48a2ff15df057a05a8afd17507f36e37">Gist permalink</a>.)</p> <p>Second is <code class="language-plaintext highlighter-rouge">flatMap</code>’s <code class="language-plaintext highlighter-rouge">maxPublishers</code> argument allows for <code class="language-plaintext highlighter-rouge">size</code> publishers to be in-flight at once. That is, there will always be at most <code class="language-plaintext highlighter-rouge">size</code> subscribed publishers from upstream until they all complete or any one fails. This is slightly different than subscribing to <code class="language-plaintext highlighter-rouge">size</code> publishers, waiting for that batch to entirely complete (or fail), <em>and then</em> subscribing to the next <code class="language-plaintext highlighter-rouge">size</code> publishers (strictly batched subscribing and outputting). I’ll refer to Adam’s approach as “buffered subscribing, batched outputting.”</p> <p>And the third note is about the <code class="language-plaintext highlighter-rouge">buffer</code> call. Adam mentioned,</p> <blockquote> <p><code class="language-plaintext highlighter-rouge">buffer</code> may or may not be necessary depending on how upstream handles demand, since one way of handling backpressure is dropping upstream values.</p> </blockquote> <p>Thankfully <a href="https://developer.apple.com/documentation/combine/publishers/sequence"><code class="language-plaintext highlighter-rouge">Publishers.Sequence</code></a> — returned from the line above the <code class="language-plaintext highlighter-rouge">buffer</code> call — handles backpressure without dropping unrequested values, so we don’t need explicit buffering. Which begs the question? When would value dropping happen?</p> <p>Turns out Tony Parker noted one instance over in the Swift Forums.</p> <blockquote> <p><a href="https://forums.swift.org/t/combine-what-are-those-multicast-functions-for/26677/12">…the behavior you’re seeing is about the <code class="language-plaintext highlighter-rouge">PassthroughSubject</code> and not <code class="language-plaintext highlighter-rouge">flatMap</code>. <code class="language-plaintext highlighter-rouge">PassthroughSubject</code> will drop values if the downstream has not made any demand for them.</a></p> </blockquote> <p>Aha! Let’s whip up a quick example to show this.</p> <script src="https://gist.github.com/jasdev/14cabfc332217439390756e50d783bf9.js"></script> <p>(<a href="https://gist.github.com/jasdev/14cabfc332217439390756e50d783bf9">Gist permalink</a>.)</p> <p>The <code class="language-plaintext highlighter-rouge">2</code> gets dropped since <code class="language-plaintext highlighter-rouge">subscriber</code>’s demand was <code class="language-plaintext highlighter-rouge">.none</code> when it was sent — the OpenCombine folks also picked up on this detail <a href="https://github.com/OpenCombine/OpenCombine/blob/1fbf688897aaaa07bb0ba7c95e9fee0f0bcfb936/Sources/OpenCombine/PassthroughSubject.swift#L15-L16">in their implementation</a>. That tangent aside, and in short, Adam’s approach to buffered subscribing, batched outputting is as follows.</p> <script src="https://gist.github.com/jasdev/d6458b7ca8444beeaf036af56a8a2c0b.js"></script> <p>(<a href="https://gist.github.com/jasdev/d6458b7ca8444beeaf036af56a8a2c0b">Gist permalink</a>.)</p> <p>⬦</p> <p>Now, for strictly batched subscribing and outputting. Rephrased, subscribing to (up to) <code class="language-plaintext highlighter-rouge">limit</code> publishers, waiting until they all complete (or fail), outputting the batch in their originating order, and repeating until all publishers are exhausted. This was where Shai and Nate lent a hand. We’ll need to pin <code class="language-plaintext highlighter-rouge">batchedSubscribe(by:)</code> below to <code class="language-plaintext highlighter-rouge">Collection</code>, since we’ll lean on its <code class="language-plaintext highlighter-rouge">count</code> property to calculate batch offsets.</p> <script src="https://gist.github.com/jasdev/fce499fd8cb81b9640e9542d7e463bb9.js"></script> <p>(<a href="https://gist.github.com/jasdev/fce499fd8cb81b9640e9542d7e463bb9">Gist permalink</a>.)</p> <p>There’s…a lot going on here — let’s start at <code class="language-plaintext highlighter-rouge">indexBreaks</code>.</p> <p><code class="language-plaintext highlighter-rouge">indexBreaks</code> plucks out every <code class="language-plaintext highlighter-rouge">limit</code> index within <code class="language-plaintext highlighter-rouge">startIndex..&lt;endIndex</code>, which are then mapped into <code class="language-plaintext highlighter-rouge">Range</code>s on line 16 by traversing <code class="language-plaintext highlighter-rouge">indexBreaks</code> pairwise on 15. Then, we convert to a publisher, tee up the failure type, <code class="language-plaintext highlighter-rouge">flatMap</code> one batch at a time onto the <a href="https://github.com/CombineCommunity/CombineExt/blob/2bb2b5ac35847f253b5024d22d8a7704abd8d24a/Sources/Operators/ZipMany.swift#L43-L64"><code class="language-plaintext highlighter-rouge">CombineExt.Collection.zip</code></a>’d subrange of <code class="language-plaintext highlighter-rouge">self</code>, and finally erase out to an <code class="language-plaintext highlighter-rouge">AnyPublisher</code> (try saying this ten times fast hah).</p> <p>(We don’t need a <code class="language-plaintext highlighter-rouge">buffer</code> call before line 19 since <code class="language-plaintext highlighter-rouge">setFailureType</code> <a href="https://github.com/OpenCombine/OpenCombine/blob/1fbf688897aaaa07bb0ba7c95e9fee0f0bcfb936/Sources/OpenCombine/Publishers/Publishers.SetFailureType.swift#L34">directly subscribes downstream to its upstream</a>, which in this case is <code class="language-plaintext highlighter-rouge">Publishers.Sequence</code> and it doesn’t drop unrequested values.)</p> <p>Using <code class="language-plaintext highlighter-rouge">batchedSubscribe(by:)</code> in the randomly-delayed example now keeps in-batch ordering.</p> <script src="https://gist.github.com/jasdev/5382af8e5e1ac56ff051cb9e33097ddb.js"></script> <p>(<a href="https://gist.github.com/jasdev/5382af8e5e1ac56ff051cb9e33097ddb">Gist permalink</a>.)</p> <p>⬦</p> <p>Phew! It’s wicked that Yariv’s question shook out 600+ words of detail. If you end up using batch subscriptions for a client-server synchronization scenario, keep in mind that you might want to <a href="/materialization-primer"><code class="language-plaintext highlighter-rouge">materialize</code></a> to avoid any one request from bottoming out the entire sync attempt. I learned this the hard way back at Peloton when pairing this approach with a <code class="language-plaintext highlighter-rouge">retry</code> operator that accidentally ended up…self-DDoSing our API when some users’ historical workouts kept 500’ing (for reasons we couldn’t quickly triage during the incident). But, I’ll save learnings from that story for another entry.</p> little self, Big Self 2021-01-18T00:00:00+00:00 https://jasdev.me/notes/little-self-big-self <p>Yesterday I learned — let’s pretend “YIL” is a thing — <a href="https://docs.swift.org/swift-book/ReferenceManual/Expressions.html#ID401">Swift’s postfix <code class="language-plaintext highlighter-rouge">self</code></a> (i.e. <code class="language-plaintext highlighter-rouge">.self</code>) can be used after <em>any</em> expression (not just types). It’s a lesser-known arcanum in the language.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1.self // ⇒ evaluates to 1. "ayy lmao".self // ⇒ evaluates to "ayy lmao". "ayy lmao".self.count // ⇒ evaluates to 8. "ayy lmao".self.count.self % 2 // ⇒ evaluates to 0. </code></pre></div></div> <p>Unlike <code class="language-plaintext highlighter-rouge">TypeName.self</code> metatype expressions, <code class="language-plaintext highlighter-rouge">SomeExpression.self</code> postfix self expressions evaluate to the value of the expression. Which adds color to the decision behind <code class="language-plaintext highlighter-rouge">\.self</code> as the identity key path — it’s a sort of postfix self as a key path. QuinceyMorris <a href="https://forums.swift.org/t/grammar-of-postfix-self-expression/39316/3">noted in a Swift Forum’s thread</a> that this precedent comes from the Objective-C days,</p> <blockquote> <p>For historical, Objective-C* reasons, you can add <code class="language-plaintext highlighter-rouge">.self</code> to pretty much any expression and the resulting value is that same expression’s value.</p> <p>[…]</p> <p>*In Objective-C, [key-value coding (KVC)] always requires an object and a <em>non-empty</em> key-path to access a value. To reference the object itself, you don’t have a key-path, so <a href="https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418954-self"><code class="language-plaintext highlighter-rouge">NSObject</code> defined a <code class="language-plaintext highlighter-rouge">self</code> method</a>, which works as key-path “self” in KVC. It works like this in Swift, too:</p> <p><code class="language-plaintext highlighter-rouge">let myself = self[keyPath: \.self]</code></p> </blockquote> <p>⬦</p> <p>To make things doubly esoteric, I might’ve had <a href="https://twitter.com/jasdev/status/1303342026042339329">too much cold brew one morning</a> when I realized <code class="language-plaintext highlighter-rouge">Self.self</code> is a valid expression and can be used to back <code class="language-plaintext highlighter-rouge">Identifiable</code> conformances for one-off alerts or sheets in SwiftUI.</p> <script src="https://gist.github.com/jasdev/e765d7410c432af94170b02630fa5039.js"></script> <p>(<a href="https://gist.github.com/jasdev/e765d7410c432af94170b02630fa5039">Gist permalink</a>.)</p> <p><code class="language-plaintext highlighter-rouge">Self.self</code> allows <code class="language-plaintext highlighter-rouge">id</code> to follow suit with the type’s name, if it’s ever refactored down the line.</p> <p>⬦</p> <p>For more on Swift’s other <code class="language-plaintext highlighter-rouge">self</code>s, Jesse Squires has you covered in a <a href="https://www.jessesquires.com/blog/2020/12/28/the-different-types-of-self-in-swift/">recent blog post</a>.</p> Simultaneously mapping over publisher values and errors 2020-12-31T00:00:00+00:00 https://jasdev.me/notes/map-result <p><a href="https://twitter.com/daltonclaybrook">Dalton</a> nerd sniped me earlier today when he asked:</p> <blockquote> <p>Combine question: I’d love a “map”-like operator that is failable — like <a href="https://developer.apple.com/documentation/combine/publisher/trymap(_:)"><code class="language-plaintext highlighter-rouge">tryMap</code></a> — but where I end up with a strongly-typed <code class="language-plaintext highlighter-rouge">Error</code>. I’m picturing something akin to,</p> <p><code class="language-plaintext highlighter-rouge">func mapResult&lt;NewOutput, NewFailure&gt;(_ transform: @escaping (Result&lt;Output, Failure&gt;) -&gt; Result&lt;NewOutput, NewFailure&gt;) -&gt; AnyPublisher&lt;NewOutput, NewFailure&gt;</code>.</p> <p>Does this exist in the framework?</p> </blockquote> <p>It doesn’t, yet <code class="language-plaintext highlighter-rouge">Result</code> and <a href="/notes/result-publisher"><code class="language-plaintext highlighter-rouge">Result.publisher</code></a> make this wicked fun to pull off.</p> <script src="https://gist.github.com/jasdev/431828f5c740c5db01cba39e18f9b88c.js"></script> <p>The <code class="language-plaintext highlighter-rouge">transform</code> argument is also a specialized form of <a href="https://github.com/pointfreeco/swift-prelude/blob/0ec0a56bb99911647d2081f99a6f6c9699a6a646/Sources/Either/Either.swift#L118-L129"><code class="language-plaintext highlighter-rouge">Either.bimap</code></a> hiding in plain sight.</p> Redirecting Publisher errors 2020-12-19T00:00:00+00:00 https://jasdev.me/notes/assigning-errors <p><a href="https://twitter.com/sharplet">Adam Sharp</a> posted a snippet in iOS Folks today that I wanted to highlight (with a couple of light CombineExt-imported edits).</p> <script src="https://gist.github.com/jasdev/2a23a4e15e0654a8c73425a543c9d09f.js"></script> <p>This is a form of <a href="/materialization-primer">materialization</a> that feels more at home in Combine. It tucks away the awkward <a href="/materialization-primer#prior-art"><code class="language-plaintext highlighter-rouge">materialize</code>-<code class="language-plaintext highlighter-rouge">share</code>-<code class="language-plaintext highlighter-rouge">errors</code> dance</a> and instead focuses on funneling a publisher’s errors directly to a <code class="language-plaintext highlighter-rouge">@Published</code> property.</p> <p>It’s tempting to use <a href="https://developer.apple.com/documentation/combine/publisher/share()"><code class="language-plaintext highlighter-rouge">Publisher.share</code></a> at <code class="language-plaintext highlighter-rouge">(1)</code> — and Adam avoided it for good reason. <a href="https://github.com/OpenCombine/OpenCombine/blob/1fbf688897aaaa07bb0ba7c95e9fee0f0bcfb936/Sources/OpenCombine/Publishers/Publishers.Share.swift#L90"><code class="language-plaintext highlighter-rouge">share</code> <em>implicitly</em> <code class="language-plaintext highlighter-rouge">autoconnect</code>s</a>, which could accidentally trigger a subscription too early at the <code class="language-plaintext highlighter-rouge">assign</code> call at <code class="language-plaintext highlighter-rouge">(2)</code>. Instead, he wired up the error-focused publisher to a multicasted <code class="language-plaintext highlighter-rouge">PassthroughSubject</code> (<a href="https://github.com/OpenCombine/OpenCombine/blob/1fbf688897aaaa07bb0ba7c95e9fee0f0bcfb936/Sources/OpenCombine/Publishers/Publishers.Multicast.swift#L120">which returns a <code class="language-plaintext highlighter-rouge">ConnectablePublisher</code></a>) and then makes sure both subscriptions are properly teed up by <code class="language-plaintext highlighter-rouge">autoconnect</code>ing the returned publisher at <code class="language-plaintext highlighter-rouge">(3)</code>.</p> Mapping with parsers 2020-11-24T00:00:00+00:00 https://jasdev.me/notes/parser-map <p>The <a href="https://www.pointfree.co/episodes/ep126-generalized-parsing-part-3#exercise-1">first exercise from Point-Free episode #126</a> felt familiar. It asks the viewer to extend <code class="language-plaintext highlighter-rouge">Parser</code> with a placeholder-named method, <code class="language-plaintext highlighter-rouge">f</code>, which accepts <em>another</em> parser from <code class="language-plaintext highlighter-rouge">Output</code> to a possibly different output, <code class="language-plaintext highlighter-rouge">NewOutput</code>, to then return a parser of type <code class="language-plaintext highlighter-rouge">Parser&lt;Input, NewOutput&gt;</code>.</p> <p>Behind the nominal garnish, a <code class="language-plaintext highlighter-rouge">Parser</code> is a function from <code class="language-plaintext highlighter-rouge">(inout Input) -&gt; Output?</code>. So, it checks out that we can tee up parsers as long as their outputs and inputs match — the tricky bit is rolling back any modifications to the <code class="language-plaintext highlighter-rouge">Input</code> argument, if the first parsing succeeds and the second fails:</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">Parser</span> <span class="p">{</span> <span class="kd">func</span> <span class="n">f</span><span class="o">&lt;</span><span class="kt">NewOutput</span><span class="o">&gt;</span><span class="p">(</span> <span class="n">_</span> <span class="nv">parser</span><span class="p">:</span> <span class="kt">Parser</span><span class="o">&lt;</span><span class="kt">Output</span><span class="p">,</span> <span class="kt">NewOutput</span><span class="o">&gt;</span> <span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Parser</span><span class="o">&lt;</span><span class="kt">Input</span><span class="p">,</span> <span class="kt">NewOutput</span><span class="o">&gt;</span> <span class="p">{</span> <span class="o">.</span><span class="kd">init</span> <span class="p">{</span> <span class="n">input</span> <span class="k">in</span> <span class="k">let</span> <span class="nv">original</span> <span class="o">=</span> <span class="n">input</span> <span class="k">guard</span> <span class="k">var</span> <span class="nv">firstOutput</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="nf">run</span><span class="p">(</span><span class="o">&amp;</span><span class="n">input</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="kc">nil</span> <span class="p">}</span> <span class="k">guard</span> <span class="k">let</span> <span class="nv">secondOutput</span> <span class="o">=</span> <span class="n">parser</span><span class="o">.</span><span class="nf">run</span><span class="p">(</span><span class="o">&amp;</span><span class="n">firstOutput</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span> <span class="n">input</span> <span class="o">=</span> <span class="n">original</span> <span class="k">return</span> <span class="kc">nil</span> <span class="p">}</span> <span class="k">return</span> <span class="n">secondOutput</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>We’re lifting a <code class="language-plaintext highlighter-rouge">Parser&lt;Input, Output&gt;</code> to a <code class="language-plaintext highlighter-rouge">Parser&lt;Input, NewOutput&gt;</code> with the help of a <code class="language-plaintext highlighter-rouge">Parser&lt;Output, NewOutput&gt;</code>. Or in prose, we’re mapping a parser with a parser‽</p> <p>Sans the <code class="language-plaintext highlighter-rouge">inout</code> rollback dance, this situation is almost the same as <a href="https://github.com/pointfreeco/swift-case-paths/blob/469ef5103670d65f554f494ce9b6a46e0ec286db/Sources/CasePaths/CasePath.swift#L46"><code class="language-plaintext highlighter-rouge">CasePath.appending(path:)</code></a> (<a href="https://github.com/pointfreeco/swift-case-paths/blob/469ef5103670d65f554f494ce9b6a46e0ec286db/Sources/CasePaths/Operators.swift#L87-L101"><code class="language-plaintext highlighter-rouge">..</code> in operator form</a>). <code class="language-plaintext highlighter-rouge">CasePath.extract</code>’s <code class="language-plaintext highlighter-rouge">(Root) -&gt; Value?</code> shape is <code class="language-plaintext highlighter-rouge">Parser.run</code>’s immutable analog. Which hints that we can lift a case path into a parser.</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">import</span> <span class="kt">CasePaths</span> <span class="kd">extension</span> <span class="kt">Parser</span> <span class="p">{</span> <span class="nf">init</span><span class="p">(</span><span class="n">_</span> <span class="nv">path</span><span class="p">:</span> <span class="kt">CasePath</span><span class="o">&lt;</span><span class="kt">Input</span><span class="p">,</span> <span class="kt">Output</span><span class="o">&gt;</span><span class="p">)</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="kd">init</span> <span class="p">{</span> <span class="n">path</span><span class="o">.</span><span class="nf">extract</span><span class="p">(</span><span class="nv">from</span><span class="p">:</span> <span class="nv">$0</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>Parsing and case paths (prisms) appear linked in ways I have a feeling Jasdev-a-couple-of-years-from-now will better grok slash be too-hyped about and to get there, I’ll probably need to watch Fraser Tweedale’s <em><a href="https://www.youtube.com/watch?v=PXoel7U-W8Q">‌Unified Parsing and Printing with Prisms</a></em> a few more times. It’s exciting that their approach seems to ease the partial isomorphism requirement proposed section 3.1 of the original <em><a href="https://www.informatik.uni-marburg.de/~rendel/unparse/rendel10invertible.pdf">Invertible Syntax Descriptions</a></em> paper (e.g. a request parser doesn’t necessarily need to make a round trip when printing back the result of its parsing phase — a scenario that comes mind is an incoming request with unparsed query parameters getting nixed on the printing phase of the round trip).</p> combineLatest’ing an empty collection 2020-11-21T00:00:00+00:00 https://jasdev.me/notes/empty-combine-latest <p><a href="https://twitter.com/PAl_l_E">Palle</a> asked a thoughtful question in <a href="https://github.com/CombineCommunity/CombineExt/issues/65">an issue</a> on the CombineExt repository the other day:</p> <blockquote> <p>When calling <a href="https://github.com/CombineCommunity/CombineExt/blob/0e82c9c7f8de751151e21a013c2ed9914603ad30/Sources/Operators/CombineLatestMany.swift#L49-L70"><code class="language-plaintext highlighter-rouge">Collection.combineLatest</code></a> on an <em>empty</em> collection of publishers, an empty publisher is returned that completes immediately.</p> <p>Instead, wouldn’t it make sense to publish just an empty array instead (<code class="language-plaintext highlighter-rouge">Just([Output]())</code>)?</p> </blockquote> <p>And they have a point. Even though <a href="https://github.com/CombineCommunity/CombineExt/blob/0e82c9c7f8de751151e21a013c2ed9914603ad30/Sources/Operators/CombineLatestMany.swift#L57-L58">CombineExt</a> and <a href="https://github.com/ReactiveX/rxjs/blob/646d022122b5603dd9f98d6318682eae1400bf22/src/internal/observable/combineLatest.ts#L351-L352">RxJS</a> (links to each’s handling) return an empty sequence, <a href="https://github.com/ReactiveX/RxSwift/blob/ef4e11a5e9e8da7f9826755ec1b7bbfacd4d0e29/RxSwift/Observables/CombineLatest%2BCollection.swift#L130-L136">RxSwift forwards the result selector applied to an empty array <em>before</em> completing</a> and ReactiveSwift <a href="https://github.com/ReactiveCocoa/ReactiveSwift/blob/aac1d75082c27fb82d1a2bc31cf73f8f4c939721/Sources/SignalProducer.swift#L2133-L2137">even allows for an <code class="language-plaintext highlighter-rouge">emptySentinel</code> to be specified in this case</a>.</p> <p>I can understand both camps.</p> <ul> <li>One could argue that <code class="language-plaintext highlighter-rouge">combineLatest</code> should only emit when any one if its inner observables does and if there are none then it’s a no-go for value events (i.e. return an empty sequence).</li> <li>One could also argue that by <em>not</em> emitting a value event, this behavior truncates operator chains that might back UIs dependent on non-terminal events. Think <code class="language-plaintext highlighter-rouge">fetchArrayOfFriendIDs.flatMapLatest { ids in ids.map(publisherOfFriendDetails).combineLatest() }.bind(…)…</code> — the bindee (?) would never hear back if we completed immediately in the empty <code class="language-plaintext highlighter-rouge">ids</code> case.</li> </ul> <p>Here’s a quick workaround to land in the second camp while using CombineExt and then I wanted to note some theory that supports the position.</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">import</span> <span class="kt">CombineExt</span> <span class="p">[</span><span class="kt">Just</span><span class="o">&lt;</span><span class="kt">Int</span><span class="o">&gt;</span><span class="p">]()</span> <span class="c1">// (1) `Just&lt;Int&gt;` for sake of example, any `Publisher` will do.</span> <span class="o">.</span><span class="nf">combineLatest</span><span class="p">()</span> <span class="o">.</span><span class="nf">replaceEmpty</span><span class="p">(</span><span class="nv">with</span><span class="p">:</span> <span class="p">[])</span> <span class="c1">// (2) Forward a `[]` value event.</span> </code></pre></div></div> <p>Now for the — <em>cracks knuckles</em> — theory.</p> <p>To derive a non-empty publisher value of <code class="language-plaintext highlighter-rouge">[Just&lt;Int&gt;]().combineLatest()</code>, we’ll take the approach the Point-Free duo did back in <a href="https://www.pointfree.co/episodes/ep4-algebraic-data-types#t1168">episode #4</a> (timestamped) when they asked what a function <code class="language-plaintext highlighter-rouge">product: ([Int]) -&gt; Int</code>, which multiplies the supplied integers together, should return when called with an empty array.</p> <p>Translating their approach means figuring out how <code class="language-plaintext highlighter-rouge">combineLatest</code> should distribute across array concatenation,</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="kt">Just</span><span class="p">(</span><span class="mi">1</span><span class="p">)]</span> <span class="o">.</span><span class="nf">combineLatest</span><span class="p">()</span> <span class="o">.</span><span class="nf">combineLatest</span><span class="p">(</span> <span class="p">[</span><span class="kt">Just</span><span class="o">&lt;</span><span class="kt">Int</span><span class="o">&gt;</span><span class="p">]()</span> <span class="o">.</span><span class="nf">combineLatest</span><span class="p">()</span> <span class="p">)</span> <span class="c1">// Yields a `Publisher&lt;([Int], [Int])&gt;`.</span> </code></pre></div></div> <p>should ≈</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">([</span><span class="kt">Just</span><span class="p">(</span><span class="mi">1</span><span class="p">)]</span> <span class="o">+</span> <span class="p">[</span><span class="kt">Just</span><span class="o">&lt;</span><span class="kt">Int</span><span class="o">&gt;</span><span class="p">]())</span><span class="o">.</span><span class="nf">combineLatest</span><span class="p">()</span> <span class="c1">// Yields a `Publisher&lt;[Int]&gt;`, hence the `≈`.</span> </code></pre></div></div> <p>The righthand side of the equals sign evaluates to a publisher that emits a sole <code class="language-plaintext highlighter-rouge">[1]</code>, which forces our hand on the left side. <code class="language-plaintext highlighter-rouge">[Just&lt;Int&gt;]().combineLatest()</code> needs to return <em>at least one</em> value event to avoid cutting off the <code class="language-plaintext highlighter-rouge">[Just(1)].combineLatest()</code> before it from emitting.</p> <p>If <code class="language-plaintext highlighter-rouge">[Just&lt;Int&gt;]().combineLatest()</code> emits a sole <code class="language-plaintext highlighter-rouge">[]</code> then the first expression will emit a <code class="language-plaintext highlighter-rouge">([1], [])</code> — which is why we can only use approximate equality because there’s an isomorphism between <code class="language-plaintext highlighter-rouge">[1]</code> and <code class="language-plaintext highlighter-rouge">([1], [])</code> in the same way there’s one between <code class="language-plaintext highlighter-rouge">1</code> and <code class="language-plaintext highlighter-rouge">(1, ())</code> in tupled form.</p> <p>All of this is to sketch out that if we view <code class="language-plaintext highlighter-rouge">combineLatest</code><sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> as a monoidal operator, then a publisher that emits a single <code class="language-plaintext highlighter-rouge">[Output]()</code> (i.e. <code class="language-plaintext highlighter-rouge">Just([Output]())</code>) acts as the unit and in turn, the result of the <a href="https://en.wikipedia.org/wiki/Empty_product">empty product</a> under the operation.</p> <hr /> <div class="footnotes" role="doc-endnotes"> <ol> <li id="fn:1" role="doc-endnote"> <p>Or <code class="language-plaintext highlighter-rouge">Publisher.zip</code>! <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> </ol> </div> From parsing operators to methods 2020-11-19T00:00:00+00:00 https://jasdev.me/notes/parsing-operators <p>Updates:</p> <h4 id="111920">11/19/20:</h4> <p>Stephen and I chatted about the reasoning behind the <code class="language-plaintext highlighter-rouge">&lt;%&gt;</code> symbol choice (instead of overloading the <code class="language-plaintext highlighter-rouge">&lt;*&gt;</code> operator like in the original invertible syntax descriptions paper).</p> <p>It comes down to the authors using <code class="language-plaintext highlighter-rouge">&lt;*&gt;</code> in both a left- <em>and</em> right-associative manner, which isn’t possible in Swift and why <a href="https://github.com/pointfreeco/swift-prelude/blob/0ec0a56bb99911647d2081f99a6f6c9699a6a646/Sources/Prelude/Operators.swift#L21-L28"><code class="language-plaintext highlighter-rouge">swift-prelude</code> split out its apply operators</a>. Having a single operator with both associativities allows for the same expression to be used in printing and parsing (snippet from section 3.2).</p> <p><img src="/public/images/parsing-associativity.png" alt="" /></p> <hr /> <p>A sign of personal growth I’ve used while learning functional programming over the years is noticing when I grok types — in this case, custom operators — that previously felt out of reach. And recently, Point-Free’s older parsing operators: <a href="https://github.com/pointfreeco/swift-web/blob/616f365ac7c94e1d9adbec3799afeeebea63913a/Sources/ApplicativeRouter/SyntaxRouter.swift#L107-L125"><code class="language-plaintext highlighter-rouge">&lt;%&gt;</code></a>, <a href="https://github.com/pointfreeco/swift-web/blob/616f365ac7c94e1d9adbec3799afeeebea63913a/Sources/ApplicativeRouter/SyntaxRouter.swift#L133-L138"><code class="language-plaintext highlighter-rouge">&lt;%</code></a>, <a href="https://github.com/pointfreeco/swift-web/blob/616f365ac7c94e1d9adbec3799afeeebea63913a/Sources/ApplicativeRouter/SyntaxRouter.swift#L127-L130"><code class="language-plaintext highlighter-rouge">%&gt;</code></a>, and <a href="https://github.com/pointfreeco/swift-web/blob/616f365ac7c94e1d9adbec3799afeeebea63913a/Sources/ApplicativeRouter/SyntaxRouter.swift#L143-L150"><code class="language-plaintext highlighter-rouge">&lt;|&gt;</code></a> clicked after the duo rewrote them as methods in episodes <a href="https://www.pointfree.co/episodes/ep120-parser-combinators-recap-part-2#t1161">#120</a> and <a href="https://www.pointfree.co/episodes/ep123-fluently-zipping-parsers">123</a>.</p> <p>Let’s step through each:</p> <h3><code class="language-plaintext highlighter-rouge">&lt;%&gt;</code></h3> <p><code class="language-plaintext highlighter-rouge">&lt;%&gt;</code>’s new name is <a href="https://github.com/pointfreeco/episode-code-samples/blob/46a3fdd7da35d079c5aaa8a4b7fc5defe2c5be39/0123-fluently-zipping-parsers/FluentZip.playground/Contents.swift#L326-L330"><code class="language-plaintext highlighter-rouge">Parser.take(_:)</code></a>. The shape hints at its meaning by having both less-than and greater-than signs indicating that the outputs of <em>both</em> operands are paired (further suggesting how <code class="language-plaintext highlighter-rouge">&lt;%</code> and <code class="language-plaintext highlighter-rouge">%&gt;</code> behave).</p> <p><code class="language-plaintext highlighter-rouge">parserOfA &lt;%&gt; parserOfB</code> returns a <code class="language-plaintext highlighter-rouge">Parser&lt;Input, (A, B)&gt;</code>.</p> <p>Brandon and Stephen probably chose the percentage symbol since the operator used in the <a href="https://www.informatik.uni-marburg.de/~rendel/unparse/rendel10invertible.pdf">original paper on invertible syntax descriptions</a> is <code class="language-plaintext highlighter-rouge">&lt;*&gt;</code>, which is often reserved for applicative <a href="https://hackage.haskell.org/package/base-4.14.0.0/docs/Prelude.html#v:-60--42--62-">sequential application</a>.</p> <hr /> <h3 id="-1"><code class="language-plaintext highlighter-rouge">&lt;%</code></h3> <p>As you probably guessed, <code class="language-plaintext highlighter-rouge">&lt;%</code> zips two parsers and discards the result of the righthand one. Or as methods, <a href="https://github.com/pointfreeco/episode-code-samples/blob/46a3fdd7da35d079c5aaa8a4b7fc5defe2c5be39/0123-fluently-zipping-parsers/FluentZip.playground/Contents.swift#L320-L324">these</a> <a href="https://github.com/pointfreeco/episode-code-samples/blob/46a3fdd7da35d079c5aaa8a4b7fc5defe2c5be39/0123-fluently-zipping-parsers/FluentZip.playground/Contents.swift#L614-L617">two</a> <code class="language-plaintext highlighter-rouge">Parser.skip(_:)</code> overloads (the latter is used to skip over the first parser in a zipped chain).</p> <hr /> <h3 id="-2"><code class="language-plaintext highlighter-rouge">%&gt;</code></h3> <p>Conversely, <code class="language-plaintext highlighter-rouge">%&gt;</code> discards the left and keeps the righthand result. What’s wicked is that the combinator methods can express this without a new name and instead as an overload on <code class="language-plaintext highlighter-rouge">take</code> constrained to <code class="language-plaintext highlighter-rouge">Parser.Output == Void</code>.</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">Parser</span> <span class="k">where</span> <span class="kt">Output</span> <span class="o">==</span> <span class="kt">Void</span> <span class="p">{</span> <span class="kd">func</span> <span class="n">take</span><span class="o">&lt;</span><span class="kt">A</span><span class="o">&gt;</span><span class="p">(</span><span class="n">_</span> <span class="nv">p</span><span class="p">:</span> <span class="kt">Parser</span><span class="o">&lt;</span><span class="kt">Input</span><span class="p">,</span> <span class="kt">A</span><span class="o">&gt;</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Parser</span><span class="o">&lt;</span><span class="kt">Input</span><span class="p">,</span> <span class="kt">A</span><span class="o">&gt;</span> <span class="p">{</span> <span class="nf">zip</span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="n">p</span><span class="p">)</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="n">_</span><span class="p">,</span> <span class="n">a</span> <span class="k">in</span> <span class="n">a</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <hr /> <h3 id="-3"><code class="language-plaintext highlighter-rouge">&lt;|&gt;</code></h3> <p>Last up is the cousin of the previous three, the analog to <a href="https://github.com/pointfreeco/episode-code-samples/blob/46a3fdd7da35d079c5aaa8a4b7fc5defe2c5be39/0120-parsers-recap-pt2/ParsingRecap.playground/Contents.swift#L310-L319"><code class="language-plaintext highlighter-rouge">Parser.oneOf(_:)</code></a>.</p> <p>Akin to how Boolean ORs (<code class="language-plaintext highlighter-rouge">||</code>) short-circuit once a <code class="language-plaintext highlighter-rouge">true</code> value is evaluated, the <code class="language-plaintext highlighter-rouge">&lt;|&gt;</code> shape signals that the combinator will run each operand in order and stop when one parses successfully.</p> <p>The operator both mirrors the original form in the previously-linked paper and follows suit with the <a href="https://hackage.haskell.org/package/base-4.14.0.0/docs/Control-Applicative.html#v:-60--124--62-"><code class="language-plaintext highlighter-rouge">Alternative</code> typeclass requirement</a><sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> from Haskell’s Prelude.</p> <hr /> <p>Hopefully this note can serve as a reference for folks reading through <a href="https://github.com/pointfreeco/pointfreeco/blob/fab6d0cc57614314d79e4bd2307f4273a474b5b5/Sources/PointFreeRouter/Routes.swift#L138-L320">the router behind pointfree.co</a> until it’s updated with the method forms of these parser combinators.</p> <hr /> <div class="footnotes" role="doc-endnotes"> <ol> <li id="fn:1" role="doc-endnote"> <p>I recently learned that <a href="https://github.com/CombineCommunity/CombineExt/tree/0e82c9c7f8de751151e21a013c2ed9914603ad30#amb"><code class="language-plaintext highlighter-rouge">Publisher.amb</code></a> feeling surprisingly similar to <code class="language-plaintext highlighter-rouge">oneOf</code> is because the operator is <code class="language-plaintext highlighter-rouge">Publisher</code>’s hidden implementation of <code class="language-plaintext highlighter-rouge">Alternative</code>’s <code class="language-plaintext highlighter-rouge">&lt;|&gt;</code> requirement (!). <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> </ol> </div> Empty reducers and the monoidal unit 2020-09-08T00:00:00+00:00 https://jasdev.me/notes/empty-reducer <p>Most writing about <a href="https://github.com/pointfreeco/swift-prelude/blob/0ec0a56bb99911647d2081f99a6f6c9699a6a646/Sources/Prelude/Monoid.swift#L1-L3">monoids</a> starts by listing out the axioms (or in programming, the protocol or type class requirements) before diving into examples. And as Jeremy Kun <a href="https://jeremykun.com/2011/06/26/teaching-mathematics-graph-theory/">notes</a>, “[this is not only confusing, but boring (!)] to an untrained student. They don’t have the prerequisite intuition for <em>why</em> definitions are needed, and they’re left mindlessly following along at best.”</p> <p>The Point-Free duo does a wonderful job at avoiding this — episodes start with and cover examples until a common shape is noticed and then chip away specializations until a more general form, and in turn, the axioms shake out. Rephrased, making an axiom earn its keep by pointing out problems that occur in its absence.</p> <p>In episode <a href="https://www.pointfree.co/episodes/ep116-redacted-swiftui-the-composable-architecture">#116</a>, Brandon and Stephen did just that with the <a href="https://github.com/pointfreeco/swift-composable-architecture/blob/58d6f5a0560ff86ad031718cb676a455ad55ea2c/Sources/ComposableArchitecture/Reducer.swift#L4-L20"><code class="language-plaintext highlighter-rouge">Reducer</code></a> type’s monoidal unit. Instead of conforming <code class="language-plaintext highlighter-rouge">Reducer</code> to <code class="language-plaintext highlighter-rouge">Monoid</code>, they instead guided us into a situation — disabling logic on <a href="https://developer.apple.com/documentation/swiftui/view/redacted(reason:)">redacted</a> views — where an <em>inert</em> reducer is needed to avoid state mutations and side effects. Or under their library’s name, <a href="https://github.com/pointfreeco/swift-composable-architecture/blob/af444eeec219280d31282d5b4657ae7c0e09e86a/Sources/ComposableArchitecture/Reducer.swift#L54-L57"><code class="language-plaintext highlighter-rouge">Reducer.empty</code></a>.</p> <p>They subtly dropped the need for this axiom <a href="https://www.pointfree.co/episodes/ep116-redacted-swiftui-the-composable-architecture#t940">roughly 16 minutes in</a> and it felt worth the extra show note here.</p> <p>For a longer treatment on the topic, here’s <a href="https://youtu.be/QOIigosUNGU?t=336">a timestamped link</a> from one of Brandon’s older talks: “Composable Reducers and Effects Systems.”</p> Conditional gestures in SwiftUI 2020-08-15T00:00:00+00:00 https://jasdev.me/notes/conditional-gestures <p>Conditionally enabling a gesture in SwiftUI wasn’t as intuitive for me compared to other modifiers. Showing or hiding a view is an <code class="language-plaintext highlighter-rouge">.opacity(someCondition ? 1 : 0)</code> (or <code class="language-plaintext highlighter-rouge">if</code>-<code class="language-plaintext highlighter-rouge">else</code> in a <code class="language-plaintext highlighter-rouge">ViewBuilder</code> or <a href="https://developer.apple.com/documentation/swiftui/view/hidden()"><code class="language-plaintext highlighter-rouge">.hidden()</code></a>) away. But for gestures? It felt off to have to <code class="language-plaintext highlighter-rouge">.gesture(someCondition ? DragGesture().onChanged { /* … */ }.onEnded { /* … */ } : DragGesture())</code>, where the first branch returns a live gesture and the second, an inert one. The types returned from the ternary need to line up — there’s an <a href="https://developer.apple.com/documentation/swiftui/anygesture"><code class="language-plaintext highlighter-rouge">AnyGesture</code></a> eraser in the framework that usually helps in these situations, yet it still begs the question of which instance to erase in the disabled case.</p> <p>A search online for “SwiftUI disable gesture” tops out with Paul Hudson’s post, “<a href="https://www.hackingwithswift.com/books/ios-swiftui/disabling-user-interactivity-with-allowshittesting">Disabling user interactivity with <code class="language-plaintext highlighter-rouge">allowsHitTesting(_:)</code></a>” and while that modifier works in some situations, it was too coarse for the one I was in. I needed to disable a drag gesture and keep a tap gesture on the same view in tact and <code class="language-plaintext highlighter-rouge">allowsHitTesting</code> wholesale disables both.</p> <p>Poking around the <code class="language-plaintext highlighter-rouge">Gesture</code> protocol’s listed conformances had the answer I was looking for — <code class="language-plaintext highlighter-rouge">Optional</code> <a href="https://developer.apple.com/documentation/swift/optional#relationships">conditionally conforms to</a> <code class="language-plaintext highlighter-rouge">Gesture</code>!</p> <p><img src="/public/images/optional_gesture.png" alt="" /></p> <p>And <a href="https://twitter.com/harlanhaskins">Harlan Haskins</a> helped me tidy the above ternary to <code class="language-plaintext highlighter-rouge">.gesture(someCondition ? DragGesture().onChanged { /* … */ }.onEnded { /* … */ } : nil)</code>. What’s wicked here is the gesture is of type <code class="language-plaintext highlighter-rouge">_EndedGesture&lt;_ChangedGesture&lt;DragGesture&gt;&gt;</code> and Swift is able to promote the <code class="language-plaintext highlighter-rouge">nil</code> to an optionally-wrapped version without any added annotations.</p> Swimming out past intuition 2020-07-26T00:00:00+00:00 https://jasdev.me/notes/swimming-past-intuition <p>After a <a href="/notes/binding-functor">recent note</a> on <code class="language-plaintext highlighter-rouge">Binding[dynamicMember:]</code>, I’ve jokingly started a Twitter thread with the type’s affordances — à la Rob Rix’s “<a href="https://twitter.com/rob_rix/status/966516982890881024">it type checks, but what does it mean?</a>”</p> <p>So far I’ve got <code class="language-plaintext highlighter-rouge">Binding.zip</code> and <code class="language-plaintext highlighter-rouge">.flatMap</code> (or alternatively, <code class="language-plaintext highlighter-rouge">join</code> paired with the dynamic member subscript).</p> <script src="https://gist.github.com/jasdev/f4a56ee700576aebc57627c23f21c431.js"></script> <p>(<a href="https://gist.github.com/jasdev/f4a56ee700576aebc57627c23f21c431">Gist permalink</a>.)</p> <p>Type checking drove the implementations since I don’t grok them yet. What does it mean to <code class="language-plaintext highlighter-rouge">flatMap</code> on <code class="language-plaintext highlighter-rouge">Binding</code>s? Is that something we’d even want to do?</p> <p>The compiler signaled the “reachability” of these functions, even if my understanding wasn’t there. And this is why proof assistants like <a href="https://leanprover.github.io/about/">Lean</a> and community efforts behind them (the <a href="https://xenaproject.wordpress.com/what-is-the-xena-project/">Xena project</a>) excite me. If mathematics can be formalized to a point where assistants can guide our work in the same way a compiler does for engineering, we can swim past our present intuitions.</p> <p>It’ll take a while for our collective knowledge to catch up to a lengthened line of sight, but we also needed telescopes before we could physically explore space.</p> Why folks use <> for same-type composition 2020-07-19T00:00:00+00:00 https://jasdev.me/notes/endo-composition <p>A wondering I had back when I started studying FP in earnest was why folks used <a href="https://github.com/pointfreeco/swift-prelude/blob/0ec0a56bb99911647d2081f99a6f6c9699a6a646/Sources/Prelude/Semigroup.swift#L1-L3"><code class="language-plaintext highlighter-rouge">&lt;&gt;</code></a> for same-type composition instead of the more general <a href="https://github.com/pointfreeco/swift-prelude/blob/0ec0a56bb99911647d2081f99a6f6c9699a6a646/Sources/Prelude/Function.swift#L9-L11"><code class="language-plaintext highlighter-rouge">&gt;&gt;&gt;</code></a> operator (which stitches <code class="language-plaintext highlighter-rouge">(A) -&gt; B</code>s with <code class="language-plaintext highlighter-rouge">(B) -&gt; C</code>s).</p> <p>And yesterday while re-watching Brandon Williams’ “Monoids, predicates and sorting functions” talk, I found <a href="https://youtu.be/VFPhPOnPiTY?t=440">a likely answer</a> (timestamped link).</p> <p>Functions in the form <code class="language-plaintext highlighter-rouge">(A) -&gt; A</code> — endomorphisms — come with a semigroup (and monoidal) structure and the diamond operator is a nod to that. The operator is subtly interchangeable with <code class="language-plaintext highlighter-rouge">&gt;&gt;&gt;</code> because <code class="language-plaintext highlighter-rouge">Endo</code>’s<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> conformance leans on function composition under the hood.</p> <script src="https://gist.github.com/jasdev/6514c9e87e8554154cbc2cd888153d26.js"></script> <p>(<a href="https://gist.github.com/jasdev/6514c9e87e8554154cbc2cd888153d26">Gist permalink</a>.)</p> <hr /> <div class="footnotes" role="doc-endnotes"> <ol> <li id="fn:1" role="doc-endnote"> <p>The nominal veneer often used over the non-nominal <code class="language-plaintext highlighter-rouge">(A) -&gt; A</code> type. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> </ol> </div> Pausing with Binding’s dynamic member subscript 2020-07-19T00:00:00+00:00 https://jasdev.me/notes/binding-functor <p>Roughly thirteen minutes into Point-Free <a href="https://www.pointfree.co/episodes/ep108-composable-swiftui-bindings-case-paths#t807">episode #108</a> (timestamped), Stephen and Brandon showed how SwiftUI’s <a href="https://developer.apple.com/documentation/swiftui/binding#"><code class="language-plaintext highlighter-rouge">Binding</code></a> type secretly has an implementation of <code class="language-plaintext highlighter-rouge">map</code> under the guise of its <a href="https://developer.apple.com/documentation/swiftui/binding/3264175-subscript">dynamic member subscript</a>.</p> <p>I didn’t fully realize the gravity of that statement until, well, a week later.</p> <p><code class="language-plaintext highlighter-rouge">Binding</code> is the first real-world functor I’ve used with two components — hidden by way of its <a href="https://developer.apple.com/documentation/swiftui/binding/3363053-init"><code class="language-plaintext highlighter-rouge">.init(get:set:)</code></a> initializer — of <em>different</em> variances. <code class="language-plaintext highlighter-rouge">Value</code> is in the covariant position on the getter and in the contravariant position on the setter. The only other two-component functors I’ve used — <code class="language-plaintext highlighter-rouge">Either</code> and tuples — are plain old bifunctors<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. And to make it even more opaque, the dynamic member subscript only accepts one argument, a <code class="language-plaintext highlighter-rouge">WritableKeyPath&lt;Value, Subject&gt;</code>, that can somehow handle the two variances.</p> <p>Below is me pausing with and unpacking how this works.</p> <p>First, let’s imagine implementing <code class="language-plaintext highlighter-rouge">Binding.map</code> without a <code class="language-plaintext highlighter-rouge">WritableKeypath</code> for a closer look.</p> <script src="https://gist.github.com/jasdev/2716559834c947b1369b5e4b1109adb0.js"></script> <p>(<a href="https://gist.github.com/jasdev/2716559834c947b1369b5e4b1109adb0">Gist permalink</a>.)</p> <p>In implementing <code class="language-plaintext highlighter-rouge">(1)</code>, we have access to <code class="language-plaintext highlighter-rouge">self.wrappedValue</code> and need to conjure a <code class="language-plaintext highlighter-rouge">Subject</code> instance. Which means that <code class="language-plaintext highlighter-rouge">Binding.map</code> needs to accept a <code class="language-plaintext highlighter-rouge">(Value) -&gt; Subject</code> transformation. Check.</p> <script src="https://gist.github.com/jasdev/8b2428e2f54da1d75231f4abc52185cf.js"></script> <p>(<a href="https://gist.github.com/jasdev/8b2428e2f54da1d75231f4abc52185cf">Gist permalink</a>.)</p> <p>Onto <code class="language-plaintext highlighter-rouge">(2)</code>.</p> <p>We need to implement <code class="language-plaintext highlighter-rouge">(Subject) -&gt; Void</code> again with only a <code class="language-plaintext highlighter-rouge">Value</code> instance in hand. That is, a way to mutate our wrapped value with a <code class="language-plaintext highlighter-rouge">Subject</code> instance — shaking out the second parameter: <code class="language-plaintext highlighter-rouge">(inout Value, Subject) -&gt; Void</code>.</p> <script src="https://gist.github.com/jasdev/ca7b06625bfcff697d6e9f77cc774d0a.js"></script> <p>(<a href="https://gist.github.com/jasdev/ca7b06625bfcff697d6e9f77cc774d0a">Gist permalink</a>.)</p> <p>Now it’s more explicit that mapping a <code class="language-plaintext highlighter-rouge">Binding&lt;Value&gt;</code> to a <code class="language-plaintext highlighter-rouge">Binding&lt;Subject&gt;</code>, requires <em>two</em> transformations. One with <code class="language-plaintext highlighter-rouge">Subject</code> in the covariant position (<code class="language-plaintext highlighter-rouge">get</code>) and the other in the contravariant (<code class="language-plaintext highlighter-rouge">set</code>).</p> <p>Which then raises the question, how does Apple pull this off with only a <code class="language-plaintext highlighter-rouge">WritableKeyPath&lt;Value, Subject&gt;</code>?</p> <p>Turns out the <code class="language-plaintext highlighter-rouge">Writable*</code> bit is important.</p> <p><code class="language-plaintext highlighter-rouge">Binding.map</code>’s <code class="language-plaintext highlighter-rouge">get</code> parameter can be satisfied by the fact that any <code class="language-plaintext highlighter-rouge">KeyPath</code> from <code class="language-plaintext highlighter-rouge">Value</code> to <code class="language-plaintext highlighter-rouge">Subject</code> is equivalent to a <code class="language-plaintext highlighter-rouge">(Value) -&gt; Subject</code> function (with the help of <a href="https://github.com/apple/swift-evolution/blob/7c50f9a5680e79ca720f75b3e636b0e43483be5d/proposals/0249-key-path-literal-function-expressions.md">SE-0249</a>).</p> <p>How about <code class="language-plaintext highlighter-rouge">set</code>?</p> <p>Can we convert a <code class="language-plaintext highlighter-rouge">WritableKeyPath&lt;Value, Subject&gt;</code> to an <code class="language-plaintext highlighter-rouge">(inout Value, Subject) -&gt; Void</code> function?</p> <p>…we can! Let’s call the conversion <code class="language-plaintext highlighter-rouge">setterFromKeyPath</code>:</p> <script src="https://gist.github.com/jasdev/31c50e00016aa03866adafd501708bde.js"></script> <p>(<a href="https://gist.github.com/jasdev/31c50e00016aa03866adafd501708bde">Gist permalink</a>.)</p> <p>This conversion is how <code class="language-plaintext highlighter-rouge">WritableKeyPath</code> packs the <code class="language-plaintext highlighter-rouge">get</code>-<code class="language-plaintext highlighter-rouge">set</code> punch needed to call <code class="language-plaintext highlighter-rouge">Binding[dynamicMember:]</code> an implementation of <code class="language-plaintext highlighter-rouge">map</code> on the type.</p> <p>A whole lot of detail packed into those first thirteen minutes of the episode, eh?</p> <hr /> <div class="footnotes" role="doc-endnotes"> <ol> <li id="fn:1" role="doc-endnote"> <p><a href="https://github.com/pointfreeco/swift-prelude/blob/0ec0a56bb99911647d2081f99a6f6c9699a6a646/Sources/Either/Either.swift#L118-L129"><code class="language-plaintext highlighter-rouge">Either.bimap</code></a> and <a href="https://hackage.haskell.org/package/base-4.14.0.0/docs/src/Data.Bifunctor.html#line-114">the source</a> for <code class="language-plaintext highlighter-rouge">(,)</code>’s <code class="language-plaintext highlighter-rouge">Bifunctor</code> instance on Hackage. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> </ol> </div> Stress testing variadic zip 2020-05-23T00:00:00+00:00 https://jasdev.me/notes/variadic-zip-cash <p>Yesterday, <a href="https://twitter.com/xcadaverx">Daniel Williams</a> and I messaged through a crasher he ran into when using <a href="https://github.com/CombineCommunity/CombineExt/blob/027163d6c825abe77c7625df2eec00fe3bae1032/Sources/Operators/ZipMany.swift"><code class="language-plaintext highlighter-rouge">CombineExt.Collection.zip</code></a> (similarly with <a href="https://github.com/CombineCommunity/CombineExt/blob/027163d6c825abe77c7625df2eec00fe3bae1032/Sources/Operators/CombineLatestMany.swift"><code class="language-plaintext highlighter-rouge">.Collection.combineLatest</code></a>).</p> <p>For the uninitiated, Combine ships with <code class="language-plaintext highlighter-rouge">zip</code> (and <code class="language-plaintext highlighter-rouge">combineLatest</code>) overloads up to arity four in the <code class="language-plaintext highlighter-rouge">Publisher</code> namespace.</p> <ul> <li><a href="https://developer.apple.com/documentation/combine/publisher/3333687-zip"><code class="language-plaintext highlighter-rouge">.zip(_:_:_:)</code></a></li> <li><a href="https://developer.apple.com/documentation/combine/publisher/3333680-combinelatest"><code class="language-plaintext highlighter-rouge">.combineLatest(_:_:_:)</code></a></li> </ul> <p>But, if you want to zip arbitrarily many publishers, you’re kind of stuck and as more Combine code gets written, folks are quickly realizing this. That’s why <a href="https://github.com/CombineCommunity/CombineExt/graphs/contributors">we’ve</a> been heads down filling in gaps with an extensions package to sit next to Combine proper.</p> <p>Daniel was attempting to first ping <code class="language-plaintext highlighter-rouge">https://hacker-news.firebaseio.com/v0/topstories.json</code> for an array of Hacker News story IDs and then hydrate each by hitting the <code class="language-plaintext highlighter-rouge">https://hacker-news.firebaseio.com/v0/item/:id.json</code> endpoint. The former returns on the order of 500 entries and that turned out to be enough to push variadic zip beyond its limits.</p> <p>We can reduce the scenario down with convenience publishers for a closer look.</p> <script src="https://gist.github.com/jasdev/18aed3f3fe550a262ce3a2823fa40fd1.js"></script> <p>(<a href="https://gist.github.com/jasdev/18aed3f3fe550a262ce3a2823fa40fd1">Gist permalink</a>.)</p> <p>(You might need to tweak <code class="language-plaintext highlighter-rouge">count</code> to trigger the crash.)</p> <p>The stack trace is a head scratcher.</p> <p><img src="/public/images/zip_crash.png" alt="" /></p> <p>And the repeated <code class="language-plaintext highlighter-rouge">Zip</code>, <code class="language-plaintext highlighter-rouge">Map</code>, and <code class="language-plaintext highlighter-rouge">PublisherBox</code> frames hint at the issue.</p> <p>CombineExt’s variadic <code class="language-plaintext highlighter-rouge">zip</code> and <code class="language-plaintext highlighter-rouge">combineLatest</code> are “composed” operators — they’re built up from existing <code class="language-plaintext highlighter-rouge">Publisher</code> methods instead of a more dedicated conformance. While this simplifies things and lets each implementation measure out to ~15 lines, it also introduces intermediate runtime overhead.</p> <p>Let’s take a look at why (in shorter form, here’s the <a href="https://github.com/CombineCommunity/CombineExt/blob/027163d6c825abe77c7625df2eec00fe3bae1032/Sources/Operators/ZipMany.swift">fuller implementation</a>).</p> <script src="https://gist.github.com/jasdev/17e11cffe79567d2b1334d1fcdbd22e8.js"></script> <p>(<a href="https://gist.github.com/jasdev/17e11cffe79567d2b1334d1fcdbd22e8">Gist permalink</a>.)</p> <ol> <li>The only way to line up <code class="language-plaintext highlighter-rouge">seed</code>’s type with <code class="language-plaintext highlighter-rouge">reduce</code>’s accumulator is to erase — or, at least I tried without in hopes of preserving <a href="/fusion-primer">fusion</a> and got type checked into a corner.</li> <li>This and the following two lines are the source of the <code class="language-plaintext highlighter-rouge">Zip</code>, <code class="language-plaintext highlighter-rouge">Map</code>, and <code class="language-plaintext highlighter-rouge">PublisherBox</code> stack trace dance. As we approach thousands of publishers, we’re triply nesting for each.</li> </ol> <p>Can we fix this?</p> <p>Yep — by writing <a href="https://github.com/ReactiveX/RxSwift/blob/a580d07ed002217fd91d8446c3a852486e9beefa/RxSwift/Observables/Zip%2BCollection.swift#L37-L169">a specialized <code class="language-plaintext highlighter-rouge">ZipCollectionType</code></a> à la RxSwift’s! But with WWDC around the corner, it’s probably best to hang tight and see if the Combine team will address the gap.</p> <p>Until then, and if you want to read more about variadic zipping, an <a href="/zip-many">older entry</a> has your back.</p> Why “sink?” 2020-04-25T00:00:00+00:00 https://jasdev.me/notes/why-sink <p>“Sink” is a word you’ll see all over <del>reactive</del> declarative programming, and Combine is no exception.</p> <p>There’s <a href="https://developer.apple.com/documentation/combine/subscribers/sink"><code class="language-plaintext highlighter-rouge">Subscribers.Sink</code></a> (the subscriber behind the two <a href="https://developer.apple.com/documentation/combine/publisher/3343978-sink"><code class="language-plaintext highlighter-rouge">Publisher.sink</code></a> overloads), <a href="https://github.com/CombineCommunity/CombineExt/blob/be7e1d0bff6be287409252f860c86f210a6dacba/Sources/Common/Sink.swift"><code class="language-plaintext highlighter-rouge">CombineExt.Sink</code></a> (albeit <code class="language-plaintext highlighter-rouge">internal</code>ly-scoped), and similarly in the framework’s predecessor, <a href="https://github.com/ReactiveX/RxSwift/blob/a580d07ed002217fd91d8446c3a852486e9beefa/RxSwift/Observables/Sink.swift"><code class="language-plaintext highlighter-rouge">RxSwift.Sink</code></a>.</p> <p>The often-cited kitchen sink metaphor aside, the term’s etymology is a bit unclear. My guess would be it borrows from the <a href="https://mathworld.wolfram.com/DigraphSink.html">corresponding graph theory term</a>.</p> <blockquote> <p>A <em>local sink</em> is a node of a directed graph with no exiting edges, also called a terminal.</p> </blockquote> <p><img src="/public/images/sink.gif" alt="" class="center-image" /></p> <p>We can view a subscription graph as a directed graph between upstream publishers, through various operators, and down towards local sinks (which, in Combine’s language are <a href="https://developer.apple.com/documentation/combine/subscriber#"><code class="language-plaintext highlighter-rouge">Subscriber</code></a>s).</p> Profunctors generalize relations 2020-04-25T00:00:00+00:00 https://jasdev.me/notes/profuctors-generalize-relations <p>An aside before the note, Day One reminded me that I started studying category theory, in earnest, almost a year ago today.</p> <p>Despite being a hobby, I’m pretty proud of how far I’ve come (I struggle with saying that aloud, since I’m the type to keep pushing forward and not really look around along the way).</p> <p>I’m</p> <ul> <li>in a <a href="https://sites.google.com/view/mact2020/home">research group</a>,</li> <li><a href="https://github.com/jasdev/18S097-swift">followed along and typeset solutions</a> for <em>Programming with Categories</em>,</li> <li>shot my shot and applied to <a href="https://www.appliedcategorytheory.org/adjoint-school-act-2020/">Adjoint School</a> and <a href="https://sites.northwestern.edu/causeway/">Causeway</a> (got rejected from the former and the latter got cancelled—still, I tried),</li> <li>and am learning from and met incredibly kind folks like <a href="https://twitter.com/JadeMasterMath">Jade</a>, <a href="https://twitter.com/sarah_zrf">Sarah</a>, and <a href="https://twitter.com/jeremyjkun">Jeremy</a>.</li> </ul> <p><a href="https://en.wikipedia.org/wiki/Richard_K._Guy">Richard Guy</a> put how I’ve felt as of late succinctly.</p> <blockquote> <p>…and I love anybody who can [do mathematics] well, so I just like to hang on and try to copy them as best I can, even though I’m not really in their league.</p> </blockquote> <p>(I’m not sure if anyone reads these entries hah (quite literally, I removed analytics on the site a few years ago). If so, pardon the moment to reflect.)</p> <p>⬦</p> <p>I was revisiting profunctors yesterday and Bartosz <a href="https://www.youtube.com/watch?v=XJgfrF3O6iE&amp;feature=youtu.be&amp;t=1152">mentioned an intuition in lecture III.6.1</a> (timestamped) that made their motivation click.</p> <blockquote> <p>You can think of a profunctor as [generalizing] a relation between objects.</p> </blockquote> <p>Huh, okay. Let’s take a step back and recap what a relation is in plain ol’ set theory and follow the intuition.</p> <p>A binary relation over two sets $X$ and $Y$ is a subset of their Cartesian product, $X \times Y$. That is, a set of ordered pairs indicating which $X$s are related to specific $Y$s.</p> <p>And now to generalize.</p> <p>First, let’s swap out the sets for categories $C$ and $D$.</p> <p>$C \times D$. Okay, the <a href="https://en.wikipedia.org/wiki/Product_category">product category</a>. Since $C$ and $D$ are possibly distinct categories, we can’t directly consider morphisms between them. But, we can in their product category—morphisms between objects $(c, d)$ and $(c^\prime, d^\prime)$ are those in the form $(f, g)$ with $f: c \rightarrow c^\prime$ and $g: d \rightarrow d^\prime$. So, in a sense, the collection of relationships between $c$ and $d$ is $\textrm{Hom}((c, d), (c^\prime, d^\prime))$.</p> <p>That hom-set is, well, a set (assuming we’re working with small categories)! What if we tried to create a functor from $C \times D \rightarrow \mathbf{Set}$ defined by $(c, d) \mapsto \ldots$</p> <p>Wait. $c$ and $d$ come from different categories and hom-sets only work in a single category. I read around to reconcile this and stumbled upon <a href="https://ncatlab.org/nlab/show/heteromorphism">heteromorphisms</a>. “Morphisms” between two objects from different categories that use a bridging functor to then construct a hom-set. I got lost trying to read further, and with warning from slide 3/41 of David Ellerman’s <a href="http://www.ellerman.org/wp-content/uploads/2016/02/Hets-talk.pdf">presentation on the topic</a>.</p> <p><img src="/public/images/here_be_chimeras.png" alt="" /></p> <p>So, let’s assume $C = D$ and carry on (I’ll understand that presentation someday).</p> <p>Okay, let’s map $c, d$ (both objects in $C$) to $\textrm{Hom}(c, d)$. And for morphisms, we need to map some $(f, g)$ for $f: c \rightarrow c^\prime$ and $g: d \rightarrow d^\prime$ to a function between $\textrm{Hom}(c, d)$ and $\textrm{Hom}(c^\prime, d^\prime)$. Let’s pluck out a morphism, say $h$ from $\textrm{Hom}(c, d)$.</p> <p>We have $f, g, h$ in hand and need to construct a morphism from $c^\prime$ to $d^\prime$. There’s…no way to do this. None of our morphisms map <em>from</em> $c^\prime$.</p> <p>That’s where the contravariance in the profunctor construction comes from when folks write $C^{\textrm{op}} \times C \rightarrow \mathbf{Set}$ (or, in the general case $C^{\textrm{op}} \times D \rightarrow \mathbf{Set}$). Taking the dual in the first component of the product category flips $f$ and now lets us get from $c^\prime$ to $d^\prime$ by way of $g \circ h \circ f$.</p> <p>It’s okay if you need to walk around the park with that composition before it makes sense. I certainly needed to and it demystified the <a href="https://github.com/pointfreeco/swift-prelude/blob/536b8856e38853854b5c5689e5b5d06da75c992e/Sources/Prelude/Func.swift#L48-L52">rogue <code class="language-plaintext highlighter-rouge">dimap</code></a>’s I’d see in Preludes.</p> <p>But, let’s take stock on how this generalizes relations. In the same-category setting, we’re constructing a <em>functor</em> that maps two objects to the ways in which they’re related, their hom-sets. Since it’s a functor, we also need to consider mapping morphisms across the functor into functions between hom-sets and <a href="https://hackage.haskell.org/package/lens-4.19.2/docs/Control-Lens-Combinators.html#v:dimap"><code class="language-plaintext highlighter-rouge">dimap</code></a> (link to Haskell’s Prelude) does just that.</p> <hr /> <p>⇒ “<a href="https://bartoszmilewski.com/2016/07/25/profunctors-as-relations/">Profunctors as relations</a>”</p> <p>⇒ “<a href="https://typeclasses.com/profunctors">Understanding profunctors</a>”</p> All metric spaces are Lawvere metric spaces 2020-04-18T00:00:00+00:00 https://jasdev.me/notes/lawvere-metric-spaces <p>I’m definitely the rookie in my <a href="https://sites.google.com/view/mact2020/home">research group</a>, so the notebook will be a bit math-heavy as I try to catch up.</p> <p>To start, here’s an entry on a topic—amongst many—<a href="https://twitter.com/JadeMasterMath">Jade</a> walked us through during our first meeting, <a href="https://ncatlab.org/nlab/show/metric+space#LawvereMetricSpace">Lawvere metric spaces</a>.</p> <p>nLab’s definiton is a bit impenetrable. At a glance, it seems like tacking on Lawvere’s name, to an already general concept, means added axioms.</p> <p>It’s…surprisingly the opposite.</p> <p>All metric spaces <em>are</em> Lawvere metric spaces—that is, we lift some of the constraints on plain ol’ metrics.</p> <p>Recapping, a metric space is a set $X$ equipped with a distance function $d: X \times X \rightarrow [0, \infty)$ under the following coherences:</p> <p>Assuming $x, y, z \in X$,</p> <ul> <li>$d(x, y) = 0 \iff x = y$ (zero-distance coincides with equality).</li> <li>$d(x, y) = d(y, x)$ (symmetry).</li> <li>$d(x, y) + d(y, z) \geq d(x, z)$ (the <a href="https://en.wikipedia.org/wiki/Triangle_inequality">triangle inequality</a>).</li> </ul> <p>And Lawvere relaxed a few bits. A Lawvere metric space has a distance function</p> <ul> <li>that respects the triangle inequality,</li> <li>whose codomain includes $\infty$ (which is helpful when we want a “disconnectedness” between points),</li> <li>and $d(x, x) = 0$ (points are zero-distance from themselves).</li> </ul> <p>We’re dropping the symmetry requirement and allowing for possibly zero distances between <em>distinct</em> points.</p> <p>The former lets us represent, e.g. in a distance as cost situation, non-symmetric costs. <a href="https://forum.azimuthproject.org/discussion/2128/lecture-31-chapter-2-lawvere-metric-spaces">Borrowing from Baez</a>, imagine the commute from $x$ to $y$ being cheaper than from $y$ to $x$.</p> <p>The easing of zero-distance being necessary and sufficient for equality to only one side of the implication adds the ability to reach points “for free” (continuing with the transportation theme).</p> <p>I need to read up on more applications this freedom affords us. In the meantime, here’s some links I’ve come across:</p> <ul> <li>Jeremy Kun’s <a href="https://jeremykun.com/2012/08/26/metric-spaces-a-primer/">metric spaces primer</a>.</li> <li><a href="https://forum.azimuthproject.org/discussion/2128/lecture-31-chapter-2-lawvere-metric-spaces">Lecture 31’s notes</a> from MIT’s ’19 ACT course.</li> <li><a href="https://math.mit.edu/~dspivak/teaching/sp18/7Sketches.pdf#page=71">Section 2.3.3</a> of Seven Sketches.</li> </ul> Easing AnyCancellable storage 2020-04-10T00:00:00+00:00 https://jasdev.me/notes/cancellable-storage <p>Quick note on <a href="https://github.com/CombineCommunity/CombineExt/pull/21">a late-night PR</a> I drafted for CombineExt. It tidies the repetitive <a href="https://developer.apple.com/documentation/combine/anycancellable/3333294-store"><code class="language-plaintext highlighter-rouge">AnyCancellable.store(in:)</code></a> calls needed to hold onto cancellation tokens.</p> <script src="https://gist.github.com/jasdev/b2fa2aebf373b7a93ca80890debe8ca8.js"></script> <p>(<a href="https://gist.github.com/jasdev/b2fa2aebf373b7a93ca80890debe8ca8">Gist permalink</a>.)</p> <p>I’ve also added a <a href="https://github.com/CombineCommunity/CombineExt/blob/54de8305085c9f7d42be85126124ee9f8fd02be8/Sources/Utilities/Set%2BAnyCancellable.swift#L48-L73"><code class="language-plaintext highlighter-rouge">Sequence</code> variant</a>.</p> <p>And both are <a href="https://github.com/CombineCommunity/CombineExt/blob/f9b7712237c439cad5d6885652251e3bc743decf/Sources/Utilities/Set%2BAnyCancellable.swift#L11"><code class="language-plaintext highlighter-rouge">Element == AnyCancellable</code> constrained</a> to avoid crowding <code class="language-plaintext highlighter-rouge">Set</code>’s namespace.</p> Weak assignment in Combine 2020-04-08T00:00:00+00:00 https://jasdev.me/notes/weak-assignment <p><code class="language-plaintext highlighter-rouge">Publisher.assign(to:on:)</code> comes with a pretty big warning label,</p> <blockquote> <p>The <code class="language-plaintext highlighter-rouge">Subscribers.Assign</code> instance created by this operator maintains a strong reference to <code class="language-plaintext highlighter-rouge">object</code> […]</p> </blockquote> <p>and we need to read this label when piping publishers to <code class="language-plaintext highlighter-rouge">@Published</code> properties in <code class="language-plaintext highlighter-rouge">ObservableObject</code> conformances.</p> <script src="https://gist.github.com/jasdev/900a9a74b3ea678590dcafe5582fa146.js"></script> <p>(<a href="https://gist.github.com/jasdev/900a9a74b3ea678590dcafe5582fa146">Gist permalink</a>.)</p> <p>Of course, we could do the <code class="language-plaintext highlighter-rouge">sink</code>-<code class="language-plaintext highlighter-rouge">weak</code>-<code class="language-plaintext highlighter-rouge">self</code>-<code class="language-plaintext highlighter-rouge">receiveValue</code> dance. But, that’s a bit of ceremony.</p> <script src="https://gist.github.com/jasdev/9a585eaf16a55444ae3eb3556b599d90.js"></script> <p>(<a href="https://gist.github.com/jasdev/9a585eaf16a55444ae3eb3556b599d90">Gist permalink</a>.)</p> <p>My first instinct was to <code class="language-plaintext highlighter-rouge">weak</code>-ly overload <code class="language-plaintext highlighter-rouge">assign</code> and <a href="https://github.com/CombineCommunity/CombineExt/pull/14">PR’d it to <code class="language-plaintext highlighter-rouge">CombineExt</code></a>, in case it’d help others, too. And with some distance and <a href="https://github.com/CombineCommunity/CombineExt/pull/14#issuecomment-611018432">thoughtful feedback</a> from both Shai and Adam, I decided to re-think that instinct.</p> <p>There’s a few downsides to <code class="language-plaintext highlighter-rouge">Publisher.weaklyAssign(to:on)</code>.</p> <ul> <li>It crowds an already packed <code class="language-plaintext highlighter-rouge">Publisher</code> namespace.</li> <li>In its current form, it doesn’t relay which argument is <code class="language-plaintext highlighter-rouge">weak</code>ly captured. A clearer signature would be <code class="language-plaintext highlighter-rouge">.assign(to:onWeak:)</code> (and similarly, for an <code class="language-plaintext highlighter-rouge">unowned</code> variant).</li> </ul> <p>Adam mentioned a couple of alternatives:</p> <ul> <li><a href="https://github.com/CombineCommunity/CombineExt/pull/14#issuecomment-611018432">“Promote” the <code class="language-plaintext highlighter-rouge">weak</code> keyword</a> to a sort of function by way of a <code class="language-plaintext highlighter-rouge">WeakBox</code> type and assign onto it. I tried to make this work—learning more than I bargained for about dynamic member lookup along the way—and ran into <a href="https://iosdevelopers.slack.com/archives/C03FWU59B/p1586378364117100">a tradeoff dead end</a>.</li> <li><a href="https://github.com/thoughtbot/Bindings/blob/87fc9a2ec4ec516bd5821d0c2557080db930972d/Sources/Bindings/BindingSink.swift#L6">Encode the weak capture semantics</a> at the type level à la the <a href="https://github.com/thoughtbot/Bindings"><code class="language-plaintext highlighter-rouge">Bindings</code></a> package. Which, felt a bit out of scope for <code class="language-plaintext highlighter-rouge">CombineExt</code>, since it’s more of an operator collection.</li> </ul> <p>So, I’m back to where I started and with a slightly modified overload. Gist’ing it below for the curious.</p> <script src="https://gist.github.com/jasdev/201a9d80ce558a16ecf142eb45bd2638.js"></script> <p>(<a href="https://gist.github.com/jasdev/201a9d80ce558a16ecf142eb45bd2638">Gist permalink</a>.)</p> <p>Now call sites can read—</p> <script src="https://gist.github.com/jasdev/5705e0587880b6beedb17ac4c066209b.js"></script> <p>(<a href="https://gist.github.com/jasdev/5705e0587880b6beedb17ac4c066209b">Gist permalink</a>.)</p> <p>Ah! Almost forgot. <a href="https://github.com/CombineCommunity/CombineExt/pull/14/files#diff-5bbd043cabe9745408ee6987619d1abfR30">Writing tests</a> for the operator had me reaching for <a href="https://developer.apple.com/documentation/swift/2430721-isknownuniquelyreferenced"><code class="language-plaintext highlighter-rouge">Swift.isKnownUniquelyReferenced(_:)</code></a>—“<a href="https://www.youtube.com/watch?v=031vKBPk5eA">a [free-function] I haven’t heard [from] in a long time, a long time.</a>”</p> There’s sliced bread, and then Result.publisher 2020-04-07T00:00:00+00:00 https://jasdev.me/notes/result-publisher <p>Another learning from <a href="http://twitter.com/sharplet">Adam</a>.</p> <p>A situation I often find myself in is sketching an operator chain and exercising both the value and failure paths by swapping upstream with <a href="https://developer.apple.com/documentation/combine/just"><code class="language-plaintext highlighter-rouge">Just</code></a> or <a href="https://developer.apple.com/documentation/combine/fail"><code class="language-plaintext highlighter-rouge">Fail</code></a>, respectively.</p> <p>And it turns out that Apple added <a href="https://developer.apple.com/documentation/swift/result/3344716-publisher">a Combine overlay to <code class="language-plaintext highlighter-rouge">Result</code> with the <code class="language-plaintext highlighter-rouge">.publisher</code> property</a> that streamlines the two. That is, while all three of <code class="language-plaintext highlighter-rouge">Just</code>, <code class="language-plaintext highlighter-rouge">Fail</code>, and <code class="language-plaintext highlighter-rouge">Result.Publisher</code> have their uses, the latter might be easier to reach for in technical writing. Moreover, it’s a quick way to <a href="https://github.com/antitypical/Result/blob/c0838342cedfefc25f6dd4f95344d376bed582c7/Result/Result.swift#L228-L236"><code class="language-plaintext highlighter-rouge">materialize</code> a <code class="language-plaintext highlighter-rouge">throw</code>ing function</a> and pipe it downstream.</p> <script src="https://gist.github.com/jasdev/7ae67582c06845e41d6007c721200971.js"></script> <p>(<a href="https://gist.github.com/jasdev/7ae67582c06845e41d6007c721200971">Gist permalink</a>.)</p> <p>Or, as I’ll call it going forward—“<a href="https://twitter.com/jasdev/status/1247190267129745414">the ol’ razzle dazzle.</a>”</p> <blockquote class="twitter-tweet" data-conversation="none" data-lang="en" data-theme="light"><p lang="und" dir="ltr"><a href="https://t.co/o7EcZqNBlj">pic.twitter.com/o7EcZqNBlj</a></p>&mdash; Jasdev Singh (@jasdev) <a href="https://twitter.com/jasdev/status/1247190267129745414?ref_src=twsrc%5Etfw">April 6, 2020</a></blockquote> <script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> Postfix type erasure 2020-03-25T00:00:00+00:00 https://jasdev.me/notes/postfix-erasure <p>A belated entry on an operator <a href="https://twitter.com/jasdev/status/1223316966829699072">I posted</a> before…all of this (<em>gestures wildly</em>) started.</p> <p>⬦</p> <p>There’s nuance in determining whether or not to type erase a publisher—my next longer-form post will cover this—but when you need to, <a href="https://developer.apple.com/documentation/combine/publishers/output/3241611-erasetoanypublisher"><code class="language-plaintext highlighter-rouge">eraseToAnyPublisher()</code></a>’s ergonomics aren’t great.</p> <p>It requires 22 characters (including a dot for the method call and <code class="language-plaintext highlighter-rouge">Void</code> argument) to apply a rather one-character concept.</p> <p>And I know operators are borderline #holy-war—still, if you’re open to them, I’ve borrowed prior art from Bow and Point-Free by using a <code class="language-plaintext highlighter-rouge">^</code> postfix operator.</p> <p>It passes the three checks any operator should.</p> <ul> <li>Does the operator overload an existing one in Swift? Thankfully not (since <a href="https://docs.swift.org/swift-book/LanguageGuide/AdvancedOperators.html#ID33">bitwise XOR</a> is infix).</li> <li>Does its shape convey its intent? To an extent! The caret hints at a sort of “lifting” and that’s what erasure is after all. Removing specific details, leaving behind a more general shape.</li> <li>Does it have prior art? Yep. <ul> <li>Bow defines one to <a href="https://bow-swift.io/docs/fp-concepts/higher-kinded-types/#casting-and-the--operator">ease higher-kinded type emulation</a>.</li> <li>Point-Free <a href="https://github.com/pointfreeco/swift-prelude/blob/c61b49392768a6fa90fe9508774cf90a80061c8b/Sources/Prelude/KeyPath.swift#L25-L31">defined a prefix variant</a> to lift key path expressions into function form before <a href="https://github.com/apple/swift-evolution/blob/53c2e80bdede03b99aee683fe6399b6e4cf0bf95/proposals/0249-key-path-literal-function-expressions.md">SE-0249</a> landed in Swift 5.2</li> </ul> </li> </ul> <p>The operator has tidied the Combine I’ve written so far. Here’s <a href="https://gist.github.com/jasdev/0cf0663c4f54446b046f946b1f7af5a9#file-postfix_type_erasure-swift">a gist</a> with its definition.</p> <script src="https://gist.github.com/jasdev/0cf0663c4f54446b046f946b1f7af5a9.js"></script> Parity and arity 2020-03-17T00:00:00+00:00 https://jasdev.me/notes/parity-arity <p>Two tucked-away, somewhat-related terms I enjoy: <a href="https://en.wikipedia.org/wiki/Parity_(mathematics)">parity</a> and <a href="https://en.wikipedia.org/wiki/Arity">arity</a>.</p> <p>The former is the odd or even-ness of an integer.</p> <p>The latter describes the number of arguments a function accepts.</p> <h2 id="example-usage-of-parity">Example usage of parity:</h2> <p><a href="https://twitter.com/_substrate/status/1239971265185607682">Today I learned about the Handshaking Lemma</a>. It states that any finite undirected graph, will have an <em>even</em> number of vertices with an <em>odd</em> <a href="https://en.wikipedia.org/wiki/Degree_(graph_theory)">degree</a>.</p> <p>The <a href="https://en.wikipedia.org/wiki/Handshaking_lemma#Proof">proof</a> rests on parity. Specifically, if you sum the degrees of every vertex in a graph, you’ll double count each edge. And that double counting implies the sum is even, and even parity is only maintained if there is an even—including zero—number of vertices with an odd degree.</p> <p>Put arithmetically, a sum can only be even if its components contain an even number of odd terms.</p> <h2 id="examples-of-arity">Examples of arity:</h2> <ul> <li>Swift’s tuple comparison operators <a href="https://github.com/apple/swift-evolution/blame/63b11d5454ce2bda65594a7879b456e33d470176/proposals/0015-tuple-comparison-operators.md#L48">topping out at arity six</a>.</li> <li><code class="language-plaintext highlighter-rouge">Publisher.zip</code> is only overloaded to <a href="https://developer.apple.com/documentation/combine/publisher/3333687-zip">arity three</a>. <ul> <li>I PR’d a <a href="https://github.com/CombineCommunity/CombineExt/pull/6">variadic overload</a> to <code class="language-plaintext highlighter-rouge">CombineCommunity/CombineExt</code> yesterday and have a post walking through it in the works.</li> </ul> </li> </ul> Contrasting isomorphisms, equivalencies, and adjunctions 2020-01-01T00:00:00+00:00 https://jasdev.me/notes/iso-equiv-adjunction <p>When I was first introduced to adjunctions, I reacted in the way Spivak anticipated during an <a href="https://youtu.be/Cf3tsAeGhBg?t=203">applied category theory lecture</a> (timestamped, transcribed below).</p> <blockquote> <p>…and when people see this definition [of an adjunction], they think “well, that seems…fine. I’m glad you told me about…that.”</p> </blockquote> <p>And it wasn’t until I stumbled upon a <a href="https://www.youtube.com/watch?v=loOJxIOmShE">Catsters lecture from 2007</a> (!) where Eugenia Cheng clarified the intent behind the definition (and contrasted it to isomorphisms and equivalencies).</p> <p>To start, assume we have two categories <code class="language-plaintext highlighter-rouge">C</code> and <code class="language-plaintext highlighter-rouge">D</code> with functors, <code class="language-plaintext highlighter-rouge">F</code> and <code class="language-plaintext highlighter-rouge">G</code> between them (<code class="language-plaintext highlighter-rouge">F</code> moving from <code class="language-plaintext highlighter-rouge">C</code> to <code class="language-plaintext highlighter-rouge">D</code> and <code class="language-plaintext highlighter-rouge">G</code> in the other direction).</p> <p>There are a few possible scenarios we could find ourselves in.</p> <ul> <li>Taking the round trip—i.e. <code class="language-plaintext highlighter-rouge">GF</code> and <code class="language-plaintext highlighter-rouge">FG</code>—is <strong>equal</strong> to the identity functors on <code class="language-plaintext highlighter-rouge">C</code> and <code class="language-plaintext highlighter-rouge">D</code> (denoted with <code class="language-plaintext highlighter-rouge">1_C</code> and <code class="language-plaintext highlighter-rouge">1_D</code> below).</li> <li>The round trip is <strong>isomorphic</strong> to each identity functor.</li> <li>Or, the round trip lands us <strong>a morphism away</strong> from where we started.</li> </ul> <p><img src="/public/images/iso-equiv-adjunction.png" alt="" /></p> <p>Moving between the scenarios, there’s a sort of “relaxing” of strictness:</p> <ul> <li>The round trip is the identity functor.</li> <li>[…] an isomorphism away from the identity functor.</li> <li>[…] a hop away from identity functor.</li> </ul> Why optics? 2019-12-15T00:00:00+00:00 https://jasdev.me/notes/why-optics <p>The better part of my weekend was spent reading Chris Penner’s incredibly well-written <em><a href="https://leanpub.com/optics-by-example">Optics by Example</a></em> and attempting the exercises with <a href="https://bow-swift.io/docs/optics/overview/">BowOptics</a>.</p> <p>I’m early in my learning. Still, I wanted to note the motivation behind optics.</p> <p>They seek to capture control flow, which, is usually baked into languages with <code class="language-plaintext highlighter-rouge">for</code>, <code class="language-plaintext highlighter-rouge">while</code>, <code class="language-plaintext highlighter-rouge">if</code>, <code class="language-plaintext highlighter-rouge">guard</code>, <code class="language-plaintext highlighter-rouge">switch</code>, and similar statements, <em>as values</em>.</p> <p>In the same way that effect systems capture effects as values—decoupling them from execution—optics separate control flow when navigating data structures from the actions taken on them.</p> <p>Sara Fransson put this well in their recent talk: <em><a href="https://youtu.be/sFzuu676pFs?t=232">Functional Lenses Through a Practical Lens</a></em>.</p> <p>■</p> <p>Noticing that optics excite me much like FRP did back when I first learned about it.</p> <p>And I haven’t even gotten to the category-theoretic backings yet.</p> <p>(<em>Attempts to contain excitement.</em> Back to reading.)</p> TIL about Publishers.MergeMany.init 2019-12-13T00:00:00+00:00 https://jasdev.me/notes/merge-many <p>A few weeks back, <a href="https://www.twitter.com/jordanmorgan10">Jordan Morgan</a> nerd sniped me<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> into writing a Combine analog to <a href="https://github.com/ReactiveX/RxSwift/blob/6b2a406b928cc7970874dcaed0ab18e7265e41ef/RxSwift/Observables/Merge.swift#L84-L120">RxSwift’s <code class="language-plaintext highlighter-rouge">ObservableType.merge(sources:)</code></a>—an operator that can merge arbitrarily many observable sequences.</p> <p>Here’s a rough, not-tested-in-production sketch (if you know a way to ease the <code class="language-plaintext highlighter-rouge">eraseToAnyPublisher</code> dance, let me know):</p> <script src="https://gist.github.com/jasdev/014f5353aa334b4e5e42f102d6276ad9.js"></script> <p>And in writing this entry, I decided to check and make sure I wasn’t missing something. After all, it’s odd (pun intended) that the merging operators on <code class="language-plaintext highlighter-rouge">Publisher</code> stop at arity seven.</p> <p><img src="/public/images/merge_overloads.png" alt="" /></p> <p>Then, I noticed the <code class="language-plaintext highlighter-rouge">Publishers.MergeMany</code> return value on the first and, below the (hopefully temporary) “No overview available” note on its documentation, there’s <a href="https://developer.apple.com/documentation/combine/publishers/mergemany/3236390-init">a variadic initializer</a>!</p> <p>So, there you have it. TIL merging a <em>sequence</em> of publishers goes by the name of <code class="language-plaintext highlighter-rouge">Publishers.MergeMany.init(_:)</code>.</p> <hr /> <div class="footnotes" role="doc-endnotes"> <ol> <li id="fn:1" role="doc-endnote"> <p>In <a href="https://iosdevelopers.slack.com/archives/C0AET0JQ5/p1574800569370700?thread_ts=1574786869.354400&amp;cid=C0AET0JQ5">iOS Folks’ #reactive channel</a>. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> </ol> </div> Naturality condition 2019-11-01T00:00:00+00:00 https://jasdev.me/notes/naturality-condition <p>I’m currently working through Mio Alter’s post, “<a href="https://mioalter.github.io/posts/2018-12-17-Yoneda.html">Yoneda, Currying, and Fusion</a>” (his other piece on <a href="https://mioalter.github.io/posts/2019-09-04-Equivalence-Relations.html">equivalence relations</a> is equally stellar).</p> <p>Early on, Mio mentions:</p> <blockquote> <p>It turns out that being a natural transformation is hard: the commutative diagram that a natural transformation has to satisfy means that the components […] fit together in the right way.</p> </blockquote> <p>Visually, the diagram formed by functors <em>F</em> and <em>G</em> between <em>C</em> and <em>D</em> and with natural transformation <em>α</em> must <em>commute</em> (signaled by the rounded arrow in the center).</p> <p><img src="/public/images/naturality_commuting_square.png" alt="" /></p> <p>I’m trying to get a sense for why this condition is “hard” to meet.</p> <p>What’s helped is making the commutativity explicit by drawing the diagonals and <em>seeing</em> that they must be the equal. The four legs of the two triangles that cut the square must share a common diagonal.</p> <p><img src="/public/images/naturality_commuting_square_explicit.png" alt="" /></p> <p>Maybe that’s why natural transformations are rare? There might be many morphisms between <em>F(A)</em> and <em>G(A)</em> and <em>F(B)</em> and <em>G(B)</em>, but only a select few (or none) which cause their compositions to coincide.</p> <p>For more on commutative diagrams, Tai-Danae Bradley has a <a href="https://www.math3ma.com/blog/commutative-diagrams-explained">post dedicated to the topic</a>.</p> Why applicatives are monoidal 2019-10-27T00:00:00+00:00 https://jasdev.me/notes/applicatives-monoidal <p>“<a href="https://en.wikipedia.org/wiki/Applicative_functor">Applicative functors are […] lax monoidal functors with tensorial strength.</a>”</p> <p>I still don’t quite have the foundation to grok the “lax” and “tensorial strength” bits (I will, some day). Yet, <a href="https://argumatronic.com/posts/2017-03-08-applicative-instances.html">seeing applicatives as monoidal</a> always felt out of reach.</p> <p>They’re often introduced with the <code class="language-plaintext highlighter-rouge">pure</code> and <code class="language-plaintext highlighter-rouge">apply</code> duo (sketched out below for <code class="language-plaintext highlighter-rouge">Optional</code>).</p> <script src="https://gist.github.com/jasdev/9ead264c0366521bea98a1baf750eaa2.js"></script> <p>(An aside, it finally clicked that the choice of “applicative” is, because, well, it supports function application inside of the functor’s context.)</p> <p>And then, coincidentally, I recently asked:</p> <blockquote> <p>is there any special terminology around types that support a <code class="language-plaintext highlighter-rouge">zip</code> in the same way functors have a <code class="language-plaintext highlighter-rouge">map</code> and monads have a <code class="language-plaintext highlighter-rouge">flatMap</code>?</p> </blockquote> <p>To which, <a href="https://twitter.com/anandabits">Matthew Johnson</a> let me in on the secret.</p> <blockquote> <p><code class="language-plaintext highlighter-rouge">zip</code> is an alternate way to formulate applicative</p> </blockquote> <p>!!!.</p> <p>That makes way more sense and sheds light on why Point-Free treated <code class="language-plaintext highlighter-rouge">map</code>, <code class="language-plaintext highlighter-rouge">flatMap</code>, and <code class="language-plaintext highlighter-rouge">zip</code> as their big three instead of introducing <code class="language-plaintext highlighter-rouge">pure</code> and <code class="language-plaintext highlighter-rouge">apply</code>.</p> <p>I can only <em>sort of</em> see that <code class="language-plaintext highlighter-rouge">apply</code> implies monoidal-ness (pardon the formality) in that it reduces an <code class="language-plaintext highlighter-rouge">Optional&lt;(A) -&gt; B&gt;</code> and <code class="language-plaintext highlighter-rouge">Optional&lt;A&gt;</code> into a single <code class="language-plaintext highlighter-rouge">Optional&lt;B&gt;</code>. However, the fact that they contained different shapes always made me wonder.</p> <p><code class="language-plaintext highlighter-rouge">zip</code> relays the ability to combine more readily. “Hand me an <code class="language-plaintext highlighter-rouge">Optional&lt;A&gt;</code> and an <code class="language-plaintext highlighter-rouge">Optional&lt;B&gt;</code> and I’ll give you an <code class="language-plaintext highlighter-rouge">Optional&lt;(A, B)&gt;</code>.”</p> <p>Yesterday, I finally got around to defining <code class="language-plaintext highlighter-rouge">apply</code> in terms of <code class="language-plaintext highlighter-rouge">zip</code> to see the equivalence.</p> <script src="https://gist.github.com/jasdev/12cf95ea1c511fa74eff67a019a13fd6.js"></script> <p>Funnily enough, Brandon pointed me to <a href="https://www.pointfree.co/episodes/ep25-the-many-faces-of-zip-part-3#exercises">exercise 25.2</a> which asks exactly this.</p> <p>In short,</p> <ul> <li>Functors allow for a <code class="language-plaintext highlighter-rouge">map</code>.</li> <li>Applicatives, a <code class="language-plaintext highlighter-rouge">zip</code>.</li> <li>Monads, a <code class="language-plaintext highlighter-rouge">flatMap</code>.</li> </ul> Type erasure and forgetful functors 2019-10-24T00:00:00+00:00 https://jasdev.me/notes/type-erasure-forgetful-functor <p>Type erasure and <a href="https://en.wikipedia.org/wiki/Forgetful_functor">forgetful functors</a>, at least in terms of intuition, feel very similar.</p> <p>One removes detail (e.g. <a href="https://developer.apple.com/documentation/combine/publishers/zip"><code class="language-plaintext highlighter-rouge">Publishers.Zip</code></a> -&gt; <a href="https://developer.apple.com/documentation/combine/anypublisher"><code class="language-plaintext highlighter-rouge">AnyPublisher</code></a>) and the other strips structure, leaving the underlying set behind (a monoid or group being mapped onto its base set).</p> <p>Wonder if there’s a way to visualize this by considering <a href="https://developer.apple.com/documentation/combine/anypublisher/3241539-erasetoanypublisher"><code class="language-plaintext highlighter-rouge">eraseToAnyPublisher</code></a> as a sort of forgetful endofunctor into a subcategory of <strong>Swift</strong> (hand waving) that only contains <code class="language-plaintext highlighter-rouge">AnyPublisher</code>s?</p> Co-, contra-, and invariance 2019-10-20T00:00:00+00:00 https://jasdev.me/notes/variance <p>Folks often refer to the component of a function’s “polarity.” “The input is in the negative position.” “The output is positive.”</p> <p>And that made me wonder, is there a, well, <em>neutral</em> polarity?</p> <p>Maybe that’s when a component is in both a negative and positive position, canceling one another out.</p> <p>Let’s see what happens.</p> <p><code class="language-plaintext highlighter-rouge">A -&gt; …</code>.</p> <p><code class="language-plaintext highlighter-rouge">A</code> is in a negative position? Check. Let’s add it to the positive spot.</p> <p><code class="language-plaintext highlighter-rouge">A -&gt; A</code>.</p> <p>This is our <a href="https://github.com/pointfreeco/swift-prelude/blob/b26e98e82c7598fd5f381b3a27b59e27e312eb58/Sources/Prelude/Endo.swift#L1-L7">old friend, <code class="language-plaintext highlighter-rouge">Endo</code></a>! At the bottom of the file, I noticed <code class="language-plaintext highlighter-rouge">imap</code> and asked some folks what the “i” stood for. Turns out it’s short for, “invariant,” which reads nicely in that both co- and contravariance net out to invariance.</p> <p>Pairing functor type, variance(s), and <code class="language-plaintext highlighter-rouge">*map</code> name:</p> <ul> <li>Functor, covariant, <a href="https://github.com/pointfreeco/swift-prelude/blob/536b8856e38853854b5c5689e5b5d06da75c992e/Sources/Prelude/Func.swift#L20-L23"><code class="language-plaintext highlighter-rouge">map</code></a>.</li> <li>Functor, contravariant, <a href="https://github.com/pointfreeco/swift-prelude/blob/536b8856e38853854b5c5689e5b5d06da75c992e/Sources/Prelude/Func.swift#L34-L37"><del><code class="language-plaintext highlighter-rouge">contramap</code></del></a><a href="https://www.pointfree.co/blog/posts/22-some-news-about-contramap"><code class="language-plaintext highlighter-rouge">pullback</code></a>.</li> <li>Bifunctor, covariant (and I’m guessing contra-, maybe both working in the same direction is what matters?), <a href="https://github.com/pointfreeco/swift-prelude/blob/b26e98e82c7598fd5f381b3a27b59e27e312eb58/Sources/Either/Either.swift#L118-L129"><code class="language-plaintext highlighter-rouge">bimap</code></a>.</li> <li>Invariant functor, invariant (co- and contravariant in the <em>same</em> component), <a href="https://github.com/pointfreeco/swift-prelude/blob/536b8856e38853854b5c5689e5b5d06da75c992e/Sources/Prelude/Endo.swift#L21-L25"><code class="language-plaintext highlighter-rouge">imap</code></a>.</li> <li>Profunctor, co- and contravariant along two components, <a href="https://github.com/pointfreeco/swift-prelude/blob/536b8856e38853854b5c5689e5b5d06da75c992e/Sources/Prelude/Func.swift#L48-L52"><code class="language-plaintext highlighter-rouge">dimap</code></a>.</li> </ul> What makes natural transformations, natural? 2019-10-19T00:00:00+00:00 https://jasdev.me/notes/natural-transformations <p>Learning category theory often involves patiently sitting with a concept until it—eventually—clicks and then considering it as a building block for the next<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>.</p> <p>Grokking natural transformations went that way for me.</p> <p>I still remember the team lunch last spring where I couldn’t keep my excitement for the abstraction a secret and had to share with everyone (I’m a blast at parties).</p> <p>After mentioning the often-cited example of a natural transformation in engineering, <a href="https://developer.apple.com/documentation/swift/collection/3017676-first"><code class="language-plaintext highlighter-rouge">Collection.first</code></a> (a transformation between the <code class="language-plaintext highlighter-rouge">Collection</code> and <code class="language-plaintext highlighter-rouge">Optional</code> functors), a teammate asked me the question heading this note:</p> <blockquote> <p>What makes a natural transformation, natural?</p> </blockquote> <p>I found an interpretation of the word.</p> <p>Say we have some categories <code class="language-plaintext highlighter-rouge">C</code> and <code class="language-plaintext highlighter-rouge">D</code> and functors <code class="language-plaintext highlighter-rouge">F</code> and <code class="language-plaintext highlighter-rouge">G</code> between them, diagrammed below:</p> <p><img src="/public/images/natural_transformation.png" alt="" /></p> <p>If we wanted to move from the <em>image</em> of <code class="language-plaintext highlighter-rouge">F</code> acting on <code class="language-plaintext highlighter-rouge">C</code> to the image of <code class="language-plaintext highlighter-rouge">G</code>, we have to find a way of moving between objects in the <em>same</em> category.</p> <p>The question, rephrased, becomes what connects objects in a category? Well, morphisms!</p> <p>Now, how do we pick them? Another condition on natural transformations is that the square formed by mapping two objects, <code class="language-plaintext highlighter-rouge">A</code> and <code class="language-plaintext highlighter-rouge">B</code> connected by a morphism <code class="language-plaintext highlighter-rouge">f</code>, across two functors <code class="language-plaintext highlighter-rouge">F</code> and <code class="language-plaintext highlighter-rouge">G</code> must commute.</p> <p>Let’s call our choices of morphisms between <code class="language-plaintext highlighter-rouge">F_A</code> and <code class="language-plaintext highlighter-rouge">G_A</code> and <code class="language-plaintext highlighter-rouge">F_B</code> and <code class="language-plaintext highlighter-rouge">G_B</code>, <code class="language-plaintext highlighter-rouge">α_A</code> and <code class="language-plaintext highlighter-rouge">α_B</code>, respectively.</p> <p><img src="/public/images/commuting_square.png" alt="" /></p> <p>Even if <code class="language-plaintext highlighter-rouge">f</code> flips directions across <code class="language-plaintext highlighter-rouge">F</code> and <code class="language-plaintext highlighter-rouge">G</code>—i.e. they’re contravariant functors—our choice in <code class="language-plaintext highlighter-rouge">α_A</code> and <code class="language-plaintext highlighter-rouge">α_B</code> is fixed!</p> <p>The definition, the choice of morphisms, seems to <em>naturally</em> follow from structure at hand. It doesn’t depend on arbitrary choices.</p> <p>Tangentially, a definition shaking out from some structure reminds me of how the <a href="https://en.wikipedia.org/wiki/Curry–Howard_correspondence">Curry–Howard correspondence</a> causes certain functions to have a unique implementation. Brandon covered this topic in a past <a href="https://vimeo.com/121953811#t=1294s">Brooklyn Swift presentation</a> (timestamped).</p> <p>For more resources on natural transformations:</p> <ul> <li><a href="https://www.math3ma.com/blog/what-is-a-natural-transformation">What is a Natural Transformation? Definition and Examples</a></li> <li><a href="https://www.math3ma.com/blog/what-is-a-natural-transformation-2">What is a Natural Transformation? Definition and Examples, Part 2</a></li> <li>Bartosz’s <a href="https://youtu.be/2LJC-XD5Ffo">1.9.1 lecture</a> on the topic.</li> </ul> <hr /> <div class="footnotes" role="doc-endnotes"> <ol> <li id="fn:1" role="doc-endnote"> <p>Then, <a href="https://twitter.com/mbrandonw/status/936376614048485385">repeating further</a>. One day, I hope to have built the machinery needed to read <a href="https://twitter.com/emilyriehl">Riehl</a>’s research <a href="http://www.math.jhu.edu/~eriehl/elements.pdf">and writing</a> on ∞-category theory. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> </ol> </div> “Oh, the morphisms you’ll see!” 2019-10-17T00:00:00+00:00 https://jasdev.me/notes/morphisms <p>There are many prefixes placed in front of the word “morphism”—here’s a glossary of the ones I’ve seen so far:</p> <ul> <li><a href="https://en.wikipedia.org/wiki/Epimorphism">Epimorphism</a> <ul> <li>The categorical analog to surjectivity—the “epi” root connotes morphisms mapping “over” the entirety of the codomain. Bartosz covers this well in <a href="https://youtu.be/O2lZkr-aAqk?t=2207">lecture 2.1</a> (timestamped) of Part I of his category theory course.</li> </ul> </li> <li><a href="https://en.wikipedia.org/wiki/Monomorphism">Monomorphism</a> <ul> <li>Injectivity’s analog and epimorphism’s dual (it blew my mind to realize injectivity and surjectivity, two properties I never thought twice about, are duals). “Mono” in that it generalizes one-to-one functions. Bartosz also <a href="https://youtu.be/O2lZkr-aAqk?t=2225">mentions them in 2.1</a>.</li> </ul> </li> <li><a href="https://en.wikipedia.org/wiki/Morphism#Some_specific_morphisms">Bimorphism</a> <ul> <li>“A morphism that is both an epimorphism and a monomorphism is called a bimorphism.”</li> <li>I don’t quite have the foundation needed to grok when a bimorphism <em>isn’t</em> an isomorphism—maybe because I spend most of my time working in <strong>Hask</strong> and <strong>Swift</strong> (<strong>Set</strong>, in disguise) and Wikipedia mentions “a category, such as <strong>Set</strong>, in which every bimorphism is an isomorphism is known as a balanced category.” On the other hand, and I need to read more into what “split” means in the following, “while every isomorphism is a bimorphism, a bimorphism is not necessarily an isomorphism. For example, in the category of commutative rings the inclusion <strong>Z</strong> ⇒ <strong>Q</strong> is a bimorphism that is not an isomorphism. However, any morphism that is both an epimorphism and a split monomorphism, or both a monomorphism and a split epimorphism, must be an isomorphism.”</li> </ul> </li> <li><a href="https://en.wikipedia.org/wiki/Isomorphism">Isomorphism</a> <ul> <li>Show up all over mathematics. A morphism that “admits a two-sided inverse, meaning that there is another morphism in [the] category [at hand] such that [their forward and backward compositions emit identity arrows on the domain and codomain, respectively].” “Iso” for equal in the sense that if an isomorphism exists, there is an sort of sameness to the two objects.</li> </ul> </li> <li><a href="https://en.wikipedia.org/wiki/Endomorphism">Endomorphism</a> <ul> <li>A morphism from an object onto itself that isn’t necessarily an identity arrow. “Endo” for “within” or “inner.” The prefix shed light on why the Point-Free folks <a href="https://github.com/pointfreeco/swift-prelude/blob/536b8856e38853854b5c5689e5b5d06da75c992e/Sources/Prelude/Endo.swift">named functions in the form <code class="language-plaintext highlighter-rouge">(A) -&gt; A</code>, <code class="language-plaintext highlighter-rouge">Endo&lt;A&gt;</code></a>. Looking at that file now, I wonder what the “i” in <code class="language-plaintext highlighter-rouge">imap</code> stands for and I may or may not have gotten nerd sniped into checking if <code class="language-plaintext highlighter-rouge">imap</code>’s definition shakes out from <a href="https://github.com/pointfreeco/swift-prelude/blob/536b8856e38853854b5c5689e5b5d06da75c992e/Sources/Prelude/Func.swift#L49-L51"><code class="language-plaintext highlighter-rouge">Func.dimap</code></a> when dealing with <code class="language-plaintext highlighter-rouge">Func&lt;A, A&gt;</code>s and <code class="language-plaintext highlighter-rouge">Z == C == B</code> (the <code class="language-plaintext highlighter-rouge">B</code> being <code class="language-plaintext highlighter-rouge">imap</code>’s generic parameter). Looks like it does?…a few messages later and <a href="https://github.com/peter-tomaselli">Peter Tomaselli</a> helped me out! The “i” stands for “invariant,” which reads nicely in that <code class="language-plaintext highlighter-rouge">imap</code>’s co- and contravariant parameters kind of cancel out to <em>invariance</em>.</li> </ul> </li> <li><a href="https://en.wikipedia.org/wiki/Automorphism">Automorphism</a> <ul> <li>An isomorphic endomorphism. “Auto” for same or self.</li> </ul> </li> <li><a href="https://en.wikipedia.org/wiki/Homomorphism">Homomorphism</a> <ul> <li>The star of algebra, a structure-preserving mapping between two algebraic structures. i.e. a homomorphism <code class="language-plaintext highlighter-rouge">f</code> on some structure with a binary operation, say <code class="language-plaintext highlighter-rouge">*</code>, will preserve it across the mapping—<code class="language-plaintext highlighter-rouge">f(a * b) = f(a) * f(b)</code>. I’ll cover the etymological relationship between “hom” and its appearances in category theory—<a href="https://en.wikipedia.org/wiki/Morphism#Hom-set">hom-sets</a> and <a href="https://en.wikipedia.org/wiki/Hom_functor">hom-functors</a>—that isn’t quite restricted to sets in the way algebra generally is in a future note.</li> </ul> </li> <li><a href="https://en.wikipedia.org/wiki/Homeomorphism">Homeomorphism</a> <ul> <li>The one I’m least familiar with—in another life (or maybe later in this one), I want to dig into (algebraic) topology. Seems to be the topologist’s isomorphism (in the category <strong>Top</strong>).</li> </ul> </li> <li><a href="https://en.wikipedia.org/wiki/Catamorphism">Catamorphism</a>, <a href="https://en.wikipedia.org/wiki/Anamorphism">Anamorphism</a>, and <a href="https://en.wikipedia.org/wiki/Hylomorphism_(computer_science)">Hylomorphism</a> <ul> <li>I’ve only dealt with these three in the functional programming sense. Catamorphisms break down a larger structure into a reduced value (“cata” for “down”), anamorphisms build structure from a smaller set of values (“ana” for “up”), and hylomorphism is an ana- followed by a catamorphism (oddly, “hylo” stands for “matter” or “wood,” wat).</li> <li>I ran into catamorphism the other day when trying to put a name on a function in the form <code class="language-plaintext highlighter-rouge">((Left) -&gt; T) -&gt; ((Right) -&gt; T) -&gt; (Either&lt;Left, Right&gt;) -&gt; T</code>. Turns out folks call this <a href="https://hackage.haskell.org/package/base-4.12.0.0/docs/Prelude.html#v:either"><code class="language-plaintext highlighter-rouge">either</code></a>, <a href="https://github.com/antitypical/Result/blob/c0838342cedfefc25f6dd4f95344d376bed582c7/Result/Result.swift#L104-L114"><code class="language-plaintext highlighter-rouge">analysis</code></a>, <code class="language-plaintext highlighter-rouge">converge</code>, or <a href="https://github.com/bow-swift/bow/blob/38e74949fb63736ce9c69c69ab66a4fc67a3b8e0/Sources/Bow/Data/Either.swift#L44-L55"><code class="language-plaintext highlighter-rouge">fold</code></a> (the last of which was somewhat surprising to me in that the <a href="https://hackage.haskell.org/package/base-4.12.0.0/docs/Prelude.html#t:Foldable"><code class="language-plaintext highlighter-rouge">Foldable</code></a> type class requires a monoidal instance, whereas this transformation doesn’t quite have the same requirement). This function is catamorphic in that it <em>reduces</em> an <code class="language-plaintext highlighter-rouge">Either</code> into a <code class="language-plaintext highlighter-rouge">T</code>.</li> <li><code class="language-plaintext highlighter-rouge">zip</code> is an example of an anamorphism that builds a <code class="language-plaintext highlighter-rouge">Zip2Sequence</code> from two <code class="language-plaintext highlighter-rouge">Sequence</code>s and by extension, <a href="https://github.com/pointfreeco/swift-prelude/blob/b26e98e82c7598fd5f381b3a27b59e27e312eb58/Sources/Prelude/Sequence.swift#L136-L145"><code class="language-plaintext highlighter-rouge">zipWith</code></a> is a hylomorphism that <code class="language-plaintext highlighter-rouge">zip</code>s and then reduces down to a summary value by a transformation.</li> <li>Hylomorphisms and <code class="language-plaintext highlighter-rouge">imap</code> both seem to be <em>compositions of dual transformations</em>. Wonder if this pattern pops up elsewhere?</li> </ul> </li> </ul> Getting an intuition around why we can call contramap a pullback 2019-10-17T00:00:00+00:00 https://jasdev.me/notes/contramap-pullback <p>Last October, <a href="https://twitter.com/mbrandonw">Brandon</a> and <a href="https://twitter.com/stephencelis">Stephen</a> reintroduced <code class="language-plaintext highlighter-rouge">contramap</code>—covered in <a href="https://www.pointfree.co/episodes/ep14-contravariance">episode 14</a>—as <code class="language-plaintext highlighter-rouge">pullback</code>.</p> <p>(The analog for the Haskell peeps is <a href="https://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Functor-Contravariant.html#v:contramap"><code class="language-plaintext highlighter-rouge">Contravariant</code>’s <code class="language-plaintext highlighter-rouge">contramap</code> requirement</a>.)</p> <blockquote> <p>However, the name <code class="language-plaintext highlighter-rouge">contramap</code> isn’t fantastic. In one way it’s nice because it is indeed the contravariant version of map. It has basically the same shape as <code class="language-plaintext highlighter-rouge">map</code>, it’s just that the arrow flips the other direction. Even so, the term may seem a little overly-jargony and may turn people off to the idea entirely, and that would be a real shame.</p> </blockquote> <blockquote> <p>Luckily, there’s a concept in math that is far more general than the idea of contravariance, and in the case of functions is precisely <code class="language-plaintext highlighter-rouge">contramap</code>. And even better it has a great name. It’s called the pullback. Intuitively it expresses the idea of pulling a structure back along a function to another structure.</p> </blockquote> <p>—“<a href="https://www.pointfree.co/blog/posts/22-some-news-about-contramap">Some news about contramap</a>”</p> <p>I had trouble getting an intuition around why <code class="language-plaintext highlighter-rouge">contramap</code>’ing is a <a href="https://en.wikipedia.org/wiki/Pullback_(category_theory)">pullback</a>, in the categorical sense and here’s why (mirroring a <a href="https://twitter.com/jasdev/status/1184592454818959360">recent Twitter thread</a>):</p> <blockquote> <p>@pointfreeco—Hey y’all 👋🏽 I’m a tad confused when it comes to grounding the canonical pullback diagram with types and functions between them. (Pardon the rough sketch hah, I forgot my LaTeX and this was more fun. 😄) /1</p> </blockquote> <p><img src="/public/images/pullback.png" alt="" /></p> <blockquote> <p>Pullbacks, generally, seem to give two morphisms and objects worth of freedom—<code class="language-plaintext highlighter-rouge">f</code>, <code class="language-plaintext highlighter-rouge">g</code>, <code class="language-plaintext highlighter-rouge">X</code>, and <code class="language-plaintext highlighter-rouge">Y</code>—whereas in code, we almost always seem to pullback along one [path across] the square. /2</p> </blockquote> <blockquote> <p>Do y’all call this operation pullback because there isn’t a restriction preventing <code class="language-plaintext highlighter-rouge">f</code> from equaling <code class="language-plaintext highlighter-rouge">g</code> and <code class="language-plaintext highlighter-rouge">X</code> equaling <code class="language-plaintext highlighter-rouge">Y</code> (collapsing the square into a linear diagram)? /3</p> </blockquote> <p>Yesterday, Eureka Zhu <a href="https://twitter.com/EurekaZhu/status/1185033773425016834">cleared up my confusion</a> on why we don’t take the upper path through pullback’s definitional diagram:</p> <blockquote> <p>In [the] <code class="language-plaintext highlighter-rouge">Set</code> [category], the pullback of <code class="language-plaintext highlighter-rouge">f: a -&gt; c</code> along <code class="language-plaintext highlighter-rouge">g: b -&gt; c</code> is <code class="language-plaintext highlighter-rouge">{ (a, b) | f a = g b }</code>, which is sometimes undecidable in Haskell [and by extension, Swift].</p> </blockquote> <p>Hand-waving past <code class="language-plaintext highlighter-rouge">Hask</code> and <code class="language-plaintext highlighter-rouge">Swift</code> <a href="https://ro-che.info/articles/2016-08-07-hask-category">not quite being categories</a>, we reason about them through the category <code class="language-plaintext highlighter-rouge">Set</code>.</p> <p>And Zhu points out that a pullback in <code class="language-plaintext highlighter-rouge">Set</code> requires us to equate two functions, <code class="language-plaintext highlighter-rouge">f</code> and <code class="language-plaintext highlighter-rouge">g</code> in the diagram above, for a subset of inputs and that’s undecidable in programming.</p> <p>How do we get around this?</p> <p>Well, setting <code class="language-plaintext highlighter-rouge">X</code> to be <code class="language-plaintext highlighter-rouge">Y</code> and <code class="language-plaintext highlighter-rouge">f</code> to <code class="language-plaintext highlighter-rouge">g</code>:</p> <p><img src="/public/images/collapsing_pullback.png" alt="" /></p> <p>Since pure functions equal themselves, we can sidestep that undecidability by collapsing the diagram. Wickedly clever.</p> <p>That’s how pullback’s flexibility allows us to consider <code class="language-plaintext highlighter-rouge">contramap</code> as a specialization of it.</p> (un)zurry 2019-10-16T00:00:00+00:00 https://jasdev.me/notes/zurry-unzurry <p><code class="language-plaintext highlighter-rouge">(un)zurry</code> moves arguments in and out of <code class="language-plaintext highlighter-rouge">Void</code>-accepting closures in the same way <code class="language-plaintext highlighter-rouge">(un)curry</code> moves arguments in and out of tuples. Concretely, here’s how they’re defined:</p> <script src="https://gist.github.com/jasdev/320cd8dc45543da0f6fca4d861f61e66.js"></script> <script src="https://gist.github.com/jasdev/31b72f034f2ba9768c905b7dad33eb00.js"></script> <p>The former is often helpful when staying point-free with functions that are close, but not quite the right shape you need. e.g. mapping unapplied method references:</p> <script src="https://gist.github.com/jasdev/ef71ee51dd159f8b0c8a8f0b660e138a.js"></script> <p>Eep.</p> <p>Let’s try to make this work.</p> <p>We’d like <code class="language-plaintext highlighter-rouge">[Int].sorted</code> to have the form <code class="language-plaintext highlighter-rouge">[Int] -&gt; [Int]</code>, and since <a href="https://github.com/apple/swift-evolution/blob/d32cf1020ee523b959ef5e0634dad197bc2e957a/proposals/0042-flatten-method-types.md">SE-0042</a> got rejected, we have to chisel it down on our own.</p> <p>First, let’s flip the first two arguments and then address the rogue <code class="language-plaintext highlighter-rouge">()</code>.</p> <script src="https://gist.github.com/jasdev/bcd7db55446b1737d80f1fd410ff1134.js"></script> <p>Getting closer—this is where <code class="language-plaintext highlighter-rouge">zurry</code> can <em>zero</em> out the initial <code class="language-plaintext highlighter-rouge">Void</code> for us.</p> <p><code class="language-plaintext highlighter-rouge">zurry(flip([Int].sorted)) // (Array&lt;Int&gt;) -&gt; Array&lt;Int&gt;</code></p> <p>Wicked, now we can return to our original example:</p> <p><code class="language-plaintext highlighter-rouge">[[2, 1], [3, 1], [4, 1]].map(zurry(flip([Int].sorted))) // [[1, 2], [1, 3], [1, 4]]</code>.</p> <p>Dually, <code class="language-plaintext highlighter-rouge">unzurry</code> shines when you’re working against a <code class="language-plaintext highlighter-rouge">() -&gt; Return</code> interface, that isn’t <code class="language-plaintext highlighter-rouge">@autoclosure</code>’d, and you only have a <code class="language-plaintext highlighter-rouge">Return</code> in hand. Instead of opening a closure, you can pass along <code class="language-plaintext highlighter-rouge">unzurry(yourReturnInstance)</code> and call it a day.</p> <p>The Point-Free folks link to Eitan Chatav’s post where he introduced the term and shows how function application is <a href="https://tangledw3b.wordpress.com/2013/01/18/cartesian-closed-categories/">a zurried form of function composition</a> (!).</p> ObservableType.do‌ or .subscribe (Publisher.handleEvents or .sink)? 2019-10-16T00:00:00+00:00 https://jasdev.me/notes/do-subscribe <p><a href="https://github.com/ReactiveX/RxSwift/blob/9b31a15520306b073cb9d46456f64826c1d6dcab/RxSwift/Observables/Do.swift#L10-L48"><code class="language-plaintext highlighter-rouge">ObservableType.do</code></a>‌ or <a href="https://github.com/ReactiveX/RxSwift/blob/9b31a15520306b073cb9d46456f64826c1d6dcab/RxSwift/ObservableType%2BExtensions.swift#L29-L84"><code class="language-plaintext highlighter-rouge">.subscribe</code></a>?</p> <p>(The question, rephrased for Combine, involves swapping in <a href="https://developer.apple.com/documentation/combine/publisher/3204713-handleevents"><code class="language-plaintext highlighter-rouge">Publisher.handleEvents</code></a> and <a href="https://developer.apple.com/documentation/combine/publisher/3343978-sink"><code class="language-plaintext highlighter-rouge">.sink</code></a>, respectively.)</p> <p><code class="language-plaintext highlighter-rouge">do</code> (<code class="language-plaintext highlighter-rouge">handleEvents</code>) gives you a hook into a sequence’s events <em>without</em> triggering a subscription, while <code class="language-plaintext highlighter-rouge">subscribe</code> (<code class="language-plaintext highlighter-rouge">sink</code>) does the same and triggers a subscription—a way to remember this is the former returns an <code class="language-plaintext highlighter-rouge">Observable</code> (<code class="language-plaintext highlighter-rouge">Publisher</code>) and the latter returns a <code class="language-plaintext highlighter-rouge">Disposable</code> (<code class="language-plaintext highlighter-rouge">AnyCancellable</code>).</p> <p>So, when should we use one over the other?</p> <p>In the past, I’ve always placed <em>effectful</em> work in the <code class="language-plaintext highlighter-rouge">do</code> block, even if meant an empty <code class="language-plaintext highlighter-rouge">subscribe()</code> call at the end of a chain.</p> <p>And I’m starting to change my mind here—putting that sole <code class="language-plaintext highlighter-rouge">do</code> work in the <code class="language-plaintext highlighter-rouge">subscribe</code> block makes the chain more succinct (there are definitely cases where it’s cleaner to use both to sort of group effects, e.g. UI-related versus persistence-related).</p> <p>I dug up an old bit from <a href="https://twitter.com/irace">Bryan Irace</a> in an iOS Slack we’re a part of that puts it well:</p> <blockquote> <p><code class="language-plaintext highlighter-rouge">do</code> is for performing a side effect inside of a bigger chain</p> </blockquote> <blockquote> <p>if all you’re doing is performing the side effect, just do that in your <code class="language-plaintext highlighter-rouge">subscribe</code> block</p> </blockquote>