<h2 id="why-exceptions-are-like-dynamic-monads">Why exceptions are like Dynamic Monads</h2> <!-- .slide: data-background-image="./burrito.svg" data-background-opacity="0.2" --> <!--n--> <p>A.K.A.: Why the point of return is a function input</p> <p>I want to build a sort of mental model, and show why nonlocal exits aren't as bad for some applications as they are made out to be.</p> <p>In some sense this headline is terrible anti-clickbait. Everything I will be talking about are:</p> <!--s--> <h2 id="functions-enums-structs">Functions + Enums + Structs</h2> <!-- .element: class="r-fit-text" --> <!--s--> <h2 id="prerequisites">Prerequisites</h2> <ul> <li>Exceptions</li> <li>Algebraic Data Types <ul> <li>Pattern Matching</li> <li>Preferably the concept of mapping an Optional</li> </ul> </li> <li>Syntax used: <ul> <li>Rust Pseudocode</li> <li>very basic Haskell</li> </ul> </li> </ul> <!--s--> <h1 id="charting-out-control-flow">Charting out control flow</h1> <!--n--> <p>A simple operation, the humble division. One of many functions in a program that might fail, let us isolate it:</p> <!--s--> <svg aria-roledescription="flowchart-v2" role="graphics-document document" viewBox="0 0 467.859375 174" height="174" class="flowchart" xmlns="http://www.w3.org/2000/svg" width="467.859375" id="container"><style>#container{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#000000;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#container .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#container .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#container .error-icon{fill:#552222;}#container .error-text{fill:#552222;stroke:#552222;}#container .edge-thickness-normal{stroke-width:1px;}#container .edge-thickness-thick{stroke-width:3.5px;}#container .edge-pattern-solid{stroke-dasharray:0;}#container .edge-thickness-invisible{stroke-width:0;fill:none;}#container .edge-pattern-dashed{stroke-dasharray:3;}#container .edge-pattern-dotted{stroke-dasharray:2;}#container .marker{fill:#666;stroke:#666;}#container .marker.cross{stroke:#666;}#container svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#container p{margin:0;}#container .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#000000;}#container .cluster-label text{fill:#333;}#container .cluster-label span{color:#333;}#container .cluster-label span p{background-color:transparent;}#container .label text,#container span{fill:#000000;color:#000000;}#container .node rect,#container .node circle,#container .node ellipse,#container .node polygon,#container .node path{fill:#eee;stroke:#999;stroke-width:1px;}#container .rough-node .label text,#container .node .label text,#container .image-shape .label,#container .icon-shape .label{text-anchor:middle;}#container .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#container .rough-node .label,#container .node .label,#container .image-shape .label,#container .icon-shape .label{text-align:center;}#container .node.clickable{cursor:pointer;}#container .root .anchor path{fill:#666!important;stroke-width:0;stroke:#666;}#container .arrowheadPath{fill:#333333;}#container .edgePath .path{stroke:#666;stroke-width:2.0px;}#container .flowchart-link{stroke:#666;fill:none;}#container .edgeLabel{background-color:white;text-align:center;}#container .edgeLabel p{background-color:white;}#container .edgeLabel rect{opacity:0.5;background-color:white;fill:white;}#container .labelBkg{background-color:rgba(255, 255, 255, 0.5);}#container .cluster rect{fill:hsl(0, 0%, 98.9215686275%);stroke:#707070;stroke-width:1px;}#container .cluster text{fill:#333;}#container .cluster span{color:#333;}#container div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(-160, 0%, 93.3333333333%);border:1px solid #707070;border-radius:2px;pointer-events:none;z-index:100;}#container .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#000000;}#container rect.text{fill:none;stroke-width:0;}#container .icon-shape,#container .image-shape{background-color:white;text-align:center;}#container .icon-shape p,#container .image-shape p{background-color:white;padding:2px;}#container .icon-shape rect,#container .image-shape rect{opacity:0.5;background-color:white;fill:white;}#container :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}</style><g><marker orient="auto" markerHeight="8" markerWidth="8" markerUnits="userSpaceOnUse" refY="5" refX="5" viewBox="0 0 10 10" class="marker flowchart-v2" id="container_flowchart-v2-pointEnd"><path style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 0 0 L 10 5 L 0 10 z"></path></marker><marker orient="auto" markerHeight="8" markerWidth="8" markerUnits="userSpaceOnUse" refY="5" refX="4.5" viewBox="0 0 10 10" class="marker flowchart-v2" id="container_flowchart-v2-pointStart"><path style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 0 5 L 10 10 L 10 0 z"></path></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5" refX="11" viewBox="0 0 10 10" class="marker flowchart-v2" id="container_flowchart-v2-circleEnd"><circle style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" r="5" cy="5" cx="5"></circle></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5" refX="-1" viewBox="0 0 10 10" class="marker flowchart-v2" id="container_flowchart-v2-circleStart"><circle style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" r="5" cy="5" cx="5"></circle></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5.2" refX="12" viewBox="0 0 11 11" class="marker cross flowchart-v2" id="container_flowchart-v2-crossEnd"><path style="stroke-width: 2; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 1,1 l 9,9 M 10,1 l -9,9"></path></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5.2" refX="-1" viewBox="0 0 11 11" class="marker cross flowchart-v2" id="container_flowchart-v2-crossStart"><path style="stroke-width: 2; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 1,1 l 9,9 M 10,1 l -9,9"></path></marker><g class="root"><g class="clusters"></g><g class="edgePaths"><path marker-end="url(#container_flowchart-v2-pointEnd)" style="" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" id="L_Input_D_0" d="M106.703,87L110.87,87C115.036,87,123.37,87,131.036,87C138.703,87,145.703,87,149.203,87L152.703,87"></path><path marker-end="url(#container_flowchart-v2-pointEnd)" style="" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" id="L_D_O_0" d="M259.247,60L265.974,55.833C272.701,51.667,286.155,43.333,296.382,39.167C306.609,35,313.609,35,317.109,35L320.609,35"></path><path marker-end="url(#container_flowchart-v2-pointEnd)" style="" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" id="L_D_E_0" d="M259.247,114L265.974,118.167C272.701,122.333,286.155,130.667,296.463,134.833C306.771,139,313.932,139,317.513,139L321.094,139"></path></g><g class="edgeLabels"><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" class="labelBkg" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" class="labelBkg" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" class="labelBkg" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g></g><g class="nodes"><g transform="translate(57.3515625, 87)" id="flowchart-Input-0" class="node default"><rect height="54" width="98.703125" y="-27" x="-49.3515625" style="" class="basic label-container"></rect><g transform="translate(-19.3515625, -12)" style="" class="label"><rect></rect><foreignObject height="24" width="38.703125"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>Input</p></span></div></foreignObject></g></g><g transform="translate(215.65625, 87)" id="flowchart-D-1" class="node default"><rect height="54" width="117.90625" y="-27" x="-58.953125" style="" class="basic label-container"></rect><g transform="translate(-28.953125, -12)" style="" class="label"><rect></rect><foreignObject height="24" width="57.90625"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>Division</p></span></div></foreignObject></g></g><g transform="translate(392.234375, 35)" id="flowchart-O-3" class="node default"><rect height="54" width="135.25" y="-27" x="-67.625" style="" class="basic label-container"></rect><g transform="translate(-37.625, -12)" style="" class="label"><rect></rect><foreignObject height="24" width="75.25"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>Ok + Value</p></span></div></foreignObject></g></g><g transform="translate(392.234375, 139)" id="flowchart-E-5" class="node default"><rect height="54" width="134.28125" y="-27" x="-67.140625" style="" class="basic label-container"></rect><g transform="translate(-37.140625, -12)" style="" class="label"><rect></rect><foreignObject height="24" width="74.28125"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>Error + Ctx</p></span></div></foreignObject></g></g></g></g></g></svg> <!--s--> <p><img src="/posts/lang-talks/18-cps/./tracks-1.png" alt="Or using the track analogy" /></p> <p>[src: <a href="https://blog.logrocket.com/javascript-either-monad-error-handling/">LogRocket</a>]</p> <!--n--> <p>Let's look at a program that divides 4 numbers and its possible flow:</p> <!--s--> <svg aria-roledescription="flowchart-v2" role="graphics-document document" viewBox="0 0 819.078125 302" height="302" class="flowchart" xmlns="http://www.w3.org/2000/svg" width="819.078125" id="container"><style>#container{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#000000;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#container .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#container .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#container .error-icon{fill:#552222;}#container .error-text{fill:#552222;stroke:#552222;}#container .edge-thickness-normal{stroke-width:1px;}#container .edge-thickness-thick{stroke-width:3.5px;}#container .edge-pattern-solid{stroke-dasharray:0;}#container .edge-thickness-invisible{stroke-width:0;fill:none;}#container .edge-pattern-dashed{stroke-dasharray:3;}#container .edge-pattern-dotted{stroke-dasharray:2;}#container .marker{fill:#666;stroke:#666;}#container .marker.cross{stroke:#666;}#container svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#container p{margin:0;}#container .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#000000;}#container .cluster-label text{fill:#333;}#container .cluster-label span{color:#333;}#container .cluster-label span p{background-color:transparent;}#container .label text,#container span{fill:#000000;color:#000000;}#container .node rect,#container .node circle,#container .node ellipse,#container .node polygon,#container .node path{fill:#eee;stroke:#999;stroke-width:1px;}#container .rough-node .label text,#container .node .label text,#container .image-shape .label,#container .icon-shape .label{text-anchor:middle;}#container .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#container .rough-node .label,#container .node .label,#container .image-shape .label,#container .icon-shape .label{text-align:center;}#container .node.clickable{cursor:pointer;}#container .root .anchor path{fill:#666!important;stroke-width:0;stroke:#666;}#container .arrowheadPath{fill:#333333;}#container .edgePath .path{stroke:#666;stroke-width:2.0px;}#container .flowchart-link{stroke:#666;fill:none;}#container .edgeLabel{background-color:white;text-align:center;}#container .edgeLabel p{background-color:white;}#container .edgeLabel rect{opacity:0.5;background-color:white;fill:white;}#container .labelBkg{background-color:rgba(255, 255, 255, 0.5);}#container .cluster rect{fill:hsl(0, 0%, 98.9215686275%);stroke:#707070;stroke-width:1px;}#container .cluster text{fill:#333;}#container .cluster span{color:#333;}#container div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(-160, 0%, 93.3333333333%);border:1px solid #707070;border-radius:2px;pointer-events:none;z-index:100;}#container .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#000000;}#container rect.text{fill:none;stroke-width:0;}#container .icon-shape,#container .image-shape{background-color:white;text-align:center;}#container .icon-shape p,#container .image-shape p{background-color:white;padding:2px;}#container .icon-shape rect,#container .image-shape rect{opacity:0.5;background-color:white;fill:white;}#container :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#container .err>*{color:red!important;}#container .err span{color:red!important;}#container .err tspan{fill:red!important;}</style><g><marker orient="auto" markerHeight="8" markerWidth="8" markerUnits="userSpaceOnUse" refY="5" refX="5" viewBox="0 0 10 10" class="marker flowchart-v2" id="container_flowchart-v2-pointEnd"><path style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 0 0 L 10 5 L 0 10 z"></path></marker><marker orient="auto" markerHeight="8" markerWidth="8" markerUnits="userSpaceOnUse" refY="5" refX="4.5" viewBox="0 0 10 10" class="marker flowchart-v2" id="container_flowchart-v2-pointStart"><path style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 0 5 L 10 10 L 10 0 z"></path></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5" refX="11" viewBox="0 0 10 10" class="marker flowchart-v2" id="container_flowchart-v2-circleEnd"><circle style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" r="5" cy="5" cx="5"></circle></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5" refX="-1" viewBox="0 0 10 10" class="marker flowchart-v2" id="container_flowchart-v2-circleStart"><circle style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" r="5" cy="5" cx="5"></circle></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5.2" refX="12" viewBox="0 0 11 11" class="marker cross flowchart-v2" id="container_flowchart-v2-crossEnd"><path style="stroke-width: 2; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 1,1 l 9,9 M 10,1 l -9,9"></path></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5.2" refX="-1" viewBox="0 0 11 11" class="marker cross flowchart-v2" id="container_flowchart-v2-crossStart"><path style="stroke-width: 2; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 1,1 l 9,9 M 10,1 l -9,9"></path></marker><g class="root"><g class="clusters"></g><g class="edgePaths"><path marker-end="url(#container_flowchart-v2-pointEnd)" style="" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" id="L_Input_D1_0" d="M106.703,87L110.87,87C115.036,87,123.37,87,131.036,87C138.703,87,145.703,87,149.203,87L152.703,87"></path><path marker-end="url(#container_flowchart-v2-pointEnd)" style="" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" id="L_D1_E1_0" d="M259.247,60L265.974,55.833C272.701,51.667,286.155,43.333,296.382,39.167C306.609,35,313.609,35,317.109,35L320.609,35"></path><path marker-end="url(#container_flowchart-v2-pointEnd)" style="" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" id="L_D1_D2_0" d="M259.247,114L265.974,118.167C272.701,122.333,286.155,130.667,297.747,134.833C309.339,139,319.068,139,323.932,139L328.797,139"></path><path marker-end="url(#container_flowchart-v2-pointEnd)" style="" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" id="L_D2_E2_0" d="M434.643,112L442.851,106.833C451.059,101.667,467.475,91.333,480.006,86.167C492.536,81,501.182,81,505.505,81L509.828,81"></path><path marker-end="url(#container_flowchart-v2-pointEnd)" style="" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" id="L_D2_D3_0" d="M434.643,166L442.851,171.167C451.059,176.333,467.475,186.667,479.183,191.833C490.891,197,497.891,197,501.391,197L504.891,197"></path><path marker-end="url(#container_flowchart-v2-pointEnd)" style="" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" id="L_D3_E3_0" d="M606.925,170L614.404,164.833C621.883,159.667,636.84,149.333,647.818,144.167C658.797,139,665.797,139,669.297,139L672.797,139"></path><path marker-end="url(#container_flowchart-v2-pointEnd)" style="" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" id="L_D3_O1_0" d="M606.925,224L614.404,229.167C621.883,234.333,636.84,244.667,650.223,249.833C663.607,255,675.417,255,681.322,255L687.227,255"></path></g><g class="edgeLabels"><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" class="labelBkg" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" class="labelBkg" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" class="labelBkg" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" class="labelBkg" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" class="labelBkg" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" class="labelBkg" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" class="labelBkg" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g></g><g class="nodes"><g transform="translate(57.3515625, 87)" id="flowchart-Input-0" class="node default"><rect height="54" width="98.703125" y="-27" x="-49.3515625" style="" class="basic label-container"></rect><g transform="translate(-19.3515625, -12)" style="" class="label"><rect></rect><foreignObject height="24" width="38.703125"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>Input</p></span></div></foreignObject></g></g><g transform="translate(215.65625, 87)" id="flowchart-D1-1" class="node default"><rect height="54" width="117.90625" y="-27" x="-58.953125" style="" class="basic label-container"></rect><g transform="translate(-28.953125, -12)" style="" class="label"><rect></rect><foreignObject height="24" width="57.90625"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>Division</p></span></div></foreignObject></g></g><g transform="translate(391.75, 35)" id="flowchart-E1-3" class="node default err"><rect height="54" width="134.28125" y="-27" x="-67.140625" style="" class="basic label-container"></rect><g transform="translate(-37.140625, -12)" style="color:red !important" class="label"><rect></rect><foreignObject height="24" width="74.28125"><div xmlns="http://www.w3.org/1999/xhtml" style="color: red !important; display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="nodeLabel" style="color:red !important"><p>Error + Ctx</p></span></div></foreignObject></g></g><g transform="translate(391.75, 139)" id="flowchart-D2-5" class="node default"><rect height="54" width="117.90625" y="-27" x="-58.953125" style="" class="basic label-container"></rect><g transform="translate(-28.953125, -12)" style="" class="label"><rect></rect><foreignObject height="24" width="57.90625"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>Division</p></span></div></foreignObject></g></g><g transform="translate(567.84375, 81)" id="flowchart-E2-7" class="node default err"><rect height="78" width="108.03125" y="-39" x="-54.015625" style="" class="basic label-container"></rect><g transform="translate(-24.015625, -24)" style="color:red !important" class="label"><rect></rect><foreignObject height="48" width="48.03125"><div xmlns="http://www.w3.org/1999/xhtml" style="color: red !important; display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="nodeLabel" style="color:red !important"><p>Error +<br/>Ctx</p></span></div></foreignObject></g></g><g transform="translate(567.84375, 197)" id="flowchart-D3-9" class="node default"><rect height="54" width="117.90625" y="-27" x="-58.953125" style="" class="basic label-container"></rect><g transform="translate(-28.953125, -12)" style="" class="label"><rect></rect><foreignObject height="24" width="57.90625"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>Division</p></span></div></foreignObject></g></g><g transform="translate(743.9375, 139)" id="flowchart-E3-11" class="node default err"><rect height="54" width="134.28125" y="-27" x="-67.140625" style="" class="basic label-container"></rect><g transform="translate(-37.140625, -12)" style="color:red !important" class="label"><rect></rect><foreignObject height="24" width="74.28125"><div xmlns="http://www.w3.org/1999/xhtml" style="color: red !important; display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="nodeLabel" style="color:red !important"><p>Error + Ctx</p></span></div></foreignObject></g></g><g transform="translate(743.9375, 255)" id="flowchart-O1-13" class="node default"><rect height="78" width="105.421875" y="-39" x="-52.7109375" style="" class="basic label-container"></rect><g transform="translate(-22.7109375, -24)" style="color:green !important" class="label"><rect></rect><foreignObject height="48" width="45.421875"><div xmlns="http://www.w3.org/1999/xhtml" style="color: green !important; display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="nodeLabel" style="color:green !important"><p>Ok +<br/>Result</p></span></div></foreignObject></g></g></g></g></g></svg> <!--s--> <p><img src="/posts/lang-talks/18-cps/./tracks-2.png" alt="Composition in the analogy of tracks" /></p> <p>[src: <a href="https://blog.logrocket.com/javascript-either-monad-error-handling/%5D">LogRocket</a>]</p> <!--n--> <p>There's a question of whether we can merge into a single bad path, and we'll get back to that.</p> <p>So then how would we write this in a language?</p> <!--s--> <!-- .slide: data-auto-animate --> <pre><code class="language-rust" data-line-numbers>fn division(lhs, rhs) { lhs / rhs } </code></pre> <!-- .element: data-id="div" --> <!--n--> <p>But what if rhs is 0?</p> <!--s--> <!-- .slide: data-auto-animate --> <pre><code data-line-numbers="2-3|3" class="language-rust">fn division(lhs, rhs) { if rhs == 0 { // ... don't return? } else { lhs / rhs } } </code></pre> <!-- .element: data-id="div" --> <!--n--> <p>Huh... we can't really return here, since what had called us probably expects a number which if I provide me catching that issue is kind of pointless since I'd act as if nothing was wrong anyway.</p> <!--s--> <svg aria-roledescription="flowchart-v2" role="graphics-document document" viewBox="0 0 536.625 94" height="94" class="flowchart" xmlns="http://www.w3.org/2000/svg" width="536.625" id="container"><style>#container{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#000000;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#container .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#container .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#container .error-icon{fill:#552222;}#container .error-text{fill:#552222;stroke:#552222;}#container .edge-thickness-normal{stroke-width:1px;}#container .edge-thickness-thick{stroke-width:3.5px;}#container .edge-pattern-solid{stroke-dasharray:0;}#container .edge-thickness-invisible{stroke-width:0;fill:none;}#container .edge-pattern-dashed{stroke-dasharray:3;}#container .edge-pattern-dotted{stroke-dasharray:2;}#container .marker{fill:#666;stroke:#666;}#container .marker.cross{stroke:#666;}#container svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#container p{margin:0;}#container .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#000000;}#container .cluster-label text{fill:#333;}#container .cluster-label span{color:#333;}#container .cluster-label span p{background-color:transparent;}#container .label text,#container span{fill:#000000;color:#000000;}#container .node rect,#container .node circle,#container .node ellipse,#container .node polygon,#container .node path{fill:#eee;stroke:#999;stroke-width:1px;}#container .rough-node .label text,#container .node .label text,#container .image-shape .label,#container .icon-shape .label{text-anchor:middle;}#container .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#container .rough-node .label,#container .node .label,#container .image-shape .label,#container .icon-shape .label{text-align:center;}#container .node.clickable{cursor:pointer;}#container .root .anchor path{fill:#666!important;stroke-width:0;stroke:#666;}#container .arrowheadPath{fill:#333333;}#container .edgePath .path{stroke:#666;stroke-width:2.0px;}#container .flowchart-link{stroke:#666;fill:none;}#container .edgeLabel{background-color:white;text-align:center;}#container .edgeLabel p{background-color:white;}#container .edgeLabel rect{opacity:0.5;background-color:white;fill:white;}#container .labelBkg{background-color:rgba(255, 255, 255, 0.5);}#container .cluster rect{fill:hsl(0, 0%, 98.9215686275%);stroke:#707070;stroke-width:1px;}#container .cluster text{fill:#333;}#container .cluster span{color:#333;}#container div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(-160, 0%, 93.3333333333%);border:1px solid #707070;border-radius:2px;pointer-events:none;z-index:100;}#container .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#000000;}#container rect.text{fill:none;stroke-width:0;}#container .icon-shape,#container .image-shape{background-color:white;text-align:center;}#container .icon-shape p,#container .image-shape p{background-color:white;padding:2px;}#container .icon-shape rect,#container .image-shape rect{opacity:0.5;background-color:white;fill:white;}#container :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}</style><g><marker orient="auto" markerHeight="8" markerWidth="8" markerUnits="userSpaceOnUse" refY="5" refX="5" viewBox="0 0 10 10" class="marker flowchart-v2" id="container_flowchart-v2-pointEnd"><path style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 0 0 L 10 5 L 0 10 z"></path></marker><marker orient="auto" markerHeight="8" markerWidth="8" markerUnits="userSpaceOnUse" refY="5" refX="4.5" viewBox="0 0 10 10" class="marker flowchart-v2" id="container_flowchart-v2-pointStart"><path style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 0 5 L 10 10 L 10 0 z"></path></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5" refX="11" viewBox="0 0 10 10" class="marker flowchart-v2" id="container_flowchart-v2-circleEnd"><circle style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" r="5" cy="5" cx="5"></circle></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5" refX="-1" viewBox="0 0 10 10" class="marker flowchart-v2" id="container_flowchart-v2-circleStart"><circle style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" r="5" cy="5" cx="5"></circle></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5.2" refX="12" viewBox="0 0 11 11" class="marker cross flowchart-v2" id="container_flowchart-v2-crossEnd"><path style="stroke-width: 2; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 1,1 l 9,9 M 10,1 l -9,9"></path></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5.2" refX="-1" viewBox="0 0 11 11" class="marker cross flowchart-v2" id="container_flowchart-v2-crossStart"><path style="stroke-width: 2; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 1,1 l 9,9 M 10,1 l -9,9"></path></marker><g class="root"><g class="clusters"></g><g class="edgePaths"><path marker-end="url(#container_flowchart-v2-pointEnd)" style="" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" id="L_Input_Div_0" d="M106.703,47L110.87,47C115.036,47,123.37,47,131.036,47C138.703,47,145.703,47,149.203,47L152.703,47"></path><path marker-end="url(#container_flowchart-v2-pointEnd)" style="" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" id="L_Div_Out_0" d="M274.609,35.426L283.46,33.688C292.31,31.95,310.01,28.475,327.054,28.157C344.098,27.838,360.484,30.677,368.678,32.096L376.871,33.515"></path><path marker-end="url(#container_flowchart-v2-pointEnd)" style="" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" id="L_Div_Out_2" d="M274.609,58.574L283.46,60.312C292.31,62.05,310.01,65.525,327.054,65.843C344.098,66.162,360.484,63.323,368.678,61.904L376.871,60.485"></path></g><g class="edgeLabels"><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" class="labelBkg" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g transform="translate(327.7109375, 25)" class="edgeLabel"><g transform="translate(-28.1015625, -12)" class="label"><foreignObject height="24" width="56.203125"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" class="labelBkg" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"><p>Success</p></span></div></foreignObject></g></g><g transform="translate(327.7109375, 69)" class="edgeLabel"><g transform="translate(-12.40625, -12)" class="label"><foreignObject height="24" width="24.8125"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" class="labelBkg" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"><p>Fail</p></span></div></foreignObject></g></g></g><g class="nodes"><g transform="translate(57.3515625, 47)" id="flowchart-Input-0" class="node default"><rect height="54" width="98.703125" y="-27" x="-49.3515625" style="" class="basic label-container"></rect><g transform="translate(-19.3515625, -12)" style="" class="label"><rect></rect><foreignObject height="24" width="38.703125"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>Input</p></span></div></foreignObject></g></g><g transform="translate(215.65625, 47)" id="flowchart-Div-1" class="node default"><rect height="54" width="117.90625" y="-27" x="-58.953125" style="" class="basic label-container"></rect><g transform="translate(-28.953125, -12)" style="" class="label"><rect></rect><foreignObject height="24" width="57.90625"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>Division</p></span></div></foreignObject></g></g><g transform="translate(454.71875, 47)" id="flowchart-Out-2" class="node default"><rect height="78" width="147.8125" y="-39" x="-73.90625" style="" class="basic label-container"></rect><g transform="translate(-43.90625, -24)" style="" class="label"><rect></rect><foreignObject height="48" width="87.8125"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>Ok + Value /<br/>Error</p></span></div></foreignObject></g></g></g></g></g></svg> <!--s--> <h2 id="monads">Monads</h2> <h3 id="algebraic-types">Algebraic Types</h3> <!--n--> <p>Here some languages choose the approach to still have the outputs connected into one, but add some structural type on top to differentiate the two possible outcomes:</p> <!--s--> <svg aria-roledescription="flowchart-v2" role="graphics-document document" viewBox="0 0 564.4375 94" height="94" class="flowchart" xmlns="http://www.w3.org/2000/svg" width="564.4375" id="container"><style>#container{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#000000;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#container .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#container .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#container .error-icon{fill:#552222;}#container .error-text{fill:#552222;stroke:#552222;}#container .edge-thickness-normal{stroke-width:1px;}#container .edge-thickness-thick{stroke-width:3.5px;}#container .edge-pattern-solid{stroke-dasharray:0;}#container .edge-thickness-invisible{stroke-width:0;fill:none;}#container .edge-pattern-dashed{stroke-dasharray:3;}#container .edge-pattern-dotted{stroke-dasharray:2;}#container .marker{fill:#666;stroke:#666;}#container .marker.cross{stroke:#666;}#container svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#container p{margin:0;}#container .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#000000;}#container .cluster-label text{fill:#333;}#container .cluster-label span{color:#333;}#container .cluster-label span p{background-color:transparent;}#container .label text,#container span{fill:#000000;color:#000000;}#container .node rect,#container .node circle,#container .node ellipse,#container .node polygon,#container .node path{fill:#eee;stroke:#999;stroke-width:1px;}#container .rough-node .label text,#container .node .label text,#container .image-shape .label,#container .icon-shape .label{text-anchor:middle;}#container .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#container .rough-node .label,#container .node .label,#container .image-shape .label,#container .icon-shape .label{text-align:center;}#container .node.clickable{cursor:pointer;}#container .root .anchor path{fill:#666!important;stroke-width:0;stroke:#666;}#container .arrowheadPath{fill:#333333;}#container .edgePath .path{stroke:#666;stroke-width:2.0px;}#container .flowchart-link{stroke:#666;fill:none;}#container .edgeLabel{background-color:white;text-align:center;}#container .edgeLabel p{background-color:white;}#container .edgeLabel rect{opacity:0.5;background-color:white;fill:white;}#container .labelBkg{background-color:rgba(255, 255, 255, 0.5);}#container .cluster rect{fill:hsl(0, 0%, 98.9215686275%);stroke:#707070;stroke-width:1px;}#container .cluster text{fill:#333;}#container .cluster span{color:#333;}#container div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(-160, 0%, 93.3333333333%);border:1px solid #707070;border-radius:2px;pointer-events:none;z-index:100;}#container .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#000000;}#container rect.text{fill:none;stroke-width:0;}#container .icon-shape,#container .image-shape{background-color:white;text-align:center;}#container .icon-shape p,#container .image-shape p{background-color:white;padding:2px;}#container .icon-shape rect,#container .image-shape rect{opacity:0.5;background-color:white;fill:white;}#container :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}</style><g><marker orient="auto" markerHeight="8" markerWidth="8" markerUnits="userSpaceOnUse" refY="5" refX="5" viewBox="0 0 10 10" class="marker flowchart-v2" id="container_flowchart-v2-pointEnd"><path style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 0 0 L 10 5 L 0 10 z"></path></marker><marker orient="auto" markerHeight="8" markerWidth="8" markerUnits="userSpaceOnUse" refY="5" refX="4.5" viewBox="0 0 10 10" class="marker flowchart-v2" id="container_flowchart-v2-pointStart"><path style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 0 5 L 10 10 L 10 0 z"></path></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5" refX="11" viewBox="0 0 10 10" class="marker flowchart-v2" id="container_flowchart-v2-circleEnd"><circle style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" r="5" cy="5" cx="5"></circle></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5" refX="-1" viewBox="0 0 10 10" class="marker flowchart-v2" id="container_flowchart-v2-circleStart"><circle style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" r="5" cy="5" cx="5"></circle></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5.2" refX="12" viewBox="0 0 11 11" class="marker cross flowchart-v2" id="container_flowchart-v2-crossEnd"><path style="stroke-width: 2; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 1,1 l 9,9 M 10,1 l -9,9"></path></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5.2" refX="-1" viewBox="0 0 11 11" class="marker cross flowchart-v2" id="container_flowchart-v2-crossStart"><path style="stroke-width: 2; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 1,1 l 9,9 M 10,1 l -9,9"></path></marker><g class="root"><g class="clusters"></g><g class="edgePaths"><path marker-end="url(#container_flowchart-v2-pointEnd)" style="" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" id="L_Input_Div_0" d="M106.703,47L110.87,47C115.036,47,123.37,47,131.036,47C138.703,47,145.703,47,149.203,47L152.703,47"></path><path marker-end="url(#container_flowchart-v2-pointEnd)" style="" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" id="L_Div_Out_0" d="M274.609,35.426L283.46,33.688C292.31,31.95,310.01,28.475,327.052,28.016C344.094,27.558,360.477,30.116,368.669,31.394L376.86,32.673"></path><path marker-end="url(#container_flowchart-v2-pointEnd)" style="" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" id="L_Div_Out_2" d="M274.609,58.574L283.46,60.312C292.31,62.05,310.01,65.525,327.052,65.984C344.094,66.442,360.477,63.884,368.669,62.606L376.86,61.327"></path></g><g class="edgeLabels"><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" class="labelBkg" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g transform="translate(327.7109375, 25)" class="edgeLabel"><g transform="translate(-28.1015625, -12)" class="label"><foreignObject height="24" width="56.203125"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" class="labelBkg" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"><p>Success</p></span></div></foreignObject></g></g><g transform="translate(327.7109375, 69)" class="edgeLabel"><g transform="translate(-12.40625, -12)" class="label"><foreignObject height="24" width="24.8125"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" class="labelBkg" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"><p>Fail</p></span></div></foreignObject></g></g></g><g class="nodes"><g transform="translate(57.3515625, 47)" id="flowchart-Input-0" class="node default"><rect height="54" width="98.703125" y="-27" x="-49.3515625" style="" class="basic label-container"></rect><g transform="translate(-19.3515625, -12)" style="" class="label"><rect></rect><foreignObject height="24" width="38.703125"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>Input</p></span></div></foreignObject></g></g><g transform="translate(215.65625, 47)" id="flowchart-Div-1" class="node default"><rect height="54" width="117.90625" y="-27" x="-58.953125" style="" class="basic label-container"></rect><g transform="translate(-28.953125, -12)" style="" class="label"><rect></rect><foreignObject height="24" width="57.90625"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>Division</p></span></div></foreignObject></g></g><g transform="translate(468.625, 47)" id="flowchart-Out-2" class="node default"><rect height="78" width="175.625" y="-39" x="-87.8125" style="" class="basic label-container"></rect><g transform="translate(-57.8125, -24)" style="" class="label"><rect></rect><foreignObject height="48" width="115.625"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>Ok Value / Error<br/>Value</p></span></div></foreignObject></g></g></g></g></g></svg> <!--n--> <p>I.E.:</p> <!--s--> <pre><code class="language-rust" data-line-numbers="3,5">fn division(lhs, rhs) { if rhs == 0 { Err(()) } else { Ok(lhs / rhs) } } </code></pre> <!--n--> <p>This is fine, and nice, however to me this seems strange. Why am I returning to the same place in both instances even though one is what should normally happen and the other should jump to some error handling routine? Try to suspend your notion of Maybes and Options, that have been brought about by many popular languages, and only look at what we really want this to do and how to achieve it, without already looking at that answer.</p> <p>Let's look at what calling this function might look like:</p> <!--s--> <pre><code class="language-rust" data-line-numbers="2-5">fn foo(lhs, rhs) { match division(lhs, rhs) { Ok(n) => ..., Err(e) => ..., } } </code></pre> <!--n--> <h2 id="working-with-these-monads">Working with these Monads</h2> <!--s--> <pre><code class="language-rust" data-line-numbers="2,4">fn foo(lhs, rhs) { division(lhs, rhs).unwrap_or_else(|e| ...) # or division(lhs, rhs).map(|v| ...) } </code></pre> <!--n--> <p>That's kinda wordy, what about:</p> <p>Much nicer, for these cases of combining and working around these structures we've built up quite the arsenal of tools, which nonetheless doesn't change the fact that it is extra effort and the latter isn't always applicable when the return type doesn't only have one primary state and error states.</p> <p>On that note, they also sometimes don't compose:</p> <!--s--> <pre><code class="language-rust" data-line-numbers="|4,9">fn division1(lhs: u64, rhs: u64) -> Result<u64, ()> { lhs.checked_div(rhs)? + ... } # The `?` operator can only be used on `Result`s, not `Option`s, in a function that returns `Result` [E0277] fn division2(lhs: u64, rhs: u64) -> Option<u64> { division1(lhs, rhs)? + ... } # The `?` operator can only be used on `Option`s, not `Result`s, in a function that returns `Option` [E0277] </code></pre> <!--n--> <blockquote> <p>Another thing this would have trouble abstracting are recoverable errors, though Jirka Beneš did mention that there's approaches to bringing through debugging and backtrace info so it should be possible</p> </blockquote> <p>And imagine handling soundly some function the flow of which looks like this:</p> <!--s--> <svg aria-roledescription="flowchart-v2" role="graphics-document document" viewBox="0 0 675.65625 604" height="604" class="flowchart" xmlns="http://www.w3.org/2000/svg" width="675.65625" id="container"><style>#container{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#000000;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#container .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#container .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#container .error-icon{fill:#552222;}#container .error-text{fill:#552222;stroke:#552222;}#container .edge-thickness-normal{stroke-width:1px;}#container .edge-thickness-thick{stroke-width:3.5px;}#container .edge-pattern-solid{stroke-dasharray:0;}#container .edge-thickness-invisible{stroke-width:0;fill:none;}#container .edge-pattern-dashed{stroke-dasharray:3;}#container .edge-pattern-dotted{stroke-dasharray:2;}#container .marker{fill:#666;stroke:#666;}#container .marker.cross{stroke:#666;}#container svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#container p{margin:0;}#container .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#000000;}#container .cluster-label text{fill:#333;}#container .cluster-label span{color:#333;}#container .cluster-label span p{background-color:transparent;}#container .label text,#container span{fill:#000000;color:#000000;}#container .node rect,#container .node circle,#container .node ellipse,#container .node polygon,#container .node path{fill:#eee;stroke:#999;stroke-width:1px;}#container .rough-node .label text,#container .node .label text,#container .image-shape .label,#container .icon-shape .label{text-anchor:middle;}#container .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#container .rough-node .label,#container .node .label,#container .image-shape .label,#container .icon-shape .label{text-align:center;}#container .node.clickable{cursor:pointer;}#container .root .anchor path{fill:#666!important;stroke-width:0;stroke:#666;}#container .arrowheadPath{fill:#333333;}#container .edgePath .path{stroke:#666;stroke-width:2.0px;}#container .flowchart-link{stroke:#666;fill:none;}#container .edgeLabel{background-color:white;text-align:center;}#container .edgeLabel p{background-color:white;}#container .edgeLabel rect{opacity:0.5;background-color:white;fill:white;}#container .labelBkg{background-color:rgba(255, 255, 255, 0.5);}#container .cluster rect{fill:hsl(0, 0%, 98.9215686275%);stroke:#707070;stroke-width:1px;}#container .cluster text{fill:#333;}#container .cluster span{color:#333;}#container div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(-160, 0%, 93.3333333333%);border:1px solid #707070;border-radius:2px;pointer-events:none;z-index:100;}#container .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#000000;}#container rect.text{fill:none;stroke-width:0;}#container .icon-shape,#container .image-shape{background-color:white;text-align:center;}#container .icon-shape p,#container .image-shape p{background-color:white;padding:2px;}#container .icon-shape rect,#container .image-shape rect{opacity:0.5;background-color:white;fill:white;}#container :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}</style><g><marker orient="auto" markerHeight="8" markerWidth="8" markerUnits="userSpaceOnUse" refY="5" refX="5" viewBox="0 0 10 10" class="marker flowchart-v2" id="container_flowchart-v2-pointEnd"><path style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 0 0 L 10 5 L 0 10 z"></path></marker><marker orient="auto" markerHeight="8" markerWidth="8" markerUnits="userSpaceOnUse" refY="5" refX="4.5" viewBox="0 0 10 10" class="marker flowchart-v2" id="container_flowchart-v2-pointStart"><path style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 0 5 L 10 10 L 10 0 z"></path></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5" refX="11" viewBox="0 0 10 10" class="marker flowchart-v2" id="container_flowchart-v2-circleEnd"><circle style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" r="5" cy="5" cx="5"></circle></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5" refX="-1" viewBox="0 0 10 10" class="marker flowchart-v2" id="container_flowchart-v2-circleStart"><circle style="stroke-width: 1; stroke-dasharray: 1, 0;" class="arrowMarkerPath" r="5" cy="5" cx="5"></circle></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5.2" refX="12" viewBox="0 0 11 11" class="marker cross flowchart-v2" id="container_flowchart-v2-crossEnd"><path style="stroke-width: 2; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 1,1 l 9,9 M 10,1 l -9,9"></path></marker><marker orient="auto" markerHeight="11" markerWidth="11" markerUnits="userSpaceOnUse" refY="5.2" refX="-1" viewBox="0 0 11 11" class="marker cross flowchart-v2" id="container_flowchart-v2-crossStart"><path style="stroke-width: 2; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 1,1 l 9,9 M 10,1 l -9,9"></path></marker><g class="root"><g class="clusters"><g data-look="classic" id="subGraph1" class="cluster"><rect height="588" width="308.390625" y="8" x="359.265625" style=""></rect><g transform="translate(471.03125, 8)" class="cluster-label"><foreignObject height="24" width="84.859375"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>Error States</p></span></div></foreignObject></g></g><g data-look="classic" id="Inputs" class="cluster"><rect height="228" width="142.28125" y="188" x="8" style=""></rect><g transform="translate(56.0546875, 188)" class="cluster-label"><foreignObject height="24" width="46.171875"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>Inputs</p></span></div></foreignObject></g></g></g><g class="edgePaths"><path marker-end="url(#container_flowchart-v2-pointEnd)" style="" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" id="L_Fuel_R_0" d="M124.133,250L128.491,250C132.849,250,141.565,250,150.09,250C158.615,250,166.948,250,176.926,253.802C186.905,257.603,198.528,265.207,204.34,269.009L210.151,272.81"></path><path marker-end="url(#container_flowchart-v2-pointEnd)" style="" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" id="L_Path_R_0" d="M125.281,354L129.448,354C133.615,354,141.948,354,150.281,354C158.615,354,166.948,354,176.926,350.198C186.905,346.397,198.528,338.793,204.34,334.991L210.151,331.19"></path><path marker-end="url(#container_flowchart-v2-pointEnd)" style="" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" id="L_R_EG_0" d="M264.025,275L275.732,240.833C287.438,206.667,310.852,138.333,326.725,104.167C342.599,70,350.932,70,367.617,70C384.302,70,409.339,70,421.857,70L434.375,70"></path><path marker-end="url(#container_flowchart-v2-pointEnd)" style="" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" id="L_R_EF_0" d="M273.276,275L283.441,260.167C293.606,245.333,313.936,215.667,328.267,200.833C342.599,186,350.932,186,372.148,186C393.365,186,427.464,186,444.513,186L461.563,186"></path><path marker-end="url(#container_flowchart-v2-pointEnd)" style="" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" id="L_R_ES_0" d="M309.266,302L313.432,302C317.599,302,325.932,302,334.266,302C342.599,302,350.932,302,367.645,302C384.357,302,409.448,302,421.993,302L434.539,302"></path><path marker-end="url(#container_flowchart-v2-pointEnd)" style="" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" id="L_R_ESH_0" d="M275.411,329L285.22,341.833C295.029,354.667,314.647,380.333,328.623,393.167C342.599,406,350.932,406,358.599,406C366.266,406,373.266,406,376.766,406L380.266,406"></path><path marker-end="url(#container_flowchart-v2-pointEnd)" style="" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" id="L_R_EDS_0" d="M264.529,329L276.152,361.167C287.775,393.333,311.02,457.667,326.81,489.833C342.599,522,350.932,522,365.832,522C380.732,522,402.198,522,412.931,522L423.664,522"></path></g><g class="edgeLabels"><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" class="labelBkg" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" class="labelBkg" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" class="labelBkg" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" class="labelBkg" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" class="labelBkg" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" class="labelBkg" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g transform="translate(0, 0)" class="label"><foreignObject height="0" width="0"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" class="labelBkg" xmlns="http://www.w3.org/1999/xhtml"><span class="edgeLabel"></span></div></foreignObject></g></g></g><g class="nodes"><g transform="translate(254.7734375, 302)" id="flowchart-R-0" class="node default"><rect height="54" width="108.984375" y="-27" x="-54.4921875" style="" class="basic label-container"></rect><g transform="translate(-24.4921875, -12)" style="" class="label"><rect></rect><foreignObject height="24" width="48.984375"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>Rocket</p></span></div></foreignObject></g></g><g transform="translate(79.140625, 250)" id="flowchart-Fuel-1" class="node default"><rect height="54" width="89.984375" y="-27" x="-44.9921875" style="" class="basic label-container"></rect><g transform="translate(-14.9921875, -12)" style="" class="label"><rect></rect><foreignObject height="24" width="29.984375"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>Fuel</p></span></div></foreignObject></g></g><g transform="translate(79.140625, 354)" id="flowchart-Path-2" class="node default"><rect height="54" width="92.28125" y="-27" x="-46.140625" style="" class="basic label-container"></rect><g transform="translate(-16.140625, -12)" style="" class="label"><rect></rect><foreignObject height="24" width="32.28125"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>Path</p></span></div></foreignObject></g></g><g transform="translate(513.4609375, 70)" id="flowchart-EG-7" class="node default"><rect height="54" width="150.171875" y="-27" x="-75.0859375" style="" class="basic label-container"></rect><g transform="translate(-45.0859375, -12)" style="" class="label"><rect></rect><foreignObject height="24" width="90.171875"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>Gravity Error</p></span></div></foreignObject></g></g><g transform="translate(513.4609375, 186)" id="flowchart-EF-8" class="node default"><rect height="78" width="95.796875" y="-39" x="-47.8984375" style="" class="basic label-container"></rect><g transform="translate(-17.8984375, -24)" style="" class="label"><rect></rect><foreignObject height="48" width="35.796875"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>Fuel<br/>Error</p></span></div></foreignObject></g></g><g transform="translate(513.4609375, 302)" id="flowchart-ES-9" class="node default"><rect height="54" width="149.84375" y="-27" x="-74.921875" style="" class="basic label-container"></rect><g transform="translate(-44.921875, -12)" style="" class="label"><rect></rect><foreignObject height="24" width="89.84375"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>Sensor Error</p></span></div></foreignObject></g></g><g transform="translate(513.4609375, 406)" id="flowchart-ESH-10" class="node default"><rect height="54" width="258.390625" y="-27" x="-129.1953125" style="" class="basic label-container"></rect><g transform="translate(-99.1953125, -12)" style="" class="label"><rect></rect><foreignObject height="24" width="198.390625"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>Southern Hemisphere Error</p></span></div></foreignObject></g></g><g transform="translate(513.4609375, 522)" id="flowchart-EDS-11" class="node default"><rect height="78" width="171.59375" y="-39" x="-85.796875" style="" class="basic label-container"></rect><g transform="translate(-55.796875, -24)" style="" class="label"><rect></rect><foreignObject height="48" width="111.59375"><div style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>Daylight-Saving<br/>Error</p></span></div></foreignObject></g></g></g></g></g></svg> <!--s--> <h1 id="exceptions">Exceptions</h1> <!--n--> <p>This brings us to our second approach, exceptions could come along and separate those two paths again:</p> <!--s--> <pre><code class="language-rust" data-line-numbers="3">fn division(lhs, rhs) { if rhs == 0 { throw DivisionError } else { return lhs / rhs } } </code></pre> <!--n--> <p>Now I can directly use my function with no extra effort or operators:</p> <!--s--> <pre><code class="language-rust">division(2, 3) + division(3, 0)</code></pre> <!--n--> <p>However this brings along the issue of the sneaky little throw that can fly up my stack to some unknown point and wreck havoc on my control flow. Or my entire program if I happen to forget that this particular function throws, which there's absolutely zero clue that that may happen.</p> <p>Java tried to approach this with Checked Exceptions, having the throw be a part of a method's signature is a good idea, and we will return back to it after a slight distraction.</p> <!--s--> <p><img src="https://www.405th.com/forums/attachments/screenshot_20191208-182157_gallery-jpg.284514/" alt="Exceptions in the analogy of tracks" /></p> <!--s--> <h1 id="cps">CPS</h1> <!--n--> <p>What if we took the idea of representing control-flow with structures and kept it as pure control-flow?</p> <p>Turning:</p> <!--s--> <pre><code class="language-rust">match division(2, 3) { Ok(x) => ..., Err(e) => ..., } </code></pre> <!--n--> <p>Into:</p> <!--s--> <!-- .slide: data-auto-animate --> <pre><code class="language-rust" data-line-numbers="3,5,10-11">fn division(lhs, rhs, err) { if rhs == 0 { err(()) } else { lhs / rhs } } division(2, 3, |e| ..., // error branch ) + ... </code></pre> <!-- .element: data-id="add-branch" --> <!--n--> <p>Oops, can't do it like this, since after <code>err</code> returns in this context <code>division</code> would also return and the value of <code>err()</code> would try to get added to something else and continue, so we want the actual good-branch continuation of <code>division</code> to be evaluated if and only if we decide that it should in our <code>division</code> function.</p> <!--s--> <!-- .slide: data-auto-animate --> <pre><code class="language-rust" data-line-numbers="3,10|">fn division(lhs, rhs, ok, err) { if rhs == 0 { err(()) } else { ok(lhs / rhs) } } division(2, 3, |x| x + ..., // ok branch |e| ..., // error branch ) </code></pre> <!-- .element: data-id="add-branch" --> <!--n--> <p>Look how these two are actually remarkable mirrors of each other:</p> <!--s--> <pre><code class="language-haskell">division(2, 3, lambda x: x + ..., // ok branch lambda e: ..., // error branch ) match division(2,3) { Ok(x) => x + ..., //ok branch Err(e) => ..., // error branch } </code></pre> <!--n--> <p>Awesome, <code>division</code> now has complete control over its own control-flow. And also here we are extremely explicit about the fact that division may return one of two ways. In addition it is now a completely normal function, with no dynamic moving parts, it only really looks at its own inputs and works with those.</p> <p>Also, our webdev friends might notice that a form of this pattern is also used in the JS language:</p> <!--s--> <pre><code class="language-javascript">foo() .then((x) => ...) .catch((e) => ...) </code></pre> <!--n--> <p>What if I don't necessarily care about a given case? I can just pass it through to my parent:</p> <!--s--> <pre><code class="language-rust">fn foo(ok, err) { division(2, 3, |result1| division(result1, 5, ok, err), err ) } </code></pre> <p>Compilers are actually smart and can convert it automatically from direct style:</p> <!-- .element: class="fragment" data-fragment-index="1" --> <pre><code class="language-rust">fn foo() -> {Err} { division(division(2, 3), 5) } </code></pre> <!-- .element: class="fragment" data-fragment-index="1" --> <!--n--> <p>Passing the <code>err</code> however deep it needs to go will cause our program to be able to go however long or deep we need but always have a point that it immediately jumps to whenever <code>err</code> is called.</p> <p>Behold the power of CPS.</p> <!--s--> <h3 id="wait-why-are-monads-like-cps">Wait why are Monads like CPS?</h3> <!--s--> <pre><code class="language-haskell">do x <- Just 4 y <- Just 5 return $ x + y </code></pre> <pre><code class="language-haskell">Just 4 >>= \x -> Just 5 >>= \y -> return $ x + y </code></pre> <!-- .element: class="fragment" --> <!--s--> <h1 id="cps-aware-functions">CPS aware functions</h1> <!--n--> <p>Now let's look at some actual practical applications.</p> <!--s--> <pre><code class="language-haskell" data-line-numbers="|3-4|5-6|5-7|8-9">data Error = DivisionByZero kdiv :: Double -> Double -> (Error -> result) -> (Double -> result) -> result kdiv x 0 abort k = abort DivisionByZero kdiv x y abort k = k (x / y) </code></pre> <!--s--> <pre><code class="language-haskell" data-line-numbers="|1-2|3|3-4|5-6">kmap :: (element -> (newElem -> return) -> return) -> [element] -> ([return] -> return) -> return kmap _ [] k = k [] kmap f (x : xs) k = f x (\v -> kmap f xs (\v2 -> k (v : v2))) </code></pre> <!--n--> <p>Now why would I want to do this?</p> <!--s--> <pre><code class="language-haskell">ok = Right err = Left ghci> kmap (\ value k -> kdiv 1 value err k) [1,2,3,4] ok Right [1.0,0.5,0.3333333333333333,0.25] ghci> kmap (\ value k -> kdiv 1 value err k) [1,2,3,4,0] ok Left "Division by zero" </code></pre> <!--n--> <p>This is immensely powerful as now the functions passed into <code>kmap</code> may invoke any of its known handlers and so the <code>kmap</code> function need not be aware of what the function is doing internally at all.</p> <p>Including for example doing IO, being CPS aware is sufficient to access IO, Mutating a Store, throwing Exceptions, etc.</p> <p>Functions like this are superseded:</p> <!--s--> <pre><code class="language-haskell">sequence :: Monad m => [m a] -> m [a] mapM :: Monad m => (a -> m b) -> [a] -> m [b] </code></pre> <!--n--> <p>We can go one step further still, CPS isn't limited to a function type necessarily but might be useful in regular values as well.</p> <!--s--> <h1 id="cps-aware-values">CPS aware values</h1> <!--s--> <pre><code class="language-haskell" data-line-numbers="|2|3|4|8">addition x y k = x $ \xval -> y $ \yval -> . k (xval + yval) forEach l localCont k = k (localCont <$> l) </code></pre> <!--s--> <pre><code class="language-haskell">ghci> addition (\f -> f 4) (\f -> forEach [0, 1, 2] f id) id [4,5,6] ghci> addition (\f -> forEach [10, 14] f id) (\f -> forEach [0, 1, 2] f id) id [[10,11,12],[14,15,16]] </code></pre> <!--s--> <h2 id="example-of-this-in-any-old-language">Example of this in any old language</h2> <!--n--> <p>This isn't some ultra-special functional language thing either, here this concept is used in regular... (semi-regular) C++, you just need to be able to type some slightly more advanced Lambda expressions.</p> <!--s--> <pre><code class="language-cpp">template <typename T> T divide( int x, int y, std::function<T(int)> ok, std::function<T()> err) { if (y == 0) { return err(); } else { return ok(x / y); } } int main() { divide<void>(6, 0, [](int x) { std::cout << "result: " << x << std::endl; }, []() { std::cout << "Err" << std::endl; }); } </code></pre> <!--s--> <h2 id="disappears-in-a-puff-of-syntax-sugar">Disappears in a puff of syntax sugar</h2> <!--n--> <p>That's enough of that, what if I'm lazy however and don't want to have to pass the unhandled continuations explicitly, while probably reducing the long ugly types?</p> <!--s--> <pre><code class="language-rust">fn foo() -> {ArithError} f64 { division(1, 5) + division(4, 0) } </code></pre> <!--n--> <p><code>foo</code> would check that division requires an <code>ArithError</code> handler apart from its regular return path and includes that in its signature.</p> <p>Meaning that to get the result you have to bind in some handler for <code>ArithError</code>.</p> <p>This wouldn't be unprecedented, similarly to how TypeClasses may be handled as implicit parameters, calling addition in actuality looks like this:</p> <pre><code class="language-haskell">add :: Num a => a -> a -> a add(1, 2, Int.NumMethodTable) </code></pre> <p>So ours would look like this behind the scenes, at which point I'd say we're approaching the ergonomics of exceptions.</p> <p>One of the main features compared to Monads is that this transformation can be done automatically by the compiler and quite often already is as CPS is one of the many steps compilers already perform.</p> <!--s--> <h2 id="this-can-be-done-quite-efficiently">This can be done quite efficiently</h2> <!--s--> <p><img src="https://static.displate.com/1200x857/displate/2023-07-14/45628836bf0ec85fedb0f8cad1321d20_c9aca0033fbed297bba6ab7218b2c5f0.jpg" alt="Source: Trust me bro" /></p> <!--n--> <blockquote> <p>I am forced to not really go into excessive implementation details here, but trust me that this can be made quite efficient and simple while keeping the power and model imagined here.</p> </blockquote> <!--s--> <h1 id="longer-winded-examples">Longer Winded examples</h1> <!--s--> <pre><code class="language-javascript">> const put = (v, k) => k(put, v) > put(2, (put, get) => get) 2 > put(2, (put, get) => { console.log(get); put(3, (put, get) => { console.log(3); }); }) 2 3 > const put3 = (v, get, k) => { let n = [v, ...get]; return k(put3, n); } > put3(3, [], (put, get) => put(4, get, (put, get) => get)) [ 4, 3 ] </code></pre> <!--s--> <p>Example of how Rust benefits in composition:</p> <pre><code class="language-rust">enum Error1 { DivisionByZero, } enum Error2 { DivisionByZero, } fn fail_option<T, Ok, Err>( lhs: u64, rhs: u64, ok: Ok, err: Err ) -> T where Ok: Fn(u64) -> T, Err: Fn(Error1) -> T, { if rhs == 0 { err(Error1::DivisionByZero) } else { ok(lhs / rhs) } } fn fail_result<T, Ok, Err>( lhs: u64, rhs: u64, ok: Ok, err: Err ) -> T where Ok: Fn(u64) -> T, Err: Fn(Error2) -> T, { if rhs == 0 { err(Error2::DivisionByZero) } else { ok(lhs / rhs) } } fn fail_both<T, Ok, Err1, Err2>( lhs: u64, rhs: u64, ok: Ok, err1: Err1, err2: Err2 ) -> T where Ok: Fn(u64) -> T + Copy, Err1: Fn(Error1) -> T, Err2: Fn(Error2) -> T + Copy, { fail_option(100, lhs, |x| fail_result(x, rhs, ok, err2), err1) } fn main() { fail_both( 3, 4, | r| println!("Success: {r}"), |_e| eprintln!("First type of error"), |_e| eprintln!("Second type of error"), ) } </code></pre> <!--s--> <pre><code class="language-lisp">(defvar *abort-handler* nil) (defun &catch (f k) (let ((*abort-handler* (lambda (err sk) (print err)))) (funcall f k))) (defun &abort (k) (funcall *abort-handler* "Aborting" k)) (defun &+ (x y k) (funcall k (+ x y))) (defun add-and-abort (x y k) (&+ x y (lambda (result) (&abort (lambda (sk) (funcall sk (funcall k result))))))) (cont:with-call/cc (defun each (&rest l) (cont:let/cc k (reduce #'append (mapcar (alexandria:compose #'uiop:ensure-list k) l)))) (defun trip () (let ((x (each 1 2 3)) (y (each 4 5 6))) (+ x y)))) (defvar *each-handler* nil) (defun &each (l k each) (each (mapcar k l))) (defun &each-tolist (f k) (labels ((impl (l sk) (k (reduce #'append (mapcar sk l))))) (let ((*each-handler* (impl '() k))) (funcall f k)))) (defun sum (to) (if (zerop to) 0 (+ (sum (1- to)) to))) (defun &sum (to k) (if (zerop to) (funcall k 0) (&sum (1- to) (lambda (res) (funcall k (+ res to)))))) </code></pre> <!--s--> <h1 id="thank-you-for-your-time">Thank you for your time</h1> <!--s--> <h3 id="any-questions">Any questions?</h3> <!-- .slide: data-background-image="./penguin.svg" data-background-opacity="0.5" data-background-size="60vh" data-backgroun-position="right" -->