<?xml version="1.0" encoding="utf-8"?>
    <feed xmlns="http://www.w3.org/2005/Atom">
      <id>https://wolfgirl.dev/blog/</id>
      <title>PolyWolf's Blog - #rust</title>
      <subtitle
        >all posts tagged "rust" on a blog written by PolyWolf</subtitle
      >
      <icon>/apple-touch-icon.png</icon>
      <link href="/tags/rust/rss.xml" rel="self" />
      <link href="https://wolfgirl.dev/blog/" />
      <updated>2026-05-20T13:43:32Z</updated>
      
    <entry>
      <title><![CDATA[Erasing Existentials]]></title>
      <link rel="alternate" href="https://wolfgirl.dev/blog/2026-05-20-erasing-existentials/" />
      <id>https://wolfgirl.dev/blog/2026-05-20-erasing-existentials/</id>
      <published>2026-05-20T13:43:32Z</published>
      <updated>2026-05-20T13:43:32Z</updated>
      <summary><![CDATA[I got nerd-sniped hard, so I wrote a blog post about how to type-erase existential quantifiers in Rust.]]></summary>
      <content type="html"><![CDATA[<p>Recently, I got nerd-sniped <em>hard</em> by <a href=https://bsky.app/profile/welltypedwit.ch/post/3mlpwmdubds2i>a post made by</a> <a href=https://welltypedwit.ch/>Alice (@welltypedwit.ch)</a>. In it, she asks:</p><blockquote><p>So, in Rust, <code>dyn Trait</code> means <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi mathvariant=normal>∃</mi><mi>s</mi><mi mathvariant=normal>.</mi><mtext>Trait</mtext><mo stretchy=false>(</mo><mi>s</mi><mo stretchy=false>)</mo><mo>∧</mo><mi>s</mi></mrow><annotation encoding=application/x-tex>\exists s. \text{Trait}(s) \land s</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class=mord>∃</span><span class="mord mathnormal">s</span><span class=mord>.</span><span class="mord text"><span class=mord>T</span><span class=mord>r</span><span class=mord>a</span><span class=mord>i</span><span class=mord>t</span></span><span class=mopen>(</span><span class="mord mathnormal">s</span><span class=mclose>)</span><span class=mspace style=margin-right:.2222em></span><span class=mbin>∧</span><span class=mspace style=margin-right:.2222em></span></span><span class=base><span class=strut style=height:.4306em></span><span class="mord mathnormal">s</span></span></span></span> , and <code>fn f() -> impl Trait</code> means <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi>f</mi><mo>:</mo><mi mathvariant=normal>∃</mi><mi>s</mi><mi mathvariant=normal>.</mi><mtext>Trait</mtext><mo stretchy=false>(</mo><mi>s</mi><mo stretchy=false>)</mo><mo>∧</mo><mo stretchy=false>(</mo><mo stretchy=false>(</mo><mo stretchy=false>)</mo><mo>→</mo><mi>s</mi><mo stretchy=false>)</mo></mrow><annotation encoding=application/x-tex>f: \exists s. \text{Trait}(s) \land (() \rightarrow s)</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=vertical-align:-.1944em;height:.8889em></span><span class="mord mathnormal" style=margin-right:.1076em>f</span><span class=mspace style=margin-right:.2778em></span><span class=mrel>:</span><span class=mspace style=margin-right:.2778em></span></span><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class=mord>∃</span><span class="mord mathnormal">s</span><span class=mord>.</span><span class="mord text"><span class=mord>T</span><span class=mord>r</span><span class=mord>a</span><span class=mord>i</span><span class=mord>t</span></span><span class=mopen>(</span><span class="mord mathnormal">s</span><span class=mclose>)</span><span class=mspace style=margin-right:.2222em></span><span class=mbin>∧</span><span class=mspace style=margin-right:.2222em></span></span><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class=mopen>((</span><span class=mclose>)</span><span class=mspace style=margin-right:.2778em></span><span class=mrel>→</span><span class=mspace style=margin-right:.2778em></span></span><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class="mord mathnormal">s</span><span class=mclose>)</span></span></span></span> , but what if I want an existential over something other than the <code>Self</code> parameter? Like, what if I have <code>Trait&lt;B></code> and I want an <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi mathvariant=normal>∃</mi><mi>s</mi><mo separator=true>,</mo><mi>b</mi><mi mathvariant=normal>.</mi><mtext>Trait</mtext><mo stretchy=false>(</mo><mi>s</mi><mo separator=true>,</mo><mi>b</mi><mo stretchy=false>)</mo><mo>∧</mo><mi>s</mi></mrow><annotation encoding=application/x-tex>\exists s, b. \text{Trait}(s, b) \land s</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class=mord>∃</span><span class="mord mathnormal">s</span><span class=mpunct>,</span><span class=mspace style=margin-right:.1667em></span><span class="mord mathnormal">b</span><span class=mord>.</span><span class="mord text"><span class=mord>T</span><span class=mord>r</span><span class=mord>a</span><span class=mord>i</span><span class=mord>t</span></span><span class=mopen>(</span><span class="mord mathnormal">s</span><span class=mpunct>,</span><span class=mspace style=margin-right:.1667em></span><span class="mord mathnormal">b</span><span class=mclose>)</span><span class=mspace style=margin-right:.2222em></span><span class=mbin>∧</span><span class=mspace style=margin-right:.2222em></span></span><span class=base><span class=strut style=height:.4306em></span><span class="mord mathnormal">s</span></span></span></span> ?</p></blockquote><p>That is a lot of fancy math symbols, but I hope by the end of this you will understand both what they mean and my answer to this question!</p><h2><a aria-hidden=true class=anchor href=#what-is-an-existential-quantifier id=heading-what-is-an-existential-quantifier></a>What Is An Existential Quantifier?</h2><p>A hint can be found in the first part of her question:</p><blockquote><p><code>dyn Trait</code> means <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi mathvariant=normal>∃</mi><mi>s</mi><mi mathvariant=normal>.</mi><mtext>Trait</mtext><mo stretchy=false>(</mo><mi>s</mi><mo stretchy=false>)</mo><mo>∧</mo><mi>s</mi></mrow><annotation encoding=application/x-tex>\exists s. \text{Trait}(s) \land s</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class=mord>∃</span><span class="mord mathnormal">s</span><span class=mord>.</span><span class="mord text"><span class=mord>T</span><span class=mord>r</span><span class=mord>a</span><span class=mord>i</span><span class=mord>t</span></span><span class=mopen>(</span><span class="mord mathnormal">s</span><span class=mclose>)</span><span class=mspace style=margin-right:.2222em></span><span class=mbin>∧</span><span class=mspace style=margin-right:.2222em></span></span><span class=base><span class=strut style=height:.4306em></span><span class="mord mathnormal">s</span></span></span></span></p></blockquote><p>Let's think about what it means to have a value of type <a href=https://doc.rust-lang.org/reference/types/trait-object.html><code>dyn Trait</code></a><sup class=footnote-ref><a data-footnote-ref href=#fn-sized id=fnref-sized>1</a></sup>. We know there is <em>some</em> underlying type, but we don't have a way of getting at that type. All we can do is call methods from <code>Trait</code>.</p><p>That's pretty much exactly what that formula says! "There exists ( <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi mathvariant=normal>∃</mi></mrow><annotation encoding=application/x-tex>\exists</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=height:.6944em></span><span class=mord>∃</span></span></span></span> ) some type <code>s</code>, such that ( <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi mathvariant=normal>.</mi></mrow><annotation encoding=application/x-tex>.</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=height:.1056em></span><span class=mord>.</span></span></span></span> ) <code>s</code> conforms to <code>Trait</code>, and ( <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mo>∧</mo></mrow><annotation encoding=application/x-tex>\land</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=height:.5556em></span><span class=mord>∧</span></span></span></span> ) you can use <code>s</code> right now". The scary math symbols are just a concise way of writing that. Math also helps us formalize the fact that, given just this <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi mathvariant=normal>∃</mi></mrow><annotation encoding=application/x-tex>\exists</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=height:.6944em></span><span class=mord>∃</span></span></span></span> statement, we have no way of knowing what <code>s</code> will be ahead of time, so in constructing a "proof" that the program is well-typed, we can only assume things about <code>s</code> that come from it conforming to <code>Trait</code><sup class=footnote-ref><a data-footnote-ref href=#fn-dyn id=fnref-dyn>2</a></sup>.</p><p>Alice also points out another place Rust has something that looks like an existential quantifier:</p><blockquote><p><code>fn f() -> impl Trait</code> means <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi>f</mi><mo>:</mo><mi mathvariant=normal>∃</mi><mi>s</mi><mi mathvariant=normal>.</mi><mtext>Trait</mtext><mo stretchy=false>(</mo><mi>s</mi><mo stretchy=false>)</mo><mo>∧</mo><mo stretchy=false>(</mo><mo stretchy=false>(</mo><mo stretchy=false>)</mo><mo>→</mo><mi>s</mi><mo stretchy=false>)</mo></mrow><annotation encoding=application/x-tex>f: \exists s. \text{Trait}(s) \land (() \rightarrow s)</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=vertical-align:-.1944em;height:.8889em></span><span class="mord mathnormal" style=margin-right:.1076em>f</span><span class=mspace style=margin-right:.2778em></span><span class=mrel>:</span><span class=mspace style=margin-right:.2778em></span></span><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class=mord>∃</span><span class="mord mathnormal">s</span><span class=mord>.</span><span class="mord text"><span class=mord>T</span><span class=mord>r</span><span class=mord>a</span><span class=mord>i</span><span class=mord>t</span></span><span class=mopen>(</span><span class="mord mathnormal">s</span><span class=mclose>)</span><span class=mspace style=margin-right:.2222em></span><span class=mbin>∧</span><span class=mspace style=margin-right:.2222em></span></span><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class=mopen>((</span><span class=mclose>)</span><span class=mspace style=margin-right:.2778em></span><span class=mrel>→</span><span class=mspace style=margin-right:.2778em></span></span><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class="mord mathnormal">s</span><span class=mclose>)</span></span></span></span></p></blockquote><p>This "<a href=https://doc.rust-lang.org/reference/types/impl-trait.html#r-type.impl-trait.return>return-position impl trait</a>" syntax is indeed another way of specifying a type such that callers of <code>f</code> only know about the fact that the return type conforms to <code>Trait</code>. Unlike with <code>dyn Trait</code>, these are fixed at compile time, but type-theoretically they're somewhat similar.</p><p>So! Now that we have a bit of understanding, let's get started on the real question:</p><blockquote><p>What if I have <code>Trait&lt;B></code> and I want an <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi mathvariant=normal>∃</mi><mi>s</mi><mo separator=true>,</mo><mi>b</mi><mi mathvariant=normal>.</mi><mtext>Trait</mtext><mo stretchy=false>(</mo><mi>s</mi><mo separator=true>,</mo><mi>b</mi><mo stretchy=false>)</mo><mo>∧</mo><mi>s</mi></mrow><annotation encoding=application/x-tex>\exists s,b. \text{Trait}(s, b) \land s</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class=mord>∃</span><span class="mord mathnormal">s</span><span class=mpunct>,</span><span class=mspace style=margin-right:.1667em></span><span class="mord mathnormal">b</span><span class=mord>.</span><span class="mord text"><span class=mord>T</span><span class=mord>r</span><span class=mord>a</span><span class=mord>i</span><span class=mord>t</span></span><span class=mopen>(</span><span class="mord mathnormal">s</span><span class=mpunct>,</span><span class=mspace style=margin-right:.1667em></span><span class="mord mathnormal">b</span><span class=mclose>)</span><span class=mspace style=margin-right:.2222em></span><span class=mbin>∧</span><span class=mspace style=margin-right:.2222em></span></span><span class=base><span class=strut style=height:.4306em></span><span class="mord mathnormal">s</span></span></span></span> ?</p></blockquote><p>That is, is it possible to have an existentially quantified type <code>S</code> that itself has another existentially quantified type variable <code>B</code>, such that <code>S: Trait&lt;B></code>?</p><h2><a aria-hidden=true class=anchor href=#attempt-1-for-exists-conversion id=heading-attempt-1-for-exists-conversion></a>Attempt 1: For-Exists Conversion</h2><p>A classic way of dealing with <em>any</em> existentials is something I call "for-exists conversion". Basically, it relies on the following identity:</p><p><span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mo stretchy=false>(</mo><mo stretchy=false>(</mo><mi mathvariant=normal>∃</mi><mi>x</mi><mi mathvariant=normal>.</mi><mi>P</mi><mo stretchy=false>(</mo><mi>x</mi><mo stretchy=false>)</mo><mo stretchy=false>)</mo><mo>→</mo><mi>Q</mi><mo stretchy=false>)</mo><mo>⟺</mo><mo stretchy=false>(</mo><mi mathvariant=normal>∀</mi><mi>x</mi><mi mathvariant=normal>.</mi><mo stretchy=false>(</mo><mi>P</mi><mo stretchy=false>(</mo><mi>x</mi><mo stretchy=false>)</mo><mo>→</mo><mi>Q</mi><mo stretchy=false>)</mo><mo stretchy=false>)</mo></mrow><annotation encoding=application/x-tex> ((\exists x.P(x)) \rightarrow Q) \Longleftrightarrow (\forall x.(P(x) \rightarrow Q)) </annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class=mopen>((</span><span class=mord>∃</span><span class="mord mathnormal">x</span><span class=mord>.</span><span class="mord mathnormal" style=margin-right:.1389em>P</span><span class=mopen>(</span><span class="mord mathnormal">x</span><span class=mclose>))</span><span class=mspace style=margin-right:.2778em></span><span class=mrel>→</span><span class=mspace style=margin-right:.2778em></span></span><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class="mord mathnormal">Q</span><span class=mclose>)</span><span class=mspace style=margin-right:.2778em></span><span class=mrel>⟺</span><span class=mspace style=margin-right:.2778em></span></span><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class=mopen>(</span><span class=mord>∀</span><span class="mord mathnormal">x</span><span class=mord>.</span><span class=mopen>(</span><span class="mord mathnormal" style=margin-right:.1389em>P</span><span class=mopen>(</span><span class="mord mathnormal">x</span><span class=mclose>)</span><span class=mspace style=margin-right:.2778em></span><span class=mrel>→</span><span class=mspace style=margin-right:.2778em></span></span><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class="mord mathnormal">Q</span><span class=mclose>))</span></span></span></span></p><details><summary>Type theory friend</summary> <p>My type theory friend said "this is just currying lol" and I was very confused, because the definition for currying I'm familiar with is</p> <p><span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mo stretchy=false>(</mo><mo stretchy=false>⟨</mo><mi>a</mi><mo separator=true>,</mo><mi>b</mi><mo stretchy=false>⟩</mo><mo>→</mo><mi>c</mi><mo stretchy=false>)</mo><mo>⟺</mo><mo stretchy=false>(</mo><mi>a</mi><mo>→</mo><mi>b</mi><mo>→</mo><mi>c</mi><mo stretchy=false>)</mo></mrow><annotation encoding=application/x-tex> (\langle a, b \rangle \rightarrow c) \Longleftrightarrow (a \rightarrow b \rightarrow c) </annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class=mopen>(⟨</span><span class="mord mathnormal">a</span><span class=mpunct>,</span><span class=mspace style=margin-right:.1667em></span><span class="mord mathnormal">b</span><span class=mclose>⟩</span><span class=mspace style=margin-right:.2778em></span><span class=mrel>→</span><span class=mspace style=margin-right:.2778em></span></span><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class="mord mathnormal">c</span><span class=mclose>)</span><span class=mspace style=margin-right:.2778em></span><span class=mrel>⟺</span><span class=mspace style=margin-right:.2778em></span></span><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class=mopen>(</span><span class="mord mathnormal">a</span><span class=mspace style=margin-right:.2778em></span><span class=mrel>→</span><span class=mspace style=margin-right:.2778em></span></span><span class=base><span class=strut style=height:.6944em></span><span class="mord mathnormal">b</span><span class=mspace style=margin-right:.2778em></span><span class=mrel>→</span><span class=mspace style=margin-right:.2778em></span></span><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class="mord mathnormal">c</span><span class=mclose>)</span></span></span></span></p> <p>That is, given a function taking a tuple, you can make it into a function returning a function, which at first glance looks nothing like the exists/forall theorem stated above.</p> <p>However! If you have a "dependent sum" <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><msub><mi mathvariant=normal>Σ</mi><mi>α</mi></msub><mi>β</mi><mo stretchy=false>(</mo><mi>α</mi><mo stretchy=false>)</mo></mrow><annotation encoding=application/x-tex>\Sigma_\alpha \beta(\alpha)</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class=mord><span class=mord>Σ</span><span class=msupsub><span class="vlist-t vlist-t2"><span class=vlist-r><span class=vlist style=height:.1514em><span style=margin-left:0;margin-right:.05em;top:-2.55em><span class=pstrut style=height:2.7em></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style=margin-right:.0037em>α</span></span></span></span><span class=vlist-s>​</span></span><span class=vlist-r><span class=vlist style=height:.15em><span></span></span></span></span></span></span><span class="mord mathnormal" style=margin-right:.0528em>β</span><span class=mopen>(</span><span class="mord mathnormal" style=margin-right:.0037em>α</span><span class=mclose>)</span></span></span></span> (type of second element <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi>β</mi></mrow><annotation encoding=application/x-tex>\beta</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=vertical-align:-.1944em;height:.8889em></span><span class="mord mathnormal" style=margin-right:.0528em>β</span></span></span></span> depends on value of first element <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi>α</mi></mrow><annotation encoding=application/x-tex>\alpha</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=height:.4306em></span><span class="mord mathnormal" style=margin-right:.0037em>α</span></span></span></span> ) and a "dependent product" <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><msub><mi mathvariant=normal>Π</mi><mi>α</mi></msub><mi>β</mi><mo stretchy=false>(</mo><mi>α</mi><mo stretchy=false>)</mo></mrow><annotation encoding=application/x-tex>\Pi_\alpha \beta(\alpha)</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class=mord><span class=mord>Π</span><span class=msupsub><span class="vlist-t vlist-t2"><span class=vlist-r><span class=vlist style=height:.1514em><span style=margin-left:0;margin-right:.05em;top:-2.55em><span class=pstrut style=height:2.7em></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style=margin-right:.0037em>α</span></span></span></span><span class=vlist-s>​</span></span><span class=vlist-r><span class=vlist style=height:.15em><span></span></span></span></span></span></span><span class="mord mathnormal" style=margin-right:.0528em>β</span><span class=mopen>(</span><span class="mord mathnormal" style=margin-right:.0037em>α</span><span class=mclose>)</span></span></span></span> (function where the return type <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi>β</mi></mrow><annotation encoding=application/x-tex>\beta</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=vertical-align:-.1944em;height:.8889em></span><span class="mord mathnormal" style=margin-right:.0528em>β</span></span></span></span> depends on the value of the input <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi>α</mi></mrow><annotation encoding=application/x-tex>\alpha</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=height:.4306em></span><span class="mord mathnormal" style=margin-right:.0037em>α</span></span></span></span> ), then you get <a href=https://ncatlab.org/nlab/show/existential+quantifier#in_dependent_type_theory>a very similar-looking identity</a><sup class=footnote-ref><a data-footnote-ref href=#fn-nlab id=fnref-nlab>3</a></sup>:</p> <p><span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mo stretchy=false>(</mo><mo stretchy=false>(</mo><msub><mi mathvariant=normal>Σ</mi><mi>α</mi></msub><mi>β</mi><mo stretchy=false>(</mo><mi>α</mi><mo stretchy=false>)</mo><mo stretchy=false>)</mo><mo>→</mo><mi>C</mi><mo stretchy=false>)</mo><mo>⟺</mo><mo stretchy=false>(</mo><msub><mi mathvariant=normal>Π</mi><mi>α</mi></msub><mi>β</mi><mo stretchy=false>(</mo><mi>α</mi><mo stretchy=false>)</mo><mo>→</mo><mi>C</mi><mo stretchy=false>)</mo></mrow><annotation encoding=application/x-tex> ((\Sigma_\alpha \beta(\alpha)) \rightarrow C) \Longleftrightarrow (\Pi_\alpha \beta(\alpha) \rightarrow C) </annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class=mopen>((</span><span class=mord><span class=mord>Σ</span><span class=msupsub><span class="vlist-t vlist-t2"><span class=vlist-r><span class=vlist style=height:.1514em><span style=margin-left:0;margin-right:.05em;top:-2.55em><span class=pstrut style=height:2.7em></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style=margin-right:.0037em>α</span></span></span></span><span class=vlist-s>​</span></span><span class=vlist-r><span class=vlist style=height:.15em><span></span></span></span></span></span></span><span class="mord mathnormal" style=margin-right:.0528em>β</span><span class=mopen>(</span><span class="mord mathnormal" style=margin-right:.0037em>α</span><span class=mclose>))</span><span class=mspace style=margin-right:.2778em></span><span class=mrel>→</span><span class=mspace style=margin-right:.2778em></span></span><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class="mord mathnormal" style=margin-right:.0715em>C</span><span class=mclose>)</span><span class=mspace style=margin-right:.2778em></span><span class=mrel>⟺</span><span class=mspace style=margin-right:.2778em></span></span><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class=mopen>(</span><span class=mord><span class=mord>Π</span><span class=msupsub><span class="vlist-t vlist-t2"><span class=vlist-r><span class=vlist style=height:.1514em><span style=margin-left:0;margin-right:.05em;top:-2.55em><span class=pstrut style=height:2.7em></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style=margin-right:.0037em>α</span></span></span></span><span class=vlist-s>​</span></span><span class=vlist-r><span class=vlist style=height:.15em><span></span></span></span></span></span></span><span class="mord mathnormal" style=margin-right:.0528em>β</span><span class=mopen>(</span><span class="mord mathnormal" style=margin-right:.0037em>α</span><span class=mclose>)</span><span class=mspace style=margin-right:.2778em></span><span class=mrel>→</span><span class=mspace style=margin-right:.2778em></span></span><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class="mord mathnormal" style=margin-right:.0715em>C</span><span class=mclose>)</span></span></span></span></p> <p>You can interpret both sides either way:</p> <ul><li>In the "types as propositions" interpretation, <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi mathvariant=normal>Σ</mi></mrow><annotation encoding=application/x-tex>\Sigma</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=height:.6833em></span><span class=mord>Σ</span></span></span></span> is <em>kind of</em> like <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi mathvariant=normal>∃</mi></mrow><annotation encoding=application/x-tex>\exists</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=height:.6944em></span><span class=mord>∃</span></span></span></span> in that, for it to be inhabited, you only need a single <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mo stretchy=false>⟨</mo><mi>α</mi><mo separator=true>,</mo><mi>β</mi><mo stretchy=false>(</mo><mi>α</mi><mo stretchy=false>)</mo><mo stretchy=false>⟩</mo></mrow><annotation encoding=application/x-tex>\langle \alpha, \beta(\alpha) \rangle</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class=mopen>⟨</span><span class="mord mathnormal" style=margin-right:.0037em>α</span><span class=mpunct>,</span><span class=mspace style=margin-right:.1667em></span><span class="mord mathnormal" style=margin-right:.0528em>β</span><span class=mopen>(</span><span class="mord mathnormal" style=margin-right:.0037em>α</span><span class=mclose>)⟩</span></span></span></span> pair, and <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi mathvariant=normal>Π</mi></mrow><annotation encoding=application/x-tex>\Pi</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=height:.6833em></span><span class=mord>Π</span></span></span></span> is <em>kind of</em> like <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi mathvariant=normal>∀</mi></mrow><annotation encoding=application/x-tex>\forall</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=height:.6944em></span><span class=mord>∀</span></span></span></span> in that, for it to be inhabited, you need to show how to get a <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi>β</mi><mo stretchy=false>(</mo><mi>α</mi><mo stretchy=false>)</mo></mrow><annotation encoding=application/x-tex>\beta(\alpha)</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class="mord mathnormal" style=margin-right:.0528em>β</span><span class=mopen>(</span><span class="mord mathnormal" style=margin-right:.0037em>α</span><span class=mclose>)</span></span></span></span> for every possible <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi>α</mi></mrow><annotation encoding=application/x-tex>\alpha</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=height:.4306em></span><span class="mord mathnormal" style=margin-right:.0037em>α</span></span></span></span> .</li><li>Interpreting them as datastructures, <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi mathvariant=normal>Σ</mi></mrow><annotation encoding=application/x-tex>\Sigma</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=height:.6833em></span><span class=mord>Σ</span></span></span></span> is just a tuple, and <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi mathvariant=normal>Π</mi></mrow><annotation encoding=application/x-tex>\Pi</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=height:.6833em></span><span class=mord>Π</span></span></span></span> is just a function, which is exactly what our original currying looks like.</li></ul> <p>So, my type theory friend argues "you should call this 'currying' instead of making up a new name". But I don't agree with that since the connection is not obvious, so I will stick with my own name for the sake of clarity :) The more you know!</p></details><p>In Rust, this can be stated as:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>fn</span> <span class=function>f_left</span><span class=punctuation>(</span><span class=variable>x</span><span class=punctuation>:</span> <span class=keyword>impl</span> <span class=type>P</span><span class=punctuation>)</span> -> <span class=type>Q</span> <span class=punctuation>{</span>
    x<span class=punctuation>.</span><span class=function>foobar</span><span class=punctuation>()</span>
<span class=punctuation>}</span>

<span class=keyword>fn</span> <span class=function>f_right</span><span class=punctuation>&lt;</span><span class=type>X</span><span class=punctuation>:</span> <span class=type>P</span><span class=punctuation>>(</span><span class=variable>x</span><span class=punctuation>:</span> <span class=type>X</span><span class=punctuation>)</span> -> <span class=type>Q</span> <span class=punctuation>{</span>
    x<span class=punctuation>.</span><span class=function>foobar</span><span class=punctuation>()</span>
<span class=punctuation>}</span></code></pre><p>"But PolyWolf!" I hear you say, "You've just written the same function twice??" And that, dear reader, is exactly what's so sneaky about this identity. It <em>seems</em> obvious, but it's actually saying something pretty profound.</p><p><code>f_left</code> really does use an existential quantifier for its argument (all you know about <code>x</code> is that it has a type conforming to <code>P</code>), while <code>f_right</code> first quantifies over all possible types for <code>X</code>, then restricts to those that satisfy the trait <code>P</code>. In Rust, you might think the former is "just sugar for" the latter, but that doesn't change the fact these are <em>different mathematical statements</em>.</p><p>To see this more clearly, let's think about what happens when we use those argument types as return types instead:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>fn</span> <span class=function>g1</span><span class=punctuation>()</span> -> <span class=keyword>impl</span> <span class=type>P</span> <span class=punctuation>{</span> <span class=type>SpecificX</span><span class=punctuation>::</span><span class=function>proof</span><span class=punctuation>()</span> <span class=punctuation>}</span>

<span class=keyword>fn</span> <span class=function>g2</span><span class=punctuation>&lt;</span><span class=type>X</span><span class=punctuation>:</span> <span class=type>P</span><span class=punctuation>>()</span> -> <span class=type>X</span> <span class=punctuation>{</span> <span class=type>X</span><span class=punctuation>::</span><span class=function>proof</span><span class=punctuation>()</span> <span class=punctuation>}</span></code></pre><p>Here, we can more clearly see that <code>impl P</code> and <code>X: P</code> are saying different things. The former, like <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi mathvariant=normal>∃</mi></mrow><annotation encoding=application/x-tex>\exists</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=height:.6944em></span><span class=mord>∃</span></span></span></span> , only requires you to demonstrate a <code>SpecificX</code> that satisfies the trait <code>P</code>, while the latter, like <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi mathvariant=normal>∀</mi></mrow><annotation encoding=application/x-tex>\forall</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=height:.6944em></span><span class=mord>∀</span></span></span></span> , requires you to produce valid values for all possible <code>X: P</code>.</p><p>So! Getting back to our original problem, we want to describe a type of the form:</p><p><span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi mathvariant=normal>∃</mi><mi>s</mi><mo separator=true>,</mo><mi>b</mi><mi mathvariant=normal>.</mi><mtext>Trait</mtext><mo stretchy=false>(</mo><mi>s</mi><mo separator=true>,</mo><mi>b</mi><mo stretchy=false>)</mo><mo>∧</mo><mi>s</mi></mrow><annotation encoding=application/x-tex>\exists s, b. \text{Trait}(s, b) \land s</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class=mord>∃</span><span class="mord mathnormal">s</span><span class=mpunct>,</span><span class=mspace style=margin-right:.1667em></span><span class="mord mathnormal">b</span><span class=mord>.</span><span class="mord text"><span class=mord>T</span><span class=mord>r</span><span class=mord>a</span><span class=mord>i</span><span class=mord>t</span></span><span class=mopen>(</span><span class="mord mathnormal">s</span><span class=mpunct>,</span><span class=mspace style=margin-right:.1667em></span><span class="mord mathnormal">b</span><span class=mclose>)</span><span class=mspace style=margin-right:.2222em></span><span class=mbin>∧</span><span class=mspace style=margin-right:.2222em></span></span><span class=base><span class=strut style=height:.4306em></span><span class="mord mathnormal">s</span></span></span></span> For the rest of this post, we're going to assume that <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mtext>Trait(s, b)</mtext></mrow><annotation encoding=application/x-tex>\text{Trait(s, b)}</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class="mord text"><span class=mord>T</span><span class=mord>r</span><span class=mord>a</span><span class=mord>i</span><span class=mord>t</span><span class=mord>(</span><span class=mord>s</span><span class=mord>,</span><span class=mord> </span><span class=mord>b</span><span class=mord>)</span></span></span></span></span> is some <code>S: Generic&lt;B></code> (renamed to avoid confusion w/ other traits introduced later), and that we can only build new types/traits that reference it, never modifying it or the following impls:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>pub</span> <span class=keyword>trait</span> <span class=type>Generic</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=keyword>fn</span> <span class=function>name</span><span class=punctuation>(</span><span class=operator>&</span><span class=variable>self</span><span class=punctuation>)</span> -> <span class=operator>&'</span><span class=label>static</span> <span class=type>str</span><span class=punctuation>;</span>
<span class=punctuation>}</span>
<span class=keyword>impl</span> <span class=type>Generic</span><span class=punctuation>&lt;</span><span class=type>f32</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>i32</span> <span class=punctuation>{</span>
    <span class=keyword>fn</span> <span class=function>name</span><span class=punctuation>(</span><span class=operator>&</span><span class=variable>self</span><span class=punctuation>)</span> -> <span class=operator>&'</span><span class=label>static</span> <span class=type>str</span> <span class=punctuation>{</span> <span class=string>"f32"</span> <span class=punctuation>}</span>
<span class=punctuation>}</span>
<span class=keyword>impl</span> <span class=type>Generic</span><span class=punctuation>&lt;</span><span class=type>f64</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>i32</span> <span class=punctuation>{</span>
    <span class=keyword>fn</span> <span class=function>name</span><span class=punctuation>(</span><span class=operator>&</span><span class=variable>self</span><span class=punctuation>)</span> -> <span class=operator>&'</span><span class=label>static</span> <span class=type>str</span> <span class=punctuation>{</span> <span class=string>"f64"</span> <span class=punctuation>}</span>
<span class=punctuation>}</span></code></pre><p>Restricting ourselves first to functions that take in such a type, we get:</p><p><span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mo stretchy=false>(</mo><mi mathvariant=normal>∃</mi><mi>s</mi><mo separator=true>,</mo><mi>b</mi><mi mathvariant=normal>.</mi><mtext>Trait</mtext><mo stretchy=false>(</mo><mi>s</mi><mo separator=true>,</mo><mi>b</mi><mo stretchy=false>)</mo><mo>∧</mo><mi>s</mi><mo stretchy=false>)</mo><mo>→</mo><mo stretchy=false>(</mo><mo stretchy=false>)</mo></mrow><annotation encoding=application/x-tex>(\exists s, b. \text{Trait}(s, b) \land s) \rightarrow ()</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class=mopen>(</span><span class=mord>∃</span><span class="mord mathnormal">s</span><span class=mpunct>,</span><span class=mspace style=margin-right:.1667em></span><span class="mord mathnormal">b</span><span class=mord>.</span><span class="mord text"><span class=mord>T</span><span class=mord>r</span><span class=mord>a</span><span class=mord>i</span><span class=mord>t</span></span><span class=mopen>(</span><span class="mord mathnormal">s</span><span class=mpunct>,</span><span class=mspace style=margin-right:.1667em></span><span class="mord mathnormal">b</span><span class=mclose>)</span><span class=mspace style=margin-right:.2222em></span><span class=mbin>∧</span><span class=mspace style=margin-right:.2222em></span></span><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class="mord mathnormal">s</span><span class=mclose>)</span><span class=mspace style=margin-right:.2778em></span><span class=mrel>→</span><span class=mspace style=margin-right:.2778em></span></span><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class=mopen>(</span><span class=mclose>)</span></span></span></span></p><p>Then, using our identity, we can rewrite it as:</p><p><span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi mathvariant=normal>∀</mi><mi>s</mi><mo separator=true>,</mo><mi>b</mi><mi mathvariant=normal>.</mi><mo stretchy=false>(</mo><mtext>Trait</mtext><mo stretchy=false>(</mo><mi>s</mi><mo separator=true>,</mo><mi>b</mi><mo stretchy=false>)</mo><mo>∧</mo><mi>s</mi><mo stretchy=false>)</mo><mo>→</mo><mo stretchy=false>(</mo><mo stretchy=false>)</mo></mrow><annotation encoding=application/x-tex>\forall s, b. (\text{Trait}(s, b) \land s) \rightarrow ()</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class=mord>∀</span><span class="mord mathnormal">s</span><span class=mpunct>,</span><span class=mspace style=margin-right:.1667em></span><span class="mord mathnormal">b</span><span class=mord>.</span><span class=mopen>(</span><span class="mord text"><span class=mord>T</span><span class=mord>r</span><span class=mord>a</span><span class=mord>i</span><span class=mord>t</span></span><span class=mopen>(</span><span class="mord mathnormal">s</span><span class=mpunct>,</span><span class=mspace style=margin-right:.1667em></span><span class="mord mathnormal">b</span><span class=mclose>)</span><span class=mspace style=margin-right:.2222em></span><span class=mbin>∧</span><span class=mspace style=margin-right:.2222em></span></span><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class="mord mathnormal">s</span><span class=mclose>)</span><span class=mspace style=margin-right:.2778em></span><span class=mrel>→</span><span class=mspace style=margin-right:.2778em></span></span><span class=base><span class=strut style=vertical-align:-.25em;height:1em></span><span class=mopen>(</span><span class=mclose>)</span></span></span></span></p><p>This results in the following function (<a href="https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=5d4a408cb9e2b259c311f65fd8de2c6c">playground link</a>):</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>fn</span> <span class=function>use_generic</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>,</span> <span class=type>S</span><span class=punctuation>:</span> <span class=type>Generic</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>>>(</span><span class=variable>s</span><span class=punctuation>:</span> <span class=operator>&</span><span class=type>S</span><span class=punctuation>)</span> -> <span class=operator>&'</span><span class=label>static</span> <span class=type>str</span> <span class=punctuation>{</span>
    s<span class=punctuation>.</span><span class=function>name</span><span class=punctuation>()</span>
<span class=punctuation>}</span></code></pre><p>Easy peasy! This is a very standard way of dealing with the problem, and imo the most clean. However, <a href=https://bsky.app/profile/welltypedwit.ch/post/3mlqosj7his2o>Alice was not satisfied</a>:</p><blockquote><p>You couldn't e.g. put this one in a data structure (behind a <code>Box</code>) though, could you?</p></blockquote><p>And that's entirely true! Due to the way we rephrased the problem in terms of <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi mathvariant=normal>∀</mi></mrow><annotation encoding=application/x-tex>\forall</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=height:.6944em></span><span class=mord>∀</span></span></span></span> , once we return a <code>Box&lt;dyn Generic&lt;B>></code>, we'd have to keep a <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi mathvariant=normal>∀</mi><mi>b</mi></mrow><annotation encoding=application/x-tex>\forall b</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=height:.6944em></span><span class=mord>∀</span><span class="mord mathnormal">b</span></span></span></span> clause around on everything that uses it, which sort of defeats the purpose of having <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi mathvariant=normal>∃</mi></mrow><annotation encoding=application/x-tex>\exists</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=height:.6944em></span><span class=mord>∃</span></span></span></span> types in the first place (<a href="https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=7889714d44805f2f7f35a7a36a4f8e85">playground link</a>):</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>fn</span> <span class=function>put_in_box</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>,</span> <span class=type>S</span><span class=punctuation>:</span> <span class=type>Generic</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>></span> + <span class=operator>'</span><span class=label>static</span><span class=punctuation>>(</span><span class=variable>s</span><span class=punctuation>:</span> <span class=type>S</span><span class=punctuation>)</span> -> <span class=type>Box</span><span class=punctuation>&lt;</span><span class=keyword>dyn</span> <span class=type>Generic</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>>></span> <span class=punctuation>{</span>
    <span class=type>Box</span><span class=punctuation>::</span><span class=function>new</span><span class=punctuation>(</span>s<span class=punctuation>)</span>
<span class=punctuation>}</span>

<span class=keyword>fn</span> <span class=function>use_box</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>>(</span><span class=variable>s</span><span class=punctuation>:</span> <span class=operator>&</span><span class=type>Box</span><span class=punctuation>&lt;</span><span class=keyword>dyn</span> <span class=type>Generic</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>>>)</span> -> <span class=operator>&'</span><span class=label>static</span> <span class=type>str</span> <span class=punctuation>{</span>
    s<span class=punctuation>.</span><span class=function>name</span><span class=punctuation>()</span>
<span class=punctuation>}</span></code></pre><p>This solution may be satisfactory for some usecases, but we'll have to try a bit harder to get things to only use <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi mathvariant=normal>∃</mi></mrow><annotation encoding=application/x-tex>\exists</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=height:.6944em></span><span class=mord>∃</span></span></span></span> <span></span>.</p><h2><a aria-hidden=true class=anchor href=#attempt-2-associated-types id=heading-attempt-2-associated-types></a>Attempt 2: Associated Types</h2><p>My next idea was to "smuggle" the <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi mathvariant=normal>∃</mi><mi>s</mi><mo separator=true>,</mo><mi>b</mi><mi mathvariant=normal>.</mi></mrow><annotation encoding=application/x-tex>\exists s, b.</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=vertical-align:-.1944em;height:.8889em></span><span class=mord>∃</span><span class="mord mathnormal">s</span><span class=mpunct>,</span><span class=mspace style=margin-right:.1667em></span><span class="mord mathnormal">b</span><span class=mord>.</span></span></span></span> quantifier behind a single <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi mathvariant=normal>∃</mi><mi>t</mi><mi mathvariant=normal>.</mi></mrow><annotation encoding=application/x-tex>\exists t.</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=height:.6944em></span><span class=mord>∃</span><span class="mord mathnormal">t</span><span class=mord>.</span></span></span></span> quantifier that Rust gives when using trait objects. A first crack at it might look something like (<a href="https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=349cf08dc829d60b126b9f52b4f411ed">playground link</a>):</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>trait</span> <span class=type>SingleAssociated</span> <span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>B</span><span class=punctuation>;</span>
<span class=punctuation>}</span>

<span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>,</span> <span class=type>S</span><span class=punctuation>:</span> <span class=type>Generic</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>>></span> <span class=type>SingleAssociated</span> <span class=keyword>for</span> <span class=type>S</span> <span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>B</span> = <span class=type>B</span><span class=punctuation>;</span>
<span class=punctuation>}</span></code></pre><p>However, we quickly run into a problem:</p><pre class=syntax-highlighting><code>error[E0207]: the type parameter `B` is not constrained by the impl trait, self type, or predicates
  --> src/main.rs:15:6
   |
15 | impl&lt;B, S: Generic&lt;B>> SingleAssociated for S {
   |      ^ unconstrained type parameter
</code></pre><p>The <a href=https://doc.rust-lang.org/error_codes/E0207.html>E0207 docs</a> do an excellent job explaining this (you should try <code>rustc --explain</code> if you haven't already!), but the short version is, whenever we're implementing traits, Rust wants there to be <em>at most one</em> concrete trait implementation for every concrete type. This restriction is called "coherence", because it would be "incoherent" if we could choose between multiple possible trait implementations for a single type<sup class=footnote-ref><a data-footnote-ref href=#fn-specialization id=fnref-specialization>4</a></sup>. When we write <code>impl&lt;B, S: Generic&lt;B>></code>, Rust considers all possible <code>B</code> and <code>S</code> that fit the criteria before moving onto the rest of the code. However, "all possible <code>B</code>" could result in more than one <code>SingleAssociated for S</code> implementations, so we are not allowed to do that.</p><p>To get around this, we'll have to put <code>B</code> in either the trait or type, to "constrain" it. We can't put it in the <code>SingleAssociated</code> trait definition (that would make it the same as <code>Generic&lt;B></code>!), and we can't put it in <code>S</code> (it's already generic), so what can we possibly do? <code>PhantomData</code> :)</p><p><a href=https://doc.rust-lang.org/std/marker/struct.PhantomData.html><code>PhantomData</code></a> is a zero-sized type that lets us to implement the <code>SingleAssociated</code> trait for something that, at runtime, is <em>basically</em> the same as a normal <code>S</code>, just with extra type-level information:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>use</span> std<span class=punctuation>::</span>marker<span class=punctuation>::</span><span class=constructor>PhantomData</span><span class=punctuation>;</span>

<span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>,</span> <span class=type>S</span><span class=punctuation>:</span> <span class=type>Generic</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>>></span> <span class=type>SingleAssociated</span> <span class=keyword>for</span> <span class=punctuation>(</span><span class=type>S</span><span class=punctuation>,</span> <span class=type>PhantomData</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>>)</span> <span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>B</span> = <span class=type>B</span><span class=punctuation>;</span>
<span class=punctuation>}</span>

<span class=keyword>fn</span> <span class=function>mk_existential</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>,</span> <span class=type>S</span><span class=punctuation>:</span> <span class=type>Generic</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>>>(</span><span class=variable>s</span><span class=punctuation>:</span> <span class=type>S</span><span class=punctuation>)</span> -> <span class=keyword>impl</span> <span class=type>SingleAssociated</span> <span class=punctuation>{</span>
    <span class=punctuation>(</span>s<span class=punctuation>,</span> <span class=constructor>PhantomData</span><span class=punctuation>)</span>
<span class=punctuation>}</span></code></pre><p>Great, mission accomplished, right? Except wait... how do we actually use it?? (<a href="https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=522b821400e55e934d0e64545c6ed571">playground link</a>)</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>fn</span> <span class=function>use_existential</span><span class=punctuation>(</span><span class=variable>_s</span><span class=punctuation>:</span> <span class=operator>&</span><span class=keyword>impl</span> <span class=type>SingleAssociated</span><span class=punctuation>)</span> -> <span class=operator>&'</span><span class=label>static</span> <span class=type>str</span> <span class=punctuation>{</span>
    <span class=macro>todo!</span><span class=punctuation>()</span>
<span class=punctuation>}</span></code></pre><p>The whole point of our existential type is to carry a proof about how we can do stuff with the <code>Generic</code> trait. But on its own, the <code>SingleAssociated</code> trait doesn't let us get at anything that implements <code>Generic&lt;B></code>. <em>We</em> know that it is only implemented for our special tuple type, but Rust doesn't, so we have to help it along by adding a second associated type + a function to get at it:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>trait</span> <span class=type>DoubleAssociated</span> <span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>S</span><span class=punctuation>:</span> <span class=type>Generic</span><span class=punctuation>&lt;</span><span class=type>Self</span><span class=punctuation>::</span><span class=type>B</span><span class=punctuation>>;</span>
    <span class=keyword>type</span> <span class=type>B</span><span class=punctuation>;</span>
    <span class=keyword>fn</span> <span class=function>as_ref</span><span class=punctuation>(</span><span class=operator>&</span><span class=variable>self</span><span class=punctuation>)</span> -> <span class=operator>&</span><span class=type>Self</span><span class=punctuation>::</span><span class=type>S</span><span class=punctuation>;</span>
<span class=punctuation>}</span>

<span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>S</span><span class=punctuation>,</span> <span class=type>B</span><span class=punctuation>></span> <span class=type>DoubleAssociated</span> <span class=keyword>for</span> <span class=punctuation>(</span><span class=type>S</span><span class=punctuation>,</span> <span class=type>PhantomData</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>>)</span>
<span class=keyword>where</span>
    <span class=type>S</span><span class=punctuation>:</span> <span class=type>Generic</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>>,</span>
<span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>S</span> = <span class=type>S</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>B</span> = <span class=type>B</span><span class=punctuation>;</span>
    <span class=keyword>fn</span> <span class=function>as_ref</span><span class=punctuation>(</span><span class=operator>&</span><span class=variable>self</span><span class=punctuation>)</span> -> <span class=operator>&</span><span class=type>Self</span><span class=punctuation>::</span><span class=type>S</span> <span class=punctuation>{</span> <span class=operator>&</span><span class=variable>self</span><span class=punctuation>.</span><span class=constant>0</span> <span class=punctuation>}</span>
<span class=punctuation>}</span></code></pre><p>This lets us use functions from <code>Generic&lt;B></code><sup class=footnote-ref><a data-footnote-ref href=#fn-self-types id=fnref-self-types>5</a></sup> (<a href="https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=a423fefa5b7e2d3963de3d99f6998912">playground link</a>):</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>fn</span> <span class=function>mk_existential</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>,</span> <span class=type>S</span><span class=punctuation>:</span> <span class=type>Generic</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>>>(</span><span class=variable>s</span><span class=punctuation>:</span> <span class=type>S</span><span class=punctuation>)</span> -> <span class=keyword>impl</span> <span class=type>DoubleAssociated</span> <span class=punctuation>{</span>
    <span class=punctuation>(</span>s<span class=punctuation>,</span> <span class=constructor>PhantomData</span><span class=punctuation>)</span>
<span class=punctuation>}</span>

<span class=keyword>fn</span> <span class=function>use_existential</span><span class=punctuation>(</span><span class=variable>s</span><span class=punctuation>:</span> <span class=operator>&</span><span class=keyword>impl</span> <span class=type>DoubleAssociated</span><span class=punctuation>)</span> -> <span class=operator>&'</span><span class=label>static</span> <span class=type>str</span> <span class=punctuation>{</span>
    s<span class=punctuation>.</span><span class=function>as_ref</span><span class=punctuation>().</span><span class=function>name</span><span class=punctuation>()</span>
<span class=punctuation>}</span></code></pre><p>Great! If we were satisfied with using <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi mathvariant=normal>∃</mi></mrow><annotation encoding=application/x-tex>\exists</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=height:.6944em></span><span class=mord>∃</span></span></span></span> <span></span> types as returned from functions, we could stop here.</p><p>...However, to satisfy Alice's request, we'd also like to be able to "erase" that type by putting it in a <code>Box&lt;dyn Trait></code>, giving it a consistent representation; otherwise, two functions that both return <code>impl DoubleAssociated</code> could actually be returning different types, and e.g. couldn't be put inside the same <code>Vec</code>. We should able to do <code>Box&lt;dyn DoubleAssociated></code>, so let's try that, shall we? (<a href="https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=7c335ebb94ff1628ef63e1ec483f6602">playground link</a>):</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>fn</span> <span class=function>mk_box</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>:</span> <span class=operator>'</span><span class=label>static</span><span class=punctuation>,</span> <span class=type>S</span><span class=punctuation>:</span> <span class=type>Generic</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>></span> + <span class=operator>'</span><span class=label>static</span><span class=punctuation>>(</span><span class=variable>s</span><span class=punctuation>:</span> <span class=type>S</span><span class=punctuation>)</span> -> <span class=type>Box</span><span class=punctuation>&lt;</span><span class=keyword>dyn</span> <span class=type>DoubleAssociated</span><span class=punctuation>&lt;</span><span class=type>S</span>=<span class=type>S</span><span class=punctuation>,</span> <span class=type>B</span>=<span class=type>B</span><span class=punctuation>>></span> <span class=punctuation>{</span>
    <span class=type>Box</span><span class=punctuation>::</span><span class=function>new</span><span class=punctuation>((</span>s<span class=punctuation>,</span> <span class=constructor>PhantomData</span><span class=punctuation>))</span>
<span class=punctuation>}</span>

<span class=keyword>fn</span> <span class=function>use_box</span><span class=punctuation>(</span><span class=variable>s</span><span class=punctuation>:</span> <span class=operator>&</span><span class=type>Box</span><span class=punctuation>&lt;</span><span class=keyword>dyn</span> <span class=type>DoubleAssociated</span><span class=punctuation>>)</span> -> <span class=operator>&'</span><span class=label>static</span> <span class=type>str</span> <span class=punctuation>{</span>
    <span class=type>DoubleAssociated</span><span class=punctuation>::</span><span class=function>as_ref</span><span class=punctuation>(</span><span class=type>Box</span><span class=punctuation>::</span><span class=function>as_ref</span><span class=punctuation>(</span>s<span class=punctuation>)).</span><span class=function>name</span><span class=punctuation>()</span>
<span class=punctuation>}</span></code></pre><p>Whuh oh</p><pre class=syntax-highlighting><code>error[E0191]: the value of the associated types `S` and `B` in `DoubleAssociated` must be specified
  --> src/main.rs:44:24
   |
14 |     type S: Generic&lt;Self::B>;
   |     ------------------------ `S` defined here
15 |     type B;
   |     ------ `B` defined here
...
44 | fn use_box(s: &Box&lt;dyn DoubleAssociated>) -> &'static str {
   |                        ^^^^^^^^^^^^^^^^
   |
help: specify the associated types
   |
44 | fn use_box(s: &Box&lt;dyn DoubleAssociated&lt;S = /* Type */, B = /* Type */>>) -> &'static str {
   |                                        ++++++++++++++++++++++++++++++++
</code></pre><p>It wants us to use <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi mathvariant=normal>∀</mi></mrow><annotation encoding=application/x-tex>\forall</annotation></semantics></math></span><span aria-hidden=true class=katex-html><span class=base><span class=strut style=height:.6944em></span><span class=mord>∀</span></span></span></span> <span></span> again... to be honest, I totally did not expect this. I thought the fact that Rust enforces "at most one concrete trait implementation per concrete type" meant associated types could be stored in the trait object vtable somehow, but my intuition was way wrong on that.</p><p>Thinking on it further, the fact that we return things type <code>Associated::S</code> means that, at runtime, different <code>DoubleAssociated</code> impls will necessarily have different vtables, because return types will have different layouts, so the trait objects with different associated types MUST be different. Rust 1 – Me 0</p><p>So, what can we take away from this? That it's impossible to fully automatically erase multiple existential bounds in Rust in the same type, because it only supports one at a time with <code>dyn Trait</code>? Unfortunately yes, that is my takeaway here :(</p><p>Still, if we're willing to put in a little bit more legwork, it is possible to manually collapse multiple existential bounds into one.</p><h2><a aria-hidden=true class=anchor href=#attempt-3-manual-erasing id=heading-attempt-3-manual-erasing></a>Attempt 3: Manual Erasing</h2><p>Forget trying to use functions from <code>Generic&lt;B></code> directly, let's instead create a trait that manually, for each function we care about, passes thru to the function from the trait:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>trait</span> <span class=type>Erased</span> <span class=punctuation>{</span>
    <span class=keyword>fn</span> <span class=function>name</span><span class=punctuation>(</span><span class=operator>&</span><span class=variable>self</span><span class=punctuation>)</span> -> <span class=operator>&'</span><span class=label>static</span> <span class=type>str</span><span class=punctuation>;</span>
<span class=punctuation>}</span>

<span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>,</span> <span class=type>S</span><span class=punctuation>:</span> <span class=type>Generic</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>>></span> <span class=type>Erased</span> <span class=keyword>for</span> <span class=punctuation>(</span><span class=type>S</span><span class=punctuation>,</span> <span class=type>PhantomData</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>>)</span> <span class=punctuation>{</span>
    <span class=keyword>fn</span> <span class=function>name</span><span class=punctuation>(</span><span class=operator>&</span><span class=variable>self</span><span class=punctuation>)</span> -> <span class=operator>&'</span><span class=label>static</span> <span class=type>str</span> <span class=punctuation>{</span>
        <span class=variable>self</span><span class=punctuation>.</span><span class=constant>0</span><span class=punctuation>.</span><span class=function>name</span><span class=punctuation>()</span>
    <span class=punctuation>}</span>
<span class=punctuation>}</span></code></pre><p>This works!! We can even box it, hooray (<a href="https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=67deae4b6f9b2145945b74b50418b89a">playground link</a>):</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>fn</span> <span class=function>mk_box</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>:</span> <span class=operator>'</span><span class=label>static</span><span class=punctuation>,</span> <span class=type>S</span><span class=punctuation>:</span> <span class=type>Generic</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>></span> + <span class=operator>'</span><span class=label>static</span><span class=punctuation>>(</span><span class=variable>s</span><span class=punctuation>:</span> <span class=type>S</span><span class=punctuation>)</span> -> <span class=type>Box</span><span class=punctuation>&lt;</span><span class=keyword>dyn</span> <span class=type>Erased</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=type>Box</span><span class=punctuation>::</span><span class=function>new</span><span class=punctuation>((</span>s<span class=punctuation>,</span> <span class=constructor>PhantomData</span><span class=punctuation>))</span>
<span class=punctuation>}</span>

<span class=keyword>fn</span> <span class=function>use_box</span><span class=punctuation>(</span><span class=variable>b</span><span class=punctuation>:</span> <span class=operator>&</span><span class=type>Box</span><span class=punctuation>&lt;</span><span class=keyword>dyn</span> <span class=type>Erased</span><span class=punctuation>>)</span> -> <span class=operator>&'</span><span class=label>static</span> <span class=type>str</span> <span class=punctuation>{</span>
    b<span class=punctuation>.</span><span class=function>name</span><span class=punctuation>()</span>
<span class=punctuation>}</span></code></pre><p>Alice has <a href=https://bsky.app/profile/welltypedwit.ch/post/3mlqu5urfjc2s>one last request for us here</a>:</p><blockquote><p>hmmm but now this one doesn't work if the trait actually uses the <code>B</code> parameter right? (because you can't use it in <code>Erased</code>)</p></blockquote><p>And that is true: <code>Erased</code> <em>intentionally</em> has no way for it to vary what types it cares about, everything needs to be concrete so it can be put inside a <code>dyn</code> without issues. If we want to deal with multiple types, we'll have to use <a href=https://doc.rust-lang.org/std/any/trait.Any.html><code>std::any::Any</code></a> to handle them at runtime. Here's an example (<a href="https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=8552501df3bcf4ae4d80aef60d3073b8">playground link</a>):</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>use</span> std<span class=punctuation>::</span>any<span class=punctuation>::</span><span class=constructor>Any</span><span class=punctuation>;</span>
<span class=keyword>use</span> std<span class=punctuation>::</span>marker<span class=punctuation>::</span><span class=constructor>PhantomData</span><span class=punctuation>;</span>

<span class=keyword>trait</span> <span class=type>Generic</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=keyword>fn</span> <span class=function>input</span><span class=punctuation>(</span><span class=variable>b</span><span class=punctuation>:</span> <span class=operator>&</span><span class=type>B</span><span class=punctuation>);</span>
    <span class=keyword>fn</span> <span class=function>output</span><span class=punctuation>(</span><span class=operator>&</span><span class=variable>self</span><span class=punctuation>)</span> -> <span class=type>B</span><span class=punctuation>;</span>
<span class=punctuation>}</span>
<span class=keyword>impl</span> <span class=type>Generic</span><span class=punctuation>&lt;</span><span class=type>f32</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>i32</span> <span class=punctuation>{</span>
    <span class=keyword>fn</span> <span class=function>input</span><span class=punctuation>(</span><span class=variable>b</span><span class=punctuation>:</span> <span class=operator>&</span><span class=type>f32</span><span class=punctuation>)</span> <span class=punctuation>{</span> <span class=macro>println!</span><span class=punctuation>(</span><span class=string>"f32: {b}"</span><span class=punctuation>)</span> <span class=punctuation>}</span>
    <span class=keyword>fn</span> <span class=function>output</span><span class=punctuation>(</span><span class=operator>&</span><span class=variable>self</span><span class=punctuation>)</span> -> <span class=type>f32</span> <span class=punctuation>{</span> <span class=operator>*</span><span class=variable>self</span> <span class=keyword>as</span> <span class=type>f32</span> <span class=punctuation>}</span>
<span class=punctuation>}</span>
<span class=keyword>impl</span> <span class=type>Generic</span><span class=punctuation>&lt;</span><span class=type>f64</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>i32</span> <span class=punctuation>{</span>
    <span class=keyword>fn</span> <span class=function>input</span><span class=punctuation>(</span><span class=variable>b</span><span class=punctuation>:</span> <span class=operator>&</span><span class=type>f64</span><span class=punctuation>)</span> <span class=punctuation>{</span> <span class=macro>println!</span><span class=punctuation>(</span><span class=string>"f64: {b}"</span><span class=punctuation>)</span> <span class=punctuation>}</span>
    <span class=keyword>fn</span> <span class=function>output</span><span class=punctuation>(</span><span class=operator>&</span><span class=variable>self</span><span class=punctuation>)</span> -> <span class=type>f64</span> <span class=punctuation>{</span> <span class=operator>*</span><span class=variable>self</span> <span class=keyword>as</span> <span class=type>f64</span> <span class=punctuation>}</span>
<span class=punctuation>}</span>

<span class=keyword>trait</span> <span class=type>Erased</span> <span class=punctuation>{</span>
    <span class=keyword>fn</span> <span class=function>input</span><span class=punctuation>(</span><span class=operator>&</span><span class=variable>self</span><span class=punctuation>,</span> <span class=variable>b</span><span class=punctuation>:</span> <span class=operator>&</span><span class=type>Box</span><span class=punctuation>&lt;</span><span class=keyword>dyn</span> <span class=type>Any</span><span class=punctuation>>);</span>
    <span class=keyword>fn</span> <span class=function>output</span><span class=punctuation>(</span><span class=operator>&</span><span class=variable>self</span><span class=punctuation>)</span> -> <span class=type>Box</span><span class=punctuation>&lt;</span><span class=keyword>dyn</span> <span class=type>Any</span><span class=punctuation>>;</span>
<span class=punctuation>}</span>

<span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>:</span> <span class=operator>'</span><span class=label>static</span><span class=punctuation>,</span> <span class=type>S</span><span class=punctuation>:</span> <span class=type>Generic</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>>></span> <span class=type>Erased</span> <span class=keyword>for</span> <span class=punctuation>(</span><span class=type>S</span><span class=punctuation>,</span> <span class=type>PhantomData</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>>)</span> <span class=punctuation>{</span>
    <span class=keyword>fn</span> <span class=function>input</span><span class=punctuation>(</span><span class=operator>&</span><span class=variable>self</span><span class=punctuation>,</span> <span class=variable>b</span><span class=punctuation>:</span> <span class=operator>&</span><span class=type>Box</span><span class=punctuation>&lt;</span><span class=keyword>dyn</span> <span class=type>Any</span><span class=punctuation>>)</span> <span class=punctuation>{</span>
        <span class=type>S</span><span class=punctuation>::</span><span class=function>input</span><span class=punctuation>(</span>b<span class=punctuation>.</span><span class=function>downcast_ref</span><span class=punctuation>().</span><span class=function>unwrap</span><span class=punctuation>())</span>
    <span class=punctuation>}</span>
    <span class=keyword>fn</span> <span class=function>output</span><span class=punctuation>(</span><span class=operator>&</span><span class=variable>self</span><span class=punctuation>)</span> -> <span class=type>Box</span><span class=punctuation>&lt;</span><span class=keyword>dyn</span> <span class=type>Any</span><span class=punctuation>></span> <span class=punctuation>{</span>
        <span class=type>Box</span><span class=punctuation>::</span><span class=function>new</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>.</span><span class=constant>0</span><span class=punctuation>.</span><span class=function>output</span><span class=punctuation>())</span>
    <span class=punctuation>}</span>
<span class=punctuation>}</span></code></pre><p>This compiles & runs successfully! Unfortunately, as you can see, manual erasure has a few drawbacks besides just dyn-compatibility.</p><details><summary>Aside on dyn-compatibility</summary> <p>I've mentioned <code>dyn Trait</code> a few times in this article, but what sorts of traits can actually be put inside a <code>dyn</code>? There are a <a href=https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility>whole bunch of rules</a> known as "dyn-compatibility" that boil down to "only do stuff that you can put inside a vtable". Most limiting is the fact that you <em>have</em> to take <code>self</code> as a parameter for every function, because that is the only way we're able to get at a value's vtable. This is why <code>Erased::input()</code> takes in <code>&self</code> while <code>Generic::input()</code> does not.</p></details><p>This process, as I've outlined above, is <em>very</em> manual: you have to rewrite every function signature to include <code>Box&lt;dyn Any></code> instead of <code>B</code>, and make a decision for each about whether to <code>.unwrap()</code> or propagate the error up somehow. If there are enough functions to do this conversion on, you <em>could</em> write a <code>macro_rules!</code> to define a <code>Generic&lt;B></code> and <code>Erased</code> trait at the same time, but that seems very finnicky and I have been nerd-sniped enough already so I will not attempt that here :P</p><p>Anyways that's it, thanks for reading, hope you learned a little something in the process!</p><section class=footnotes data-footnotes><ol><li id=fn-sized><p>Ignore for the moment that trait objects are <code>!Sized</code>, it is not relevant to our purposes right now :) <a aria-label="Back to reference 1" class=footnote-backref data-footnote-backref data-footnote-backref-idx=1 href=#fnref-sized>↩</a></p></li><li id=fn-dyn><p>And the fact that it's actually represented by a trait object, of course. <a aria-label="Back to reference 2" class=footnote-backref data-footnote-backref data-footnote-backref-idx=2 href=#fnref-dyn>↩</a></p></li><li id=fn-nlab><p>Very sorry for inflicting nlab type theory upon u, it is a cognitohazard of the highest order, nothing in it makes sense until you stare very very hard and then suddenly your brain falls out and everything makes sense and you can start speaking the same gibberish. <a aria-label="Back to reference 3" class=footnote-backref data-footnote-backref data-footnote-backref-idx=3 href=#fnref-nlab>↩</a></p></li><li id=fn-specialization><p>At least, not until the <a href=https://rust-lang.github.io/rfcs/1210-impl-specialization.html>specialization RFC</a> lands, which it probably never will :') <a aria-label="Back to reference 4" class=footnote-backref data-footnote-backref data-footnote-backref-idx=4 href=#fnref-specialization>↩</a></p></li><li id=fn-self-types><p>If we wanted to use functions from <code>Generic&lt;B></code> that accepted <code>self</code> (no borrow) or <code>&mut self</code>, we'd have to add more functions to <code>DoubleAssociated</code> for those too, but like <code>as_ref()</code>, they're also trivial. <a aria-label="Back to reference 5" class=footnote-backref data-footnote-backref data-footnote-backref-idx=5 href=#fnref-self-types>↩</a></p></li></ol></section>]]></content>
      <author>
        <name>PolyWolf</name>
        <uri>https://wolf.girl.technology/</uri>
      </author>
    </entry>
  
    <entry>
      <title><![CDATA[Deriving `Functor` in Rust]]></title>
      <link rel="alternate" href="https://wolfgirl.dev/blog/2025-04-14-deriving-functor-in-rust/" />
      <id>https://wolfgirl.dev/blog/2025-04-14-deriving-functor-in-rust/</id>
      <published>2025-04-14T15:37:56Z</published>
      <updated>2025-04-14T15:37:56Z</updated>
      <summary><![CDATA[Wow it's been a hot minute! I kept meaning to write about this, but each time I sat down, I came up with a new idea that I just _had_ to...]]></summary>
      <content type="html"><![CDATA[<p>Wow it's been a hot minute! I kept meaning to write about this, but each time I sat down, I came up with a new idea that I just <em>had</em> to try out. At this point, I have tried enough things, and am reasonably confident all my explanations will be correct. So, without further ado,</p><h2><a aria-hidden=true class=anchor href=#problem-statement id=heading-problem-statement></a>Problem Statement</h2><p>Given:</p><ul><li>A finite set of <code>struct</code>s/<code>enum</code>s (henceforth referred to as "items"), possibly generic</li></ul><p>Create:</p><ul><li>A way to, for each item in the set, recursively transform any other item, including inner generic parameters, if possible.</li></ul><p>Let's take a look at a simple example:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>struct</span> <span class=type>ListNode</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=property>val</span><span class=punctuation>:</span> <span class=type>Val</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>,</span>
    <span class=property>next</span><span class=punctuation>:</span> <span class=type>Option</span><span class=punctuation>&lt;</span><span class=type>Box</span><span class=punctuation>&lt;</span><span class=type>ListNode</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>>></span>
<span class=punctuation>}</span>
<span class=keyword>struct</span> <span class=type>Val</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>(</span><span class=type>T</span><span class=punctuation>);</span></code></pre><p>Then, given a <code>ListNode&lt;T></code>, we'd like to be able to turn it into a <code>ListNode&lt;U></code>, just by specifying how to turn <code>Val&lt;T></code> into <code>Val&lt;U></code>.</p><h2><a aria-hidden=true class=anchor href=#approach-1-write-a-traversal-manually id=heading-approach-1-write-a-traversal-manually></a>Approach 1: Write a traversal manually</h2><pre class=syntax-highlighting><code class=language-rust><span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span> <span class=type>ListNode</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=keyword>fn</span> <span class=function>transform</span><span class=punctuation>&lt;</span><span class=type>U</span><span class=punctuation>>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>Val</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>)</span> -> <span class=type>Val</span><span class=punctuation>&lt;</span><span class=type>U</span><span class=punctuation>>)</span> -> <span class=type>ListNode</span><span class=punctuation>&lt;</span><span class=type>U</span><span class=punctuation>></span> <span class=punctuation>{</span>
        <span class=type>ListNode</span> <span class=punctuation>{</span>
            <span class=property>val</span><span class=punctuation>:</span> <span class=function>f</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>.</span><span class=property>val</span><span class=punctuation>),</span>
            <span class=property>next</span><span class=punctuation>:</span> <span class=variable>self</span><span class=punctuation>.</span><span class=property>next</span><span class=punctuation>.</span><span class=function>map</span><span class=punctuation>(</span>|n| <span class=type>Box</span><span class=punctuation>::</span><span class=function>new</span><span class=punctuation>((</span><span class=operator>*</span>n<span class=punctuation>).</span><span class=function>transform</span><span class=punctuation>(</span>f<span class=punctuation>))),</span>
        <span class=punctuation>}</span>
    <span class=punctuation>}</span>
<span class=punctuation>}</span></code></pre><p>Great! Nice & simple. However, manually mapping through the <code>Option</code> and <code>Box</code> is a bit tedious, especially if we want to write other, similar transforms. Let's see if we can solve that.</p><h2><a aria-hidden=true class=anchor href=#approach-2-functor-time id=heading-approach-2-functor-time></a>Approach 2: <code>Functor</code> time!!</h2><p>In my <a href=https://wolfgirl.dev/blog/2024-11-24-a-novel-idea-about-functor-in-rust/>last post on this subject</a>, I covered this crazy wacky trait I called <code>Functor</code>, invented specifically to do these sorts of transforms. Here's the trait definition<sup class=footnote-ref><a data-footnote-ref href=#fn-functor id=fnref-functor>1</a></sup>:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>trait</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>Output</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>Input</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>Mapped</span><span class=punctuation>;</span>
    <span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>Self</span><span class=punctuation>::</span><span class=type>Input</span><span class=punctuation>)</span> -> <span class=type>Output</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Mapped</span><span class=punctuation>;</span>
<span class=punctuation>}</span></code></pre><p>And here's how it's used (<a href="https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=986293cbdec0b24e8f2f7a3f3406067e">playground link</a>), with doc comments explaining what each part means:</p><pre class=syntax-highlighting><code class=language-rust><span class=comment>/// There are 4 types involved in any Functor implementation: {Input, Output} x {Inner, Container}.
/// What a Functor implementation means is, given a function InputInner -> OutputInner, you can write a function InputContainer -> OutputContainer.
/// Writing the full name of each type is a bit hard, so we have shorthand:
/// + InputInner = Input (Val&lt;T>)
/// + OutputInner = Output (Val&lt;U>)
/// + InputContainer = Self (ListNode&lt;T>)
/// + OutputContainer = Mapped (ListNode&lt;U>)
</span><span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>,</span> <span class=type>U</span><span class=punctuation>></span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>Val</span><span class=punctuation>&lt;</span><span class=type>U</span><span class=punctuation>>></span> <span class=keyword>for</span> <span class=type>ListNode</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>Input</span> = <span class=type>Val</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>;</span>
    <span class=keyword>type</span> <span class=type>Mapped</span> = <span class=type>ListNode</span><span class=punctuation>&lt;</span><span class=type>U</span><span class=punctuation>>;</span>
    <span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=keyword>mut</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>Val</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>)</span> -> <span class=type>Val</span><span class=punctuation>&lt;</span><span class=type>U</span><span class=punctuation>>)</span> -> <span class=type>ListNode</span><span class=punctuation>&lt;</span><span class=type>U</span><span class=punctuation>></span> <span class=punctuation>{</span>
        <span class=type>ListNode</span> <span class=punctuation>{</span>
            <span class=property>val</span><span class=punctuation>:</span> <span class=function>f</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>.</span><span class=property>val</span><span class=punctuation>),</span>
            <span class=property>next</span><span class=punctuation>:</span> <span class=variable>self</span><span class=punctuation>.</span><span class=property>next</span><span class=punctuation>.</span><span class=function>fmap</span><span class=punctuation>(</span><span class=operator>&</span><span class=keyword>mut</span> f<span class=punctuation>),</span>
        <span class=punctuation>}</span>
    <span class=punctuation>}</span>
<span class=punctuation>}</span>
<span class=comment>/// Given all types T that can be mapped over, allow Option&lt;T> to be mapped over in the same way, automatically un/re-wrapping the Some case as required
</span><span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>,</span> <span class=type>Output</span><span class=punctuation>></span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>Output</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>Option</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span>
<span class=keyword>where</span>
    <span class=type>T</span><span class=punctuation>:</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>Output</span><span class=punctuation>></span>
<span class=punctuation>{</span>
	<span class=comment>/// Input is the type given as input to the internal transform. In this case, because we are transparent, we use T's input
</span>    <span class=keyword>type</span> <span class=type>Input</span> = <span class=type>T</span><span class=punctuation>::</span><span class=type>Input</span><span class=punctuation>;</span>
    <span class=comment>/// Mapped is the external type after the transform has been done. Because we are wrapping T as input, we also wrap T's mapped type as output.
</span>    <span class=keyword>type</span> <span class=type>Mapped</span> = <span class=type>Option</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>::</span><span class=type>Mapped</span><span class=punctuation>>;</span>
    <span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span>
        <span class=variable>self</span><span class=punctuation>,</span> <span class=comment>//: Option&lt;T></span>
        <span class=variable>f</span><span class=punctuation>:</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>T</span><span class=punctuation>::</span><span class=type>Input</span><span class=punctuation>)</span> -> <span class=type>Output</span><span class=punctuation>,</span>
    <span class=punctuation>)</span> -> <span class=type>Option</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>::</span><span class=type>Mapped</span><span class=punctuation>></span> <span class=punctuation>{</span>
        <span class=type>Option</span><span class=punctuation>::</span><span class=function>map</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> |x| x<span class=punctuation>.</span><span class=function>fmap</span><span class=punctuation>(</span>f<span class=punctuation>))</span>
    <span class=punctuation>}</span>
<span class=punctuation>}</span>

<span class=comment>/// The implementation for Box&lt;T> is very similar to that for Option&lt;T>, included for completeness.
</span><span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>,</span> <span class=type>Output</span><span class=punctuation>></span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>Output</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>Box</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span>
<span class=keyword>where</span>
    <span class=type>T</span><span class=punctuation>:</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>Output</span><span class=punctuation>>,</span>
<span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>Input</span> = <span class=type>T</span><span class=punctuation>::</span><span class=type>Input</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>Mapped</span> = <span class=type>Box</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>::</span><span class=type>Mapped</span><span class=punctuation>>;</span>
    <span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span>
        <span class=variable>self</span><span class=punctuation>,</span>
        <span class=variable>f</span><span class=punctuation>:</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>Self</span><span class=punctuation>::</span><span class=type>Input</span><span class=punctuation>)</span> -> <span class=type>Output</span><span class=punctuation>,</span>
    <span class=punctuation>)</span> -> <span class=type>Box</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>::</span><span class=type>Mapped</span><span class=punctuation>></span> <span class=punctuation>{</span>
        <span class=type>Box</span><span class=punctuation>::</span><span class=function>new</span><span class=punctuation>((</span><span class=operator>*</span><span class=variable>self</span><span class=punctuation>).</span><span class=function>fmap</span><span class=punctuation>(</span>f<span class=punctuation>))</span>
    <span class=punctuation>}</span>
<span class=punctuation>}</span></code></pre><p>Neat! This way, if we're writing a lot of transforms, they'll be composable with existing container types like <code>Option</code> & <code>Box</code>, as well as any other container types we might try.</p><p>However, I'm still a bit sad about having to write these transforms by hand. Especially given the fact that, while <code>Functor</code> is nice to implement for wrapper types like <code>Option</code> and <code>Box</code>, it isn't as nice for "AST-like" types...</p><h3><a aria-hidden=true class=anchor href=#problems-with-ast-like-types id=heading-problems-with-ast-like-types></a>Problems With AST-like Types</h3><p>Consider what would happen if we had the following set of items:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>struct</span> <span class=type>A</span> <span class=punctuation>{</span>
    <span class=property>b</span><span class=punctuation>:</span> <span class=type>B</span><span class=punctuation>,</span>
<span class=punctuation>}</span>
<span class=keyword>struct</span> <span class=type>B</span> <span class=punctuation>{</span>
    <span class=property>c</span><span class=punctuation>:</span> <span class=type>C</span><span class=punctuation>,</span>
<span class=punctuation>}</span>
<span class=keyword>struct</span> <span class=type>C</span><span class=punctuation>(</span><span class=type>i32</span><span class=punctuation>);</span></code></pre><p>Naturally, we'd want to call <code>a.fmap(|c: C| ...)</code>. But! There is no item <code>C</code> in any direct fields of <code>A</code>, only indirectly thru <code>b: B</code>. If we only cared about this one <code>fmap</code> call, we could write:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>impl</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>C</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>A</span> <span class=punctuation>{</span>
	<span class=keyword>type</span> <span class=type>Input</span> = <span class=type>C</span><span class=punctuation>;</span>
	<span class=keyword>type</span> <span class=type>Mapped</span> = <span class=type>A</span><span class=punctuation>;</span>
	<span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>C</span><span class=punctuation>)</span> -> <span class=type>C</span><span class=punctuation>)</span> -> <span class=type>A</span> <span class=punctuation>{</span>
		<span class=type>A</span> <span class=punctuation>{</span> <span class=property>b</span><span class=punctuation>:</span> <span class=variable>self</span><span class=punctuation>.</span><span class=function>fmap</span><span class=punctuation>(</span>f<span class=punctuation>)</span> <span class=punctuation>}</span>
	<span class=punctuation>}</span>
<span class=punctuation>}</span>
<span class=keyword>impl</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>C</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>B</span> <span class=punctuation>{</span>
	<span class=keyword>type</span> <span class=type>Input</span> = <span class=type>C</span><span class=punctuation>;</span>
	<span class=keyword>type</span> <span class=type>Mapped</span> = <span class=type>B</span><span class=punctuation>;</span>
	<span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=keyword>mut</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>C</span><span class=punctuation>)</span> -> <span class=type>C</span><span class=punctuation>)</span> -> <span class=type>B</span> <span class=punctuation>{</span>
		<span class=type>B</span> <span class=punctuation>{</span> <span class=property>c</span><span class=punctuation>:</span> <span class=function>f</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>.</span><span class=property>c</span><span class=punctuation>)</span> <span class=punctuation>}</span>
	<span class=punctuation>}</span>
<span class=punctuation>}</span></code></pre><p>Which is fine. But, you might eventually add an item <code>D</code> that you also want to map over from <code>A</code>. Then you'd have to write:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>impl</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>D</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>A</span> <span class=punctuation>{</span> ... <span class=punctuation>}</span>
<span class=keyword>impl</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>D</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>B</span> <span class=punctuation>{</span> ... <span class=punctuation>}</span>
<span class=keyword>impl</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>D</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>C</span> <span class=punctuation>{</span> ... <span class=punctuation>}</span></code></pre><p>Apparently, the number of <code>Functor</code> implementations you have to write scales linearly with both the depth of the tree <em>and</em> the number of things you want to map over, so quadratically in total, which is quite bad.</p><p>Ideally, you'd like to just scale linearly with the size of the tree. Here's one approach for this:</p><pre class=syntax-highlighting><code class=language-rust><span class=comment>/// These "base impls" can all be easily generated with a macro
</span><span class=keyword>impl</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>A</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>A</span> <span class=punctuation>{</span>
	<span class=keyword>type</span> <span class=type>Input</span> = <span class=type>A</span><span class=punctuation>;</span>
	<span class=keyword>type</span> <span class=type>Mapped</span> = <span class=type>A</span><span class=punctuation>;</span>
	<span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=keyword>mut</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>A</span><span class=punctuation>)</span> -> <span class=type>A</span><span class=punctuation>)</span> -> <span class=type>A</span> <span class=punctuation>{</span>
		<span class=function>f</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>)</span>
	<span class=punctuation>}</span>
<span class=punctuation>}</span>
<span class=keyword>impl</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>B</span> <span class=punctuation>{</span> ... <span class=punctuation>}</span>
<span class=keyword>impl</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>C</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>C</span> <span class=punctuation>{</span> ... <span class=punctuation>}</span>

<span class=comment>/// These "recursive impl"s are written manually, but that's OK, we only need one per item!
</span><span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>A</span> <span class=keyword>where</span> <span class=type>B</span><span class=punctuation>:</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>,</span> <span class=type>Mapped</span>=<span class=type>B</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>Input</span> = &lt;<span class=type>B</span> <span class=keyword>as</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span>><span class=punctuation>::</span><span class=type>Input</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>Mapped</span> = <span class=type>A</span><span class=punctuation>;</span>
    <span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>Self</span><span class=punctuation>::</span><span class=type>Input</span><span class=punctuation>)</span> -> <span class=type>T</span><span class=punctuation>)</span> -> <span class=type>A</span> <span class=punctuation>{</span>
        <span class=type>Self</span> <span class=punctuation>{</span>
            <span class=property>b</span><span class=punctuation>:</span> <span class=variable>self</span><span class=punctuation>.</span><span class=property>b</span><span class=punctuation>.</span><span class=function>fmap</span><span class=punctuation>(</span>f<span class=punctuation>),</span>
        <span class=punctuation>}</span>
    <span class=punctuation>}</span>
<span class=punctuation>}</span>
<span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>B</span> <span class=keyword>where</span> <span class=type>C</span><span class=punctuation>:</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>,</span> <span class=type>Mapped</span>=<span class=type>C</span><span class=punctuation>></span> <span class=punctuation>{</span> ... <span class=punctuation>}</span></code></pre><p>Seasoned Rust veterans may be able to spot the problem: <code>impl&lt;A> Functor&lt;A> for A</code> and <code>impl&lt;T> Functor&lt;T> for A</code> conflict! Never mind that <code>where</code> clause, the Rust compiler is sometimes very particular about preventing future conflicts too<sup class=footnote-ref><a data-footnote-ref href=#fn-conflicts id=fnref-conflicts>2</a></sup>, just in case <code>B: Functor&lt;A, Mapped=B></code> somehow.</p><p>If only there were a way to automatically write all those quadratic dumb-but-non-conflicting implementations... then it wouldn't matter that there's so many...<sup class=footnote-ref><a data-footnote-ref href=#fn-compiletimes id=fnref-compiletimes>3</a></sup></p><h2><a aria-hidden=true class=anchor href=#approach-3-use-a-proc_macro id=heading-approach-3-use-a-proc_macro></a>Approach 3: Use A <code>proc_macro</code></h2><p>Turns out, there's enough information in the item definitions alone to generate the traversals automatically. All we really need is:</p><ol><li>A list of all items considered a part of the AST</li><li>The type of each field in each item</li><li>A way of determining valid transforms to generate for each item</li><li>A way of generating each valid transform</li></ol><p>We need (1) because of the problem presented before: it's not possible to generate <code>Functor</code> impls for <em>arbitrary</em> types, so we must explicitly choose the ones we want to map over.</p><p>(2) and (4) are also fairly straightforward given Rust's de-facto introspection libraries, <a href=https://docs.rs/syn/latest/syn/>syn</a> & <a href=https://docs.rs/quote/latest/quote/>quote</a><sup class=footnote-ref><a data-footnote-ref href=#fn-dtolnay id=fnref-dtolnay>4</a></sup>. There are some subtleties when it comes to actually extracting the information we want (the Rust type grammar is so much...), but overall "get the type of a field" and "get the shape of an item" is not too hard. Same with generating the transform; once you have the information of "what type am I transforming" and "what fields should I transform for that type", emitting the code is fairly straightforward. You can <a href=https://github.com/p0lyw0lf/pwcc/blob/715eca08fc3e6acc5ba65030d096bf383c78c054/functional_macros/src/traits/functor.rs>take a look at my code</a> if you're curious, but I promise it's really boring.</p><p>(3), on the other hand, is much more difficult + interesting, and is a big reason I kept delaying this piece. Turns out re-creating the Rust trait implementation coherence semantics is pretty involved!!</p><h3><a aria-hidden=true class=anchor href=#what-even-is-a-valid-transformation id=heading-what-even-is-a-valid-transformation></a>What Even Is A Valid Transformation?</h3><p>Let's start out with the most basic criteria. Any valid implementation of <code>Functor&lt;B> for A</code> comes about because:</p><ol><li><code>A</code> has a field with type <code>B</code>, where "with type" looks inside any wrapper types like <code>Option</code>/<code>Box</code>, OR</li><li><code>A</code> has a field with type <code>C</code>, where <code>Functor&lt;B> for C</code> is a valid transform.</li></ol><p>We get the information for (1) when reading in the items initially. To get (2), we must turn to the horror that is graph algorithms in Rust. Get ready for some math!</p><h3><a aria-hidden=true class=anchor href=#1-the-lattice-step id=heading-1-the-lattice-step></a>1: The Lattice Step</h3><p>I call this a "lattice" because it encodes a "higher-than" relationship that's useful for what I'm doing. It's not a <em>true</em> lattice by many definitions of the word, but im the programmer so i get to name things badly ok</p><p>What this step looks like is, taking all edges matching <code>Functor&lt;C> for A</code> (denoted as <code>A -> C</code>) + <code>C -> B</code>, and collecting them into a new edge <code>A -> B</code>. This is so we can get all the possible implementations that would be covered by case (2) above. There are a few subtleties when it comes to unifying generic contexts, but like the mathematician I'm cosplaying as, I'll leave those for the appendix<sup class=footnote-ref><a data-footnote-ref href=#fn-unifying id=fnref-unifying>5</a></sup>.</p><p>However! We're not done yet. Creating all these new edges gives us all the <em>possible</em> implementations, but not all the <em>valid</em> ones. To do this, we need to filter out all invalid edges, so let's look at some ways an implementation (as defined by an edge) can be invalid.</p><h3><a aria-hidden=true class=anchor href=#2-filter-out-conflicting-generic-instantiations id=heading-2-filter-out-conflicting-generic-instantiations></a>2: Filter Out Conflicting Generic Instantiations</h3><p>Consider the following:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>struct</span> <span class=type>A1</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>,</span> <span class=type>U</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=property>bt</span><span class=punctuation>:</span> <span class=type>B</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>,</span>
    <span class=property>bu</span><span class=punctuation>:</span> <span class=type>B</span><span class=punctuation>&lt;</span><span class=type>U</span><span class=punctuation>>,</span>
<span class=punctuation>}</span>
<span class=keyword>struct</span> <span class=type>B</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>(</span><span class=type>T</span><span class=punctuation>);</span></code></pre><p>If we try list out all the possible implementations, we get:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>,</span> <span class=type>U</span><span class=punctuation>></span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>></span> <span class=keyword>for</span> <span class=type>A1</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>,</span> <span class=type>U</span><span class=punctuation>></span> <span class=punctuation>{</span> ... <span class=punctuation>}</span>
<span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>,</span> <span class=type>U</span><span class=punctuation>></span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>&lt;</span><span class=type>U</span><span class=punctuation>>></span> <span class=keyword>for</span> <span class=type>A1</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>,</span> <span class=type>U</span><span class=punctuation>></span> <span class=punctuation>{</span> ... <span class=punctuation>}</span></code></pre><p>Note we have two implementations, because <code>B&lt;T></code> and <code>B&lt;U></code> are different types. But clearly, these implementations conflict! This is because <code>Functor&lt;B&lt;T>></code> and <code>Functor&lt;B&lt;U>></code> are <em>not</em> different traits. Without any preference for which implementation to keep, we discard both.</p><p>There is a similar conflict if we instead have:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>struct</span> <span class=type>A2</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=property>bt</span><span class=punctuation>:</span> <span class=type>B</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>,</span>
    <span class=property>bi</span><span class=punctuation>:</span> <span class=type>B</span><span class=punctuation>&lt;</span><span class=type>i32</span><span class=punctuation>>,</span>
<span class=punctuation>}</span></code></pre><p>Because <code>Functor&lt;B&lt;T>></code> <em>could</em> be the same trait as <code>Functor&lt;B&lt;i32>></code>, and without a "type inequality" bound (not possible to express in Rust iirc), we can't generate these. In this case, we just prefer the <code>Functor&lt;B&lt;i32>></code> implementation that only transforms <code>bi</code>, because without generic parameters, implementations can never conflict.</p><p>The algorithm for deciding if a pair of instantiations conflict is as follows:</p><ol><li>For each pair of arguments in order: <ol><li>If both are generic, they must be the same generic. Otherwise, there's a conflict.</li><li>If only one is generic, there's a conflict</li><li>If neither are generic, there's no conflict.</li></ol></li></ol><h3><a aria-hidden=true class=anchor href=#3-restrict-which-generic-parameters-are-transformed id=heading-3-restrict-which-generic-parameters-are-transformed></a>3: Restrict Which Generic Parameters Are Transformed</h3><p>This is a bit of a case of me being burned by making my <code>Functor</code> too complex, but screw it I <em>did</em> want that complexity. The situation is, if you have something like:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>struct</span> <span class=type>A</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=property>b</span><span class=punctuation>:</span> <span class=type>B</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>,</span>
    <span class=property>c</span><span class=punctuation>:</span> <span class=type>C</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>,</span>
<span class=punctuation>}</span>
<span class=keyword>struct</span> <span class=type>B</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=property>c</span><span class=punctuation>:</span> <span class=type>C</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>,</span>
<span class=punctuation>}</span>
<span class=keyword>struct</span> <span class=type>C</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>(</span><span class=type>T</span><span class=punctuation>);</span></code></pre><p>And we try to write the "full" implementation for <code>A -> B</code> that transforms its generic parameter:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>,</span> <span class=type>U</span><span class=punctuation>></span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>&lt;</span><span class=type>U</span><span class=punctuation>>></span> <span class=keyword>for</span> <span class=type>A</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>Input</span> = <span class=type>B</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>;</span>
    <span class=keyword>type</span> <span class=type>Mapped</span> = <span class=type>A</span><span class=punctuation>&lt;</span><span class=type>U</span><span class=punctuation>>;</span>
    <span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>B</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>>)</span> -> <span class=type>B</span><span class=punctuation>&lt;</span><span class=type>U</span><span class=punctuation>>)</span> -> <span class=type>A</span><span class=punctuation>&lt;</span><span class=type>U</span><span class=punctuation>></span> <span class=punctuation>{</span>
        <span class=type>A</span> <span class=punctuation>{</span>
            <span class=property>b</span><span class=punctuation>:</span> <span class=function>f</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>.</span><span class=property>b</span><span class=punctuation>),</span>
            <span class=property>c</span><span class=punctuation>:</span> <span class=variable>self</span><span class=punctuation>.</span><span class=property>c</span><span class=punctuation>,</span> <span class=comment>// ERROR: expected C&lt;U>, got C&lt;T></span>
        <span class=punctuation>}</span>
    <span class=punctuation>}</span>
<span class=punctuation>}</span></code></pre><p>Oh no type error!! The reason it happens is because we can't transform the generic type of <code>c</code> even though we need to. There are a couple parts to this:</p><ol><li>How do we know we need to transform <code>c</code>? <ul><li>Because the generic context used by field <code>b</code>, and therefore the generic context transformed by <code>b</code>, is also used by field <code>c</code>.</li></ul></li><li>How do we know we cannot transform <code>c</code>? <ul><li>We look at all the outgoing edges from item <code>C</code>, and if there are none that would satisfy the transformation <code>C -> B</code> (for the same instantiated item <code>B</code> as is being considered for field <code>b</code>), then field <code>c</code> cannot be transformed.</li></ul></li></ol><p>Anyways, this is all just to check if we can transform the generic parameter <code>T</code> to <code>U</code>. If we can't, no worries, we can just generate a <code>Functor</code> implementation that doesn't transform <code>T</code> :) Like any sane person would do :)</p><h3><a aria-hidden=true class=anchor href=#what-is-the-correct-order-for-these-steps id=heading-what-is-the-correct-order-for-these-steps></a>What Is The Correct Order For These Steps?</h3><p>If you've been paying attention (no worries if not, it's a long one, you're almost there!), you may have noticed something: step 3 above look at outgoing edges for all nodes one level "below" the current. However, step 2 explicitly removes some edges from the graph. So! We must be smart about the order in which we look at nodes for step 3, so we don't end up keeping an edge <code>A -> B</code> only to later remove the edge <code>C -> B</code> that made keeping it possible.</p><p>I claim the correct traversal order is "reverse <a href=https://en.wikipedia.org/wiki/Topological_sorting>topological sort</a> order". (Whoopee, more graph algorithms in Rust!) Basically, we should start with nodes that don't point to anything (the "bottom"), then nodes that only point to previously explored nodes, and so on. This ensures we don't end up in that bad scenario.</p><p>However! The topological sort order is only defined for directed graphs with no loops. Our graph <em>can</em> have loops, and indeed, that's a big feature for this macro in general, being able to support trees & linked lists & such, which all have edges that look like <code>A -> A</code> or even <code>A -> B -> C -> A</code>. Are we out of luck?</p><p>Not yet! By first finding the <a href=https://en.wikipedia.org/wiki/Strongly_connected_component>strongly connected components</a> (omg more graph algorithms!!), we can do a topological sort on <em>those</em>, which are guaranteed to not have loops between them, and our traversal order will be good.</p><p>"But what about the order within a strongly connected component?", you may ask. Well, thanks to our lattice step earlier, every loop <code>A -> B -> C -> A</code> in the graph will become a clique, so if <code>A</code>, <code>B</code>, and <code>C</code> are in the same strongly connected component, <code>A -> B</code> & <code>C -> B</code> will always exist. Wow it's almost like I'm actually doing math!!</p><p>So, the correct order of steps is:</p><ol><li>Lattice step</li><li>Filter out conflicting instantiations</li><li>Topological sort</li><li>Restrict generic parameters</li></ol><p>And that's all there is to it! ;)</p><h2><a aria-hidden=true class=anchor href=#conclusion id=heading-conclusion></a>Conclusion</h2><p>At long last, we have reached the end of everything I know about automatically deriving a <code>Functor</code> implementation. These rules have worked out for me in practice, for <a href=https://github.com/p0lyw0lf/pwcc/blob/715eca08fc3e6acc5ba65030d096bf383c78c054/pwcc/src/parser.rs#L24-L164>two very</a> <a href=https://github.com/p0lyw0lf/pwcc/blob/715eca08fc3e6acc5ba65030d096bf383c78c054/pwcc/src/codegen.rs#L19-L111>large ASTs</a>. There are more things I know, like deriving <a href=https://github.com/p0lyw0lf/pwcc/blob/715eca08fc3e6acc5ba65030d096bf383c78c054/functional/src/try_functor.rs#L8-L25>TryFunctor</a> and <a href=https://github.com/p0lyw0lf/pwcc/blob/715eca08fc3e6acc5ba65030d096bf383c78c054/functional/src/foldable.rs#L5-L22>Foldable</a> and <a href=https://github.com/p0lyw0lf/pwcc/blob/715eca08fc3e6acc5ba65030d096bf383c78c054/functional/src/test_visit.rs#L7-L50>Visit</a>, but those all use these same basic principles too. There are also many more things I do not know, like "what if a field contains a generic type but doesn't have one of the designated items?" or "how can I prevent mapping invalid field types, like <code>fn(T) -> U</code>" or "am I using <code>panic!</code> to handle errors too often", but those are all left for later/never. I do not recommend anyone else attempt to use this code btw, it is simply a dangerous toy for my own amusement, and it <em>will</em> blow up in ur face.</p><p>Anyways, thanks for reading, see u next time!! :3</p><section class=footnotes data-footnotes><ol><li id=fn-functor><p>If you look in <a href=https://github.com/p0lyw0lf/pwcc/blob/d40f72bfd97c8b95bda9e12ec9637c1d4d6fe56c/functional/src/functor.rs#L21-L25>my repository</a>, you'll see I actually use a couple modifications: (1) it takes in an <code>&mut impl FnMut</code>, and (2) it takes in an extra <code>RecursiveCall</code> parameter. These are for structures like binary trees, where (1) the same <code>f</code> can't be moved into both calls, and trying to <code>&mut</code> it generates an infinite sequence of <code>&mut</code> checks, and (2) controlling when <code>f</code> would be called on a parent node: before its children, after its children, or not calling into children at all. Anyways! Not important for this piece, all the other relevant details are the same. <a aria-label="Back to reference 1" class=footnote-backref data-footnote-backref data-footnote-backref-idx=1 href=#fnref-functor>↩</a></p></li><li id=fn-conflicts><p>As it turns out, if all of these implementations & items are in the same crate as <code>Functor</code>, with private visibility, this actually works out! (<a href="https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=022e8efdd57f9085dd0729093a64f046">playground link</a>.) The compiler can prove that the implementations never conflict, and these will be the only implementations that exist, so everything is groovy. Unfortunately, I do define my <code>Functor</code> trait in a separate crate from my AST items, for pretty obvious separation-of-concern reasons, so everything I've said still stands. <a aria-label="Back to reference 2" class=footnote-backref data-footnote-backref data-footnote-backref-idx=2 href=#fnref-conflicts>↩</a></p></li><li id=fn-compiletimes><p>Except for compile times, of course :P It did eventually get so bad in my project that I had to make "being <code>Input</code>" opt-in, reducing hot-rebuild times from 30s to 5s. <a aria-label="Back to reference 3" class=footnote-backref data-footnote-backref data-footnote-backref-idx=3 href=#fnref-compiletimes>↩</a></p></li><li id=fn-dtolnay><p>I should really get off these... dtolnay did my boy JeanHeyd real dirty, introwospection in its full form would have been perfect for this project, but alas. If I can stomach runtime reflection instead of compile-time reflection (which might happen as soon as I take a real look at binary sizes), I should consider using <a href=https://docs.rs/facet-reflect/latest/facet_reflect/>facet-reflect</a> instead... <a aria-label="Back to reference 4" class=footnote-backref data-footnote-backref data-footnote-backref-idx=4 href=#fnref-dtolnay>↩</a></p></li><li id=fn-unifying><p>Consider a struct declared as <code>A&lt;T></code> with field type <code>B&lt;T></code>, and a struct declared as <code>B&lt;U></code> with field type <code>C&lt;U></code>. When generating the edge <code>A -> C</code>, we want to turn the <code>C&lt;U></code> declaration into a <code>C&lt;T></code> one. You have to lookup the variables in the generic context to do the mapping appropriately, neat stuff! <a aria-label="Back to reference 5" class=footnote-backref data-footnote-backref data-footnote-backref-idx=5 href=#fnref-unifying>↩</a></p></li></ol></section>]]></content>
      <author>
        <name>PolyWolf</name>
        <uri>https://wolf.girl.technology/</uri>
      </author>
    </entry>
  
    <entry>
      <title><![CDATA[One Neat Trick For Writing Large `macro_rules!`]]></title>
      <link rel="alternate" href="https://wolfgirl.dev/blog/2025-01-17-one-neat-trick-for-writing-large-macro-rules-/" />
      <id>https://wolfgirl.dev/blog/2025-01-17-one-neat-trick-for-writing-large-macro-rules-/</id>
      <published>2025-01-18T01:18:03Z</published>
      <updated>2025-01-18T01:18:03Z</updated>
      <summary><![CDATA[First, let's start with a related "neat trick" that lets you write _composable_ `macro_rules!`. Say you have a macro that generates struc...]]></summary>
      <content type="html"><![CDATA[<p>First, let's start with a related "neat trick" that lets you write <em>composable</em> <code>macro_rules!</code>. Say you have a macro that generates struct definitions<sup class=footnote-ref><a data-footnote-ref href=#fn-1 id=fnref-1>1</a></sup>:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>macro_rules</span>! node <span class=punctuation>{</span>
    <span class=punctuation>(</span>
        $name<span class=punctuation>:</span>ident <span class=punctuation>[</span> $subnode<span class=punctuation>:</span>ident <span class=punctuation>]</span>
    <span class=punctuation>)</span> => <span class=punctuation>{</span>
        <span class=keyword>pub</span> <span class=keyword>struct</span> $name<span class=punctuation>(</span><span class=constructor>Vec</span><span class=punctuation>&lt;</span>$subnode<span class=punctuation>>);</span>
    <span class=punctuation>};</span>
    <span class=punctuation>(</span>
        $name<span class=punctuation>:</span>ident <span class=punctuation>(</span> $<span class=punctuation>(</span>
            <span class=operator>*</span> $field<span class=punctuation>:</span>ident <span class=punctuation>:</span> $ty<span class=punctuation>:</span>ty
        <span class=punctuation>)</span>+ <span class=punctuation>)</span>
    <span class=punctuation>)</span> => <span class=punctuation>{</span>
        <span class=keyword>pub</span> <span class=keyword>struct</span> $name <span class=punctuation>{</span> $<span class=punctuation>(</span>
            $field<span class=punctuation>:</span> $ty<span class=punctuation>,</span>
        <span class=punctuation>)</span>+ <span class=punctuation>}</span>
    <span class=punctuation>};</span>
    <span class=punctuation>(</span>
        $name<span class=punctuation>:</span>ident <span class=punctuation>(</span> $<span class=punctuation>(</span>
            + $ty<span class=punctuation>:</span>ident
        <span class=punctuation>)</span>+ <span class=punctuation>)</span>
    <span class=punctuation>)</span> => <span class=punctuation>{</span>
        <span class=keyword>pub</span> <span class=keyword>enum</span> $name <span class=punctuation>{</span> $<span class=punctuation>(</span>
            $ty<span class=punctuation>(</span>$ty<span class=punctuation>),</span>
        <span class=punctuation>)</span>+ <span class=punctuation>}</span>
    <span class=punctuation>};</span>
<span class=punctuation>}</span></code></pre><p>Then, what you might want to do next is generate all these all in one macro invocation, so you don't have to write <code>node!()</code> over and over again. You might think, "darn, looks like I'll have to write all these exact branches again in order to capture them in an outer macro". But fear not, weary traveler! For I bear great news:</p><blockquote><p>Neat Trick 1: Use <code>tt</code> to capture arguments that you pass to other macro invocations.</p></blockquote><pre class=syntax-highlighting><code class=language-rust><span class=keyword>macro_rules</span>! nodes <span class=punctuation>{</span>
    <span class=punctuation>(</span> $<span class=punctuation>(</span>
        $name<span class=punctuation>:</span>ident $tt<span class=punctuation>:</span>tt <span class=punctuation>;</span>
    <span class=punctuation>)</span><span class=operator>*</span> <span class=punctuation>)</span> => <span class=punctuation>{</span>
        $<span class=punctuation>(</span>
            <span class=macro>node!</span><span class=punctuation>(</span>$name $tt<span class=punctuation>);</span>
        <span class=punctuation>)</span><span class=operator>*</span>
    <span class=punctuation>}</span>
<span class=punctuation>}</span></code></pre><p><code>tt</code>, or TokenTree, represents a pair of braces (round <code>()</code>, square <code>[]</code>, or curly <code>{}</code>)<sup class=footnote-ref><a data-footnote-ref href=#fn-2 id=fnref-2>2</a></sup>, with <em>anything</em> inside them. Rust is really good about enforcing braces to be in pairs even at the lex level, and this is represented in macros, which <em>do</em> just take in an arbitrary list of tokens.</p><p>So now we have a macro that we can use like this:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>mod</span> ast <span class=punctuation>{</span>
    <span class=macro>nodes!</span><span class=punctuation>(</span>
        <span class=constructor>Program</span><span class=punctuation>[</span><span class=constructor>Function</span><span class=punctuation>];</span>
        <span class=function>Function</span><span class=punctuation>(</span>
            <span class=operator>*</span>open_brace<span class=punctuation>:</span> <span class=constructor>OpenBrace</span>
            <span class=operator>*</span>body<span class=punctuation>:</span> <span class=constructor>Body</span>
            <span class=operator>*</span>close_brace<span class=punctuation>:</span> <span class=constructor>CloseBrace</span>
        <span class=punctuation>);</span>
        <span class=constructor>Body</span><span class=punctuation>[</span><span class=constructor>BlockItem</span><span class=punctuation>];</span>
        <span class=function>BlockItem</span><span class=punctuation>(</span> +<span class=constructor>Statement</span> +<span class=constructor>Declaration</span> <span class=punctuation>);</span>
        <span class=comment>// ...</span>
    <span class=punctuation>);</span>
<span class=punctuation>}</span></code></pre><p>But what if that's not good enough? What if we need <strong><em>more</em></strong>?</p><h2><a aria-hidden=true class=anchor href=#more id=heading-more></a>More?</h2><p>To keep things short: in my <a href=https://github.com/p0lyw0lf/pwcc>compiler project</a>, I have a proc_macro that wants to read in a whole bunch of <code>struct</code>/<code>enum</code> definitions at once, by being an attribute on a module. However! There is a dilemma: the simplest approach doesn't work.</p><pre class=syntax-highlighting><code class=language-rust><span class=attribute>#</span><span class=punctuation>[</span><span class=attribute>my_proc_macro</span><span class=punctuation>]</span>
<span class=keyword>mod</span> ast <span class=punctuation>{</span>
    <span class=macro>nodes!</span><span class=punctuation>(</span><span class=comment>/* ... */</span><span class=punctuation>);</span>
<span class=punctuation>}</span></code></pre><p>This is because of the order in which Rust runs macros. <code>#[my_proc_macro]</code> runs first, because it could transform the output to be <em>literally anything</em> (see the <code>#[cfg]</code> attribute macros, which wholesale remove the thing they're applied to). What is this means, however, is that when <code>#[my_proc_macro]</code> runs, all it sees is a module with a single macro invocation, which it doesn't like, so it moves on without doing any work :(. Only then does <code>nodes!()</code> run and generate all the <code>struct</code>/<code>enum</code> definitions, too late.</p><p>Our goal is to have <code>#[my_proc_macro]</code> operate on a module with expanded <code>struct</code>/<code>enum</code> definitions. Knowing how Rust evaluates macros, we have two options:</p><ol><li>Have <code>#[my_proc_macro]</code> eagerly expand all macro invocations inside it.</li><li>Have <code>nodes!()</code> generate everything in one shot, applying <code>#[my_proc_macro]</code> to the finished product.</li></ol><p>(1) is not possible unless we're The Rust Compiler/willing to resort to dirty hacks, so we're left with (2).</p><h2><a aria-hidden=true class=anchor href=#generating-everything-in-one-pass id=heading-generating-everything-in-one-pass></a>Generating Everything In One Pass</h2><blockquote><p>Neat Trick 2: Inside a repetition <code>$()*</code>, you can use <code>$()?</code> like individual <code>macro_rules!</code> cases, and the results will be the same, so long as you use an appropriate delimiter<sup class=footnote-ref><a data-footnote-ref href=#fn-3 id=fnref-3>3</a></sup>.</p></blockquote><pre class=syntax-highlighting><code class=language-rust><span class=keyword>macro_rules</span>! nodes_prime <span class=punctuation>{</span>
    <span class=punctuation>(</span>
        $<span class=punctuation>(</span>
            $name<span class=punctuation>:</span>ident
            $<span class=punctuation>(</span>
                <span class=comment>// Case 1</span>
                <span class=punctuation>[</span> $a_subnode<span class=punctuation>:</span>ident <span class=punctuation>]</span>
            <span class=punctuation>)</span>?
            $<span class=punctuation>(</span>
                <span class=comment>// Case 2</span>
                <span class=punctuation>(</span> $<span class=punctuation>(</span>
                    <span class=operator>*</span> $b_field<span class=punctuation>:</span>ident <span class=punctuation>:</span> $b_ty<span class=punctuation>:</span>ty
                <span class=punctuation>)</span>+ <span class=punctuation>)</span>
            <span class=punctuation>)</span>?
            $<span class=punctuation>(</span>
                <span class=comment>// Case 3</span>
                <span class=punctuation>(</span> $<span class=punctuation>(</span>
                    + $c_ty<span class=punctuation>:</span>ident
                <span class=punctuation>)</span>+ <span class=punctuation>)</span>
            <span class=punctuation>)</span>?
            <span class=comment>// Delimiter</span>
            <span class=punctuation>;</span>
        <span class=punctuation>)</span><span class=operator>*</span>
    <span class=punctuation>)</span> => <span class=punctuation>{</span>
        <span class=attribute>#</span><span class=punctuation>[</span><span class=attribute>my_proc_macro</span><span class=punctuation>]</span>
        <span class=keyword>mod</span> ast <span class=punctuation>{</span>
            <span class=keyword>use</span> <span class=keyword>super</span><span class=punctuation>::</span><span class=operator>*</span><span class=punctuation>;</span>

        $<span class=punctuation>(</span>
            $<span class=punctuation>(</span>
                <span class=comment>// Case 1</span>
                <span class=keyword>pub</span> <span class=keyword>struct</span> $name<span class=punctuation>(</span><span class=constructor>Vec</span><span class=punctuation>&lt;</span>$a_subnode<span class=punctuation>>);</span>
            <span class=punctuation>)</span>?
            $<span class=punctuation>(</span>
                <span class=comment>// Case 2</span>
                <span class=keyword>pub</span> <span class=keyword>struct</span> $name <span class=punctuation>{</span> $<span class=punctuation>(</span>
                    $b_field<span class=punctuation>:</span> $b_ty<span class=punctuation>,</span>
                <span class=punctuation>)</span>+ <span class=punctuation>}</span>
            <span class=punctuation>)</span>?
            $<span class=punctuation>(</span>
                <span class=comment>// Case 3</span>
                <span class=keyword>pub</span> <span class=keyword>enum</span> $name <span class=punctuation>{</span> $<span class=punctuation>(</span>
                    $c_ty<span class=punctuation>(</span>$c_ty<span class=punctuation>),</span>
                <span class=punctuation>)</span>+ <span class=punctuation>}</span>
            <span class=punctuation>)</span>?
        <span class=punctuation>)</span><span class=operator>*</span>

        <span class=punctuation>}</span>
    <span class=punctuation>};</span>
<span class=punctuation>}</span></code></pre><p>Whoa! That's a big macro, huh? Hopefully each part makes sense given the previous two macros.</p><p>How this trick works is, whenever Rust encounters a series of <code>$()?</code>, it checks the next token to see which branch matches. Assuming exactly one does, it takes that branch and emits that case within the repetition. Neat! However, there are some downsides:</p><ol><li>The criteria for making <code>$()?</code> cases distinct is stricter than making <code>macro_rules!</code> cases distinct, due to the eager one-token lookahead.</li><li>You must make the variable names in each case distinct, which either leads to visual clutter (prefixes) or confusion (no prefixes).</li><li>The compiler will yell at you real good if you mess up at either of the above.</li></ol><p>Still, at least for me, the benefit of generating everything in one shot & being compatible with <code>#[my_proc_macro]</code> easily outweighs the cost. I hope to have a blog post soon about what exactly I'm <em>doing</em> in <code>#[my_proc_macro]</code> that makes it so cool (whoops I hinted at such a post <a href=https://wolfgirl.dev/blog/2024-11-24-a-novel-idea-about-functor-in-rust/>months ago already</a>, oh well). Anyways, take care, see you next time!!</p><section class=footnotes data-footnotes><ol><li id=fn-1><p>Apologies if you have trouble reading <code>macro_rules!</code> syntax from a cold start. I hope the usage example later helps. <a aria-label="Back to reference 1" class=footnote-backref data-footnote-backref data-footnote-backref-idx=1 href=#fnref-1>↩</a></p></li><li id=fn-2><p>Yes, this list is complete, we're not forgetting angle <code>&lt;></code> braces, which need to be paired up in types. Rust doesn't force those to be paired up, because those tokens can also be part of comparison operators! <a aria-label="Back to reference 2" class=footnote-backref data-footnote-backref data-footnote-backref-idx=2 href=#fnref-2>↩</a></p></li><li id=fn-3><p>We can even nest this trick as much as we want! That's how we get things like <a href=https://github.com/p0lyw0lf/pwcc/blob/bf75533bba40f9f0a06ab66a7807a5f87c72147e/pwcc/src/parser/macros.rs#L24-L174>this monstrosity</a> :3 <a aria-label="Back to reference 3" class=footnote-backref data-footnote-backref data-footnote-backref-idx=3 href=#fnref-3>↩</a></p></li></ol></section>]]></content>
      <author>
        <name>PolyWolf</name>
        <uri>https://wolf.girl.technology/</uri>
      </author>
    </entry>
  
    <entry>
      <title><![CDATA[A Novel Idea About `Functor` In Rust?]]></title>
      <link rel="alternate" href="https://wolfgirl.dev/blog/2024-11-24-a-novel-idea-about-functor-in-rust/" />
      <id>https://wolfgirl.dev/blog/2024-11-24-a-novel-idea-about-functor-in-rust/</id>
      <published>2024-11-24T22:24:16Z</published>
      <updated>2024-11-24T22:24:16Z</updated>
      <summary><![CDATA[I may have accidentally come up with a paper-worthy idea while just playing around with my silly compiler project]]></summary>
      <content type="html"><![CDATA[<p>So. In <a href=https://wolfgirl.dev/blog/2024-11-13-swift-almost-does-a-really-cool-thing-i-want/>my last post</a> about this compiler project, I wrote out the following trait definition, explaining that it's what I was using to do type-safe AST transformations:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>trait</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>Inner</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>Input</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>Output</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>Mapped</span><span class=punctuation>;</span>
    <span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=operator>&</span><span class=keyword>mut</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>Self</span><span class=punctuation>::</span><span class=type>Input</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Output</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Mapped</span><span class=punctuation>;</span>
<span class=punctuation>}</span></code></pre><p>I called it a "specialized Functor" because I didn't quite understand what exactly a Functor was, but I knew that, at the very least, what I had was a little different.</p><p>Upon reading this, Alice of <a href=https://welltypedwit.ch/>welltypedwit.ch</a>, who actually knows what she's talking about when it comes to functional programming, <a href=https://bsky.app/profile/welltypedwit.ch/post/3latirgqtjk2h>pointed me towards</a> a Haskell package called <a href=https://hackage.haskell.org/package/uniplate-1.6.13>uniplate</a>, noting that what I was doing sounded like its <a href=https://hackage.haskell.org/package/uniplate-1.6.13/docs/Data-Generics-Uniplate-Operations.html#v:transformBi><code>transformBi</code></a> operation.</p><p>To fully understand what she meant by this, let's break down the type signature:</p><h2><a aria-hidden=true class=anchor href=#transformbi id=heading-transformbi></a><code>transformBi</code></h2><p>In Haskell:</p><pre class=syntax-highlighting><code class=language-haskell>transformBi :: Biplate from to => (to -> to) -> from -> from
</code></pre><p>What is this saying? Even if you understand Haskell, it can sometimes still be tricky to get what certain operations <em>mean</em>. In this case, I think about it like:</p><blockquote><p>Given a type <code>From</code>, which is a container type like the root of an AST, and a type <code>To</code>, which is like an inner node of an AST, transform <code>From</code> by applying an operation each time we encounter a node of type <code>To</code> in the tree.</p></blockquote><p>Indeed, that is very similar to what I was trying to do previously! So why did I call my trait <code>Functor</code>, when this description almost exactly matches? Well mostly out of ignorance, yes, but also, when I later looked up the equivalent Rust crate <a href=https://docs.rs/uniplate/latest/uniplate/trait.Biplate.html><code>uniplate</code></a>, its type signature looked something more like<sup class=footnote-ref><a data-footnote-ref href=#fn-1 id=fnref-1>1</a></sup>:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>trait</span> <span class=type>Biplate</span><span class=punctuation>&lt;</span><span class=type>To</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=keyword>fn</span> <span class=function>transform_bi</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=variable>op</span><span class=punctuation>:</span> <span class=keyword>fn</span><span class=punctuation>(</span><span class=type>To</span><span class=punctuation>)</span> -> <span class=type>To</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>;</span>
<span class=punctuation>}</span></code></pre><p>Quite different from what I ended up with! Note that this is <em>homogenous</em> in the types it maps over; it transforms ASTs to be ones of the same type, whereas I was interested in transforming ASTs to have <em>different types</em>, because the type of the AST can enforce specific invariants. So that's why I was, and still am, interested in something like <code>fmap</code> instead:</p><h2><a aria-hidden=true class=anchor href=#fmap id=heading-fmap></a><code>fmap</code></h2><p>In <a href=https://hackage.haskell.org/package/base-4.20.0.1/docs/Data-Functor.html>Haskell</a>:</p><pre class=syntax-highlighting><code class=language-haskell>fmap :: Functor f => (a -> b) -> f a -> f b
</code></pre><p>I think about this like:</p><blockquote><p>Given a container type <code>F</code> over homogenous elements <code>A</code>, you can transform each <code>A</code> into a <code>B</code> in order to get a new container <code>F</code> over homogenous elements <code>B</code>.</p></blockquote><p>And <em>that's</em> why I thought I wanted a <code>Functor</code>! Initially, I had only parameterized my AST on how it represented locations in memory. I had one type (<code>A</code>) for <code>HardwareRegister | StackAddress | PseudoRegister</code>, and another (<code>B</code>) for just <code>HardwareRegister | StackAddress</code>, in order to guarantee I could never emit assembly code that contained pseudo-registers. A regalloc<sup class=footnote-ref><a data-footnote-ref href=#fn-2 id=fnref-2>2</a></sup> pass would get rid of all the pseudo-registers, and I would run that pass with a specially-crafted <code>fmap</code> call.</p><p>I was inspired by how the Rust crate <a href=https://docs.rs/fmap/latest/fmap/trait.Functor.html><code>fmap</code></a> translated the Haskell signature<sup class=footnote-ref><a data-footnote-ref href=#fn-3 id=fnref-3>3</a></sup>:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>trait</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>B</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>Inner</span><span class=punctuation>;</span>  <span class=comment>// a.k.a. A</span>
    <span class=keyword>type</span> <span class=type>Mapped</span><span class=punctuation>;</span> <span class=comment>// a.k.a. F&lt;B></span>
    <span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=variable>op</span><span class=punctuation>:</span> <span class=operator>&</span><span class=keyword>mut</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>Self</span><span class=punctuation>::</span><span class=type>Inner</span><span class=punctuation>)</span> -> <span class=type>B</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Mapped</span><span class=punctuation>;</span>
<span class=punctuation>}</span></code></pre><p>Now you can hopefully understand how I ended up where I did! My version of <code>Functor</code> is exactly this, just with an explict associated parameter for <code>B</code> so writing generic implementations is easier<sup class=footnote-ref><a data-footnote-ref href=#fn-6 id=fnref-6>4</a></sup>.</p><h2><a aria-hidden=true class=anchor href=#going-beyond id=heading-going-beyond></a>Going beyond?</h2><p>While playing around with this <code>Functor</code> trait, I realized something interesting. Unlike Haskell's <code>Functor</code> typeclass, where <code>f</code> is necessarily a type with a generic parameter, and <code>fmap</code> necessarily transforms that generic parameter, Rust's <code>Functor</code> is a lot more like <code>Biplate</code>, in that you can specialize the transform to only be over certain inner elements!</p><p>When I realized this, I started trying to shoehorn everything into it. I didn't have to just write one <code>impl&lt;L: Location> Functor&lt;L> for Program&lt;L></code> implementation, I could write multiple<sup class=footnote-ref><a data-footnote-ref href=#fn-4 id=fnref-4>5</a></sup>!</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>struct</span> <span class=type>Location</span><span class=punctuation>&lt;</span><span class=type>L</span><span class=punctuation>>(</span><span class=type>L</span><span class=punctuation>);</span> <span class=comment>// Needs to be a newtype so we can avoid potentially conflicting implementations</span>
<span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>Input</span><span class=punctuation>,</span> <span class=type>Output</span><span class=punctuation>></span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>Location</span><span class=punctuation>&lt;</span><span class=type>Output</span><span class=punctuation>>></span> <span class=keyword>for</span> <span class=type>Program</span><span class=punctuation>&lt;</span><span class=type>Input</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>Input</span> = <span class=type>Location</span><span class=punctuation>&lt;</span><span class=type>Input</span><span class=punctuation>>;</span>
    <span class=keyword>type</span> <span class=type>Output</span> = <span class=type>Location</span><span class=punctuation>&lt;</span><span class=type>Output</span><span class=punctuation>>;</span>
    <span class=keyword>type</span> <span class=type>Mapped</span> = <span class=type>Program</span><span class=punctuation>&lt;</span><span class=type>Output</span><span class=punctuation>>;</span>
    <span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=operator>&</span><span class=keyword>mut</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>Self</span><span class=punctuation>::</span><span class=type>Input</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Output</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Mapped</span> <span class=punctuation>{</span> ..<span class=punctuation>.</span><span class=property> </span><span class=punctuation>}</span><span class=property>
</span><span class=punctuation>}</span><span class=property>
</span><span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>I</span><span class=punctuation>,</span><span class=property> </span><span class=type>O</span><span class=punctuation>></span><span class=property> </span><span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>Expression</span><span class=punctuation>&lt;</span><span class=type>O</span><span class=punctuation>>></span><span class=property> </span><span class=keyword>for</span><span class=property> </span><span class=type>Program</span><span class=punctuation>&lt;</span><span class=type>I</span><span class=punctuation>></span><span class=property> </span><span class=punctuation>{</span><span class=property> ... </span><span class=punctuation>}</span><span class=property>
</span><span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>I</span><span class=punctuation>,</span><span class=property> </span><span class=type>O</span><span class=punctuation>></span><span class=property> </span><span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>Statement</span><span class=punctuation>&lt;</span><span class=type>O</span><span class=punctuation>>></span><span class=property> </span><span class=keyword>for</span><span class=property> </span><span class=type>Program</span><span class=punctuation>&lt;</span><span class=type>I</span><span class=punctuation>></span><span class=property> </span><span class=punctuation>{</span><span class=property> ... </span><span class=punctuation>}</span></code></pre><p>The restrictions of Rust's trait system, and needing to specify explicit <code>Input</code>/<code>Output</code>/<code>Mapped</code> types, actually makes <code>Functor</code> <em>more</em> flexible than its Haskell counterpart! I'm using it to create a class of functions that look like:</p><pre class=syntax-highlighting><code class=language-haskell>fmapBi :: Triplate from to => (to a -> to b) -> from a -> from b
</code></pre><p>and even <em>this</em>, despite being a pretty neat extension of both <code>Functor</code> and <code>Biplate</code> in Haskell, still doesn't capture the full generality of the Rust trait. Still, for my purposes, this "best of both worlds" is all I need: the ability to transform only specific inner nodes (from <code>Biplate</code>), and the ability to change the parameterization of the container type (from <code>Functor</code>).</p><p>I'd like to write a paper on this. The authors of <code>uniplate</code> <a href=https://ndmitchell.com/downloads/paper-uniform_boilerplate_and_list_processing-30_sep_2007.pdf>wrote one</a> explaining their approach, as did the author of <a href=https://hackage.haskell.org/package/multiplate><code>multiplate</code></a> (which I don't quite understand fully, TODO <a href=https://arxiv.org/pdf/1103.2841v1>read it</a> i guess). I'm not 100% sure I have a novel idea on my hands, but at the very least, I think it's a new useful way of looking at things that I'll be happy to include in my toy compiler.</p><hr><p>In case you're wondering, "PolyWolf, why has it taken you so long to get this post out? Surely you haven't spent over two weeks just thinking?", yes I have been thinking about this for a while, but mostly in the context of writing a proper Rust <code>proc_macro</code> that creates these implementations for me, just given the AST definition. It's unfortunately much much more involved than writing a <code>macro_rules!</code><sup class=footnote-ref><a data-footnote-ref href=#fn-5 id=fnref-5>6</a></sup>, especially trying to handle all the edge-cases properly. I don't know why I waited to publish this blog post until I finished writing this new macro; I didn't even need to explain anything about it in this post lol. Leaving that for a future blog post, maybe once I publish the crate perhaps !</p><p>As always, you can check out the code at <a href=https://github.com/p0lyw0lf/pwcc>github:p0lyw0lf/pwcc</a>; it <em>sort of</em> has documentation right now, if u squint</p><section class=footnotes data-footnotes><ol><li id=fn-1><p>The real thing's actually worse in my opinion: <code>fn transform_bi(&self, op: Arc&lt;dyn Fn(To) -> To>) -> Self</code>. You have to deal with the overhead of <code>Arc</code>, <code>dyn</code>, <em>and</em> <code>Clone</code>-ing your entire AST?? No thanks. This is actually one of the biggest motivators I have for wanting to write a paper about this, to show these operations can be done in-place, zero-copy (given a sufficiently smart compiler, I hope). <a aria-label="Back to reference 1" class=footnote-backref data-footnote-backref data-footnote-backref-idx=1 href=#fnref-1>↩</a></p></li><li id=fn-2><p>(fake (deragatory)), I haven't gotten that far in the book yet, so all pseudo-registers just get assigned to unique stack addresses :) <a aria-label="Back to reference 2" class=footnote-backref data-footnote-backref data-footnote-backref-idx=2 href=#fnref-2>↩</a></p></li><li id=fn-3><p>Again, this signature is a massive simplification. The real one cares about lifetimes, thread safety, etc. etc. all good stuff a proper Rust library needs to care about <a aria-label="Back to reference 3" class=footnote-backref data-footnote-backref data-footnote-backref-idx=3 href=#fnref-3>↩</a></p></li><li id=fn-6><p>I'm reading this again and I'm actually not too convinced by that argument anymore. Maybe it was so I could write things like <code>TryFunctor</code> (returns a <code>Result</code>) or <code>AsyncFunctor</code> (returns an <code>impl Future</code>) without having to change the trait at all? That seems bad tho, and I should probably just write separate traits lol <a aria-label="Back to reference 4" class=footnote-backref data-footnote-backref data-footnote-backref-idx=4 href=#fnref-6>↩</a></p></li><li id=fn-4><p>Given appropriate macro/specialization support, which is what the previous post was about :P <a aria-label="Back to reference 5" class=footnote-backref data-footnote-backref data-footnote-backref-idx=5 href=#fnref-4>↩</a></p></li><li id=fn-5><p>I will curse dtolnay forever for making <code>syn</code>/<code>quote</code> the only way to do this, but maybe I'm just more frustated by how hard the logic is in general... <a aria-label="Back to reference 6" class=footnote-backref data-footnote-backref data-footnote-backref-idx=6 href=#fnref-5>↩</a></p></li></ol></section>]]></content>
      <author>
        <name>PolyWolf</name>
        <uri>https://wolf.girl.technology/</uri>
      </author>
    </entry>
  
    <entry>
      <title><![CDATA[Swift *almost* does a really cool thing I want]]></title>
      <link rel="alternate" href="https://wolfgirl.dev/blog/2024-11-13-swift-almost-does-a-really-cool-thing-i-want/" />
      <id>https://wolfgirl.dev/blog/2024-11-13-swift-almost-does-a-really-cool-thing-i-want/</id>
      <published>2024-11-13T13:36:20Z</published>
      <updated>2024-11-13T13:36:20Z</updated>
      <summary><![CDATA[On my [toy compiler](https://wolfgirl.dev/blog/2024-10-17-work-on-my-toy-compiler-is-progressing-smoothly/), I have a lot of AST manipula...]]></summary>
      <content type="html"><![CDATA[<p>On my <a href=https://wolfgirl.dev/blog/2024-10-17-work-on-my-toy-compiler-is-progressing-smoothly/>toy compiler</a>, I have a lot of AST manipulations I need to do. There is the "parsed" AST, the "IR" AST, and the "codegen" AST, all of which need various passes defined for them. Most passes boil down to "transform all nodes of a given type in-place". Now, because my ASTs are kinda big, and I have so many of them, and I have so many node types I want to map over, I <em>really</em> don't want to have to write out this mapping function by hand.</p><p>My solution for Rust was to write a "specialized <a href=https://hackage.haskell.org/package/base-4.20.0.1/docs/Data-Functor.html>Functor</a>" trait like so<sup class=footnote-ref><a data-footnote-ref href=#fn-1 id=fnref-1>1</a></sup>:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>trait</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>Inner</span><span class=punctuation>></span> <span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>Input</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>Output</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>Mapped</span><span class=punctuation>;</span>
    <span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=operator>&</span><span class=keyword>mut</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>Self</span><span class=punctuation>::</span><span class=type>Input</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Output</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Mapped</span><span class=punctuation>;</span>
<span class=punctuation>}</span>

<span class=comment>// example of how it's implemented:</span>
<span class=keyword>impl</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>,</span> <span class=type>Inner</span><span class=punctuation>,</span> <span class=type>A</span><span class=punctuation>,</span> <span class=type>B</span><span class=punctuation>></span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>Inner</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>Vec</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>></span>
<span class=keyword>where</span>
    <span class=type>T</span><span class=punctuation>:</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>Inner</span><span class=punctuation>,</span> <span class=type>Input</span>=<span class=type>A</span><span class=punctuation>,</span> <span class=type>Output</span>=<span class=type>B</span><span class=punctuation>>,</span>
<span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>Input</span> = <span class=type>A</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>Output</span> = <span class=type>B</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>Mapped</span> = <span class=type>Vec</span><span class=punctuation>&lt;</span><span class=type>T</span><span class=punctuation>::</span><span class=type>Mapped</span><span class=punctuation>>;</span>
    <span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=operator>&</span><span class=keyword>mut</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>Self</span><span class=punctuation>::</span><span class=type>Input</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Output</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Mapped</span> <span class=punctuation>{</span>
        <span class=variable>self</span><span class=punctuation>.</span><span class=function>into_iter</span><span class=punctuation>().</span><span class=function>map</span><span class=punctuation>(</span><span class=operator>&</span><span class=keyword>mut</span> |<span class=variable>x</span><span class=punctuation>:</span> <span class=type>T</span>| x<span class=punctuation>.</span><span class=function>fmap</span><span class=punctuation>(</span>f<span class=punctuation>)).</span><span class=function>collect</span><span class=punctuation>()</span>
    <span class=punctuation>}</span>
<span class=punctuation>}</span></code></pre><p>I call this a "specialized Functor" because, unlike the Haskell version, it allows for implementations on arbitrary container types (e.x. parent AST nodes), for specific inner types (e.x. child AST nodes). To give an extremely simplified example:</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>struct</span> <span class=type>Function</span> <span class=punctuation>{</span>
    <span class=property>name</span><span class=punctuation>:</span> <span class=type>String</span><span class=punctuation>,</span>
    <span class=property>body</span><span class=punctuation>:</span> <span class=type>Vec</span><span class=punctuation>&lt;</span><span class=type>Statement</span><span class=punctuation>>,</span>
<span class=punctuation>}</span>

<span class=keyword>enum</span> <span class=type>Statement</span> <span class=punctuation>{</span>
    <span class=constructor>Return</span> <span class=punctuation>{</span> <span class=property>val</span><span class=punctuation>:</span> <span class=type>isize</span> <span class=punctuation>},</span>
<span class=punctuation>}</span></code></pre><p>A hand-written <code>Functor</code> implementation for <code>isize</code> would look like</p><pre class=syntax-highlighting><code class=language-rust><span class=keyword>impl</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>isize</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>isize</span> <span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>Input</span> = <span class=type>isize</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>Output</span> = <span class=type>isize</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>Mapped</span> = <span class=type>isize</span><span class=punctuation>;</span>
    <span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=operator>&</span><span class=keyword>mut</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>Self</span><span class=punctuation>::</span><span class=type>Input</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Output</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Mapped</span> <span class=punctuation>{</span>
        <span class=function>f</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>)</span>
    <span class=punctuation>}</span>
<span class=punctuation>}</span>

<span class=keyword>impl</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>isize</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>Statement</span> <span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>Input</span> = <span class=type>isize</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>Output</span> = <span class=type>isize</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>Mapped</span> = <span class=type>Statement</span><span class=punctuation>;</span>
    <span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=operator>&</span><span class=keyword>mut</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>Self</span><span class=punctuation>::</span><span class=type>Input</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Output</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Mapped</span> <span class=punctuation>{</span>
        <span class=keyword>match</span> <span class=variable>self</span> <span class=punctuation>{</span>
            <span class=type>Statement</span><span class=punctuation>::</span><span class=constructor>Return</span> <span class=punctuation>{</span> val <span class=punctuation>}</span> => <span class=type>Statement</span><span class=punctuation>::</span><span class=type>Return</span> <span class=punctuation>{</span>
                <span class=property>val</span><span class=punctuation>:</span> val<span class=punctuation>.</span><span class=function>fmap</span><span class=punctuation>(</span>f<span class=punctuation>),</span>
            <span class=punctuation>},</span>
        <span class=punctuation>}</span>
    <span class=punctuation>}</span>
<span class=punctuation>}</span>

<span class=keyword>impl</span> <span class=type>Functor</span><span class=punctuation>&lt;</span><span class=type>isize</span><span class=punctuation>></span> <span class=keyword>for</span> <span class=type>Program</span> <span class=punctuation>{</span>
    <span class=keyword>type</span> <span class=type>Input</span> = <span class=type>isize</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>Output</span> = <span class=type>isize</span><span class=punctuation>;</span>
    <span class=keyword>type</span> <span class=type>Mapped</span> = <span class=type>Program</span><span class=punctuation>;</span>
    <span class=keyword>fn</span> <span class=function>fmap</span><span class=punctuation>(</span><span class=variable>self</span><span class=punctuation>,</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=operator>&</span><span class=keyword>mut</span> <span class=keyword>impl</span> <span class=type>FnMut</span><span class=punctuation>(</span><span class=type>Self</span><span class=punctuation>::</span><span class=type>Input</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Output</span><span class=punctuation>)</span> -> <span class=type>Self</span><span class=punctuation>::</span><span class=type>Mapped</span> <span class=punctuation>{</span>
        <span class=type>Program</span> <span class=punctuation>{</span>
            <span class=property>name</span><span class=punctuation>:</span> <span class=variable>self</span><span class=punctuation>.</span><span class=property>name</span><span class=punctuation>,</span>
            <span class=property>body</span><span class=punctuation>:</span> <span class=variable>self</span><span class=punctuation>.</span><span class=property>body</span><span class=punctuation>.</span><span class=function>fmap</span><span class=punctuation>(</span>f<span class=punctuation>),</span>
        <span class=punctuation>}</span>
    <span class=punctuation>}</span>
<span class=punctuation>}</span>

<span class=attribute>#</span><span class=punctuation>[</span><span class=attribute>test</span><span class=punctuation>]</span>
<span class=keyword>fn</span> <span class=function>map_isize</span><span class=punctuation>()</span> <span class=punctuation>{</span>
    <span class=keyword>let</span> x = <span class=type>Program</span> <span class=punctuation>{</span>
        <span class=property>name</span><span class=punctuation>:</span> <span class=string>"main"</span><span class=punctuation>.</span><span class=function>into</span><span class=punctuation>(),</span>
        <span class=property>body</span><span class=punctuation>:</span> <span class=punctuation>[</span><span class=type>Statement</span><span class=punctuation>::</span><span class=type>Return</span> <span class=punctuation>{</span> <span class=property>val</span><span class=punctuation>:</span> <span class=constant>1</span> <span class=punctuation>}].</span><span class=function>into</span><span class=punctuation>(),</span>
    <span class=punctuation>};</span>
    <span class=comment>// Pretend we derived Clone</span>
    <span class=keyword>let</span> y = <span class=type>Functor</span><span class=punctuation>::&lt;</span><span class=type>isize</span><span class=punctuation>>::</span><span class=function>fmap</span><span class=punctuation>(</span>x<span class=punctuation>.</span><span class=function>clone</span><span class=punctuation>(),</span> |i| i + <span class=constant>1</span><span class=punctuation>);</span>
    <span class=comment>// Pretend we derived PartialEq</span>
    <span class=macro>assert_eq!</span><span class=punctuation>(</span>y<span class=punctuation>,</span> <span class=constructor>Program</span> <span class=punctuation>{</span>
        <span class=property>name</span><span class=punctuation>:</span> <span class=string>"main"</span><span class=punctuation>.</span><span class=function>into</span><span class=punctuation>(),</span>
        <span class=property>body</span><span class=punctuation>:</span> <span class=punctuation>[</span><span class=type>Statement</span><span class=punctuation>::</span><span class=constructor>Return</span> <span class=punctuation>{</span> <span class=property>val</span><span class=punctuation>:</span> <span class=constant>2</span> <span class=punctuation>}].</span><span class=function>into</span><span class=punctuation>(),</span>
    <span class=punctuation>});</span>
<span class=punctuation>}</span></code></pre><p>That is, we re-construct the value in-place, recursively calling <code>fmap</code> to transform the appropriate fields<sup class=footnote-ref><a data-footnote-ref href=#fn-3 id=fnref-3>2</a></sup>.</p><p>Now, because the entire point of this is for me to be really lazy, I did not want to have to write out all these <code>Functor</code> implementations manually. So naturally, I <a href=https://github.com/p0lyw0lf/pwcc/blob/6598df9aa48c28b4fe577540e7d824343510670f/functional/src/functor.rs>wrote a macro</a><sup class=footnote-ref><a data-footnote-ref href=#fn-2 id=fnref-2>3</a></sup> that does most of the work for me. All I have to do now is specify, for a given inner type, for each AST node above it in the tree, what fields I'd like to map over.</p><p>Unfortunately, that's still too much work!!! I have a lot of AST nodes and inner types I want to map over, so what I'd really like is a way to define something like this:</p><pre class=syntax-highlighting><code class=language-swift><span class=keyword>func</span> <span class=function>fmap</span><span class=operator>&lt;</span><span class=variable>Container</span><span class=punctuation>,</span> <span class=variable>Inner</span><span class=operator>></span><span class=punctuation>(</span><span class=variable>over</span> <span class=variable>c</span><span class=punctuation>:</span> <span class=type>Container</span><span class=punctuation>,</span> <span class=variable>f</span><span class=punctuation>:</span> <span class=punctuation>(</span><span class=type>Inner</span><span class=punctuation>)</span> <span class=operator>-></span> <span class=type>Inner</span><span class=punctuation>)</span> <span class=operator>-></span> <span class=type>Container</span> <span class=punctuation>{</span>
  <span class=comment>// somehow `fmap` over all of `c`'s fields</span>
  <span class=keyword>return</span> <span class=variable>c</span>;
<span class=punctuation>}</span>

<span class=keyword>func</span> <span class=function>fmap</span><span class=operator>&lt;</span><span class=variable>Inner</span><span class=operator>></span><span class=punctuation>(</span><span class=variable>over</span> <span class=variable>v</span><span class=punctuation>:</span> <span class=type>Inner</span><span class=punctuation>,</span> <span class=variable>f</span> <span class=punctuation>(</span><span class=variable>Inner</span><span class=punctuation>)</span> <span class=operator>-></span> <span class=type>Inner</span><span class=punctuation>)</span> <span class=operator>-></span> <span class=type>Inner</span> <span class=punctuation>{</span>
  <span class=keyword>return</span> <span class=function>f</span><span class=punctuation>(</span><span class=variable>v</span><span class=punctuation>)</span>;
<span class=punctuation>}</span></code></pre><p>Basically: if we're at the target node type, just call the function (definition 2), otherwise, recursively call into all fields (definition 1). Easy as pie!</p><p>Wait a minute... that syntax highlighting... yep, this "pseudocode" is in fact, valid Swift! It even does exactly we want!!! See?</p><pre class=syntax-highlighting><code class=language-swift><span class=function>print</span><span class=punctuation>(</span><span class=function>fmap</span><span class=punctuation>(</span><span class=number>1</span><span class=punctuation>,</span> <span class=punctuation>{</span> <span class=variable>$0</span> <span class=operator>+</span> <span class=number>1</span> <span class=punctuation>}))</span>; <span class=comment>// 2</span>
<span class=function>print</span><span class=punctuation>(</span><span class=function>fmap</span><span class=punctuation>(</span><span class=string>"hello"</span><span class=punctuation>,</span> <span class=punctuation>{</span> <span class=variable>$0</span> <span class=operator>+</span> <span class=number>1</span> <span class=punctuation>}))</span>; <span class=comment>// "hello"</span>
<span class=function>print</span><span class=punctuation>(</span><span class=function>fmap</span><span class=punctuation>(</span><span class=string>"hello"</span><span class=punctuation>,</span> <span class=punctuation>{</span> <span class=variable>$0</span> <span class=operator>+</span> <span class=string>"_world"</span> <span class=punctuation>}))</span>; <span class=comment>// "hello_world"</span></code></pre><p>This kind of definition is impossible to do in Rust thanks to a little thing called <a href="https://doc.rust-lang.org/reference/items/implementations.html?highlight=coherence#trait-implementation-coherence">Coherence</a>, a.k.a. No <a href=https://rust-lang.github.io/rfcs/1210-impl-specialization.html>Specialization</a>. What Specialization allows us to do is write down those 2 definitions for <code>fmap</code>, and the compiler will automatically choose the "least generic" one according to some rules.</p><p>Great! Now all I have to do is implement some compile-time introspection to get it to map over the appropriate fields. How hard could that be?</p><p>[2 hours of trying to read Swift documentation and finding mostly AI-generated blog posts. Thank goodness for StackOverflow at least]</p><p>Upon looking further, it seems the built-in read-only reflection with <a href=https://developer.apple.com/documentation/swift/mirror>Mirror</a> only happens at runtime, and the <a href=https://github.com/wickwirew/Runtime>Runtime</a> read-write reflection library is same way (aptly-named). Also, there doesn't seem to be a way to dynamically call <code>fmap</code> based on the runtime type; instead, all recursive calls result in an <code>Any</code> type for <code>Container</code>, since that's all that can be resolved statically. By passing in types as arguments, I can get all the way down to "cannot convert value of type 'any Any.Type' to expected argument type '(any Any).Type'", which really illustrates the predicament.</p><p>I <em>might</em> be able to hack something with the <code>dynamic</code> keyword or like Protocols or whatever, but I'm still not pleased about runtime-only reflection. I am willing to pay the slight extra binary cost for all these definitions, and Swift doesn't let me do that. Unfortunate that this was a dead end, but still pretty fun to learn something new!</p><p>Think I'll try Haskell next? :P</p><section class=footnotes data-footnotes><ol><li id=fn-1><p>Note this trait has extra considerations for "what if the thing we're mapping over is polymorphic?". The complexity is about the same even without that consideration, unfortunately... <a aria-label="Back to reference 1" class=footnote-backref data-footnote-backref data-footnote-backref-idx=1 href=#fnref-1>↩</a></p></li><li id=fn-3><p>Note the strange function invocation outside the trait. The compiler has a hard time inferring what <code>Functor</code> implementation to use otherwise, for some reason. That's how you know we're doing something cursed! <a aria-label="Back to reference 2" class=footnote-backref data-footnote-backref data-footnote-backref-idx=2 href=#fnref-3>↩</a></p></li><li id=fn-2><p>(Not sure where to put this but it's still relevant for footnotes) I also had the idea of using this macro to define both <code>Functor&lt;Inner></code> and <code>Functor&lt;Result&lt;Inner, Err>></code> implementations. This works because <code>Inner</code> is never a <code>Result</code>, so everything has coherence! These latter implementations would have <code>Input=Inner, Output=Result&lt;Inner, Err></code> and could be configured to fail early or to collect all errors. Unfortunately, I ran into Coherence problems again when I tried to <code>impl Functor&lt;Result&lt;T, E>> for Vec&lt;Result&lt;T, E>></code> (for <code>Mapped=Result&lt;Vec&lt;T, E>></code>). I <em>could</em> just write another trait as well, but man that's annoying! <a aria-label="Back to reference 3" class=footnote-backref data-footnote-backref data-footnote-backref-idx=3 href=#fnref-2>↩</a></p></li></ol></section>]]></content>
      <author>
        <name>PolyWolf</name>
        <uri>https://wolf.girl.technology/</uri>
      </author>
    </entry>
  
    </feed>
  