Free AI web copilot to create summaries, insights and extended knowledge, download it at here
2797
Abstract
is long forgotten, and <code>format</code> gets called with a value that was not validated first</li><li>a new business requirement arises which requires different “levels” of validation. Again, <code>format</code> might expect those parts to be there from the beginning, and gets called with input that was validated in a different way than it expects.</li></ul><p id="3e74">A specific example of the latter case might be <code>CalculationResult = ClientData</code>, where we're validating that we have all the client's data. When we're first creating a client, we might only require a name and an e-mail, but once we’re at the point where we’re e.g. signing a contract, we also need an address and bank account. So we modify the <code>valid</code> function to only check the appropriate parts of <code>ClientData</code> based on the context and think we're done. However, we don't realize that <code>format</code> expects all the values to be there, and get a runtime error — the <a href="https://readmedium.com/preface-a65cb535d122#2662">worst kind of error</a>.</p><p id="2e81">Another problem is that we don’t know which “outputs” can actually be generated by this code. If everything goes well, we can see clearly what happens, but what if the result is not valid? An exception gets thrown, and that gets handled in one of the callers, i.e. someplace else. It’s not clear where that place is, how we get there from here (there are usually multiple callers) and what happens there. Things get even more complicated when using things like <a href="https://www.baeldung.com/exception-handling-for-rest-with-spring"><code>ControllerAdv>ice, Excepti</code>onHandler</a> and similar constructs (which is often the case in the real world). To be sure of what happens what an exception gets thrown, one must backtrack through all possible execution paths from the point the exception gets thrown, which is not a feasible approach. In other words, exceptions break <b>local reasoning</b>.</p><p id="3dae">In total, this means that there are (at least) two different places where responses get produced, which means (at least) two different places we need to be aware of, manage, maintain and keep in sync when making changes. Worst of all, we need to do all this manually — the compiler will not let us know if we change one, but forget to change the other.</p><p id="a998">It turns out that these problems have a solution. <b>The core idea is to represent <i>everything</i> (including error states) as data types</b>:</p>
<figure id="075a">
<div>
<div>
<img class="ratio" src="http://placehold.it/16x9">
<iframe class="" src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fpl.kotl.in%2F-4ioREB4U&display_name=Ko
Options
tlin+Playground&url=https%3A%2F%2Fpl.kotl.in%2F-4ioREB4U&image=https%3A%2F%2Fplay.kotlinlang.org%2Fassets%2Fog-image.png&key=a19fcc184b9711e1b4764040d3dc5c07&type=text%2Fhtml&schema=kotl" allowfullscreen="" frameborder="0" height="300" width="800">
</div>
</div>
</figure></iframe></div></div></figure><p id="1e38">Even in code as simple as this, this approach leads to a markedly better results.</p><p id="eb3d">The key benefits are:</p><ul><li>Illegal states (in fact all states) are explicit, and represented by types.</li><li>Function signatures <a href="https://readmedium.com/preface-a65cb535d122#39fa">communicate and enforce assumptions</a> (<code>format</code> requires its input to be validated first, and the type denotes the manner in which the validation is performed — you can differentiate between e.g. <code>PartialValidationResult</code> and <code>FullValidationResult</code>)</li><li>The data flow is completely linear. There are no branches, jumps, no catch blocks, no special situations, it’s just <code>calculateValue -> validate -> format -> sendResponse</code></li><li>The ability to reason locally is recovered. Formatting of all data is done in one place, for all scenarios. Again, no special situations, no alternative ways a response can get sent, no <code>ExceptionHandler</code>s, no <code>ControllerAdvice</code> etc.</li><li>Since we use sealed classes, whenever we add a new state (e.g. a <code>PartialValidationResult</code>), we are immediately told which parts of the code we need to adapt. We’ve completely removed a whole category of errors. Again.</li></ul><p id="6154">For more on this, I highly recommend reading <a href="https://fsharpforfunandprofit.com/posts/designing-with-types-making-illegal-states-unrepresentable/">this great article</a>. In fact, you should go ahead and read <a href="https://fsharpforfunandprofit.com/series/designing-with-types/">the entire series</a>, it will make you a better person.</p><p id="c99c">Go back to <a href="https://readmedium.com/sealed-hierarchies-preventing-runtime-errors-a72c4b0c295c">Safely Emulating Dynamic Dispatch</a>, jump to the <a href="https://readmedium.com/table-of-contents-c52573cfa291">Table of Contents</a>, or continue to <a href="https://readmedium.com/sealed-hierarchies-strongly-typed-domain-modeling-c8e2731ea181">Modeling States and Structure</a>.</p><figure id="8ecd"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*biBSB579iezsNvEQ_NMLBg.png"><figcaption><a href="https://www.etnetera.cz/prace-u-nas?utm_source=medium&utm_medium=GabrielShanahan&utm_campaign=KotlinPrimer&utm_content=join-our-team&utm_term=KotlinPrimer#pozice">Join me in Etnetera</a></figcaption></figure></article></body>