Skip to content

Commit 0b1f463

Browse files
committed
push
1 parent 7d4afda commit 0b1f463

1 file changed

Lines changed: 28 additions & 16 deletions

File tree

asymmetric-types-in-protobuf/index.html

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!DOCTYPE html>
2-
<html lang="en">
2+
<html lang="EN-AU">
33

44
<!-- TODO: When i try to open the page on localhost it blocks loading for a very long time. I suspect it's
55
blocking on loading the stylesheet. I don't want it to do that. -->
@@ -662,6 +662,7 @@ <h2><a id="preamble" href="#preamble"></a>Preamble</h2>
662662
</p>
663663

664664
<p>A definition can be said to made "narrower" than its previous version when:
665+
<!-- TODO it would be nice if each of these entries floated above the message and enum-->
665666
<ul>
666667
<li>For messages (a product type), we add a new field</li>
667668
<li>For enums (a sum type), we remove a value</li>
@@ -690,6 +691,8 @@ <h2><a id="preamble" href="#preamble"></a>Preamble</h2>
690691
</figure>
691692

692693
<p>Conversely, a definition can can be made "wider" than its previous version when:</p>
694+
695+
<!-- TODO it would be nice if each of these entries floated above the message and enum-->
693696
<ul>
694697
<li>for messages, we remove a field</li>
695698
<li>for enums, we add a value</li>
@@ -731,8 +734,7 @@ <h2><a id="problems-with-symmetric-types" href="#problems-with-symmetric-types">
731734

732735
<p>Assume an environment where we do not control deployment ordering, such for a given release, producers or consumers may be deployed before each other.</p>
733736

734-
<p>If an engineer needs to narrow a message, such as trying to add a <code>required</code>
735-
field they create an incompatibility risk. A message sent by an old producer breaks a new consumer:</p>
737+
<p>If an engineer needs to narrow a message, such as trying to add a <code>required</code>, field they create an incompatibility risk. A message sent by an old producer can break a new consumer:</p>
736738

737739

738740
<!-- TODO: required string doesn't trigger a deserialization error because it has a deserialization defualt. We need a new example
@@ -821,7 +823,7 @@ <h2><a id="problems-with-symmetric-types" href="#problems-with-symmetric-types">
821823
});
822824
</script>
823825

824-
<p>This also occurs when widening a message, by removing a <code>required</code> field. A message sent by a new producer breaks an old consumer:</p>
826+
<p>This also occurs when widening a message, by removing a <code>required</code> field. A message sent by a new producer can break an old consumer:</p>
825827

826828
<figure>
827829
<div class="container-2-col">
@@ -906,12 +908,12 @@ <h2><a id="problems-with-symmetric-types" href="#problems-with-symmetric-types">
906908
</script>
907909

908910
<p>
909-
This incompatibility risk doesn't just happen when adding or removing a <code>required</code> field directly: the same kind of risk occurs when transitioning an <code>optional</code> field to <code>required</code>, or <code>required</code> to <code>optional</code>.
911+
This incompatibility risk doesn't just happen when adding or removing a <code>required</code> field directly: the same kind of risk occurs when evolving an <code>optional</code> field to <code>required</code>, or <code>required</code> to <code>optional</code>.
910912
</p>
911913

912914
<!-- TODO: verify that these enums actually explode. I think it might be related to the `required`-ness of the enum field reference. -->
913915
<!-- TODO explain open and closed enums in an aside https://protobuf.dev/programming-guides/enum/#:~:text=enum%E2%80%99s%20default%20value.-,Implications%20of%20Closed%20Enums,-The%20behavior%20of -->
914-
<p>Enum evolutions also carry incompatibility risk, <i>when the enum is used as a <code>required</code> field</i>, for example, when narrowing an enum, by removing an enum value. An enum sent by an old producer breaks a new consumer:</p>
916+
<p>Enum evolutions also carry incompatibility risk, <i>when the enum is used as a <code>required</code> field</i>, for example, when narrowing an enum, by removing an enum value. An enum sent by an old producer can break a new consumer:</p>
915917

916918
<figure>
917919
<div class="container-2-col">
@@ -1004,7 +1006,7 @@ <h2><a id="problems-with-symmetric-types" href="#problems-with-symmetric-types">
10041006
</script>
10051007

10061008

1007-
<p>And also when widening an enum, by adding a value. An enum sent by a new producer breaks an old consumer:</p>
1009+
<p>And also when widening an enum, by adding a value. An enum sent by a new producer break can break an old consumer:</p>
10081010

10091011
<figure>
10101012
<div class="container-2-col">
@@ -1538,11 +1540,11 @@ <h4><a id="constructor-required-deserialization-optional"
15381540
</p>
15391541
</aside>
15401542

1541-
<!-- TODO: Quote directly from typical docs -->
1542-
<p>In <a href="https://github.com/stepchowfun/typical#required-optional-and-asymmetric-fields">Typical</a>, the <code>asymmetric</code> label forces writers to provide a non-null value at the construction site but permits
1543-
readers to deserialize payloads where that value is absent.</p>
1543+
<p>In <a href="https://github.com/stepchowfun/typical#required-optional-and-asymmetric-fields">Typical</a> adding the <code>asymmetric</code> label to a field generates code treats the field as required in the constructor, such that it must be parameterized, whilst treating it as optional in its deserializer and by public field access.
1544+
15441545

1545-
<p>Using the email API example from Typical's tutorial, we can add a new <code>from</code> field to <code>SendEmailRequest</code> via a safe intermediate state:</p>
1546+
<!-- TODO: should we explicit `required` syntax? Does Typical support that? -->
1547+
<p>Using the example from Typical README, we can see that by first adding field <code>from</code> with an<code>asymmetric</code> label, we can safely evolve it it to a (labelless) required.</p>
15461548

15471549
<figure class="semifullwidth">
15481550
<div class="container-3-col" id="required">
@@ -1631,16 +1633,20 @@ <h4><a id="constructor-required-deserialization-optional"
16311633
});
16321634
</script>
16331635
<p>
1634-
This same intermediate state also permits:
1636+
How wonderful! Using <code>asymmetric</code> as this intermediate stage admits:
16351637
</p>
16361638

16371639
<!-- TODO: do we even use this terminology (optional/required) in typical? It hasn't been established yet:-->
16381640
<ul>
1639-
<li>transitioning from <code>optional</code> to <code>required</code></li>
1640-
<li>transitioning from <code>required</code> to <code>optional</code></li>
1641-
<li>removing a <code>required</code> field</li>
1641+
<li>safe evolution from <code>optional</code> to <code>required</code></li>
1642+
<li>safe evolution <code>required</code> to <code>optional</code></li>
1643+
<li>safe removal of a <code>required</code> field</li>
16421644
</ul>
16431645

1646+
<p>
1647+
Before moving on, I think it's worth looking at what <code>asymmetric</code> does to the generated Rust code:
1648+
</p>
1649+
16441650

16451651
<pre data-lang="text"><code>struct SendEmailRequest {
16461652
to: String = 0
@@ -1650,7 +1656,6 @@ <h4><a id="constructor-required-deserialization-optional"
16501656
}</code></pre>
16511657

16521658

1653-
16541659
<pre data-lang="rust"><code>pub struct SendEmailRequestOut {
16551660
pub to: String,
16561661
pub from: String,
@@ -1678,7 +1683,14 @@ <h4><a id="constructor-required-deserialization-optional"
16781683
}
16791684
}</code></pre>
16801685

1686+
<!-- TODO: explain generated code-->
1687+
<!-- TODO: get impl for the deserializer-->
1688+
1689+
<p>Porting this behaviour to <code>proto2</code> is not trivial.</p>
16811690

1691+
<p>
1692+
It is simple
1693+
</p>
16821694
<pre><code>import "google/protobuf/descriptor.proto";
16831695

16841696
extend google.protobuf.FieldOptions {

0 commit comments

Comments
 (0)