<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:media="http://search.yahoo.com/mrss/"
>

<channel>
	<title>SE-425 &#8211; Wade Tregaskis</title>
	<atom:link href="https://wadetregaskis.com/tags/se-425/feed/" rel="self" type="application/rss+xml" />
	<link>https://wadetregaskis.com</link>
	<description></description>
	<lastBuildDate>Thu, 14 Mar 2024 00:29:53 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://wadetregaskis.com/wp-content/uploads/2016/03/Stitch-512x512-1-256x256.png</url>
	<title>SE-425 &#8211; Wade Tregaskis</title>
	<link>https://wadetregaskis.com</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">226351702</site>	<item>
		<title>The privileges &#038; challenges of being a primitive type for Codable in Swift</title>
		<link>https://wadetregaskis.com/the-privileges-challenges-of-being-a-primitive-type-for-codable-in-swift/</link>
					<comments>https://wadetregaskis.com/the-privileges-challenges-of-being-a-primitive-type-for-codable-in-swift/#respond</comments>
		
		<dc:creator><![CDATA[]]></dc:creator>
		<pubDate>Thu, 14 Mar 2024 00:29:49 +0000</pubDate>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Codable]]></category>
		<category><![CDATA[Encodable]]></category>
		<category><![CDATA[JSONEncoder]]></category>
		<category><![CDATA[SE-425]]></category>
		<category><![CDATA[Swift]]></category>
		<category><![CDATA[UInt128]]></category>
		<guid isPermaLink="false">https://wadetregaskis.com/?p=7849</guid>

					<description><![CDATA[This is an elaboration of some posts made in the discussion of SE-425: 128-bit Integer Types. I think it warrants sharing a little more broadly &#8211; not because most people ever need to care about this, but rather because it&#8217;s interesting. You might have noticed that the primitive types e.g.: …all conform to Codable (which&#8230; <a class="read-more-link" href="https://wadetregaskis.com/the-privileges-challenges-of-being-a-primitive-type-for-codable-in-swift/" data-wpel-link="internal">Read more</a>]]></description>
										<content:encoded><![CDATA[
<p>This is an elaboration of some posts made in <a href="https://forums.swift.org/t/se-0425-128-bit-integer-types/70456" data-wpel-link="external" target="_blank" rel="external noopener">the discussion of</a> <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0425-int128.md" data-wpel-link="external" target="_blank" rel="external noopener">SE-425: 128-bit Integer Types</a>.  I think it warrants sharing a little more broadly &#8211; not because most people ever need to care about this, but rather because it&#8217;s interesting.</p>



<p>You might have noticed that the primitive types e.g.:</p>



<ul class="wp-block-list">
<li><code>Bool</code></li>



<li><code>Int</code> &amp; <code>UInt</code> (and all fixed-sized variants, e.g. <code>Int8</code>)</li>



<li><code>String</code></li>



<li><code>Float</code></li>



<li><code>Double</code></li>



<li><code>Nil</code></li>
</ul>



<p>…all conform to <code><a href="https://developer.apple.com/documentation/swift/codable" data-wpel-link="external" target="_blank" rel="external noopener">Codable</a></code> (which is just a convenience protocol combining <code><a href="https://developer.apple.com/documentation/swift/encodable" data-wpel-link="external" target="_blank" rel="external noopener">Encodable</a></code> &amp; <code><a href="https://developer.apple.com/documentation/swift/decodable" data-wpel-link="external" target="_blank" rel="external noopener">Decodable</a></code>).  That means they have two methods:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-disabled" data-code-block-pro-font-family="" style="font-size:.875rem;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><pre class="shiki light-plus" style="background-color: #FFFFFF" tabindex="0"><code><span class="line"><span style="color: #0000FF">func</span><span style="color: #000000"> </span><span style="color: #795E26">encode</span><span style="color: #000000">(</span><span style="color: #795E26">to</span><span style="color: #000000"> </span><span style="color: #001080">encoder</span><span style="color: #000000">: any Encoder) </span><span style="color: #AF00DB">throws</span></span>
<span class="line"><span style="color: #0000FF">init</span><span style="color: #000000">(</span><span style="color: #795E26">from</span><span style="color: #000000"> </span><span style="color: #001080">decoder</span><span style="color: #000000">: any Decoder) </span><span style="color: #AF00DB">throws</span></span></code></pre></div>



<p>This is good &#8211; it ensures you can reason about <code>Encodable</code> and <code>Decodable</code> types and not have any weird exceptions for these primitives, e.g. an <code>Array&lt;MyCodableStruct&gt;</code> is just as <code>Codable</code> as <code>Array&lt;String&gt;</code>.  <code>Array</code> doesn&#8217;t have to special-case the primitive types, it just calls <code>encode</code> on its <code>Element</code>s irrespectively.</p>



<p>However, if you stop and think about it, it might seem like there&#8217;s an infinite loop here.  For example, if you call <code>encode(to:)</code> on <code>UInt64</code>, it has only the <code><a href="https://developer.apple.com/documentation/swift/encoder" data-wpel-link="external" target="_blank" rel="external noopener">Encoder</a></code> APIs to work with &#8211; it <em>has</em> to use those APIs, because it doesn&#8217;t&nbsp;<em>actually</em>&nbsp;know how to serialise itself, because it has no idea what the serialisation format is &#8211; that&#8217;s defined by the particular&nbsp;<code>Encoder</code>&nbsp;/&nbsp;<code>Decoder</code>&nbsp;in use.</p>



<p>So, basically <code>UInt64</code> has to call an <code>encode</code> method somewhere, like <a href="https://developer.apple.com/documentation/swift/singlevalueencodingcontainer/encode(_:)-687yj" data-wpel-link="external" target="_blank" rel="external noopener">this one</a>, which would surely then just call <code>encode(to: self)</code> on the original <code>UInt64</code>, right?  Infinite recursion!</p>



<p>Of course, no, thankfully.</p>



<p>The first part of that thinking is correct &#8211; see for example <a href="https://github.com/apple/swift/blob/675fda3a4bb2d0a9693a43548de60901dbdfc0e1/stdlib/public/core/Codable.swift#L5308" data-wpel-link="external" target="_blank" rel="external noopener"><code>UInt64</code>s actual implementation of <code>Codable</code></a>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-disabled" data-code-block-pro-font-family="" style="font-size:.875rem;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><pre class="shiki light-plus" style="background-color: #FFFFFF" tabindex="0"><code><span class="line"><span style="color: #0000FF">extension</span><span style="color: #000000"> </span><span style="color: #267F99">UInt64</span><span style="color: #000000">: Codable {</span></span>
<span class="line"><span style="color: #000000">  </span><span style="color: #0000FF">public</span><span style="color: #000000"> </span><span style="color: #0000FF">init</span><span style="color: #000000">(</span><span style="color: #795E26">from</span><span style="color: #000000"> </span><span style="color: #001080">decoder</span><span style="color: #000000">: any Decoder) </span><span style="color: #AF00DB">throws</span><span style="color: #000000"> {</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">self</span><span style="color: #000000"> = </span><span style="color: #AF00DB">try</span><span style="color: #000000"> decoder.</span><span style="color: #795E26">singleValueContainer</span><span style="color: #000000">().</span><span style="color: #795E26">decode</span><span style="color: #000000">(</span><span style="color: #267F99">UInt64</span><span style="color: #000000">.self)</span></span>
<span class="line"><span style="color: #000000">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #000000">  </span><span style="color: #0000FF">public</span><span style="color: #000000"> </span><span style="color: #0000FF">func</span><span style="color: #000000"> </span><span style="color: #795E26">encode</span><span style="color: #000000">(</span><span style="color: #795E26">to</span><span style="color: #000000"> </span><span style="color: #001080">encoder</span><span style="color: #000000">: any Encoder) </span><span style="color: #AF00DB">throws</span><span style="color: #000000"> {</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">var</span><span style="color: #000000"> container = encoder.</span><span style="color: #795E26">singleValueContainer</span><span style="color: #000000">()</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #AF00DB">try</span><span style="color: #000000"> container.</span><span style="color: #795E26">encode</span><span style="color: #000000">(</span><span style="color: #0000FF">self</span><span style="color: #000000">)</span></span>
<span class="line"><span style="color: #000000">  }</span></span>
<span class="line"><span style="color: #000000">}</span></span></code></pre></div>



<p>The trick to avoiding self-recursion is that encoders &amp; decoders <em>must special-case the primitive types</em>.  They may <em>never</em> call <code>encode(to: self)</code> / <code>init(from: self)</code> on the primitive types.</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>😔 Alas the compiler does not enforce this &#8211; if this rule is broken, the consequence is indeed infinite recursion at runtime.</p>
</div></div>



<p>The way they do this is two-fold:</p>



<ol class="wp-block-list">
<li>There are specific <a href="https://developer.apple.com/documentation/swift/singlevalueencodingcontainer" data-wpel-link="external" target="_blank" rel="external noopener">overloads of <code>encode</code></a> (and <code><a href="https://developer.apple.com/documentation/swift/singlevaluedecodingcontainer" data-wpel-link="external" target="_blank" rel="external noopener">decode</a></code>) for the primitive types.<br><br>An encoder / decoder doesn&#8217;t <em>technically</em> have to provide those specialised overloads &#8211; it can just implement the generic version, which will satisfy the protocol constraints &#8211; but this is ultimately only an optimisation because either way it <em>must</em> serialise those primitive values <em>intrinsically</em>…</li>



<li>The implementation of the generic methods <em>must</em> special-case the primitive types.  e.g. <a href="https://github.com/apple/swift-foundation/blob/69f473d8879ec494b4bcc5f97d091c2b5d263acb/Sources/FoundationEssentials/JSON/JSONEncoder.swift#L1112" data-wpel-link="external" target="_blank" rel="external noopener">the pertinent bit of <code>JSONEncoder</code>&#8216;s implementation</a>:</li>
</ol>



<div class="wp-block-kevinbatdorf-code-block-pro padding-disabled" data-code-block-pro-font-family="" style="font-size:.875rem;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><pre class="shiki light-plus" style="background-color: #FFFFFF" tabindex="0"><code><span class="line"><span style="color: #0000FF">func</span><span style="color: #000000"> </span><span style="color: #795E26">wrapGeneric</span><span style="color: #000000">&lt;</span><span style="color: #0000FF">T</span><span style="color: #000000">: </span><span style="color: #267F99">Encodable</span><span style="color: #000000">&gt;(</span><span style="color: #795E26">_</span><span style="color: #000000"> </span><span style="color: #001080">value</span><span style="color: #000000">: T, </span><span style="color: #795E26">for</span><span style="color: #000000"> </span><span style="color: #001080">node</span><span style="color: #000000">: _CodingPathNode, </span><span style="color: #795E26">_</span><span style="color: #000000"> </span><span style="color: #001080">additionalKey</span><span style="color: #000000">: (some CodingKey)? = _CodingKey?.</span><span style="color: #001080">none</span><span style="color: #000000">) </span><span style="color: #AF00DB">throws</span><span style="color: #000000"> -&gt; JSONReference? {</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #AF00DB">switch</span><span style="color: #000000"> T.self {</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #AF00DB">case</span><span style="color: #000000"> is Date.Type:</span></span>
<span class="line"><span style="color: #000000">        </span><span style="color: #008000">// Respect Date encoding strategy</span></span>
<span class="line"><span style="color: #000000">        </span><span style="color: #AF00DB">return</span><span style="color: #000000"> </span><span style="color: #AF00DB">try</span><span style="color: #000000"> </span><span style="color: #0000FF">self</span><span style="color: #000000">.</span><span style="color: #795E26">wrap</span><span style="color: #000000">(value as! Date, </span><span style="color: #795E26">for</span><span style="color: #000000">: node, additionalKey)</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #AF00DB">case</span><span style="color: #000000"> is Data.Type:</span></span>
<span class="line"><span style="color: #000000">        </span><span style="color: #008000">// Respect Data encoding strategy</span></span>
<span class="line"><span style="color: #000000">        </span><span style="color: #AF00DB">return</span><span style="color: #000000"> </span><span style="color: #AF00DB">try</span><span style="color: #000000"> </span><span style="color: #0000FF">self</span><span style="color: #000000">.</span><span style="color: #795E26">wrap</span><span style="color: #000000">(value as! Data, </span><span style="color: #795E26">for</span><span style="color: #000000">: node, additionalKey)</span></span>
<span class="line"><span style="color: #0000FF">#</span><span style="color: #AF00DB">if</span><span style="color: #0000FF"> FOUNDATION_FRAMEWORK</span><span style="color: #000000"> </span><span style="color: #008000">// TODO: Reenable once URL and Decimal are moved</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #AF00DB">case</span><span style="color: #000000"> is URL.Type:</span></span>
<span class="line"><span style="color: #000000">        </span><span style="color: #008000">// Encode URLs as single strings.</span></span>
<span class="line"><span style="color: #000000">        </span><span style="color: #0000FF">let</span><span style="color: #000000"> url = value as! URL</span></span>
<span class="line"><span style="color: #000000">        </span><span style="color: #AF00DB">return</span><span style="color: #000000"> </span><span style="color: #0000FF">self</span><span style="color: #000000">.</span><span style="color: #795E26">wrap</span><span style="color: #000000">(url.</span><span style="color: #001080">absoluteString</span><span style="color: #000000">)</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #AF00DB">case</span><span style="color: #000000"> is Decimal.Type:</span></span>
<span class="line"><span style="color: #000000">        </span><span style="color: #0000FF">let</span><span style="color: #000000"> decimal = value as! Decimal</span></span>
<span class="line"><span style="color: #000000">        </span><span style="color: #AF00DB">return</span><span style="color: #000000"> .</span><span style="color: #795E26">number</span><span style="color: #000000">(decimal.</span><span style="color: #001080">description</span><span style="color: #000000">)</span></span>
<span class="line"><span style="color: #0000FF">#</span><span style="color: #AF00DB">endif</span><span style="color: #0000FF"> </span><span style="color: #008000">// FOUNDATION_FRAMEWORK</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #AF00DB">case</span><span style="color: #000000"> is _JSONStringDictionaryEncodableMarker.Type:</span></span>
<span class="line"><span style="color: #000000">        </span><span style="color: #AF00DB">return</span><span style="color: #000000"> </span><span style="color: #AF00DB">try</span><span style="color: #000000"> </span><span style="color: #0000FF">self</span><span style="color: #000000">.</span><span style="color: #795E26">wrap</span><span style="color: #000000">(value as! [</span><span style="color: #267F99">String</span><span style="color: #000000"> : Encodable], </span><span style="color: #795E26">for</span><span style="color: #000000">: node, additionalKey)</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #AF00DB">case</span><span style="color: #000000"> is _JSONDirectArrayEncodable.Type:</span></span>
<span class="line"><span style="color: #000000">        </span><span style="color: #0000FF">let</span><span style="color: #000000"> array = value as! _JSONDirectArrayEncodable</span></span>
<span class="line"><span style="color: #000000">        </span><span style="color: #AF00DB">if</span><span style="color: #000000"> options.</span><span style="color: #001080">outputFormatting</span><span style="color: #000000">.</span><span style="color: #795E26">contains</span><span style="color: #000000">(.</span><span style="color: #001080">prettyPrinted</span><span style="color: #000000">) {</span></span>
<span class="line"><span style="color: #000000">            </span><span style="color: #AF00DB">return</span><span style="color: #000000"> .</span><span style="color: #0000FF">init</span><span style="color: #000000">(.</span><span style="color: #795E26">directArray</span><span style="color: #000000">(array.</span><span style="color: #795E26">individualElementRepresentation</span><span style="color: #000000">(</span><span style="color: #795E26">options</span><span style="color: #000000">: options)))</span></span>
<span class="line"><span style="color: #000000">        } </span><span style="color: #AF00DB">else</span><span style="color: #000000"> {</span></span>
<span class="line"><span style="color: #000000">            </span><span style="color: #AF00DB">return</span><span style="color: #000000"> .</span><span style="color: #0000FF">init</span><span style="color: #000000">(.</span><span style="color: #795E26">nonPrettyDirectArray</span><span style="color: #000000">(array.</span><span style="color: #795E26">nonPrettyJSONRepresentation</span><span style="color: #000000">(</span><span style="color: #795E26">options</span><span style="color: #000000">: options)))</span></span>
<span class="line"><span style="color: #000000">        }</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #AF00DB">default</span><span style="color: #000000">:</span></span>
<span class="line"><span style="color: #000000">        </span><span style="color: #AF00DB">break</span></span>
<span class="line"><span style="color: #000000">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #AF00DB">return</span><span style="color: #000000"> </span><span style="color: #AF00DB">try</span><span style="color: #000000"> </span><span style="color: #795E26">_wrapGeneric</span><span style="color: #000000">({</span></span>
<span class="line"><span style="color: #000000">        </span><span style="color: #AF00DB">try</span><span style="color: #000000"> value.</span><span style="color: #795E26">encode</span><span style="color: #000000">(</span><span style="color: #795E26">to</span><span style="color: #000000">: </span><span style="color: #0000FF">$0</span><span style="color: #000000">)</span></span>
<span class="line"><span style="color: #000000">    }, </span><span style="color: #795E26">for</span><span style="color: #000000">: node, additionalKey)</span></span>
<span class="line"><span style="color: #000000">}</span></span></code></pre></div>



<p>This specialisation <em>inside the generic method</em> is required even if there are specialised overloads, because the type of <code>T</code> is not always known at runtime; sometimes it truly is an <a href="https://docs.swift.org/swift-book/documentation/the-swift-programming-language/opaquetypes/" data-wpel-link="external" target="_blank" rel="external noopener">existential</a>.  So the specialisations can&#8217;t always be called directly.  In the case of <code>JSONEncoder</code> (above) it <em>manually</em> unboxes the existential and invokes the appropriate specialisation.</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>Tangentially, notice how <code>JSONEncoder</code> has special-case handling of <code>Date</code>, <code>Data</code>, and <code>Dict</code>, even though those types are not considered &#8216;primitives&#8217; and do have their own, fully-functional <code>Codable</code> implementations in terms of the primitive types (e.g. <a href="https://github.com/apple/swift-foundation/blob/69f473d8879ec494b4bcc5f97d091c2b5d263acb/Sources/FoundationEssentials/Data/Data.swift#L2756" data-wpel-link="external" target="_blank" rel="external noopener"><code>Data</code></a>&#8216;s).  This is because <code>JSONEncoder</code> believes it can do a better job for those types, given its specific knowledge of the JSON format.  Encoders &amp; decoders are always allowed to specialise <em>additional</em> types beyond the primitive types.</p>
</div></div>



<p>For all types other than those primitive types, their <code>Codable</code> representation must ultimately be defined in terms of <em>only</em> those primitive types (with allowances for keyed containers (a la <code>Dictionary</code>) and unkeyed containers (a la <code>Array</code>) to provide structure).</p>



<p>Consider for example <a href="https://github.com/apple/swift/blob/675fda3a4bb2d0a9693a43548de60901dbdfc0e1/stdlib/public/core/Codable.swift#L5372" data-wpel-link="external" target="_blank" rel="external noopener"><code>Optional</code>&#8216;s <code>Codable</code> conformance</a>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-disabled" data-code-block-pro-font-family="" style="font-size:.875rem;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><pre class="shiki light-plus" style="background-color: #FFFFFF" tabindex="0"><code><span class="line"><span style="color: #0000FF">public</span><span style="color: #000000"> </span><span style="color: #0000FF">func</span><span style="color: #000000"> </span><span style="color: #795E26">encode</span><span style="color: #000000">(</span><span style="color: #795E26">to</span><span style="color: #000000"> </span><span style="color: #001080">encoder</span><span style="color: #000000">: any Encoder) </span><span style="color: #AF00DB">throws</span><span style="color: #000000"> {</span></span>
<span class="line"><span style="color: #000000">  </span><span style="color: #0000FF">var</span><span style="color: #000000"> container = encoder.</span><span style="color: #795E26">singleValueContainer</span><span style="color: #000000">()</span></span>
<span class="line"><span style="color: #000000">  </span><span style="color: #AF00DB">switch</span><span style="color: #000000"> </span><span style="color: #0000FF">self</span><span style="color: #000000"> {</span></span>
<span class="line"><span style="color: #000000">  </span><span style="color: #AF00DB">case</span><span style="color: #000000"> .</span><span style="color: #001080">none</span><span style="color: #000000">: </span><span style="color: #AF00DB">try</span><span style="color: #000000"> container.</span><span style="color: #795E26">encodeNil</span><span style="color: #000000">()</span></span>
<span class="line"><span style="color: #000000">  </span><span style="color: #AF00DB">case</span><span style="color: #000000"> .</span><span style="color: #001080">some</span><span style="color: #000000">(</span><span style="color: #0000FF">let</span><span style="color: #000000"> wrapped): </span><span style="color: #AF00DB">try</span><span style="color: #000000"> container.</span><span style="color: #795E26">encode</span><span style="color: #000000">(wrapped)</span></span>
<span class="line"><span style="color: #000000">  }</span></span>
<span class="line"><span style="color: #000000">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #0000FF">public</span><span style="color: #000000"> </span><span style="color: #0000FF">init</span><span style="color: #000000">(</span><span style="color: #795E26">from</span><span style="color: #000000"> </span><span style="color: #001080">decoder</span><span style="color: #000000">: any Decoder) </span><span style="color: #AF00DB">throws</span><span style="color: #000000"> {</span></span>
<span class="line"><span style="color: #000000">  </span><span style="color: #0000FF">let</span><span style="color: #000000"> container = </span><span style="color: #AF00DB">try</span><span style="color: #000000"> decoder.</span><span style="color: #795E26">singleValueContainer</span><span style="color: #000000">()</span></span>
<span class="line"><span style="color: #000000">  </span><span style="color: #AF00DB">if</span><span style="color: #000000"> container.</span><span style="color: #795E26">decodeNil</span><span style="color: #000000">() {</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">self</span><span style="color: #000000"> = .</span><span style="color: #001080">none</span></span>
<span class="line"><span style="color: #000000">  }  </span><span style="color: #AF00DB">else</span><span style="color: #000000"> {</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">let</span><span style="color: #000000"> element = </span><span style="color: #AF00DB">try</span><span style="color: #000000"> container.</span><span style="color: #795E26">decode</span><span style="color: #000000">(Wrapped.self)</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">self</span><span style="color: #000000"> = .</span><span style="color: #001080">some</span><span style="color: #000000">(element)</span></span>
<span class="line"><span style="color: #000000">  }</span></span>
<span class="line"><span style="color: #000000">}</span></span></code></pre></div>



<p>It doesn&#8217;t have the luxury of a special-case guaranteed by all encoders &amp; decoders, so it has to figure out how to represent itself using only the primitive types.</p>



<p>It demonstrates <em>both</em> ways of doing this:  direct use of primitive types, and indirect (a.k.a. punting the problem downwards).</p>



<p>If the <code>Optional</code> is empty, it encodes itself as simply a <code>nil</code> value (one of the supported primitive types).</p>



<p>If it&#8217;s not empty, it simply defers to the wrapped value to do the work.  That value must then do the same thing &#8211; figure out a way to represent itself using only the primitive types and/or punt the challenge to its component types.</p>



<p>So if you have e.g. <code>Optional&lt;Int64></code>, that essentially looks like:</p>



<ol class="wp-block-list">
<li><code><a href="https://github.com/apple/swift/blob/675fda3a4bb2d0a9693a43548de60901dbdfc0e1/stdlib/public/core/Codable.swift#L5379" data-wpel-link="external" target="_blank" rel="external noopener">Optional.encode(to: someJSONEncoder)</a></code> which just calls…</li>



<li><code><a href="https://github.com/apple/swift/blob/675fda3a4bb2d0a9693a43548de60901dbdfc0e1/stdlib/public/core/Codable.swift#L5025" data-wpel-link="external" target="_blank" rel="external noopener">Int64.encode(to: someJSONEncoder)</a></code> which just calls…</li>



<li><code><a href="https://github.com/apple/swift-foundation/blob/69f473d8879ec494b4bcc5f97d091c2b5d263acb/Sources/FoundationEssentials/JSON/JSONEncoder.swift#L907" data-wpel-link="external" target="_blank" rel="external noopener">someJSONEncoder.singleValueContainer().encode(self)</a></code> (where self is the <code>Int64</code> value), which has some further redirection through abstractions but ultimately does the <em>actual</em> serialisation.</li>
</ol>



<h2 class="wp-block-heading">Can new primitive types be added?</h2>



<p>That is in fact what prompted this post and the discussion that precipitated it.  <code>Int128</code> &amp; <code>UInt128</code> or finally coming to Swift (properly &#8211; the standard library has had them internally for a while).  But that raises the question of how they will be supported for <code>Codable</code>.  Technically, the three options are:</p>



<ol class="wp-block-list">
<li>Be added to the Codable system as primitive types (i.e. additional overloads for them on <a href="https://developer.apple.com/documentation/swift/singlevalueencodingcontainer" data-wpel-link="external" target="_blank" rel="external noopener">SingleValueEncodingContainer</a> &amp; friends).</li>



<li><em>Not</em> be added as <code>Codable</code>-recognised primitive types, and instead implement their <code>Codable</code> conformance in terms of only the existing primitive types, e.g. as strings instead, or pairs of 64-bit integers, etc.</li>



<li>Not support <code>Codable</code> at all.</li>
</ol>



<p>Option #3 is obviously <em>highly</em> undesirable.  Note that if the standard library doesn&#8217;t provide <code>Codable</code> conformance for these types, <em>no-one can</em>, because protocol conformances cannot be added outside of the modules which define at least one of (a) the type in question or (b) the protocol in question.  Since the standard library defines both in this case, it is the <em>only</em> place where the conformance may be made.</p>



<p>Option #2 is the only other option most types have; most don&#8217;t have the luxury of getting special treatment from encoders &amp; decoders like the standard library&#8217;s primitive types do.  But it&#8217;s a frustrating option for <code>Int128</code> &amp; <code>UInt128</code> because it adds runtime and possibly wire-format overhead to their use as <code>Codable</code>, and makes their serialisation &amp; deserialisation more complicated.</p>



<p>Interestingly, it does <em>not</em> preclude them from being supported more efficiently &amp; elegantly by encoders &amp; decoders that do intrinsically supported them because, as we saw with <code>JSONEncoder</code>, the encoder &amp; decoder are always free to <em>override</em> the standard representation for any type.</p>



<p>Option #1 seems simple and obviously appropriate for these types that are, after all, just new siblings into the <code>FixedWidthInteger</code> standard library family.  However, adding new requirements (properties, methods, etc) to a protocol is a <em>breaking change</em>; it is both source-incompatible and binary-incompatible.  …<em>unless</em> the new requirements come with default implementations.  The problem is, what would the default implementation be?</p>



<p>There are technically three sub-options:</p>



<ol class="wp-block-list">
<li>Have the default implementation be effectively the same as Option #2 above; implemented in terms of the <em>existing</em> primitive types.</li>



<li>Throw an exception (fortunately all the relevant encoding &amp; decoding methods are already marked <code>throws</code> with no restriction on the thrown type, because they predate <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0413-typed-throws.md" data-wpel-link="external" target="_blank" rel="external noopener">typed throws</a>).</li>



<li>Crash (or hang, or similar).</li>
</ol>



<p>At time of writing the debate is ongoing as to whether sub-option #1 or #2 is the best option for <code>Int128</code> &amp; <code>UInt128</code>.  Personally I think throwing an exception is the best option (for reasons <a href="https://forums.swift.org/t/se-0425-128-bit-integer-types/70456/35" data-wpel-link="external" target="_blank" rel="external noopener">detailed in the forum thread</a>), but only time will tell what Swift ultimately chooses.</p>



<p>Either way, the important thing is for encoders &amp; decoders to <em>actually</em> add support for the new primitives.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://wadetregaskis.com/the-privileges-challenges-of-being-a-primitive-type-for-codable-in-swift/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">7849</post-id>	</item>
	</channel>
</rss>
