Daniel's Blog https://danilafe.com/ Recent content on Daniel's Blog Hugo -- gohugo.io en-us Wed, 31 Dec 2025 00:00:00 +0000 Reasons to Love the Field of Programming Languages https://danilafe.com/blog/i_love_programming_languages/ Wed, 31 Dec 2025 00:00:00 +0000 https://danilafe.com/blog/i_love_programming_languages/ <p>I work at HPE on the <a href="https://chapel-lang.org"class="external-link">Chapel Programming Language<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. Recently, another HPE person asked me:</p> <blockquote> <p>So, you work on the programming language. What&rsquo;s next for you?</p> </blockquote> <p>This caught me off-guard because I hadn&rsquo;t even conceived of moving on. I don&rsquo;t want to move on, because <strong>I love the field of programming languages</strong>. In addition, I have come to think there is something in PL for everyone, from theorists to developers to laypeople. So, in that spirit, I am writing this list as a non-exhaustive survey that holds the dual purpose of explaining my personal infatuation with PL, and providing others with ways to engage with PL that align with their existing interests. I try to provide rationale for each claim, but you can just read the reasons themselves and skip the rest.</p> <p>My general thesis goes something like this: programming languages are a unique mix of the <strong>inherently human and social</strong> and the <strong>deeply mathematical</strong>, a mix that often remains deeply grounded in the practical, <strong>low-level realities of our hardware</strong>.</p> <p>Personally, I find all of these properties equally important, but we have to start somewhere. Let&rsquo;s begin with the human aspect of programming languages.</p> <a href="#human-aspects-of-pl"> <h3 id="human-aspects-of-pl">Human Aspects of PL</h3> </a> <blockquote> <p>Programs must be written for people to read, and only incidentally for machines to execute.</p> <p>&mdash; Abelson &amp; Sussman, <em>Structure and Interpretation of Computer Programs</em>.</p> </blockquote> <p>As we learn more about the other creatures that inhabit our world, we discover that they are similar to us in ways that we didn&rsquo;t expect. However, our language is unique to us. It gives us the ability to go far beyond the simple sharing of information: we communicate abstract concepts, social dynamics, stories. In my view, storytelling is our birthright more so than anything else.</p> <p>I think this has always been reflected in the broader discipline of programming. <em>Code should always tell a story</em>, I&rsquo;ve heard throughout my education and career. <em>It should explain itself</em>. In paradigms such as <a href="https://en.wikipedia.org/wiki/Literate_programming"class="external-link">literate programming<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, we explicitly mix prose and code. Notebook technologies like <a href="https://jupyter.org/"class="external-link">Jupyter<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> intersperse computation with explanations thereof.</p> <ul> <li><strong>Reason 1</strong>: programming languages provide the foundation of expressing human thought and stories through code.</li> </ul> <p>From flowery prose to clinical report, human expression takes a wide variety of forms. The need to vary our descriptions is also well-served by the diversity of PL paradigms. From stateful transformations in languages like Python and C++, through pure and immutable functions in Haskell and Lean, to fully declarative statements-of-fact in Nix, various languages have evolved to support the many ways in which we wish to describe our world and our needs.</p> <ul> <li><strong>Reason 2</strong>: diverse programming languages enable different perspectives and ways of storytelling, allowing us choice in how to express our thoughts and solve our problems.</li> </ul> <p>Those human thoughts of ours are not fundamentally grounded in logic, mathematics, or anything else. They are a product of millennia of evolution through natural selection, of adaptation to ever-changing conditions. Our cognition is limited, rife with blind spots, and partial to the subject matter at hand. We lean on objects, actors, contracts, and more as helpful, mammal-compatible analogies. I find this to be beautiful; here is something we can really call ours.</p> <ul> <li><strong>Reason 3</strong>: programming languages imbue the universe&rsquo;s fundamental rules of computation with humanity&rsquo;s identity and idiosyncrasies. They carve out a home for us within impersonal reality.</li> </ul> <p>Storytelling (and, more generally, writing) is not just about communicating with others. Writing helps clarify one&rsquo;s own thoughts, and to think deeper. In his 1979 Turing Award lecture, <a href="https://www.eecg.utoronto.ca/~jzhu/csc326/readings/iverson.pdf"class="external-link">Notation as a Tool of Thought<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, Kenneth Iverson, the creator of <a href="https://tryapl.org/"class="external-link">APL<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, highlighted ways in which programming languages, with their notation, can help express patterns and facilitate thinking.</p> <p>Throughout computing history, programming languages built abstractions that &mdash; together with advances in hardware &mdash; made it possible to create ever more complex software. Dijkstra&rsquo;s <a href="https://en.wikipedia.org/wiki/Structured_programming"class="external-link">structured programming<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> crystallized the familiar patterns of <code>if</code>/<code>else</code> and <code>while</code> out of a sea of control flow. Structures and objects partitioned data and state into bundles that could be reasoned about, or put out of mind when irrelevant. Recently, I dare say that notions of ownership and lifetimes popularized by Rust have clarified how we think about memory.</p> <ul> <li><strong>Reason 4</strong>: programming languages combat complexity, and give us tools to think and reason about unwieldy and difficult problems.</li> </ul> <p>The fight against complexity occurs on more battlegrounds than PL design alone. Besides its syntax and semantics, a programming language is comprised of its surrounding tooling: its interpreter or compiler, perhaps its package manager or even its editor. Language designers and developers take great care to <a href="https://elm-lang.org/news/compiler-errors-for-humans"class="external-link">improve the quality of error messages<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, to provide <a href="https://chapel-lang.org/blog/posts/chapel-lsp/"class="external-link">convenient editor tooling<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, and build powerful package managers like <a href="https://yarnpkg.com/"class="external-link">Yarn<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. Thus, in each language project, there is room for folks who, even if they are not particularly interested in grammars or semantics, care about the user experience.</p> <ul> <li><strong>Reason 5</strong>: programming languages provide numerous opportunities for thoughtful forays into the realms of User Experience and Human-Computer Interaction.</li> </ul> <p>I hope you agree, by this point, that programming languages are fundamentally tethered to the human. Like any human endeavor, then, they don&rsquo;t exist in isolation. To speak a language, one usually wants a partner who understands and speaks that same language. Likely, one wants a whole community, topics to talk about, or even a set of shared beliefs or mythologies. This desire maps onto the realm of programming languages. When using a particular PL, you want to talk to others about your code, implement established design patterns, use existing libraries.</p> <p>I mentioned mythologies earlier. In some ways, language communities do more than share know-how about writing code. In many cases, I think language communities rally around ideals embodied by their language. The most obvious example seems to be Rust. From what I&rsquo;ve seen, the Rust community believes in language design that protects its users from the pitfalls of low-level programming. The Go community believes in radical simplicity. Julia actively incorporates contributions from diverse research projects into an interoperable set of scientific packages.</p> <ul> <li><strong>Reason 6</strong>: programming languages are complex collaborative social projects that have the power to champion innovative ideas within the field of computer science.</li> </ul> <p>So far, I&rsquo;ve presented interpretations of the field of PL as tools for expression and thought, human harbor to the universe&rsquo;s ocean, and collaborative social projects. These interpretations coexist and superimpose, but they are only a fraction of the whole. What has kept me enamored with PL is that it blends these human aspects with a mathematical ground truth, through fundamental connections to computation and mathematics.</p> <a href="#the-mathematics-of-pl"> <h3 id="the-mathematics-of-pl">The Mathematics of PL</h3> </a> <blockquote> <p>Like buses: you wait two thousand years for a definition of “effectively calculable”, and then three come along at once.</p> <p>&mdash; Philip Wadler, <em>Propositions as Types</em></p> </blockquote> <p>There are two foundations, <a href="https://en.wikipedia.org/wiki/Lambda_calculus"class="external-link">lambda calculus<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> and <a href="https://en.wikipedia.org/wiki/Turing_machine"class="external-link">Turing machines<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, that underpin most modern PLs. The abstract notion of Turing machines is closely related to, and most similar among the &ldquo;famous&rdquo; computational models, to the <a href="https://en.wikipedia.org/wiki/Von_Neumann_architecture"class="external-link">von Neumann Architecture<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. Through bottom-up organization of &ldquo;control unit instructions&rdquo; into &ldquo;structured programs&rdquo; into the imperative high-level languages today, we can trace the influence of Turing machines in C++, Python, Java, and many others. At the same time, and running on the same hardware functional programming languages like Haskell represent a chain of succession from the lambda calculus, embellished today with types and numerous other niceties. These two lineages are inseparably linked: they have been mathematically proven to be equivalent. They are two worlds coexisting.</p> <p>The two foundations have a crucial property in common: they are descriptions of what can be computed. Both were developed initially as mathematical formalisms. They are rooted not only in pragmatic concerns of &ldquo;what can I do with these transistors?&rdquo;, but in the deeper questions of &ldquo;what can be done with a computer?&rdquo;.</p> <ul> <li><strong>Reason 7</strong>: general-purpose programming languages are built on foundations of computation, and wield the power to compute anything we consider &ldquo;effectively computable at all&rdquo;.</li> </ul> <p>Because of these mathematical beginnings, we have long had precise and powerful ways to talk about what code written in a particular language <em>means</em>. This is the domain of <em>semantics</em>. Instead of reference implementations of languages (CPython for Python, <code>rustc</code> for Rust), and instead of textual specifications, we can explicitly map constructs in languages either to mathematical objects (<a href="https://en.wikipedia.org/wiki/Denotational_semantics"class="external-link">denotational semantics<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>) or to (abstractly) execute them (<a href="https://en.wikipedia.org/wiki/Operational_semantics"class="external-link">operational semantics<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>).</p> <p>To be honest, the precise and mathematical nature of these tools is, for me, justification enough to love them. However, precise semantics for languages have real advantages. For one, they allow us to compare programs&rsquo; real behavior with what we <em>expect</em>, giving us a &ldquo;ground truth&rdquo; when trying to fix bugs or evolve the language. For another, they allow us to confidently make optimizations: if you can <em>prove</em> that a transformation won&rsquo;t affect a program&rsquo;s behavior, but make it faster, you can safely use it. Finally, the discipline of formalizing programming language semantics usually entails boiling them down to their most essential components. Stripping the <a href="https://en.wikipedia.org/wiki/Syntactic_sugar"class="external-link">syntax sugar<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> helps clarify how complex combinations of features should behave together.</p> <p>Some of these techniques bear a noticeable resemblance to the study of semantics in linguistics. Given our preceding discussion on the humanity of programming languages, perhaps that&rsquo;s not too surprising.</p> <ul> <li><strong>Reason 8</strong>: programming languages can be precisely formalized, giving exact, mathematical descriptions of how they should work.</li> </ul> <p>In talking about how programs behave, we run into an important limitation of reasoning about Turing machines and lambda calculus, stated precisely in <a href="https://en.wikipedia.org/wiki/Rice%27s_theorem"class="external-link">Rice&rsquo;s theorem<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>: all non-trivial semantic properties of programs (termination, throwing errors) are undecidable. There will always be programs that elude not only human analysis, but algorithmic understanding.</p> <p>It is in the context of this constraint that I like to think about type systems. The beauty of type systems, to me, is in how they tame the impossible. Depending on the design of a type system, a well-typed program may well be guaranteed not to produce any errors, or produce only the &ldquo;expected&rdquo; sort of errors. By constructing reasonable <em>approximations</em> of program behavior, type systems allow us to verify that programs are well-behaved in spite of Rice&rsquo;s theorem. Much of the time, too, we can do so in a way that is straightforward for humans to understand and machines to execute.</p> <ul> <li><strong>Reason 9</strong>: in the face of the fundamentally impossible, type systems pragmatically grant us confidence in our programs for surprisingly little conceptual cost.</li> </ul> <p>At first, type systems look like engineering formalisms. That may well be the original intention, but in our invention of type systems, we have actually completed a quadrant of a deeper connection: the <a href="https://en.wikipedia.org/wiki/Curry%E2%80%93Howard_correspondence"class="external-link">Curry-Howard isomorphism<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. <a href="https://en.wikipedia.org/wiki/Proposition"class="external-link">Propositions<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, in the logical sense, correspond one-to-one with types of programs, and proofs of these propositions correspond to programs that have the matching type.</p> <p>This is an incredibly deep connection. In adding parametric polymorphism to a type system (think Java generics, or C++ templates without specialization), we augment the corresponding logic with the &ldquo;for all x&rdquo; (\(\forall x\)) quantifier. Restrict the copying of values in a way similar to Rust, and you get an <a href="https://en.wikipedia.org/wiki/Affine_logic"class="external-link">affine logic<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, capable of reasoning about resources and their use. In languages like Agda with <a href="https://en.wikipedia.org/wiki/Dependent_type"class="external-link">dependent types<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, you get a system powerful enough <a href="https://en.wikipedia.org/wiki/Intuitionistic_type_theory"class="external-link">to serve as a foundation for mathematics<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. Suddenly, you can write code and mathematically prove properties about that code in the same language. I&rsquo;ve done this in my work with <a href="https://danilafe.com/series/static-program-analysis-in-agda/">formally-verified static program analysis</a>.</p> <p>This connection proves appealing even from the perspective of &ldquo;regular&rdquo; mathematics. We have developed established engineering practices for writing code: review, deployment, documentation. What if we could use the same techniques for doing mathematics? What if, through the deep connection of programming languages to logic, we could turn mathematics into a computer-verified, collaborative endeavor? I therefore present:</p> <ul> <li><strong>Reason 10</strong>: type systems for programming languages deeply correspond to logic, allowing us to mathematically prove properties about code, using code, and to advance mathematics through the practices of software engineering.</li> </ul> <details> <summary>Bonus meta-reason to love the mathy side of PL!</summary><p>In addition to the theoretical depth, I also find great enjoyment in the way that PL is practiced. Here more than elsewhere, creativity and artfulness come into play. In PL, <a href="https://en.wikipedia.org/wiki/Rule_of_inference"class="external-link">inference rules<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> are a lingua franca through which the formalisms I&rsquo;ve mentioned above are expressed and shared. They are such a central tool in the field that I&rsquo;ve developed <a href="https://danilafe.com/blog/bergamot/">a system for exploring them interactively</a> on this blog.</p> <p>In me personally, inference rules spark joy. They are a concise and elegant way to do much of the formal heavy-lifting I described in this section; we use them for operational semantics, type systems, and sometimes more. When navigating the variety and complexity of the many languages and type systems out there, we can count on inference rules to take us directly to what we need to know. This same variety naturally demands flexibility in how rules are constructed, and what notation is used. Though this can sometimes be troublesome (one <a href="https://labs.oracle.com/pls/apex/f?p=LABS%3A0%3A%3AAPPLICATION_PROCESS%3DGETDOC_INLINE%3A%3A%3ADOC_ID%3A959%22"class="external-link">paper<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> I&rsquo;ve seen describes <strong>27</strong> different ways of writing the simple operation of substitution in literature!), it also creates opportunities for novel and elegant ways of formalizing PL.</p> <ul> <li><strong>Bonus Reason</strong>: the field of programming languages has a standard technique for expressing its formalisms, which precisely highlights core concepts and leaves room for creative expression and elegance.</li> </ul> </details> <p>I know that mathematics is a polarizing subject. Often, I find myself torn between wanting precision and eschewing overzealous formalism. The cusp between the two is probably determined by my own tolerance for abstraction. Regardless of how much abstraction you are interested in learning about, PL has another dimension, close to the ground: more often than not, our languages need to execute on real hardware.</p> <a href="#pragmatics-of-pl"> <h3 id="pragmatics-of-pl">Pragmatics of PL</h3> </a> <p>Your perfectly-designed language can be completely useless if there is no way to <span class="sidenote"> <label class="sidenote-label" for="execute-note">execute it</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="execute-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Technically, there are language that don't care if you execute them at all. Many programs in theorem-proving languages like Agda and Rocq exist only to be type-checked. So, you could nitpick this claim; or, you could take it more generally: your language can be useless if there's no way to make it efficiently do what it's been made to do. <span class="sidenote-delimiter">]</span> </span> </span> efficiently. Thus, the field of PL subsumes not only the theoretical foundations of languages and their human-centric design; it includes also their realization as software.</p> <p>The overall point of this section is that there is much depth to the techniques involved in bringing a programming language to life. If you are a tinkerer or engineer at heart, you will never run out of avenues of exploration. The reasons are all framed from this perspective.</p> <p>One fascinating aspect to programming languages is the &ldquo;direction&rdquo; from which they have grown. On one side, you have languages that came together from the need to control and describe hardware. I&rsquo;d say that this is the case for C and C++, Fortran, and others. More often than not, these languages are compiled to machine code. Still subject to human constraints, these languages often evolve more user-facing features as time goes on. On the other side, you have languages developed to enable people to write software, later faced constraints of actually working efficiently. These are languages like Python, Ruby, and JavaScript. These languages are often interpreted (executed by a dedicated program), with techniques such as <a href="https://en.wikipedia.org/wiki/Just-in-time_compilation"class="external-link">just-in-time compilation<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. There is no one-size-fits-all way to execute a language, and as a result,</p> <ul> <li><strong>Reason 11</strong>: the techniques of executing programming languages are varied and rich. From compilation, to JIT, to interpretation, the field has many sub-disciplines, each with its own know-hows and tricks.</li> </ul> <p>At the same time, someone whose goal is to actually develop a compiler likely doesn&rsquo;t want to develop everything from scratch. To do so would be a daunting task, especially if you want the compiler to run beyond the confines of a personal machine. CPU <a href="https://en.wikipedia.org/wiki/Instruction_set_architecture"class="external-link">architectures<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> and operating system differences are hard for any individual to keep up with. Fortunately, we have a gargantuan ongoing effort in the field: the <a href="https://llvm.org/"class="external-link">LLVM Project<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. LLVM spans numerous architectures and targets, and has become a common back-end for languages like C++ (via <a href="https://clang.llvm.org/get_started.html"class="external-link">Clang<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>), Swift, and Rust. LLVM helps share and distribute the load of keeping up with the ongoing march of architectures and OSes. It also provides a shared playground upon which to experiment with language implementations, optimizations, and more.</p> <ul> <li><strong>Reason 12</strong>: large projects like LLVM enable language designers to lean on decades of precedent to develop a compiler for their language.</li> </ul> <p>Though LLVM is powerful, it does not automatically grant languages implemented with it good performance. In fact, no other tool does. To make a language run fast requires a deep understanding of the language itself, the hardware upon which it runs, and the tools used to execute it. That is a big ask! Modern computers are extraordinarily complex. Techniques such as <a href="https://en.wikipedia.org/wiki/Out-of-order_execution"class="external-link">out-of-order execution<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, <a href="https://en.wikipedia.org/wiki/Cache_%28computing%29#HARDWARE"class="external-link">caching<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, and <a href="https://en.wikipedia.org/wiki/Speculative_execution"class="external-link">speculative execution<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> are constantly at play. This means that any program is subject to hard-to-predict and often unintuitive effects. On top of that, depending on your language&rsquo;s capabilities, performance work can often entail working with additional hardware, such as GPUs and NICs, which have their own distinct performance characteristics. This applies both to compiled and interpreted languages. Therefore, I give you:</p> <ul> <li><strong>Reason 13</strong>: improving the performance of a programming language is rife with opportunities to engage with low-level details of the hardware and operating system.</li> </ul> <p>In the <a href="#the-mathematics-of-pl"class="same-page-link">mathematics section<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#arrow-up"/> </svg></a>, we talked about how constructing correct optimizations requires an understanding of the language&rsquo;s semantics. It was one of the practical uses for having a mathematical definition of a language. Reason 13 is where that comes in, but the synthesis is not automatic. In fact, a discipline sits in-between defining how a language behaves and optimizing programs: program analysis. Algorithms that analyze properties of programs such as <a href="https://en.wikipedia.org/wiki/Reaching_definition"class="external-link">reaching definitions<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> enable optimizations such as <a href="https://en.wikipedia.org/wiki/Loop-invariant_code_motion"class="external-link">loop-invariant code motion<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, which can have very significant performance impact. At the same time, for an analysis to be correct, it must be grounded in the program&rsquo;s mathematical semantics. There are many fascinating techniques in this discipline, including <a href="https://cs.au.dk/~amoeller/spa/spa.pdf"class="external-link">ones that use lattice theory<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>.</p> <ul> <li><strong>Reason 14</strong>: the sub-discipline of program analysis serves as a grounded application of PL theory to PL practice, enabling numerous optimizations and transformations.</li> </ul> <p>The programs your compiler generates are software, and, as we just saw, may need to be tweaked for performance. But the compiler and/or interpreter is itself a piece of software, and its own performance. Today&rsquo;s language implementations are subject to demands that hadn&rsquo;t been there historically. For instance, languages are used to provide <a href="https://microsoft.github.io/language-server-protocol/"class="external-link">language servers<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> to enable editors to give users deeper insights into their code. Today, a language implementation may be called upon every keystroke to provide a typing user live updates. This has led to the introduction of techniques like the <a href="https://ollef.github.io/blog/posts/query-based-compilers.html"class="external-link">query architecture<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> (see also <a href="https://github.com/salsa-rs/salsa"class="external-link">salsa<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>) to avoid redundant work and re-used intermediate results. New language implementations like that of <a href="https://github.com/carbon-language/carbon-lang"class="external-link">Carbon<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> are exploring alternative representations of programs in memory. In short,</p> <ul> <li><strong>Reason 15</strong>: language implementations are themselves pieces of software, subject to unique constraints and requiring careful and innovative engineering.</li> </ul> <a href="#conclusion"> <h3 id="conclusion">Conclusion</h3> </a> <p>I&rsquo;ve now given a tour of ways in which I found the PL field compelling, organized across three broad categories. There is just one more reason I&rsquo;d like to share.</p> <p>I was 16 years old when I got involved with the world of programming languages and compilers. Though I made efforts to learn about it through literature (the <em>Dragon Book</em>, and <em>Modern Compiler Design</em>), I simply didn&rsquo;t have the background to find these resources accessible. However, all was not lost. The PL community online has been, and still is, a vibrant and enthusiastic place. I have found it to be welcoming of folks with backgrounds spanning complete beginners and experts alike. Back then, it gave me accessible introductions to anything I wanted. Now, every week I see new articles go by that challenge my intuitions, teach me new things, or take PL ideas to absurd and humorous extremes. So, my final reason:</p> <ul> <li> <p><strong>Reason 16</strong>: the programming languages community is full of brilliant, kind, welcoming and enthusiastic people, who dedicate much of their time to spreading the joy of the field.</p> <p>I ❤️ the programming languages community.</p> </li> </ul> Chapel's Runtime Types as an Interesting Alternative to Dependent Types https://danilafe.com/blog/chapel_runtime_types/ Sun, 02 Mar 2025 22:52:01 -0800 https://danilafe.com/blog/chapel_runtime_types/ <p>One day, when I was in graduate school, the Programming Languages research group was in a pub for a little gathering. Amidst beers, fries, and overpriced sandwiches, the professor and I were talking about <a href="https://en.wikipedia.org/wiki/Dependent_type"class="external-link">dependent types<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. Speaking loosely and imprecisely, these are types that are somehow constructed from <em>values</em> in a language, like numbers.</p> <p>For example, in C++, <a href="https://en.cppreference.com/w/cpp/container/array"class="external-link"><code>std::array</code><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> is a dependent type. An instantiation of the <em>type</em> <code>array</code>, like <code>array&lt;string, 3&gt;</code> is constructed from the type of its elements (here, <code>string</code>) and a value representing the number of elements (here, <code>3</code>). This is in contrast with types like <code>std::vector</code>, which only depends on a type (e.g., <code>vector&lt;string&gt;</code> would be a dynamically-sized collection of strings).</p> <p>I was extolling the virtues of general dependent types, like you might find in <a href="https://www.idris-lang.org/"class="external-link">Idris<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> or <a href="https://agda.readthedocs.io/en/latest/getting-started/what-is-agda.html"class="external-link">Agda<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>: more precise function signatures! The <span class="sidenote"> <label class="sidenote-label" for="curry-howard-note">Curry-Howard isomorphism!</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="curry-howard-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> The Curry-Howard isomorphism is a common theme on this blog. I've <a href="https://danilafe.com/blog/typesafe_interpreter_revisited/#curry-howard-correspondence"> written about it myself</a>, but you can also take a look at the <a href="https://en.wikipedia.org/wiki/Curry%E2%80%93Howard_correspondence"> Wikipedia page</a>. <span class="sidenote-delimiter">]</span> </span> </span> The professor was skeptical. He had been excited about dependent types in the past, but nowadays he felt over them. They were cool, he said, but there are few practical uses. In fact, he posed a challenge:</p> <blockquote id="bounds-quote"> <p>Give me one good reason to use dependent types in practice that doesn&rsquo;t involve keeping track of bounds for lists and matrices!</p> </blockquote> <p>This challenge alludes to fixed-length lists &ndash; <a href="https://agda.github.io/agda-stdlib/master/Data.Vec.html"class="external-link">vectors<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> &ndash; which are one of the first dependently-typed data structures one learns about. Matrices are effectively vectors-of-vectors. In fact, even in giving my introductory example above, I demonstrated the C++ equivalent of a fixed-length list, retroactively supporting the professor&rsquo;s point.</p> <p>It&rsquo;s not particularly important to write down how I addressed the challenge; suffice it to say that the notion resonated with some of the other students present in the pub. In the midst of practical development, how much of dependent types&rsquo; power can you leverage, and how much power do you pay for but never use?</p> <p>A second round of beers arrived. The argument was left largely unresolved, and conversation flowed to other topics. Eventually, I graduated, and started working on the <a href="https://chapel-lang.org/"class="external-link">Chapel language<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> team (I also <a href="https://chapel-lang.org/blog/authors/daniel-fedorin/"class="external-link">write on the team&rsquo;s blog<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>).</p> <p>When I started looking at Chapel programs, I could not believe my eyes&hellip;</p> <a href="#a-taste-of-chapels-array-types"> <h3 id="a-taste-of-chapels-array-types">A Taste of Chapel&rsquo;s Array Types</h3> </a> <p>Here&rsquo;s a simple Chapel program that creates an array of 10 integers.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Chapel" data-lang="Chapel"><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">A</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="mi">0</span><span class="o">..</span><span class="mi">9</span><span class="p">]</span><span class="w"> </span><span class="kt">int</span><span class="p">;</span><span class="w"> </span></span></span></code></pre></div><p>Do you see the similarity to the <code>std::array</code> example above? Of course, the syntax is quite different, but in <em>essence</em> I think the resemblance is uncanny. Let&rsquo;s mangle the type a bit &mdash; producing invalid Chapel programs &mdash; just for the sake of demonstration.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Chapel" data-lang="Chapel"><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">B</span><span class="p">:</span><span class="w"> </span><span class="nx">array</span><span class="p">(</span><span class="mi">0</span><span class="o">..</span><span class="mi">9</span><span class="p">,</span><span class="w"> </span><span class="kt">int</span><span class="p">);</span><span class="w"> </span><span class="c1">// first, strip the syntax sugar </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span><span class="w"> </span><span class="nx">C</span><span class="p">:</span><span class="w"> </span><span class="nx">array</span><span class="p">(</span><span class="kt">int</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="o">..</span><span class="mi">9</span><span class="p">);</span><span class="w"> </span><span class="c1">// swap the order of the arguments to match C++ </span></span></span></code></pre></div><p>Only one difference remains: in C++, arrays are always indexed from zero. Thus, writing <code>array&lt;int, 10&gt;</code> would implicitly create an array whose indices start with <code>0</code> and end in <code>9</code>. In Chapel, array indices can start at values other than zero (it happens to be useful for elegantly writing numerical programs), so the type explicitly specifies a lower and a higher bound. Other than that, though, the two types look very similar.</p> <p>In general, Chapel arrays have a <em>domain</em>, typically stored in variables like <code>D</code>. The domain of <code>A</code> above is <code>{0..9}</code>. This domain is part of the array&rsquo;s type.</p> <p>Before I move on, I&rsquo;d like to pause and state a premise that is crucial for the rest of this post: <strong>I think knowing the size of a data structure, like <code>std::array</code> or Chapel&rsquo;s <code>[0..9] int</code>, is valuable</strong>. If this premise were not true, there&rsquo;d be no reason to prefer <code>std::array</code> to <code>std::vector</code>, or care that Chapel has indexed arrays. However, having this information can help in numerous ways, such as:</p> <ul> <li> <p><strong>Enforcing compatible array shapes.</strong> For instance, the following Chapel code would require two arrays passed to function <code>foo</code> to have the same size.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Chapel" data-lang="Chapel"><span class="line"><span class="cl"><span class="k">proc</span><span class="w"> </span><span class="nf">doSomething</span><span class="p">(</span><span class="nx">people</span><span class="p">:</span><span class="w"> </span><span class="p">[?</span><span class="nx">D</span><span class="p">]</span><span class="w"> </span><span class="nx">person</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="nx">D</span><span class="p">]</span><span class="w"> </span><span class="nx">personInfo</span><span class="p">)</span><span class="w"> </span><span class="p">{}</span><span class="w"> </span></span></span></code></pre></div><p>Similarly, we can enforce the fact that an input to a function has the same shape as the output:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Chapel" data-lang="Chapel"><span class="line"><span class="cl"><span class="k">proc</span><span class="w"> </span><span class="nf">transform</span><span class="p">(</span><span class="nx">input</span><span class="p">:</span><span class="w"> </span><span class="p">[?</span><span class="nx">D</span><span class="p">]</span><span class="w"> </span><span class="kt">int</span><span class="p">):</span><span class="w"> </span><span class="p">[</span><span class="nx">D</span><span class="p">]</span><span class="w"> </span><span class="kt">string</span><span class="p">;</span><span class="w"> </span></span></span></code></pre></div></li> <li> <p><strong>Consistency in generics</strong>. Suppose you have a generic function that declares a new variable of a given type, and just returns it:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Chapel" data-lang="Chapel"><span class="line"><span class="cl"><span class="k">proc</span><span class="w"> </span><span class="nf">defaultValue</span><span class="p">(</span><span class="kd">type</span><span class="w"> </span><span class="nx">argType</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">x</span><span class="p">:</span><span class="w"> </span><span class="nx">argType</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">x</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> </span></span></span></code></pre></div><p>Code like this exists in &ldquo;real&rdquo; Chapel software, by the way &mdash; the example is not contrived. By including the bounds etc. into the array type, we can ensure that <code>x</code> is appropriately allocated. Then, <code>defaultValue([1,2,3].type)</code> would return an array of three default-initialized integers.</p> </li> <li> <p><strong>Eliding boundary checking</strong>. Boundary checking is useful for safety, since it ensures that programs don&rsquo;t read or write past the end of allocated memory. However, bounds checking is also slow. Consider the following function that sums two arrays:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Chapel" data-lang="Chapel"><span class="line"><span class="cl"><span class="k">proc</span><span class="w"> </span><span class="nf">sumElementwise</span><span class="p">(</span><span class="nx">A</span><span class="p">:</span><span class="w"> </span><span class="p">[?</span><span class="nx">D</span><span class="p">]</span><span class="w"> </span><span class="kt">int</span><span class="p">,</span><span class="w"> </span><span class="nx">B</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="nx">D</span><span class="p">]</span><span class="w"> </span><span class="kt">int</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">C</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="nx">D</span><span class="p">]</span><span class="w"> </span><span class="kt">int</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="nx">idx</span><span class="w"> </span><span class="kd">in</span><span class="w"> </span><span class="nx">D</span><span class="w"> </span><span class="k">do</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nx">C</span><span class="p">[</span><span class="nx">idx</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">A</span><span class="p">[</span><span class="nx">idx</span><span class="p">]</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">B</span><span class="p">[</span><span class="nx">idx</span><span class="p">];</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> </span></span></span></code></pre></div><p>Since arrays <code>A</code>, <code>B</code>, and <code>C</code> have the same domain <code>D</code>, we don&rsquo;t need to do bound checking when accessing any of their elements. I don&rsquo;t believe this is currently an optimisation in Chapel, but it&rsquo;s certainly on the table.</p> </li> <li> <p><strong>Documentation</strong>. Including the size of the array as part of type signature clarifies the intent of the code being written. For instance, in the following function:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Chapel" data-lang="Chapel"><span class="line"><span class="cl"><span class="k">proc</span><span class="w"> </span><span class="nf">sendEmails</span><span class="p">(</span><span class="nx">numEmails</span><span class="p">:</span><span class="w"> </span><span class="kt">int</span><span class="p">,</span><span class="w"> </span><span class="nx">destinationAddrs</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="mi">1</span><span class="o">..</span><span class="nx">numEmails</span><span class="p">]</span><span class="w"> </span><span class="nx">address</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="cm">/* ... */</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span></code></pre></div><p>It&rsquo;s clear from the type of the <code>destinationAddrs</code>s that there ought to be exactly as many <code>destinationAddrs</code> as the number of emails that should be sent.</p> </li> </ul> <p>Okay, recap: C++ has <code>std::array</code>, which is a dependently-typed container that represents an array with a fixed number of elements. Chapel has something similar. I think these types are valuable.</p> <p>At this point, it sort of looks like I&rsquo;m impressed with Chapel for copying a C++ feature from 2011. Not so! As I played with Chapel programs more and more, arrays miraculously supported patterns that I knew I couldn&rsquo;t write in C++. The underlying foundation of Chapel&rsquo;s array types is quite unlike any other. Before we get to that, though, let&rsquo;s take a look at how dependent types are normally used (by us mere mortal software engineers).</p> <a href="#difficulties-with-dependent-types"> <h3 id="difficulties-with-dependent-types">Difficulties with Dependent Types</h3> </a> <p>Let&rsquo;s start by looking at a simple operation on fixed-length lists: reversing them. One might write a reverse function for &ldquo;regular&rdquo; lists, ignoring details like ownership, copying, that looks like this:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">reverse</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">);</span> </span></span></code></pre></div><p>This function is not general: it won&rsquo;t help us reverse lists of strings, for instance. The &ldquo;easy fix&rdquo; is to replace <code>int</code> with some kind of placeholder that can be replaced with any type.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="n">reverse</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">);</span> </span></span></code></pre></div><p>You can try compiling this code, but you will immediately run into an error. What the heck is <code>T</code>? Normally, when we name a variable, function, or type (e.g., by writing <code>vector</code>, <code>reverse</code>), we are referring to its declaration somewhere else. At this time, <code>T</code> is not declared anywhere. It just &ldquo;appears&rdquo; in our function&rsquo;s type. To fix this, we add a declaration for <code>T</code> by turning <code>reverse</code> into a template:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">template</span> <span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="o">&gt;</span> </span></span><span class="line"><span class="cl"><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="n">reverse</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">);</span> </span></span></code></pre></div><p>The new <code>reverse</code> above takes two arguments: a type and a list of values of that type. So, to <em>really</em> call this <code>reverse</code>, we need to feed the type of our list&rsquo;s elements into it. This is normally done automatically (in C++ and otherwise) but under the hood, invocations might look like this:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">reverse</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">({</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">});</span> <span class="c1">// produces 3, 2, 1 </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">reverse</span><span class="o">&lt;</span><span class="n">string</span><span class="o">&gt;</span><span class="p">({</span><span class="s">&#34;world&#34;</span><span class="p">,</span> <span class="s">&#34;hello&#34;</span><span class="p">})</span> <span class="c1">// produces &#34;hello&#34;, &#34;world&#34; </span></span></span></code></pre></div><p>This is basically what we have to do to write <code>reverse</code> on <code>std::array</code>, which, includes an additional parameter that encodes its length. We might start with the following (using <code>n</code> as a placeholder for length, and observing that reversing an array doesn&rsquo;t change its length):</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">std</span><span class="o">::</span><span class="n">array</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span> <span class="n">n</span><span class="o">&gt;</span> <span class="n">reverse</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">array</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span> <span class="n">n</span><span class="o">&gt;</span><span class="p">);</span> </span></span></code></pre></div><p>Once again, to make this compile, we need to add template parameters for <code>T</code> and <code>n</code>.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">template</span> <span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="p">,</span> <span class="n">size_t</span> <span class="n">n</span><span class="o">&gt;</span> </span></span><span class="line"><span class="cl"><span class="n">std</span><span class="o">::</span><span class="n">array</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span> <span class="n">n</span><span class="o">&gt;</span> <span class="n">reverse</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">array</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span> <span class="n">n</span><span class="o">&gt;</span><span class="p">);</span> </span></span></code></pre></div><p>Now, you might be asking&hellip;</p> <p class="dialog"> <span class="message side-question"> <span class="message-sender"><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#moon"/> </svg></span> <span class="message-text"> This section is titled "Difficulties with Dependent Types". What's the difficulty? </span> </span> </p> <p>Well, here&rsquo;s the kicker. C++ templates are a <strong>compile-time mechanism</strong>. As a result, arguments to <code>template</code> (like <code>T</code> and <code>n</code>) must be known when the program is being compiled. This, in turn, means <span class="sidenote"> <label class="sidenote-label" for="deptype-note">the following program doesn&rsquo;t work:</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="deptype-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> The observant reader might have noticed that one of the Chapel programs we saw above, <code>sendEmails</code>, does something similar. The <code>numEmails</code> argument is used in the type of the <code>destinationAddrs</code> parameter. That program is valid Chapel. <span class="sidenote-delimiter">]</span> </span> </span> </p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">buildArray</span><span class="p">(</span><span class="n">size_t</span> <span class="n">len</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">array</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span> <span class="n">len</span><span class="o">&gt;</span> <span class="n">myArray</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="c1">// do something with myArray </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> </span></span></code></pre></div><p>You can&rsquo;t use these known-length types like <code>std::array</code> with any length that is not known at compile-time. But that&rsquo;s a lot of things! If you&rsquo;re reading from an input file, chances are, you don&rsquo;t know how big that file is. If you&rsquo;re writing a web server, you likely don&rsquo;t know the length the HTTP requests. With every setting a user can tweak when running your code, you sacrifice the ability to use templated types.</p> <p>Also, how do you <em>return</em> a <code>std::array</code>? If the size of the returned array is known in advance, you just list that size:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">std</span><span class="o">::</span><span class="n">array</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span> <span class="mi">10</span><span class="o">&gt;</span> <span class="n">createArray</span><span class="p">();</span> </span></span></code></pre></div><p>If the size is not known at compile-time, you might want to do something like the following &mdash; using an argument <code>n</code> in the type of the returned array &mdash; but it would not compile:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">auto</span> <span class="nf">computeNNumbers</span><span class="p">(</span><span class="n">size_t</span> <span class="n">n</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">std</span><span class="o">::</span><span class="n">array</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span> <span class="n">n</span><span class="o">&gt;</span><span class="p">;</span> <span class="c1">// not valid C++ </span></span></span></code></pre></div><p>Moreover, you actually can&rsquo;t use <code>createArray</code> to figure out the required array size, and <em>then</em> return an array that big, even if in the end you only used compile-time-only computations in the body of <code>createArray</code>. What you would need is to provide a &ldquo;bundle&rdquo; of a value and a type that is somehow built from that value.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="c1">// magic_pair is invented syntax, will not even remotely work </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">auto</span> <span class="nf">createArray</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="n">magic_pair</span><span class="o">&lt;</span><span class="n">size_t</span> <span class="n">size</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">array</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span> <span class="n">size</span><span class="o">&gt;&gt;</span><span class="p">;</span> </span></span></code></pre></div><p>This pair contains a <code>size</code> (suppose it&rsquo;s known at compilation time for the purposes of appeasing C++) as well as an array that uses that <code>size</code> as its template argument. This is not real C++ &ndash; not even close &ndash; but such pairs are a well-known concept. They are known as <a href="https://unimath.github.io/agda-unimath/foundation.dependent-pair-types.html"class="external-link">dependent pairs<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, or, if you&rsquo;re trying to impress people, \(\Sigma\)-types. In Idris, you could write <code>createArray</code> like this:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">createArray</span> <span class="ow">:</span> <span class="ow">()</span> <span class="ow">-&gt;</span> <span class="ow">(</span>n <span class="ow">:</span> <span class="kt">Nat</span> <span class="ow">**</span> <span class="kt">Vec</span> n <span class="kt">Int</span><span class="ow">)</span> </span></span></code></pre></div><p>There are languages out there &ndash; that are not C++, alas &ndash; that support dependent pairs, and as a result make it more convenient to use types that depend on values. Not only that, but a lot of these languages do not force dependent types to be determined at compile-time. You could write that coveted <code>readArrayFromFile</code> function:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">readArrayFromFile</span> <span class="ow">:</span> <span class="kt">String</span> <span class="ow">-&gt;</span> <span class="kt">IO</span> <span class="ow">(</span>n <span class="ow">:</span> <span class="kt">Nat</span> <span class="ow">**</span> <span class="kt">Vec</span> n <span class="kt">String</span><span class="ow">)</span> </span></span></code></pre></div><p>Don&rsquo;t mind <code>IO</code>; in pure languages like Idris, this type is a necessity when interacting when reading data in and sending it out. The key is that <code>readArrayFromFile</code> produces, at runtime, a pair of <code>n</code>, which is the size of the resulting array, and a <code>Vec</code> of that many <code>String</code>s (e.g., one string per line of the file).</p> <p>Dependent pairs are cool and very general. However, the end result of types with bounds which are not determined at compile-time is that you&rsquo;re <em>required</em> to use dependent pairs. Thus, you must always carry the array&rsquo;s length together with the array itself.</p> <p>The bottom line is this:</p> <ul> <li> <p>In true dependently typed languages, a type that depends on a value (like <code>Vec</code> in Idris) lists that value in its type. When this value is listed by referring to an identifier &mdash; like <code>n</code> in <code>Vec n String</code> above &mdash; this identifier has to be defined somewhere, too. This necessitates dependent pairs, in which the first element is used syntactically as the &ldquo;definition point&rdquo; of a type-level value. For example, in the following piece of code:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="ow">(</span>n <span class="ow">:</span> <span class="kt">Nat</span> <span class="ow">**</span> <span class="kt">Vec</span> n <span class="kt">String</span><span class="ow">)</span> </span></span></code></pre></div><p>The <code>n : Nat</code> part of the pair serves both to say that the first element is a natural number, and to introduce a variable <code>n</code> that refers to this number so that the second type (<code>Vec n String</code>) can refer to it.</p> <p>A lot of the time, you end up carrying this extra value (bound to <code>n</code> above) with your type.</p> </li> <li> <p>In more mainstream languages, things are even more restricted: dependently typed values are a compile-time property, and thus, cannot be used with runtime values like data read from a file, arguments passed in to a function, etc..</p> </li> </ul> <a href="#hiding-runtime-values-from-the-type"> <h3 id="hiding-runtime-values-from-the-type">Hiding Runtime Values from the Type</h3> </a> <p>Let&rsquo;s try to think of ways to make things more convenient. First of all, as we saw, in Idris, it&rsquo;s possible to use runtime values in types. Not only that, but Idris is a compiled language, so presumably we can compile dependently typed programs with runtime-enabled dependent types. The trick is to forget some information: turn a vector <code>Vec n String</code> into two values (the size of the vector and the vector itself), and forget &ndash; for the purposes of generating code &ndash; that they&rsquo;re related. Whenever you pass in a <code>Vec n String</code>, you can compile that similarly to how you&rsquo;d compile passing in a <code>Nat</code> and <code>List String</code>. Since the program has already been type checked, you can be assured that you don&rsquo;t encounter cases when the size and the actual vector are mismatched, or anything else of that nature.</p> <p>Additionally, you don&rsquo;t always need the length of the vector at all. In a good chunk of Idris code, the size arguments are only used to ensure type correctness and rule out impossible cases; they are never accessed at runtime. As a result, you can <em>erase</em> the size of the vector altogether. In fact, <a href="https://github.com/idris-lang/Idris2/"class="external-link">Idris 2<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> leans on <a href="https://bentnib.org/quantitative-type-theory.html"class="external-link">Quantitative Type Theory<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> to make erasure easier.</p> <p>At this point, one way or another, we&rsquo;ve &ldquo;entangled&rdquo; the vector with a value representing its size:</p> <ul> <li>When a vector of some (unknown, but fixed) length needs to be produced from a function, we use dependent pairs.</li> <li>Even in other cases, when compiling, we end up treating a vector as a length value and the vector itself.</li> </ul> <p>Generally speaking, a good language design practice is to hide extraneous complexity, and to remove as much boilerplate as necessary. If the size value of a vector is always joined at the hip with the vector, can we avoid having to explicitly write it?</p> <p>This is pretty much exactly what Chapel does. It <em>allows</em> explicitly writing the domain of an array as part of its type, but doesn&rsquo;t <em>require</em> it. When you do write it (re-using my original snippet above):</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Chapel" data-lang="Chapel"><span class="line"><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">A</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="mi">0</span><span class="o">..</span><span class="mi">9</span><span class="p">]</span><span class="w"> </span><span class="kt">int</span><span class="p">;</span><span class="w"> </span></span></span></code></pre></div><p>What you are really doing is creating a value (the <a href="https://chapel-lang.org/docs/primers/ranges.html"class="external-link">range<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> <code>0..9</code>), and entangling it with the type of <code>A</code>. This is very similar to what a language like Idris would do under the hood to compile a <code>Vec</code>, though it&rsquo;s not quite the same.</p> <p>At the same time, you can write code that omits the bounds altogether:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Chapel" data-lang="Chapel"><span class="line"><span class="cl"><span class="k">proc</span><span class="w"> </span><span class="nf">processArray</span><span class="p">(</span><span class="nx">A</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w"> </span><span class="kt">int</span><span class="p">):</span><span class="w"> </span><span class="kt">int</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">proc</span><span class="w"> </span><span class="nf">createArray</span><span class="p">():</span><span class="w"> </span><span class="p">[]</span><span class="w"> </span><span class="kt">int</span><span class="p">;</span><span class="w"> </span></span></span></code></pre></div><p>In all of these examples, there is an implicit runtime value (the bounds) that is associated with the array&rsquo;s type. However, we are never forced to explicitly thread through or include a size. Where reasoning about them is not necessary, Chapel&rsquo;s domains are hidden away. Chapel refers to the implicitly present value associated with an array type as its <em>runtime type</em>.</p> <p>I hinted earlier that things are not quite the same in this representation as they are in my simplified model of Idris. In Idris, as I mentioned earlier, the values corresponding to vectors&rsquo; indices can be erased if they are not used. In Chapel, this is not the case &mdash; a domain always exists at runtime. At the surface level, this means that you may pay for more than what you use. However, domains enable a number of interesting patterns of array code. We&rsquo;ll get to that in a moment; first, I want to address a question that may be on your mind:</p> <p class="dialog"> <span class="message side-question"> <span class="message-sender"><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#moon"/> </svg></span> <span class="message-text"> At this point, this looks just like keeping a <code>.length</code> field as part of the array value. Most languages do this. What's the difference between this and Chapel's approach? </span> </span> </p> <p>This is a fair question. The key difference is that the length exists even if an array does not. The following is valid Chapel code (re-using the <code>defaultValue</code> snippet above):</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Chapel" data-lang="Chapel"><span class="line"><span class="cl"><span class="k">proc</span><span class="w"> </span><span class="nf">defaultValue</span><span class="p">(</span><span class="kd">type</span><span class="w"> </span><span class="nx">argType</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">x</span><span class="p">:</span><span class="w"> </span><span class="nx">argType</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">x</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">proc</span><span class="w"> </span><span class="nf">doSomething</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">type</span><span class="w"> </span><span class="nx">MyArray</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="mi">1</span><span class="o">..</span><span class="mi">10</span><span class="p">]</span><span class="w"> </span><span class="kt">int</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">A</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">defaultValue</span><span class="p">(</span><span class="nx">MyArray</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> </span></span></span></code></pre></div><p>Here, we created an array <code>A</code> with the right size (10 integer elements) without having another existing array as a reference. This might seem like a contrived example (I could&rsquo;ve just as well written <code>var A: [1..10] int</code>), but the distinction is incredibly helpful for generic programming. Here&rsquo;s a piece of code from the Chapel standard library, which implements a part of Chapel&rsquo;s <a href="https://chapel-lang.org/docs/primers/reductions.html"class="external-link">reduction<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> support:</p> <div class="highlight-group" data-base-path="%!s(<nil>)" data-file-path="modules/internal/ChapelReduce.chpl"> <div class="highlight-label">From <a href="https://github.com/chapel-lang/chapel/blob/e8ff8ee9a67950408cc6d4c3220ac647817ddae3/modules/internal/ChapelReduce.chpl#L146">ChapelReduce.chpl</a>, around line 146</div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Chapel" data-lang="Chapel"><span class="line"><span class="cl"><span class="w"> </span><span class="k">inline</span><span class="w"> </span><span class="k">proc</span><span class="w"> </span><span class="nf">identity</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">x</span><span class="p">:</span><span class="w"> </span><span class="nx">chpl__sumType</span><span class="p">(</span><span class="nx">eltType</span><span class="p">);</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">x</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span></span></span></code></pre></div> </div> <p>Identity elements are important when performing operations like sums and products, for many reasons. For one, they tell you what the sum (e.g.) should be when there are no elements at all. For another, they can be used as an initial value for an accumulator. In Chapel, when you are performing a reduction, there is a good chance you will need several accumulators &mdash; one for each thread performing a part of the reduction.</p> <p>That <code>identity</code> function looks almost like <code>defaultValue</code>! Since it builds the identity element from the type, and since the type includes the array&rsquo;s dimensions, summing an array-of-arrays, even if it&rsquo;s empty, will produce the correct output.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Chapel" data-lang="Chapel"><span class="line"><span class="cl"><span class="kd">type</span><span class="w"> </span><span class="nx">Coordinate</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="mi">1</span><span class="o">..</span><span class="mi">3</span><span class="p">]</span><span class="w"> </span><span class="kt">real</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">var</span><span class="w"> </span><span class="nx">Empty</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="mi">0</span><span class="o">..&lt;</span><span class="mi">0</span><span class="p">]</span><span class="w"> </span><span class="nx">Coordinate</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nx">writeln</span><span class="p">(</span><span class="o">+</span><span class="w"> </span><span class="k">reduce</span><span class="w"> </span><span class="nx">Empty</span><span class="p">);</span><span class="w"> </span><span class="c1">// sum up an empty list of coordinates </span></span></span></code></pre></div><p>As I mentioned before, having the domain be part of the type can also enable indexing optimizations &mdash; without any need for <a href="https://en.wikipedia.org/wiki/Interprocedural_optimization"class="external-link">interprocedural analysis<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> &mdash; in functions like <code>sumElementwise</code>:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Chapel" data-lang="Chapel"><span class="line"><span class="cl"><span class="k">proc</span><span class="w"> </span><span class="nf">sumElementwise</span><span class="p">(</span><span class="nx">A</span><span class="p">:</span><span class="w"> </span><span class="p">[?</span><span class="nx">D</span><span class="p">]</span><span class="w"> </span><span class="kt">int</span><span class="p">,</span><span class="w"> </span><span class="nx">B</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="nx">D</span><span class="p">]</span><span class="w"> </span><span class="kt">int</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">C</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="nx">D</span><span class="p">]</span><span class="w"> </span><span class="kt">int</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="nx">idx</span><span class="w"> </span><span class="kd">in</span><span class="w"> </span><span class="nx">D</span><span class="w"> </span><span class="k">do</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nx">C</span><span class="p">[</span><span class="nx">idx</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">A</span><span class="p">[</span><span class="nx">idx</span><span class="p">]</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">B</span><span class="p">[</span><span class="nx">idx</span><span class="p">];</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> </span></span></span></code></pre></div><p>The C++ equivalent of this function &ndash; using <code>vectors</code> to enable arbitrary-size lists of numbers read from user input, and <code>.at</code> to enable bounds checks &mdash; does not include enough information for this optimization to be possible.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">sumElementwise</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">A</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">B</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">C</span><span class="p">(</span><span class="n">A</span><span class="p">.</span><span class="n">size</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="n">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">A</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">C</span><span class="p">.</span><span class="n">at</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="o">=</span> <span class="n">A</span><span class="p">.</span><span class="n">at</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="o">+</span> <span class="n">B</span><span class="p">.</span><span class="n">at</span><span class="p">(</span><span class="n">i</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></div><p>All in all, this makes for a very interesting mix of features:</p> <ul> <li><strong>Chapel arrays have their bounds as part of types</strong>, like <code>std::array</code> in C++ and <code>Vec</code> in Idris. This enables all the benefits I&rsquo;ve described above.</li> <li><strong>The bounds don&rsquo;t have to be known at compile-time</strong>, like all dependent types in Idris. This means you can read arrays from files (e.g.) and still reason about their bounds as part of the type system.</li> <li><strong>Domain information can be hidden when it&rsquo;s not used</strong>, and does not require explicit additional work like template parameters or dependent pairs.</li> </ul> <p>Most curiously, runtime types only extend to arrays and domains. In that sense, they are not a general purpose replacement for dependent types. Rather, they make arrays and domains special, and single out the exact case my professor was <a href="#bounds-quote"class="same-page-link">talking about in the introduction</a>. Although at times I&rsquo;ve <a href="https://chapel-lang.org/blog/posts/linear-multistep/"class="external-link">twisted Chapel&rsquo;s type system in unconventional ways<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> to simulate dependent types, rarely have I felt a need for them while programming in Chapel. In that sense &mdash; and in the &ldquo;practical software engineering&rdquo; domain &mdash; I may have been proven wrong.</p> <a href="#pitfalls-of-runtime-types"> <h3 id="pitfalls-of-runtime-types">Pitfalls of Runtime Types</h3> </a> <p>Should all languages do things the way Chapel does? I don&rsquo;t think so. Like most features, runtime types like that in Chapel are a language design tradeoff. Though I&rsquo;ve covered their motivation and semantics, perhaps I should mention the downsides.</p> <p>The greatest downside is that, generally speaking, <em>types are not always a compile-time property</em>. We saw this earlier with <code>MyArray</code>:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Chapel" data-lang="Chapel"><span class="line"><span class="cl"><span class="kd">type</span><span class="w"> </span><span class="nx">MyArray</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="mi">1</span><span class="o">..</span><span class="mi">10</span><span class="p">]</span><span class="w"> </span><span class="kt">int</span><span class="p">;</span><span class="w"> </span></span></span></code></pre></div><p>Here, the domain of <code>MyArray</code> (one-dimensional with bounds <code>1..10</code>) is a runtime value. It has an <span class="sidenote"> <label class="sidenote-label" for="dce-note">execution-time cost.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="dce-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> The execution-time cost is, of course, modulo <a href="https://en.wikipedia.org/wiki/Dead-code_elimination">dead code elimination</a> etc.. If my snippet made up the entire program being compiled, the end result would likely do nothing, since <code>MyArray</code> isn't used anywhere. <span class="sidenote-delimiter">]</span> </span> </span> Moreover, types that serve as arguments to functions (like <code>argType</code> for <code>defaultValue</code>), or as their return values (like the result of <code>chpl__sumType</code>) also have an execution-time backing. This is quite different from most compiled languages. For instance, in C++, templates are &ldquo;stamped out&rdquo; when the program is compiled. A function with a <code>typename T</code> template parameter called with type <code>int</code>, in terms of generated code, is always the same as a function where you search-and-replaced <code>T</code> with <code>int</code>. This is called <a href="https://en.wikipedia.org/wiki/Monomorphization"class="external-link">monomorphization<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, by the way. In Chapel, however, if the function is instantiated with an array type, it will have an additional parameter, which represents the runtime component of the array&rsquo;s type.</p> <p>The fact that types are runtime entities means that compile-time type checking is insufficient. Take, for instance, the above <code>sendEmails</code> function:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Chapel" data-lang="Chapel"><span class="line"><span class="cl"><span class="k">proc</span><span class="w"> </span><span class="nf">sendEmails</span><span class="p">(</span><span class="nx">numEmails</span><span class="p">:</span><span class="w"> </span><span class="kt">int</span><span class="p">,</span><span class="w"> </span><span class="nx">destinationAddrs</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="mi">1</span><span class="o">..</span><span class="nx">numEmails</span><span class="p">]</span><span class="w"> </span><span class="nx">address</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="cm">/* ... */</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span></code></pre></div><p>Since <code>numEmails</code> is a runtime value (it&rsquo;s a regular argument!), we can&rsquo;t ensure at compile-time that a value of some array matches the <code>[1..numEmails] address</code> type. As a result, Chapel defers bounds checking to when the <code>sendEmails</code> function is invoked.</p> <p>This leads to some interesting performance considerations. Take two Chapel records (similar to <code>struct</code>s in C++) that simply wrap a value. In one of them, we provide an explicit type for the field, and in the other, we leave the field type generic.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Chapel" data-lang="Chapel"><span class="line"><span class="cl"><span class="k">record</span><span class="w"> </span><span class="nc">R1</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">field</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="mi">1</span><span class="o">..</span><span class="mi">10</span><span class="p">]</span><span class="w"> </span><span class="kt">int</span><span class="p">;</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">record</span><span class="w"> </span><span class="nc">R2</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">field</span><span class="p">;</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">var</span><span class="w"> </span><span class="nx">A</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">6</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span><span class="mi">9</span><span class="p">,</span><span class="mi">10</span><span class="p">];</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">var</span><span class="w"> </span><span class="nx">r1</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="nx">R1</span><span class="p">(</span><span class="nx">A</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">var</span><span class="w"> </span><span class="nx">r2</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="nx">R2</span><span class="p">(</span><span class="nx">A</span><span class="p">);</span><span class="w"> </span></span></span></code></pre></div><p>In a conversation with a coworker, I learned that these are not the same. That&rsquo;s because the record <code>R1</code> explicitly specifies a type for <code>field</code>. Since the type has a runtime component, the constructor of <code>R1</code> will actually perform a runtime check to ensure that the argument has 10 elements. <code>R2</code> will not do this, since there isn&rsquo;t any other type to check against.</p> <p>Of course, the mere existence of an additional runtime component is a performance consideration. To ensure that Chapel programs perform as well as possible, the Chapel standard library attempts to avoid using runtime components wherever possible. This leads to a distinction between a &ldquo;static type&rdquo; (known at compile-time) and a &ldquo;dynamic type&rdquo; (requiring a runtime value). The <code>chpl__sumType</code> function we saw mentioned above uses static components of types, because we don&rsquo;t want each call to <code>+ reduce</code> to attempt to run a number of extraneous runtime queries.</p> <a href="#conclusion"> <h3 id="conclusion">Conclusion</h3> </a> <p>Though runtime types are not a silver bullet, I find them to be an elegant middle-ground solution to the problem of tracking array bounds. They enable optimizations, generic programming, and more, without the complexity of a fully dependently-typed language. They are also quite unlike anything I&rsquo;ve seen in any other language.</p> <p>What&rsquo;s more, this post only scratches the surface of what&rsquo;s possible using arrays and domains. Besides encoding array bounds, domains include information about how an array is distributed across several nodes (see the <a href="https://chapel-lang.org/docs/primers/distributions.html"class="external-link">distributions primer<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>), and how it&rsquo;s stored in memory (see the <a href="https://chapel-lang.org/blog/posts/announcing-chapel-2.3/#sparse-computations"class="external-link">sparse computations<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> section of the recent 2.3 release announcement). In general, they are a very flavorful component to Chapel&rsquo;s &ldquo;special sauce&rdquo; as a language for parallel computing.</p> <p>You can read more about arrays and domains in the <a href="https://chapel-lang.org/docs/primers/arrays.html"class="external-link">corresponding primer<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>.</p> Implementing and Verifying "Static Program Analysis" in Agda, Part 9: Verifying the Forward Analysis https://danilafe.com/blog/09_spa_agda_verified_forward/ Wed, 25 Dec 2024 19:00:00 -0800 https://danilafe.com/blog/09_spa_agda_verified_forward/ <p>In the previous post, we put together a number of powerful pieces of machinery to construct a sign analysis. However, we still haven&rsquo;t verified that this analysis produces correct results. For the most part, we already have the tools required to demonstrate correctness; the most important one is the <a href="https://danilafe.com/blog/07_spa_agda_semantics_and_cfg/">validity of our CFGs</a> relative to <a href="https://danilafe.com/blog/05_spa_agda_semantics/">the semantics of the little language</a>.</p> <a href="#high-level-algorithm"> <h3 id="high-level-algorithm">High-Level Algorithm</h3> </a> <p>We&rsquo;ll keep working with the sign lattice as an example, keeping in mind how what we do generalizes to a any lattice \(L\) describing a variable&rsquo;s state. The general shape of our argument will be as follows, where I&rsquo;ve underlined and numbered assumptions or aspects that we have yet to provide.</p> <ol> <li> <p>Our fixed-point analysis from the previous section gave us a result \(r\) that satisfies the following equation:</p> $$ r = \text{update}(\text{join}(r)) $$ <p>Above \(\text{join}\) applies the <a href="https://danilafe.com/blog/08_spa_agda_forward/#join-preds">predecessor-combining function</a> from the previous post to each state (corresponding to <code>joinAll</code> in Agda) and \(\text{update}\) performs one round of abstract interpretation.</p> </li> <li> <p>Because of the <a href="https://danilafe.com/blog/07_spa_agda_semantics_and_cfg/">correspondence of our semantics and CFGs</a>, each program evaluation in the form \(\rho, s \Rightarrow \rho'\) corresponds to a path through the Control Flow Graph. Along the path, each node contains simple statements, which correspond to intermediate steps in evaluating the program. These will also be in the form \(\rho_1, b \Rightarrow \rho_2\).</p> </li> <li> <p>We will proceed iteratively, stepping through the trace one basic block at a time. At each node in the graph:</p> <ul> <li> <p>We will assume that the beginning state (the variables in \(\rho_1\)) are <span class="internal-ref" id="internal-ref-correctly-described"> correctly described <span class="internal-ref-counter">1</span></span> by one of the predecessors of the current node. Since <span class="internal-ref" id="internal-ref-disjunction"> joining represents "or" <span class="internal-ref-counter">2</span></span>, that is the same as saying that \(\text{join}(r)\) contains an accurate description of \(\rho_1\).</p> </li> <li> <p>Because <span class="internal-ref" id="internal-ref-abstract-interpretation"> the abstract interpretation function preserves accurate descriptions <span class="internal-ref-counter">3</span></span>, if \(\text{join}(r)\) contains an accurate description \(\rho_1\), then applying our abstract interpretation function via \(\text{update}\) should result in a map that contains an accurate-described \(\rho_2\). In other words, \(\text{update}(\text{join}(r))\) describes \(\rho_2\) at the current block. <span class="internal-ref" id="internal-ref-equivalence"> By the equation above <span class="internal-ref-counter">4</span></span>, that&rsquo;s the same as saying \(r\) describes \(\rho_2\) at the current block.</p> </li> <li> <p>Since the trace is a path through a graph, there must be an edge from the current basic block to the next. This means that the current basic block is a predecessor of the next one. From the previous point, we know that \(\rho_2\) is accurately described by this predecessor, fulfilling our earlier assumption and allowing us to continue iteration.</p> </li> </ul> </li> </ol> <p>So, what are the missing pieces?</p> <ol> <li>We need to define what it means for a lattice (like our sign lattice) to &ldquo;correctly describe&rdquo; what happens when evaluating a program for real. For example, the \(+\) in sign analysis describes values that are bigger than zero, and a map like <code>{x:+}</code> states that <code>x</code> can only take on positive values.</li> <li>We&rsquo;ve seen before <a href="https://danilafe.com/blog/01_spa_agda_lattices/#lub-glub-or-and">the \((\sqcup)\) operator models disjunction (&ldquo;A or B&rdquo;)</a>, but that was only an informal observation; we&rsquo;ll need to specify it preceisely.</li> <li>Each analysis <a href="https://danilafe.com/blog/08_spa_agda_forward/#general-evaluator">provides an abstract interpretation <code>eval</code> function</a>. However, until now, nothing has formally constrained this function; we could return \(+\) in every case, even though that would not be accurate. We will need, for each analysis, a proof that its <code>eval</code> preserves accurate descriptions.</li> <li>The equalities between our lattice elements <a href="https://danilafe.com/blog/01_spa_agda_lattices/#definitional-equality">are actually equivalences</a>, which helps us use simpler representations of data structures. Thus, even in statements of the fixed point algorithm, our final result is a value \(a\) such that \(a \approx f(a)\). We need to prove that our notion of equivalent lattice elements plays nicely with correctness.</li> </ol> <p>Let&rsquo;s start with the first bullet point.</p> <a href="#a-formal-definition-of-correctness"> <h3 id="a-formal-definition-of-correctness">A Formal Definition of Correctness</h3> </a> <p>When a variable is mapped to a particular sign (like <code>{ &quot;x&quot;: + }</code>), what that really says is that the value of <code>x</code> is greater than zero. Recalling from <a href="https://danilafe.com/blog/05_spa_agda_semantics/#notation-for-environments">the post about our language&rsquo;s semantics</a> that we use the symbol \(\rho\) to represent mappings of variables to their values, we might write this claim as:</p> $$ \rho(\texttt{x}) &amp;gt; 0 $$ <p>This is a good start, but it&rsquo;s a little awkward defining the meaning of &ldquo;plus&rdquo; by referring to the context in which it&rsquo;s used (the <code>{ &quot;x&quot;: ... }</code> portion of the expression above). Instead, let&rsquo;s associate with each sign (like \(+\)) a predicate: a function that takes a value, and makes a claim about that value (&ldquo;this is positive&rdquo;):</p> $$ \llbracket &#43; \rrbracket\ v = v &amp;gt; 0 $$ <p>The notation above is a little weird unless you, like me, have a background in Programming Language Theory (❤️). This comes from <a href="https://en.wikipedia.org/wiki/Denotational_semantics"class="external-link">denotational semantics<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>; generally, one writes:</p> $$ \llbracket \text{thing} \rrbracket = \text{the meaning of the thing} $$ <p>Where \(\llbracket \cdot \rrbracket\) is really a function (we call it the <em>semantic function</em>) that maps things to their meaning. Then, the above equation is similar to the more familiar \(f(x) = x+1\): function and arguments on the left, definition on the right. When the &ldquo;meaning of the thing&rdquo; is itself a function, we could write it explicitly using lambda-notation:</p> $$ \llbracket \text{thing} \rrbracket = \lambda x.\ \text{body of the function} $$ <p>Or, we could use the Haskell style and write the new variable on the left of the equality:</p> $$ \llbracket \text{thing} \rrbracket\ x = \text{body of the function} $$ <p>That is precisely what I&rsquo;m doing above with \(\llbracket + \rrbracket\). With this in mind, we could define the entire semantic function for the sign lattice as follows:</p> $$ \llbracket &#43; \rrbracket\ v = v\ \texttt{&amp;gt;}\ 0 \\ \llbracket 0 \rrbracket\ v = v\ \texttt{=}\ 0 \\ \llbracket - \rrbracket\ v = v\ \texttt{&amp;lt;}\ 0 \\ \llbracket \top \rrbracket\ v = \text{true} \\ \llbracket \bot \rrbracket\ v = \text{false} $$ <p>In Agda, the integer type already distinguishes between &ldquo;negative natural&rdquo; or &ldquo;positive natural&rdquo; cases, which made it possible to define the semantic function <span class="sidenote"> <label class="sidenote-label" for="without-note">without using inequalities.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="without-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Reasoning about inequalities is painful, sometimes requiring a number of lemmas to arrive at a result that is intuitively obvious. Coq has a powerful tactic called <a href="https://coq.inria.fr/doc/v8.11/refman/addendum/micromega.html#coq:tacn.lia"><code>lia</code></a> that automatically solves systems of inequalities, and I use it liberally. However, lacking such a tactic in Agda, I would like to avoid inequalities if they are not needed. <span class="sidenote-delimiter">]</span> </span> </span> </p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Sign.agda" data-first-line="114" data-last-line="119" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Sign.agda#L114-L119">Sign.agda</a>, lines 114 through 119</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">114 </span><span class="lnt">115 </span><span class="lnt">116 </span><span class="lnt">117 </span><span class="lnt">118 </span><span class="lnt">119 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="nf">⟦_⟧ᵍ</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>SignLattice<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Value<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>⟦_⟧ᵍ<span class="w"> </span>⊥ᵍ<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>⟦_⟧ᵍ<span class="w"> </span>⊤ᵍ<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊤<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>⟦_⟧ᵍ<span class="w"> </span>[<span class="w"> </span>+<span class="w"> </span>]ᵍ<span class="w"> </span>v<span class="w"> </span><span class="ow">=</span><span class="w"> </span>Σ<span class="w"> </span>ℕ<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>n<span class="w"> </span><span class="ow">→</span><span class="w"> </span>v<span class="w"> </span>≡<span class="w"> </span>↑ᶻ<span class="w"> </span><span class="o">(</span>+_<span class="w"> </span><span class="o">(</span>suc<span class="w"> </span>n<span class="o">)))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>⟦_⟧ᵍ<span class="w"> </span>[<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span>]ᵍ<span class="w"> </span>v<span class="w"> </span><span class="ow">=</span><span class="w"> </span>v<span class="w"> </span>≡<span class="w"> </span>↑ᶻ<span class="w"> </span><span class="o">(</span>+_<span class="w"> </span>zero<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>⟦_⟧ᵍ<span class="w"> </span>[<span class="w"> </span>-<span class="w"> </span>]ᵍ<span class="w"> </span>v<span class="w"> </span><span class="ow">=</span><span class="w"> </span>Σ<span class="w"> </span>ℕ<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>n<span class="w"> </span><span class="ow">→</span><span class="w"> </span>v<span class="w"> </span>≡<span class="w"> </span>↑ᶻ<span class="w"> </span>-[1+<span class="w"> </span>n<span class="w"> </span>]<span class="o">)</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Notably, \(\llbracket \top \rrbracket\ v\) always holds, and \(\llbracket \bot \rrbracket\ v\) never does. <strong>In general</strong>, we will always need to define a semantic function for whatever lattice we are choosing for our analysis.</p> <p>It&rsquo;s important to remember from the previous post that the sign lattice (or, more generally, our lattice \(L\)) is only a component of the <a href="https://danilafe.com/blog/08_spa_agda_forward/#whole-lattice">lattice we use to instantiate the analysis</a>. We at least need to define what it means for the \(\text{Variable} \to \text{Sign}\) portion of that lattice to be correct. This way, we&rsquo;ll have correctness criteria for each key (CFG node) in the top-level \(\text{Info}\) lattice. Since a map from variables to their sign characterizes not a single value \(v\) but a whole environment \(\rho\), something like this is a good start:</p> $$ \llbracket \texttt{\{} x_1: s_1, ..., x_n: s_n \texttt{\}} \rrbracket\ \rho = \llbracket s_1 \rrbracket\ \rho(x_1)\ \text{and}\ ...\ \text{and}\ \llbracket s_n \rrbracket\ \rho(x_n) $$ <p>As a concrete example, we might get:</p> $$ \llbracket \texttt{\{} \texttt{x}: &#43;, \texttt{y}: - \texttt{\}} \rrbracket\ \rho = \rho(\texttt{x})\ \texttt{&amp;gt;}\ 0\ \text{and}\ \rho(\texttt{y})\ \texttt{&amp;lt;}\ 0 $$ <p>This is pretty good, but not quite right. For instance, the initial state of the program &mdash; before running the analysis &mdash; assigns \(\bot\) to each element. This is true because our fixed-point algorithm <a href="https://danilafe.com/blog/04_spa_agda_fixedpoint/#start-least">starts with the least element of the lattice</a>. But even for a single-variable map <code>{x: ⊥ }</code>, the semantic function above would give:</p> $$ \llbracket \texttt{\{} \texttt{x}: \bot \texttt{\}} \rrbracket\ \rho = \text{false} $$ <p>That&rsquo;s clearly not right: our initial state should be possible, lest the entire proof be just a convoluted <a href="https://en.wikipedia.org/wiki/Principle_of_explosion"class="external-link"><em>ex falso</em><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>!</p> <p>There is another tricky aspect of our analysis, which is primarily defined <a href="https://danilafe.com/blog/08_spa_agda_forward/#join-preds">using the join (\(\sqcup\)) operator</a>. Observe the following example:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="c1">// initial state: { x: ⊥ } </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">if</span> <span class="n">b</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">x</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">// state: { x: + } </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> <span class="k">else</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// state unchanged: { x: ⊥ } </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="c1">// state: { x: + } ⊔ { x: ⊥ } = { x: + } </span></span></span></code></pre></div><p>Notice that in the final state, the sign of <code>x</code> is <code>+</code>, even though when <code>b</code> is <code>false</code>, the variable is never set. In a simple language like ours, without variable declaration points, this is probably the best we could hope for. The crucial observation, though, is that the oddness only comes into play with variables that are not set. In the &ldquo;initial state&rdquo; case, none of the variables have been modified; in the <code>else</code> case of the conditional, <code>x</code> was never assigned to. We can thus relax our condition to an if-then: if a variable is in our environment \(\rho\), then the variable-sign lattice&rsquo;s interpretation accurately describes it.</p> $$ \begin{array}{ccc} \llbracket \texttt{\{} x_1: s_1, ..., x_n: s_n \texttt{\}} \rrbracket\ \rho &amp;amp; = &amp;amp; &amp;amp; \textbf{if}\ x_1 \in \rho\ \textbf{then}\ \llbracket s_1 \rrbracket\ \rho(x_1)\ \\ &amp;amp; &amp;amp; \text{and} &amp;amp; ... \\ &amp;amp; &amp;amp; \text{and} &amp;amp; \textbf{if}\ x_n \in \rho\ \textbf{then}\ \llbracket s_n \rrbracket\ \rho(x_n) \end{array} $$ <p>The first &ldquo;weird&rdquo; case now results in the following:</p> $$ \llbracket \texttt{\{} \texttt{x}: \bot \texttt{\}} \rrbracket\ \rho = \textbf{if}\ \texttt{x} \in \rho\ \textbf{then}\ \text{false} $$ <p>Which is just another way of saying:</p> $$ \llbracket \texttt{\{} \texttt{x}: \bot \texttt{\}} \rrbracket\ \rho = \texttt{x} \notin \rho $$ <p>In the second case, the interpretation also results in a true statement:</p> $$ \llbracket \texttt{\{} \texttt{x}: &#43; \texttt{\}} \rrbracket\ \rho = \textbf{if}\ \texttt{x} \in \rho\ \textbf{then}\ \texttt{x} &amp;gt; 0 $$ <p>In Agda, I encode the fact that a verified analysis needs a semantic function \(\llbracket\cdot\rrbracket\) for its element lattice \(L\) by taking such a function as an argument called <code>⟦_⟧ˡ</code>:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Forward.agda" data-first-line="246" data-last-line="253" data-agda-block data-source-offset="8"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Forward.agda#L246-L253">Forward.agda</a>, lines 246 through 253</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">246 </span><span class="lnt">247 </span><span class="lnt">248 </span><span class="lnt">249 </span><span class="hl"><span class="lnt">250 </span></span><span class="lnt">251 </span><span class="lnt">252 </span><span class="lnt">253 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="kr">module</span><span class="w"> </span><span class="n">WithInterpretation</span><span class="w"> </span><span class="o">(</span>latticeInterpretationˡ<span class="w"> </span><span class="ow">:</span><span class="w"> </span>LatticeInterpretation<span class="w"> </span>isLatticeˡ<span class="o">)</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">open</span><span class="w"> </span>LatticeInterpretation<span class="w"> </span>latticeInterpretationˡ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">using</span><span class="w"> </span><span class="o">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">renaming</span><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span><span class="o">(</span><span class="w"> </span>⟦_⟧<span class="w"> </span>to<span class="w"> </span>⟦_⟧ˡ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>⟦⟧-respects-≈<span class="w"> </span>to<span class="w"> </span>⟦⟧ˡ-respects-≈ˡ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>⟦⟧-⊔-∨<span class="w"> </span>to<span class="w"> </span>⟦⟧ˡ-⊔ˡ-∨<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">)</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>I then define the semantic function for the variable-sign lattice in the following way, which eschews the &ldquo;&hellip;&rdquo; notation in favor of a more Agda-compatible (and equivalent) form:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Forward.agda" data-first-line="255" data-last-line="256" data-agda-block data-source-offset="12"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Forward.agda#L255-L256">Forward.agda</a>, lines 255 through 256</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">255 </span><span class="lnt">256 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">⟦_⟧ᵛ</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>VariableValues<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Env<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>⟦_⟧ᵛ<span class="w"> </span>vs<span class="w"> </span>ρ<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>k<span class="w"> </span>l<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>k<span class="w"> </span>,<span class="w"> </span>l<span class="o">)</span><span class="w"> </span>∈ᵛ<span class="w"> </span>vs<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>v<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>k<span class="w"> </span>,<span class="w"> </span>v<span class="o">)</span><span class="w"> </span>Language.∈<span class="w"> </span>ρ<span class="w"> </span><span class="ow">→</span><span class="w"> </span>⟦<span class="w"> </span>l<span class="w"> </span>⟧ˡ<span class="w"> </span>v<span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The above reads roughly as follows:</p> <blockquote> <p>For every variable <code>k</code> and sign [or, more generally, lattice element] <code>l</code> in the variable map lattice, if <code>k</code> is in the environment <code>ρ</code>, then it satisfies the predicate given by the semantic function applied to <code>l</code>.</p> </blockquote> <p>Let&rsquo;s recap: we have defined a semantic function for our sign lattice, and noted that to define a verified analysis, we always need such a semantic function. We then showed how to construct a semantic function for a whole variable map (of type \(\text{Variable} \to \text{Sign}\), or \(\text{Variable}\to L\) in general). We also wrote some Agda code doing all this. As a result, we have filled in the missing piece for <a href="#internal-ref-correctly-described"> property <span class="internal-ref-counter">1</span></a>.</p> <p>However, the way that we brought in the semantic function in the Agda code above hints that there&rsquo;s more to be discussed. What&rsquo;s <code>latticeInterpretationˡ</code>? In answering that question, we&rsquo;ll provide evidence for <a href="#internal-ref-disjunction"> property <span class="internal-ref-counter">2</span></a> and <a href="#internal-ref-equivalence"> property <span class="internal-ref-counter">4</span></a>.</p> <a href="#properties-of-the-semantic-function"> <h3 id="properties-of-the-semantic-function">Properties of the Semantic Function</h3> </a> <p>As we briefly saw earlier, we loosened the notion of equality to that equivalences, which made it possible to ignore things like the ordering of key-value pairs in maps. That&rsquo;s great and all, but nothing is stopping us from defining semantic functions that violate our equivalence! Supposing \(a \approx f(a)\), as far as Agda is concerned, even though \(a\) and \(f(a)\) are &ldquo;equivalent&rdquo;, \(\llbracket a \rrbracket\) and \(\llbracket f(a) \rrbracket\) may be totally different. For a semantic function to be correct, it must produce the same predicate for equivalent elements of lattice \(L\). That&rsquo;s <a href="#internal-ref-equivalence"> missing piece <span class="internal-ref-counter">4</span></a>.</p> <p>Another property of semantic functions that we will need to formalize is that \((\sqcup)\) represents disjunction. This comes into play when we reason about the correctness of predecessors in a Control Flow Graph. Recall that during the last step of processing a given node, when we are trying to move on to the next node in the trace, we have knowledge that the current node&rsquo;s variable map accurately describes the intermediate environment. In other words, \(\llbracket \textit{vs}_i \rrbracket\ \rho_2\) holds, where \(\textit{vs}_i\) is the variable map for the current node. We can generalize this kowledge a little, and get:</p> $$ \llbracket \textit{vs}_1 \rrbracket\ \rho_2\ \text{or}\ ...\ \text{or}\ \llbracket \textit{vs}_n \rrbracket\ \rho_2 $$ <p>However, the assumption that we <em>need</em> to hold when moving on to a new node is in terms of \(\textit{JOIN}\), which combines all the predecessors&rsquo; maps \(\textit{vs}_1, ..., \textit{vs}_n\) using \((\sqcup)\). Thus, we will need to be in a world where the following claim is true:</p> $$ \llbracket \textit{vs}_1 \sqcup ... \sqcup \textit{vs}_n \rrbracket\ \rho $$ <p>To get from one to the other, we will need to rely explicitly on the fact that \((\sqcup)\) encodes &ldquo;or&rdquo;. It&rsquo;s not necessary for the forward analysis, but a similar property ought to hold for \((\sqcap)\) and &ldquo;and&rdquo;. This constraint provides <a href="#internal-ref-disjunction"> missing piece <span class="internal-ref-counter">2</span></a>.</p> <p>I defined a new data type that bundles a semantic function with proofs of the properties in this section; that&rsquo;s precisely what <code>latticeInterpretationˡ</code> is:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Semantics.agda" data-first-line="66" data-last-line="73" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Semantics.agda#L66-L73">Semantics.agda</a>, lines 66 through 73</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">66 </span><span class="lnt">67 </span><span class="lnt">68 </span><span class="lnt">69 </span><span class="lnt">70 </span><span class="lnt">71 </span><span class="lnt">72 </span><span class="lnt">73 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="kr">record</span><span class="w"> </span>LatticeInterpretation<span class="w"> </span><span class="o">{</span>l<span class="o">}</span><span class="w"> </span><span class="o">{</span>L<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>l<span class="o">}</span><span class="w"> </span><span class="o">{</span>_≈_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>L<span class="w"> </span><span class="ow">→</span><span class="w"> </span>L<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>l<span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span>_⊔_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>L<span class="w"> </span><span class="ow">→</span><span class="w"> </span>L<span class="w"> </span><span class="ow">→</span><span class="w"> </span>L<span class="o">}</span><span class="w"> </span><span class="o">{</span>_⊓_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>L<span class="w"> </span><span class="ow">→</span><span class="w"> </span>L<span class="w"> </span><span class="ow">→</span><span class="w"> </span>L<span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>isLattice<span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsLattice<span class="w"> </span>L<span class="w"> </span>_≈_<span class="w"> </span>_⊔_<span class="w"> </span>_⊓_<span class="o">)</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span><span class="o">(</span>lsuc<span class="w"> </span>l<span class="o">)</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">field</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⟦_⟧</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>L<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Value<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⟦⟧-respects-≈</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>l₁<span class="w"> </span>l₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>L<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>l₁<span class="w"> </span>≈<span class="w"> </span>l₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>⟦<span class="w"> </span>l₁<span class="w"> </span>⟧<span class="w"> </span>⇒<span class="w"> </span>⟦<span class="w"> </span>l₂<span class="w"> </span>⟧<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⟦⟧-⊔-∨</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>l₁<span class="w"> </span>l₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>L<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>⟦<span class="w"> </span>l₁<span class="w"> </span>⟧<span class="w"> </span>∨<span class="w"> </span>⟦<span class="w"> </span>l₂<span class="w"> </span>⟧<span class="o">)</span><span class="w"> </span>⇒<span class="w"> </span>⟦<span class="w"> </span>l₁<span class="w"> </span>⊔<span class="w"> </span>l₂<span class="w"> </span>⟧<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⟦⟧-⊓-∧</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>l₁<span class="w"> </span>l₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>L<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>⟦<span class="w"> </span>l₁<span class="w"> </span>⟧<span class="w"> </span>∧<span class="w"> </span>⟦<span class="w"> </span>l₂<span class="w"> </span>⟧<span class="o">)</span><span class="w"> </span>⇒<span class="w"> </span>⟦<span class="w"> </span>l₁<span class="w"> </span>⊓<span class="w"> </span>l₂<span class="w"> </span>⟧<span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>In short, to leverage the framework for verified analysis, you would need to provide a semantic function that interacts properly with <code>≈</code> and <code>∨</code>.</p> <a href="#correctness-of-the-evaluator"> <h3 id="correctness-of-the-evaluator">Correctness of the Evaluator</h3> </a> <p>All that&rsquo;s left is <a href="#internal-ref-abstract-interpretation"> the last missing piece, <span class="internal-ref-counter">3</span></a>, which requires that <code>eval</code> matches the semantics of our language. Recall the signature of <code>eval</code>:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Forward.agda" data-first-line="166" data-last-line="166" data-agda-block data-source-offset="4"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Forward.agda#L166-L166">Forward.agda</a>, line 166</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">166 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="kr">module</span><span class="w"> </span><span class="n">WithEvaluator</span><span class="w"> </span><span class="o">(</span>eval<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">→</span><span class="w"> </span>VariableValues<span class="w"> </span><span class="ow">→</span><span class="w"> </span>L<span class="o">)</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>It operates on expressions and variable maps, which themselves associate a sign (or, generally, an element of lattice \(L\)), with each variable. The &ldquo;real&rdquo; evaluation judgement, on the other hand, is in the form \(\rho, e \Downarrow v\), and reads &ldquo;expression \(e\) in environment \(\rho\) evaluates to value \(v\)&rdquo;. In Agda:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Semantics.agda" data-first-line="27" data-last-line="27" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Semantics.agda#L27-L27">Semantics.agda</a>, line 27</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">27 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="kr">data</span><span class="w"> </span>_,_⇒ᵉ_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Value<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Let&rsquo;s line up the types of <code>eval</code> and the judgement. I&rsquo;ll swap the order of arguments for <code>eval</code> to make the correspondence easier to see:</p> $$ \begin{array}{ccccccc} \text{eval} &amp;amp; : &amp;amp; (\text{Variable} \to \text{Sign}) &amp;amp; \to &amp;amp; \text{Expr} &amp;amp; \to &amp;amp; \text{Sign} \\ \cdot,\cdot\Downarrow\cdot &amp;amp; : &amp;amp; (\text{Variable} \to \text{Value}) &amp;amp; \to &amp;amp; \text{Expr} &amp;amp; \to &amp;amp; \text{Value} &amp;amp; \to &amp;amp; \text{Set} \\ &amp;amp; &amp;amp; \underbrace{\phantom{(\text{Variable} \to \text{Value})}}_{\text{environment-like inputs}} &amp;amp; &amp;amp; &amp;amp; &amp;amp; \underbrace{\phantom{Value}}_{\text{value-like outputs}} \end{array} $$ <p>Squinting a little, it&rsquo;s almost like the signature of <code>eval</code> is the signature for the evaluation judgement, but it forgets a few details (the exact values of the variables) in favor of abstractions (their signs). To show that <code>eval</code> behaves correctly, we&rsquo;ll want to prove that this forgetful correspondence holds.</p> <p>Concretely, for any expression \(e\), take some environment \(\rho\), and &ldquo;forget&rdquo; the exact values, getting a sign map \(\textit{vs}\). Now, evaluate the expression to some value \(v\) using the semantics, and also, compute the expression&rsquo;s expected sign \(s\) using <code>eval</code>. The sign should be the same as forgetting \(v\)&rsquo;s exact value. Mathematically,</p> $$ \forall e, \rho, v, \textit{vs}.\ \textbf{if}\ \llbracket\textit{vs}\rrbracket \rho\ \text{and}\ \rho, e \Downarrow v\ \textbf{then}\ \llbracket \text{eval}\ \textit{vs}\ e\rrbracket v $$ <p>In Agda:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Forward.agda" data-first-line="286" data-last-line="287" data-agda-block data-source-offset="12"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Forward.agda#L286-L287">Forward.agda</a>, lines 286 through 287</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">286 </span><span class="lnt">287 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="nf">InterpretationValid</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>InterpretationValid<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>vs<span class="w"> </span>ρ<span class="w"> </span>e<span class="w"> </span>v<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>ρ<span class="w"> </span>,<span class="w"> </span>e<span class="w"> </span>⇒ᵉ<span class="w"> </span>v<span class="w"> </span><span class="ow">→</span><span class="w"> </span>⟦<span class="w"> </span>vs<span class="w"> </span>⟧ᵛ<span class="w"> </span>ρ<span class="w"> </span><span class="ow">→</span><span class="w"> </span>⟦<span class="w"> </span>eval<span class="w"> </span>e<span class="w"> </span>vs<span class="w"> </span>⟧ˡ<span class="w"> </span>v<span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>For a concrete analysis, we need to prove the above claim. In the case of sign analysis, this boils down to a rather cumbersome proof by cases. I will collapse the proofs to save some space and avoid overwhelming the reader.</p> <p> <details><summary><strong>(Click here to expand the proof of correctness for plus)</strong></summary><div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Sign.agda" data-first-line="237" data-last-line="258" data-agda-block data-source-offset="4"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Sign.agda#L237-L258">Sign.agda</a>, lines 237 through 258</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">237 </span><span class="lnt">238 </span><span class="lnt">239 </span><span class="lnt">240 </span><span class="lnt">241 </span><span class="lnt">242 </span><span class="lnt">243 </span><span class="lnt">244 </span><span class="lnt">245 </span><span class="lnt">246 </span><span class="lnt">247 </span><span class="lnt">248 </span><span class="lnt">249 </span><span class="lnt">250 </span><span class="lnt">251 </span><span class="lnt">252 </span><span class="lnt">253 </span><span class="lnt">254 </span><span class="lnt">255 </span><span class="lnt">256 </span><span class="lnt">257 </span><span class="lnt">258 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="nf">plus-valid</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>g₁<span class="w"> </span>g₂<span class="o">}</span><span class="w"> </span><span class="o">{</span>z₁<span class="w"> </span>z₂<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>⟦<span class="w"> </span>g₁<span class="w"> </span>⟧ᵍ<span class="w"> </span><span class="o">(</span>↑ᶻ<span class="w"> </span>z₁<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>⟦<span class="w"> </span>g₂<span class="w"> </span>⟧ᵍ<span class="w"> </span><span class="o">(</span>↑ᶻ<span class="w"> </span>z₂<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>⟦<span class="w"> </span>plus<span class="w"> </span>g₁<span class="w"> </span>g₂<span class="w"> </span>⟧ᵍ<span class="w"> </span><span class="o">(</span>↑ᶻ<span class="w"> </span><span class="o">(</span>z₁<span class="w"> </span>Int.+<span class="w"> </span>z₂<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus-valid<span class="w"> </span><span class="o">{</span>⊥ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>_<span class="o">}</span><span class="w"> </span>⊥<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus-valid<span class="w"> </span><span class="o">{</span>[<span class="w"> </span>+<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>⊥ᵍ<span class="o">}</span><span class="w"> </span>_<span class="w"> </span>⊥<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus-valid<span class="w"> </span><span class="o">{</span>[<span class="w"> </span>-<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>⊥ᵍ<span class="o">}</span><span class="w"> </span>_<span class="w"> </span>⊥<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus-valid<span class="w"> </span><span class="o">{</span>[<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>⊥ᵍ<span class="o">}</span><span class="w"> </span>_<span class="w"> </span>⊥<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus-valid<span class="w"> </span><span class="o">{</span>⊤ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>⊥ᵍ<span class="o">}</span><span class="w"> </span>_<span class="w"> </span>⊥<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus-valid<span class="w"> </span><span class="o">{</span>⊤ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>[<span class="w"> </span>+<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>tt<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus-valid<span class="w"> </span><span class="o">{</span>⊤ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>[<span class="w"> </span>-<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>tt<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus-valid<span class="w"> </span><span class="o">{</span>⊤ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>[<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>tt<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus-valid<span class="w"> </span><span class="o">{</span>⊤ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>⊤ᵍ<span class="o">}</span><span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>tt<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus-valid<span class="w"> </span><span class="o">{</span>[<span class="w"> </span>+<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>[<span class="w"> </span>+<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">(</span>n₁<span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span><span class="o">(</span>n₂<span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>_<span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus-valid<span class="w"> </span><span class="o">{</span>[<span class="w"> </span>+<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>[<span class="w"> </span>-<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>tt<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus-valid<span class="w"> </span><span class="o">{</span>[<span class="w"> </span>+<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>[<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">(</span>n₁<span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span>refl<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>_<span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus-valid<span class="w"> </span><span class="o">{</span>[<span class="w"> </span>+<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>⊤ᵍ<span class="o">}</span><span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>tt<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus-valid<span class="w"> </span><span class="o">{</span>[<span class="w"> </span>-<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>[<span class="w"> </span>+<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>tt<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus-valid<span class="w"> </span><span class="o">{</span>[<span class="w"> </span>-<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>[<span class="w"> </span>-<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">(</span>n₁<span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span><span class="o">(</span>n₂<span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>_<span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus-valid<span class="w"> </span><span class="o">{</span>[<span class="w"> </span>-<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>[<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">(</span>n₁<span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span>refl<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>_<span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus-valid<span class="w"> </span><span class="o">{</span>[<span class="w"> </span>-<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>⊤ᵍ<span class="o">}</span><span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>tt<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus-valid<span class="w"> </span><span class="o">{</span>[<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>[<span class="w"> </span>+<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span>refl<span class="w"> </span><span class="o">(</span>n₂<span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>_<span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus-valid<span class="w"> </span><span class="o">{</span>[<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>[<span class="w"> </span>-<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span>refl<span class="w"> </span><span class="o">(</span>n₂<span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>_<span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus-valid<span class="w"> </span><span class="o">{</span>[<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>[<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span>refl<span class="w"> </span>refl<span class="w"> </span><span class="ow">=</span><span class="w"> </span>refl<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus-valid<span class="w"> </span><span class="o">{</span>[<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>⊤ᵍ<span class="o">}</span><span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>tt<span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div> </div> </details> <details><summary><strong>(Click here to expand the proof of correctness for minus)</strong></summary><div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Sign.agda" data-first-line="261" data-last-line="282" data-agda-block data-source-offset="4"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Sign.agda#L261-L282">Sign.agda</a>, lines 261 through 282</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">261 </span><span class="lnt">262 </span><span class="lnt">263 </span><span class="lnt">264 </span><span class="lnt">265 </span><span class="lnt">266 </span><span class="lnt">267 </span><span class="lnt">268 </span><span class="lnt">269 </span><span class="lnt">270 </span><span class="lnt">271 </span><span class="lnt">272 </span><span class="lnt">273 </span><span class="lnt">274 </span><span class="lnt">275 </span><span class="lnt">276 </span><span class="lnt">277 </span><span class="lnt">278 </span><span class="lnt">279 </span><span class="lnt">280 </span><span class="lnt">281 </span><span class="lnt">282 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="nf">minus-valid</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>g₁<span class="w"> </span>g₂<span class="o">}</span><span class="w"> </span><span class="o">{</span>z₁<span class="w"> </span>z₂<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>⟦<span class="w"> </span>g₁<span class="w"> </span>⟧ᵍ<span class="w"> </span><span class="o">(</span>↑ᶻ<span class="w"> </span>z₁<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>⟦<span class="w"> </span>g₂<span class="w"> </span>⟧ᵍ<span class="w"> </span><span class="o">(</span>↑ᶻ<span class="w"> </span>z₂<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>⟦<span class="w"> </span>minus<span class="w"> </span>g₁<span class="w"> </span>g₂<span class="w"> </span>⟧ᵍ<span class="w"> </span><span class="o">(</span>↑ᶻ<span class="w"> </span><span class="o">(</span>z₁<span class="w"> </span>Int.-<span class="w"> </span>z₂<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>minus-valid<span class="w"> </span><span class="o">{</span>⊥ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>_<span class="o">}</span><span class="w"> </span>⊥<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>minus-valid<span class="w"> </span><span class="o">{</span>[<span class="w"> </span>+<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>⊥ᵍ<span class="o">}</span><span class="w"> </span>_<span class="w"> </span>⊥<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>minus-valid<span class="w"> </span><span class="o">{</span>[<span class="w"> </span>-<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>⊥ᵍ<span class="o">}</span><span class="w"> </span>_<span class="w"> </span>⊥<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>minus-valid<span class="w"> </span><span class="o">{</span>[<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>⊥ᵍ<span class="o">}</span><span class="w"> </span>_<span class="w"> </span>⊥<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>minus-valid<span class="w"> </span><span class="o">{</span>⊤ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>⊥ᵍ<span class="o">}</span><span class="w"> </span>_<span class="w"> </span>⊥<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>minus-valid<span class="w"> </span><span class="o">{</span>⊤ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>[<span class="w"> </span>+<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>tt<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>minus-valid<span class="w"> </span><span class="o">{</span>⊤ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>[<span class="w"> </span>-<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>tt<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>minus-valid<span class="w"> </span><span class="o">{</span>⊤ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>[<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>tt<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>minus-valid<span class="w"> </span><span class="o">{</span>⊤ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>⊤ᵍ<span class="o">}</span><span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>tt<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>minus-valid<span class="w"> </span><span class="o">{</span>[<span class="w"> </span>+<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>[<span class="w"> </span>+<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>tt<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>minus-valid<span class="w"> </span><span class="o">{</span>[<span class="w"> </span>+<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>[<span class="w"> </span>-<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">(</span>n₁<span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span><span class="o">(</span>n₂<span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>_<span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>minus-valid<span class="w"> </span><span class="o">{</span>[<span class="w"> </span>+<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>[<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">(</span>n₁<span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span>refl<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>_<span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>minus-valid<span class="w"> </span><span class="o">{</span>[<span class="w"> </span>+<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>⊤ᵍ<span class="o">}</span><span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>tt<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>minus-valid<span class="w"> </span><span class="o">{</span>[<span class="w"> </span>-<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>[<span class="w"> </span>+<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">(</span>n₁<span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span><span class="o">(</span>n₂<span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>_<span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>minus-valid<span class="w"> </span><span class="o">{</span>[<span class="w"> </span>-<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>[<span class="w"> </span>-<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>tt<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>minus-valid<span class="w"> </span><span class="o">{</span>[<span class="w"> </span>-<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>[<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">(</span>n₁<span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span>refl<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>_<span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>minus-valid<span class="w"> </span><span class="o">{</span>[<span class="w"> </span>-<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>⊤ᵍ<span class="o">}</span><span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>tt<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>minus-valid<span class="w"> </span><span class="o">{</span>[<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>[<span class="w"> </span>+<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span>refl<span class="w"> </span><span class="o">(</span>n₂<span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>_<span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>minus-valid<span class="w"> </span><span class="o">{</span>[<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>[<span class="w"> </span>-<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span>refl<span class="w"> </span><span class="o">(</span>n₂<span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>_<span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>minus-valid<span class="w"> </span><span class="o">{</span>[<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>[<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span>refl<span class="w"> </span>refl<span class="w"> </span><span class="ow">=</span><span class="w"> </span>refl<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>minus-valid<span class="w"> </span><span class="o">{</span>[<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span>]ᵍ<span class="o">}</span><span class="w"> </span><span class="o">{</span>⊤ᵍ<span class="o">}</span><span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>tt<span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div> </div> </details></p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Sign.agda" data-first-line="284" data-last-line="294" data-agda-block data-source-offset="4"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Sign.agda#L284-L294">Sign.agda</a>, lines 284 through 294</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">284 </span><span class="lnt">285 </span><span class="lnt">286 </span><span class="lnt">287 </span><span class="lnt">288 </span><span class="lnt">289 </span><span class="lnt">290 </span><span class="lnt">291 </span><span class="lnt">292 </span><span class="lnt">293 </span><span class="lnt">294 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="nf">eval-Valid</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>InterpretationValid<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>eval-Valid<span class="w"> </span><span class="o">(</span>⇒ᵉ-+<span class="w"> </span>ρ<span class="w"> </span>e₁<span class="w"> </span>e₂<span class="w"> </span>z₁<span class="w"> </span>z₂<span class="w"> </span>ρ,e₁⇒z₁<span class="w"> </span>ρ,e₂⇒z₂<span class="o">)</span><span class="w"> </span>⟦vs⟧ρ<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>plus-valid<span class="w"> </span><span class="o">(</span>eval-Valid<span class="w"> </span>ρ,e₁⇒z₁<span class="w"> </span>⟦vs⟧ρ<span class="o">)</span><span class="w"> </span><span class="o">(</span>eval-Valid<span class="w"> </span>ρ,e₂⇒z₂<span class="w"> </span>⟦vs⟧ρ<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>eval-Valid<span class="w"> </span><span class="o">(</span>⇒ᵉ--<span class="w"> </span>ρ<span class="w"> </span>e₁<span class="w"> </span>e₂<span class="w"> </span>z₁<span class="w"> </span>z₂<span class="w"> </span>ρ,e₁⇒z₁<span class="w"> </span>ρ,e₂⇒z₂<span class="o">)</span><span class="w"> </span>⟦vs⟧ρ<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>minus-valid<span class="w"> </span><span class="o">(</span>eval-Valid<span class="w"> </span>ρ,e₁⇒z₁<span class="w"> </span>⟦vs⟧ρ<span class="o">)</span><span class="w"> </span><span class="o">(</span>eval-Valid<span class="w"> </span>ρ,e₂⇒z₂<span class="w"> </span>⟦vs⟧ρ<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>eval-Valid<span class="w"> </span><span class="o">{</span>vs<span class="o">}</span><span class="w"> </span><span class="o">(</span>⇒ᵉ-Var<span class="w"> </span>ρ<span class="w"> </span>x<span class="w"> </span>v<span class="w"> </span>x,v∈ρ<span class="o">)</span><span class="w"> </span>⟦vs⟧ρ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">with</span><span class="w"> </span>∈k-decᵛ<span class="w"> </span>x<span class="w"> </span><span class="o">(</span>proj₁<span class="w"> </span><span class="o">(</span>proj₁<span class="w"> </span>vs<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>yes<span class="w"> </span>x∈kvs<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⟦vs⟧ρ<span class="w"> </span><span class="o">(</span>proj₂<span class="w"> </span><span class="o">(</span>locateᵛ<span class="w"> </span><span class="o">{</span>x<span class="o">}</span><span class="w"> </span><span class="o">{</span>vs<span class="o">}</span><span class="w"> </span>x∈kvs<span class="o">))</span><span class="w"> </span>x,v∈ρ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>no<span class="w"> </span>x∉kvs<span class="w"> </span><span class="ow">=</span><span class="w"> </span>tt<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>eval-Valid<span class="w"> </span><span class="o">(</span>⇒ᵉ-ℕ<span class="w"> </span>ρ<span class="w"> </span><span class="mi">0</span><span class="o">)</span><span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>refl<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>eval-Valid<span class="w"> </span><span class="o">(</span>⇒ᵉ-ℕ<span class="w"> </span>ρ<span class="w"> </span><span class="o">(</span>suc<span class="w"> </span>n&#39;<span class="o">))</span><span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>n&#39;<span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This completes <a href="#internal-ref-abstract-interpretation"> our last missing piece, <span class="internal-ref-counter">3</span></a>. All that&rsquo;s left is to put everything together.</p> <a href="#proving-the-analysis-correct"> <h3 id="proving-the-analysis-correct">Proving The Analysis Correct</h3> </a> <a href="#lifting-expression-evaluation-correctness-to-statements"> <h4 id="lifting-expression-evaluation-correctness-to-statements">Lifting Expression Evaluation Correctness to Statements</h4> </a> <p>The individual analyses (like the sign analysis) provide only an evaluation function for <em>expressions</em>, and thus only have to prove correctness of that function. However, our language is made up of statements, with judgements in the form \(\rho, s \Rightarrow \rho'\). Now that we&rsquo;ve shown (or assumed) that <code>eval</code> behaves correctly when evaluating expressions, we should show that this correctness extends to evaluating statements, which in the forward analysis implementation is handled by the <a href="https://danilafe.com/blog/08_spa_agda_forward/#define-updateVariablesFromStmt"><code>updateVariablesFromStmt</code> function</a>.</p> <p>The property we need to show looks very similar to the property for <code>eval</code>:</p> $$ \forall b, \rho, \rho&amp;#39;, \textit{vs}.\ \textbf{if}\ \llbracket\textit{vs}\rrbracket \rho\ \text{and}\ \rho, b \Rightarrow \rho&amp;#39;\ \textbf{then}\ \llbracket \text{updateVariablesFromStmt}\ \textit{vs}\ b\rrbracket \rho&amp;#39; $$ <p>In Agda:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Forward.agda" data-first-line="291" data-last-line="291" data-agda-block data-source-offset="16"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Forward.agda#L291-L291">Forward.agda</a>, line 291</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">291 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="nf">updateVariablesFromStmt-matches</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>bs<span class="w"> </span>vs<span class="w"> </span>ρ₁<span class="w"> </span>ρ₂<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>ρ₁<span class="w"> </span>,<span class="w"> </span>bs<span class="w"> </span>⇒ᵇ<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>⟦<span class="w"> </span>vs<span class="w"> </span>⟧ᵛ<span class="w"> </span>ρ₁<span class="w"> </span><span class="ow">→</span><span class="w"> </span>⟦<span class="w"> </span>updateVariablesFromStmt<span class="w"> </span>bs<span class="w"> </span>vs<span class="w"> </span>⟧ᵛ<span class="w"> </span>ρ₂<span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The proof is straightforward, and relies on the semantics of the <a href="https://danilafe.com/blog/08_spa_agda_forward/#generalized-update">map update</a>. Specifically, in the case of an assignment statement \(x \leftarrow e\), all we do is store the new sign computed from \(e\) into the map at \(x\). To prove the correctness of the entire final environment \(\rho'\), there are two cases to consider:</p> <ul> <li>A variable in question is the newly-updated \(x\). In this case, since <code>eval</code> produces correct signs, the variable clearly has the correct sign. This is the first highlighted chunk in the below code.</li> <li>A variable in question is different from \(x\). In this case, its value in the environment \(\rho'\) should be the same as it was prior, and its sign in the updated variable map is the same as it was in the original. Since the original map correctly described the original environment, we know the sign is correct. This is the second highlighted chunk in the below code.</li> </ul> <p>The corresponding Agda proof is as follows:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Forward.agda" data-first-line="291" data-last-line="305" data-agda-block data-source-offset="16"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Forward.agda#L291-L305">Forward.agda</a>, lines 291 through 305</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">291 </span><span class="lnt">292 </span><span class="lnt">293 </span><span class="lnt">294 </span><span class="hl"><span class="lnt">295 </span></span><span class="hl"><span class="lnt">296 </span></span><span class="hl"><span class="lnt">297 </span></span><span class="lnt">298 </span><span class="lnt">299 </span><span class="hl"><span class="lnt">300 </span></span><span class="hl"><span class="lnt">301 </span></span><span class="hl"><span class="lnt">302 </span></span><span class="hl"><span class="lnt">303 </span></span><span class="hl"><span class="lnt">304 </span></span><span class="hl"><span class="lnt">305 </span></span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="nf">updateVariablesFromStmt-matches</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>bs<span class="w"> </span>vs<span class="w"> </span>ρ₁<span class="w"> </span>ρ₂<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>ρ₁<span class="w"> </span>,<span class="w"> </span>bs<span class="w"> </span>⇒ᵇ<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>⟦<span class="w"> </span>vs<span class="w"> </span>⟧ᵛ<span class="w"> </span>ρ₁<span class="w"> </span><span class="ow">→</span><span class="w"> </span>⟦<span class="w"> </span>updateVariablesFromStmt<span class="w"> </span>bs<span class="w"> </span>vs<span class="w"> </span>⟧ᵛ<span class="w"> </span>ρ₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>updateVariablesFromStmt-matches<span class="w"> </span><span class="o">{</span>_<span class="o">}</span><span class="w"> </span><span class="o">{</span>vs<span class="o">}</span><span class="w"> </span><span class="o">{</span>ρ₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>ρ₁<span class="o">}</span><span class="w"> </span><span class="o">(</span>⇒ᵇ-noop<span class="w"> </span>ρ₁<span class="o">)</span><span class="w"> </span>⟦vs⟧ρ₁<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⟦vs⟧ρ₁<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>updateVariablesFromStmt-matches<span class="w"> </span><span class="o">{</span>_<span class="o">}</span><span class="w"> </span><span class="o">{</span>vs<span class="o">}</span><span class="w"> </span><span class="o">{</span>ρ₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>_<span class="o">}</span><span class="w"> </span><span class="o">(</span>⇒ᵇ-←<span class="w"> </span>ρ₁<span class="w"> </span>k<span class="w"> </span>e<span class="w"> </span>v<span class="w"> </span>ρ,e⇒v<span class="o">)</span><span class="w"> </span>⟦vs⟧ρ₁<span class="w"> </span><span class="o">{</span>k&#39;<span class="o">}</span><span class="w"> </span><span class="o">{</span>l<span class="o">}</span><span class="w"> </span>k&#39;,l∈vs&#39;<span class="w"> </span><span class="o">{</span>v&#39;<span class="o">}</span><span class="w"> </span>k&#39;,v&#39;∈ρ₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">with</span><span class="w"> </span>k<span class="w"> </span>≟ˢ<span class="w"> </span>k&#39;<span class="w"> </span><span class="ow">|</span><span class="w"> </span>k&#39;,v&#39;∈ρ₂<span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"></span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>yes<span class="w"> </span>refl<span class="w"> </span><span class="ow">|</span><span class="w"> </span>here<span class="w"> </span>_<span class="w"> </span>v<span class="w"> </span>_<span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span><span class="kr">rewrite</span><span class="w"> </span>updateVariablesFromExpression-k∈ks-≡<span class="w"> </span>k<span class="w"> </span>e<span class="w"> </span><span class="o">{</span>l<span class="w"> </span><span class="ow">=</span><span class="w"> </span>vs<span class="o">}</span><span class="w"> </span><span class="o">(</span>Any.here<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span>k&#39;,l∈vs&#39;<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span>interpretationValidˡ<span class="w"> </span>ρ,e⇒v<span class="w"> </span>⟦vs⟧ρ₁<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>yes<span class="w"> </span>k≡k&#39;<span class="w"> </span><span class="ow">|</span><span class="w"> </span>there<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span>k&#39;≢k<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥-elim<span class="w"> </span><span class="o">(</span>k&#39;≢k<span class="w"> </span><span class="o">(</span>sym<span class="w"> </span>k≡k&#39;<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>no<span class="w"> </span>k≢k&#39;<span class="w"> </span><span class="ow">|</span><span class="w"> </span>here<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥-elim<span class="w"> </span><span class="o">(</span>k≢k&#39;<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"></span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>no<span class="w"> </span>k≢k&#39;<span class="w"> </span><span class="ow">|</span><span class="w"> </span>there<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span>k&#39;,v&#39;∈ρ₁<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span><span class="kr">let</span><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span>k&#39;∉[k]<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span><span class="o">{</span><span class="w"> </span><span class="o">(</span>Any.here<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>k≢k&#39;<span class="w"> </span>refl<span class="w"> </span><span class="o">})</span><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span>k&#39;,l∈vs<span class="w"> </span><span class="ow">=</span><span class="w"> </span>updateVariablesFromExpression-k∉ks-backward<span class="w"> </span>k<span class="w"> </span>e<span class="w"> </span><span class="o">{</span>l<span class="w"> </span><span class="ow">=</span><span class="w"> </span>vs<span class="o">}</span><span class="w"> </span>k&#39;∉[k]<span class="w"> </span>k&#39;,l∈vs&#39;<span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span><span class="kr">in</span><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span>⟦vs⟧ρ₁<span class="w"> </span>k&#39;,l∈vs<span class="w"> </span>k&#39;,v&#39;∈ρ₁<span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>From this, it follows with relative ease that each basic block in the lattice, when evaluated, produces an environment that matches the prediction of our forward analysis.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Forward.agda" data-first-line="318" data-last-line="318" data-agda-block data-source-offset="16"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Forward.agda#L318-L318">Forward.agda</a>, line 318</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">318 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="nf">updateAll-matches</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>s<span class="w"> </span>sv<span class="w"> </span>ρ₁<span class="w"> </span>ρ₂<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>ρ₁<span class="w"> </span>,<span class="w"> </span><span class="o">(</span>code<span class="w"> </span>s<span class="o">)</span><span class="w"> </span>⇒ᵇˢ<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>⟦<span class="w"> </span>variablesAt<span class="w"> </span>s<span class="w"> </span>sv<span class="w"> </span>⟧ᵛ<span class="w"> </span>ρ₁<span class="w"> </span><span class="ow">→</span><span class="w"> </span>⟦<span class="w"> </span>variablesAt<span class="w"> </span>s<span class="w"> </span><span class="o">(</span>updateAll<span class="w"> </span>sv<span class="o">)</span><span class="w"> </span>⟧ᵛ<span class="w"> </span>ρ₂<span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#walking-the-trace"> <h4 id="walking-the-trace">Walking the Trace</h4> </a> <p>Finally, we get to the meat of the proof, which follows the <a href="#high-level-algorithm"class="same-page-link">outline<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#arrow-up"/> </svg></a>. First, let&rsquo;s take a look at <code>stepTrace</code>, which implements the second bullet in our iterative procedure. I&rsquo;ll show the code, then we can discuss it in detail.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Forward.agda" data-first-line="324" data-last-line="342" data-agda-block data-source-offset="16"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Forward.agda#L324-L342">Forward.agda</a>, lines 324 through 342</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">324 </span><span class="lnt">325 </span><span class="lnt">326 </span><span class="lnt">327 </span><span class="lnt">328 </span><span class="lnt">329 </span><span class="lnt">330 </span><span class="lnt">331 </span><span class="lnt">332 </span><span class="lnt">333 </span><span class="lnt">334 </span><span class="lnt">335 </span><span class="lnt">336 </span><span class="lnt">337 </span><span class="lnt">338 </span><span class="lnt">339 </span><span class="lnt">340 </span><span class="lnt">341 </span><span class="lnt">342 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="nf">stepTrace</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>s₁<span class="w"> </span>ρ₁<span class="w"> </span>ρ₂<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>⟦<span class="w"> </span>joinForKey<span class="w"> </span>s₁<span class="w"> </span>result<span class="w"> </span>⟧ᵛ<span class="w"> </span>ρ₁<span class="w"> </span><span class="ow">→</span><span class="w"> </span>ρ₁<span class="w"> </span>,<span class="w"> </span><span class="o">(</span>code<span class="w"> </span>s₁<span class="o">)</span><span class="w"> </span>⇒ᵇˢ<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>⟦<span class="w"> </span>variablesAt<span class="w"> </span>s₁<span class="w"> </span>result<span class="w"> </span>⟧ᵛ<span class="w"> </span>ρ₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>stepTrace<span class="w"> </span><span class="o">{</span>s₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>ρ₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>ρ₂<span class="o">}</span><span class="w"> </span>⟦joinForKey-s₁⟧ρ₁<span class="w"> </span>ρ₁,bss⇒ρ₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">let</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">-- I&#39;d use rewrite, but Agda gets a memory overflow (?!).</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⟦joinAll-result⟧ρ₁<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>subst<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>vs<span class="w"> </span><span class="ow">→</span><span class="w"> </span>⟦<span class="w"> </span>vs<span class="w"> </span>⟧ᵛ<span class="w"> </span>ρ₁<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>sym<span class="w"> </span><span class="o">(</span>variablesAt-joinAll<span class="w"> </span>s₁<span class="w"> </span>result<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⟦joinForKey-s₁⟧ρ₁<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⟦analyze-result⟧ρ₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>updateAll-matches<span class="w"> </span><span class="o">{</span>sv<span class="w"> </span><span class="ow">=</span><span class="w"> </span>joinAll<span class="w"> </span>result<span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>ρ₁,bss⇒ρ₂<span class="w"> </span>⟦joinAll-result⟧ρ₁<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>analyze-result≈result<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>≈ᵐ-sym<span class="w"> </span><span class="o">{</span>result<span class="o">}</span><span class="w"> </span><span class="o">{</span>updateAll<span class="w"> </span><span class="o">(</span>joinAll<span class="w"> </span>result<span class="o">)}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>result≈analyze-result<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>analyze-s₁≈s₁<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>variablesAt-≈<span class="w"> </span>s₁<span class="w"> </span><span class="o">(</span>updateAll<span class="w"> </span><span class="o">(</span>joinAll<span class="w"> </span>result<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>result<span class="w"> </span><span class="o">(</span>analyze-result≈result<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">in</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⟦⟧ᵛ-respects-≈ᵛ<span class="w"> </span><span class="o">{</span>variablesAt<span class="w"> </span>s₁<span class="w"> </span><span class="o">(</span>updateAll<span class="w"> </span><span class="o">(</span>joinAll<span class="w"> </span>result<span class="o">))}</span><span class="w"> </span><span class="o">{</span>variablesAt<span class="w"> </span>s₁<span class="w"> </span>result<span class="o">}</span><span class="w"> </span><span class="o">(</span>analyze-s₁≈s₁<span class="o">)</span><span class="w"> </span>ρ₂<span class="w"> </span>⟦analyze-result⟧ρ₂<span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The first <code>let</code>-bound variable, <code>⟦joinAll-result⟧ρ₁</code> is kind of an intermediate result, which I was forced to introduced because <code>rewrite</code> caused Agda to allocate ~100GB of memory. It simply makes use of the fact that <code>joinAll</code>, the function that performs predecessor joining for each node in the CFG, sets every key of the map accordingly.</p> <p>The second <code>let</code>-bound variable, <code>⟦analyze-result⟧</code>, steps through a given node&rsquo;s basic block and leverages our proof of statement-correctness to validate that the final environment <code>ρ₂</code> matches the predication of the analyzer.</p> <p>The last two <code>let</code>-bound variables apply the equation we wrote above:</p> $$ r = \text{update}(\text{join}(r)) $$ <p>Recall that <code>analyze</code> is the combination of <code>update</code> and <code>join</code>:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Forward.agda" data-first-line="226" data-last-line="227" data-agda-block data-source-offset="8"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Forward.agda#L226-L227">Forward.agda</a>, lines 226 through 227</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">226 </span><span class="lnt">227 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="nf">analyze</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>StateVariables<span class="w"> </span><span class="ow">→</span><span class="w"> </span>StateVariables<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>analyze<span class="w"> </span><span class="ow">=</span><span class="w"> </span>updateAll<span class="w"> </span>∘<span class="w"> </span>joinAll<span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Finally, the <code>in</code> portion of the code uses <code>⟦⟧ᵛ-respects-≈ᵛ</code>, a proof of <a href="#internal-ref-equivalence"> property <span class="internal-ref-counter">4</span></a>, to produce the final claim in terms of the <code>result</code> map.</p> <p>Knowing how to step, we can finally walk the entire trace, implementing the iterative process:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Forward.agda" data-first-line="344" data-last-line="357" data-agda-block data-source-offset="16"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Forward.agda#L344-L357">Forward.agda</a>, lines 344 through 357</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">344 </span><span class="lnt">345 </span><span class="lnt">346 </span><span class="lnt">347 </span><span class="lnt">348 </span><span class="lnt">349 </span><span class="lnt">350 </span><span class="lnt">351 </span><span class="lnt">352 </span><span class="lnt">353 </span><span class="lnt">354 </span><span class="lnt">355 </span><span class="lnt">356 </span><span class="lnt">357 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="nf">walkTrace</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>s₁<span class="w"> </span>s₂<span class="w"> </span>ρ₁<span class="w"> </span>ρ₂<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>⟦<span class="w"> </span>joinForKey<span class="w"> </span>s₁<span class="w"> </span>result<span class="w"> </span>⟧ᵛ<span class="w"> </span>ρ₁<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Trace<span class="w"> </span><span class="o">{</span>graph<span class="o">}</span><span class="w"> </span>s₁<span class="w"> </span>s₂<span class="w"> </span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>⟦<span class="w"> </span>variablesAt<span class="w"> </span>s₂<span class="w"> </span>result<span class="w"> </span>⟧ᵛ<span class="w"> </span>ρ₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>walkTrace<span class="w"> </span><span class="o">{</span>s₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>s₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>ρ₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>ρ₂<span class="o">}</span><span class="w"> </span>⟦joinForKey-s₁⟧ρ₁<span class="w"> </span><span class="o">(</span>Trace-single<span class="w"> </span>ρ₁,bss⇒ρ₂<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>stepTrace<span class="w"> </span><span class="o">{</span>s₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>ρ₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>ρ₂<span class="o">}</span><span class="w"> </span>⟦joinForKey-s₁⟧ρ₁<span class="w"> </span>ρ₁,bss⇒ρ₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>walkTrace<span class="w"> </span><span class="o">{</span>s₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>s₂<span class="o">}</span><span class="w"> </span><span class="o">{</span>ρ₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>ρ₂<span class="o">}</span><span class="w"> </span>⟦joinForKey-s₁⟧ρ₁<span class="w"> </span><span class="o">(</span>Trace-edge<span class="w"> </span><span class="o">{</span>ρ₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>ρ<span class="o">}</span><span class="w"> </span><span class="o">{</span>idx₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>s<span class="o">}</span><span class="w"> </span>ρ₁,bss⇒ρ<span class="w"> </span>s₁→s₂<span class="w"> </span>tr<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">let</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⟦result-s₁⟧ρ<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>stepTrace<span class="w"> </span><span class="o">{</span>s₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>ρ₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>ρ<span class="o">}</span><span class="w"> </span>⟦joinForKey-s₁⟧ρ₁<span class="w"> </span>ρ₁,bss⇒ρ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>s₁∈incomingStates<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>[]-∈<span class="w"> </span>result<span class="w"> </span><span class="o">(</span>edge⇒incoming<span class="w"> </span>s₁→s₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>variablesAt-∈<span class="w"> </span>s₁<span class="w"> </span>result<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⟦joinForKey-s⟧ρ<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⟦⟧ᵛ-foldr<span class="w"> </span>⟦result-s₁⟧ρ<span class="w"> </span>s₁∈incomingStates<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">in</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>walkTrace<span class="w"> </span>⟦joinForKey-s⟧ρ<span class="w"> </span>tr<span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The first step &mdash; assuming that one of the predecessors of the current node satisfies the initial environment <code>ρ₁</code> &mdash; is captured by the presence of the argument <code>⟦joinForKey-s₁⟧ρ₁</code>. We expect the calling code to provide a proof of that.</p> <p>The second step, in both cases, is implemented using <code>stepTrace</code>, as we saw above. That results in a proof that at the end of the current basic block, the final environment <code>ρ₂</code> is accurately described.</p> <p>From there, we move on to the third iterative step, if necessary. The sub-expression <code>edge⇒incoming s₁→s₂</code> validates that, since we have an edge from the current node to the next, we are listed as a predecessor. This, in turn, means that we are included in the list of states-to-join for the \(\textit{JOIN}\) function. That fact is stored in <code>s₁∈incomingStates</code>. Finally, relying on <a href="#internal-ref-disjunction"> property <span class="internal-ref-counter">2</span></a>, we construct an assumption fit for a recursive invocation of <code>walkTrace</code>, and move on to the next CFG node. The <code>foldr</code> here is motivated by the fact that &ldquo;summation&rdquo; using \((\sqcup)\) is a fold.</p> <p>When the function terminates, what we have is a proof that the final program state is accurately described by the results of our program analysis. All that&rsquo;s left is to kick off the walk. To do that, observe that the initial state has no predecessors (how could it, if it&rsquo;s at the beginning of the program?). That, in turn, means that this state maps every variable to the bottom element. Such a variable configuration only permits the empty environment \(\rho = \varnothing\). If the program evaluation starts in an empty environment, we have the assumption needed to kick off the iteration.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Forward.agda" data-first-line="359" data-last-line="366" data-agda-block data-source-offset="16"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Forward.agda#L359-L366">Forward.agda</a>, lines 359 through 366</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">359 </span><span class="lnt">360 </span><span class="lnt">361 </span><span class="lnt">362 </span><span class="lnt">363 </span><span class="lnt">364 </span><span class="hl"><span class="lnt">365 </span></span><span class="lnt">366 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="nf">joinForKey-initialState-⊥ᵛ</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>joinForKey<span class="w"> </span>initialState<span class="w"> </span>result<span class="w"> </span>≡<span class="w"> </span>⊥ᵛ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>joinForKey-initialState-⊥ᵛ<span class="w"> </span><span class="ow">=</span><span class="w"> </span>cong<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>ins<span class="w"> </span><span class="ow">→</span><span class="w"> </span>foldr<span class="w"> </span>_⊔ᵛ_<span class="w"> </span>⊥ᵛ<span class="w"> </span><span class="o">(</span>result<span class="w"> </span>[<span class="w"> </span>ins<span class="w"> </span>]<span class="o">))</span><span class="w"> </span>initialState-pred-∅<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nf">⟦joinAll-initialState⟧ᵛ∅</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>⟦<span class="w"> </span>joinForKey<span class="w"> </span>initialState<span class="w"> </span>result<span class="w"> </span>⟧ᵛ<span class="w"> </span>[]<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>⟦joinAll-initialState⟧ᵛ∅<span class="w"> </span><span class="ow">=</span><span class="w"> </span>subst<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>vs<span class="w"> </span><span class="ow">→</span><span class="w"> </span>⟦<span class="w"> </span>vs<span class="w"> </span>⟧ᵛ<span class="w"> </span>[]<span class="o">)</span><span class="w"> </span><span class="o">(</span>sym<span class="w"> </span>joinForKey-initialState-⊥ᵛ<span class="o">)</span><span class="w"> </span>⟦⊥ᵛ⟧ᵛ∅<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"></span><span class="nf">analyze-correct</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>ρ<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>[]<span class="w"> </span>,<span class="w"> </span>rootStmt<span class="w"> </span>⇒ˢ<span class="w"> </span>ρ<span class="w"> </span><span class="ow">→</span><span class="w"> </span>⟦<span class="w"> </span>variablesAt<span class="w"> </span>finalState<span class="w"> </span>result<span class="w"> </span>⟧ᵛ<span class="w"> </span>ρ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>analyze-correct<span class="w"> </span><span class="o">{</span>ρ<span class="o">}</span><span class="w"> </span>∅,s⇒ρ<span class="w"> </span><span class="ow">=</span><span class="w"> </span>walkTrace<span class="w"> </span><span class="o">{</span>initialState<span class="o">}</span><span class="w"> </span><span class="o">{</span>finalState<span class="o">}</span><span class="w"> </span><span class="o">{</span>[]<span class="o">}</span><span class="w"> </span><span class="o">{</span>ρ<span class="o">}</span><span class="w"> </span>⟦joinAll-initialState⟧ᵛ∅<span class="w"> </span><span class="o">(</span>trace<span class="w"> </span>∅,s⇒ρ<span class="o">)</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Take a look at the highlighted line in the above code block in particular. It states precisely what we were hoping to see: that, when evaluating a program, the final state when it terminates is accurately described by the <code>result</code> of our static program analysis at the <code>finalState</code> in the CFG. We have done it!</p> <a href="#future-work"> <h3 id="future-work">Future Work</h3> </a> <p>It took a lot of machinery to get where we are, but there&rsquo;s still lots of things to do.</p> <ol> <li> <p><strong>Correctness beyond the final state</strong>: the statement we&rsquo;ve arrived at only shows that the final state of the program matches the results of the analysis. In fact, the property hold for all intermediate states, too. The only snag is that it&rsquo;s more difficult to <em>state</em> such a claim.</p> <p>To do something like that, we probably need a notion of &ldquo;incomplete evaluations&rdquo; of our language, which run our program but stop at some point before the end. A full execution would be a special case of such an &ldquo;incomplete evaluation&rdquo; that stops in the final state. Then, we could restate <code>analyze-correct</code> in terms of partial evaluations, which would strengthen it.</p> </li> <li> <p><strong>A more robust language and evaluation process</strong>: we noted above that our join-based analysis is a little bit weird, particularly in the cases of uninitialized variables. There are ways to adjust our language (e.g., introducing variable declaration points) and analysis functions (e.g., only allowing assignment for declared variables) to reduce the weirdness somewhat. They just lead to a more complicated language.</p> </li> <li> <p><strong>A more general correctness condition</strong>: converting lattice elements into predicates on values gets us far. However, some types of analyses make claims about more than the <em>current</em> values of variables. For instance, <em>live variable analysis</em> checks if a variable&rsquo;s current value is going to be used in the future. Such an analysis can help guide register (re)allocation. To talk about future uses of a variable, the predicate will need to be formulated in terms of the entire evaluation proof tree. This opens a whole can of worms that I haven&rsquo;t begun to examine.</p> </li> </ol> <p>Now that I&rsquo;m done writing up my code so far, I will start exploring these various avenues of work. In the meantime, though, thanks for reading!</p> Implementing and Verifying "Static Program Analysis" in Agda, Part 8: Forward Analysis https://danilafe.com/blog/08_spa_agda_forward/ Sun, 01 Dec 2024 15:09:07 -0800 https://danilafe.com/blog/08_spa_agda_forward/ <p>In the previous post, I showed that the Control Flow graphs we built of our programs match how they are really executed. This means that we can rely on these graphs to compute program information. In this post, we finally get to compute that information. Here&rsquo;s a quick bit paraphrasing from last time that provides a summary of our approach:</p> <ol> <li>We will construct a finite-height lattice. Every single element of this lattice will contain information about each variable at each node in the Control Flow Graph.</li> <li>We will then define a monotonic function that update this information using the structure encoded in the CFG’s edges and nodes.</li> <li>Then, using the fixed-point algorithm, we will find the least element of the lattice, which will give us a precise description of all program variables at all points in the program.</li> <li>Because we have just validated our CFGs to be faithful to the language’s semantics, we’ll be able to prove that our algorithm produces accurate results.</li> </ol> <p>Let&rsquo;s jump right into it!</p> <a href="#choosing-a-lattice"> <h3 id="choosing-a-lattice">Choosing a Lattice</h3> </a> <p>A lot of this time, we have been <a href="https://danilafe.com/blog/01_spa_agda_lattices/">talking about lattices</a>, particularly <a href="https://danilafe.com/blog/03_spa_agda_fixed_height/">lattices of finite height</a>. These structures represent things we know about the program, and provide operators like \((\sqcup)\) and \((\sqcap)\) that help us combine such knowledge.</p> <p>The forward analysis code I present here will work with any finite-height lattice, with the additional constraint that equivalence of lattices is decidable, which comes from <a href="https://danilafe.com/blog/04_spa_agda_fixedpoint/">the implementation of the fixed-point algorithm</a>, in which we routinely check if a function&rsquo;s output is the same as its input.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Forward.agda" data-first-line="4" data-last-line="8" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Forward.agda#L4-L8">Forward.agda</a>, lines 4 through 8</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="kr">module</span><span class="w"> </span><span class="n">Analysis.Forward</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span>L<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="o">}</span><span class="w"> </span><span class="o">{</span>h<span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span>_≈ˡ_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>L<span class="w"> </span><span class="ow">→</span><span class="w"> </span>L<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="o">}</span><span class="w"> </span><span class="o">{</span>_⊔ˡ_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>L<span class="w"> </span><span class="ow">→</span><span class="w"> </span>L<span class="w"> </span><span class="ow">→</span><span class="w"> </span>L<span class="o">}</span><span class="w"> </span><span class="o">{</span>_⊓ˡ_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>L<span class="w"> </span><span class="ow">→</span><span class="w"> </span>L<span class="w"> </span><span class="ow">→</span><span class="w"> </span>L<span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>isFiniteHeightLatticeˡ<span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsFiniteHeightLattice<span class="w"> </span>L<span class="w"> </span>h<span class="w"> </span>_≈ˡ_<span class="w"> </span>_⊔ˡ_<span class="w"> </span>_⊓ˡ_<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>≈ˡ-dec<span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsDecidable<span class="w"> </span>_≈ˡ_<span class="o">)</span><span class="w"> </span><span class="kr">where</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The finite-height lattice <code>L</code> is intended to describe the state of a single variable. One example of a lattice that can be used as <code>L</code> is our sign lattice. We&rsquo;ve been using the sign lattice in our examples <a href="https://danilafe.com/blog/01_spa_agda_lattices/#lattices">from the very beginning</a>, and we will stick with it for the purposes of this explanation. However, this lattice alone does not describe our program, since it only talks about a single sign; programs have lots of variables, all of which can have different signs! So, we might go one step further and define a map lattice from variables to their signs:</p> $$ \text{Variable} \to \text{Sign} $$ <p>We <a href="https://danilafe.com/blog/02_spa_agda_combining_lattices/#the-map-lattice">have seen</a> that we can turn any lattice \(L\) into a map lattice \(A \to L\), for any type of keys \(A\). In this case, we will define \(A \triangleq \text{Variable}\), and \(L \triangleq \text{Sign}\). The <a href="https://danilafe.com/blog/02_spa_agda_combining_lattices/#the-map-lattice">sign lattice has a finite height</a>, and I&rsquo;ve proven that, as long as we pick a finite set of keys, <a href="https://danilafe.com/blog/03_spa_agda_fixed_height/#fixed-height-of-the-map-lattice">map lattices \(A \to L\) have a finite height if \(L\) has a finite height</a>. Since a program&rsquo;s text is finite, \(\text{Variable}\) is a finite set, and we have ourselves a finite-height lattice \(\text{Variable} \to \text{Sign}\).</p> <p id="whole-lattice">We&rsquo;re on the right track, but even the lattice we have so far is not sufficient. That&rsquo;s because variables have different signs at different points in the program! You might initialize a variable with <code>x = 1</code>, making it positive, and then go on to compute some arbitrary function using loops and conditionals. For each variable, we need to keep track of its sign at various points in the code. When we <a href="https://danilafe.com/blog/06_spa_agda_cfg/">defined Control Flow Graphs</a>, we split our programs into sequences of statements that are guaranteed to execute together &mdash; basic blocks. For our analysis, we&rsquo;ll keep per-variable for each basic block in the program. Since basic blocks are nodes in the Control Flow Graph of our program, our whole lattice will be as follows:</p> $$ \text{Info} \triangleq \text{NodeId} \to (\text{Variable} \to \text{Sign}) $$ <p>We follow the same logic we just did for the variable-sign lattice; since \(\text{Variable} \to \text{Sign}\) is a lattice of finite height, and since \(\text{NodeId}\) is a finite set, the whole \(\text{Info}\) map will be a lattice with a finite height.</p> <p>Notice that both the sets of \(\text{Variable}\) and \(\text{NodeId}\) depend on the program in question. The lattice we use is slightly different for each input program! We can use Agda&rsquo;s parameterized modules to automaitcally parameterize all our functions over programs:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Forward.agda" data-first-line="36" data-last-line="37" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Forward.agda#L36-L37">Forward.agda</a>, lines 36 through 37</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">36 </span><span class="lnt">37 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="kr">module</span><span class="w"> </span><span class="n">WithProg</span><span class="w"> </span><span class="o">(</span>prog<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Program<span class="o">)</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">open</span><span class="w"> </span>Program<span class="w"> </span>prog</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Now, let&rsquo;s make the informal descriptions above into code, by instantiating our map lattice modules. First, I invoked the code for the smaller variable-sign lattice. This ended up being quite long, so that I could rename variables I brought into scope. I will collapse the relevant code block; suffice to say that I used the suffix <code>v</code> (e.g., renaming <code>_⊔_</code> to <code>_⊔ᵛ_</code>) for properties and operators to do with variable-sign maps (in Agda: <code>VariableValuesFiniteMap</code>).</p> <details><summary><strong>(Click here to expand the module uses for variable-sign maps)</strong></summary><div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Forward.agda" data-first-line="41" data-last-line="82" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Forward.agda#L41-L82">Forward.agda</a>, lines 41 through 82</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span><span class="lnt">65 </span><span class="lnt">66 </span><span class="lnt">67 </span><span class="lnt">68 </span><span class="lnt">69 </span><span class="lnt">70 </span><span class="lnt">71 </span><span class="lnt">72 </span><span class="lnt">73 </span><span class="lnt">74 </span><span class="lnt">75 </span><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span><span class="lnt">79 </span><span class="lnt">80 </span><span class="lnt">81 </span><span class="lnt">82 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="kr">module</span><span class="w"> </span><span class="n">VariableValuesFiniteMap</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>Lattice.FiniteValueMap.WithKeys<span class="w"> </span>_≟ˢ_<span class="w"> </span>isLatticeˡ<span class="w"> </span>vars<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">open</span><span class="w"> </span>VariableValuesFiniteMap<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">using</span><span class="w"> </span><span class="o">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">renaming</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span><span class="w"> </span>FiniteMap<span class="w"> </span>to<span class="w"> </span>VariableValues<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>isLattice<span class="w"> </span>to<span class="w"> </span>isLatticeᵛ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>_≈_<span class="w"> </span>to<span class="w"> </span>_≈ᵛ_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>_⊔_<span class="w"> </span>to<span class="w"> </span>_⊔ᵛ_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>_≼_<span class="w"> </span>to<span class="w"> </span>_≼ᵛ_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>≈₂-dec⇒≈-dec<span class="w"> </span>to<span class="w"> </span>≈ˡ-dec⇒≈ᵛ-dec<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>_∈_<span class="w"> </span>to<span class="w"> </span>_∈ᵛ_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>_∈k_<span class="w"> </span>to<span class="w"> </span>_∈kᵛ_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>_updating_via_<span class="w"> </span>to<span class="w"> </span>_updatingᵛ_via_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>locate<span class="w"> </span>to<span class="w"> </span>locateᵛ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>m₁≼m₂⇒m₁[k]≼m₂[k]<span class="w"> </span>to<span class="w"> </span>m₁≼m₂⇒m₁[k]ᵛ≼m₂[k]ᵛ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>∈k-dec<span class="w"> </span>to<span class="w"> </span>∈k-decᵛ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>all-equal-keys<span class="w"> </span>to<span class="w"> </span>all-equal-keysᵛ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>public<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">open</span><span class="w"> </span>IsLattice<span class="w"> </span>isLatticeᵛ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">using</span><span class="w"> </span><span class="o">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">renaming</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span><span class="w"> </span>⊔-Monotonicˡ<span class="w"> </span>to<span class="w"> </span>⊔ᵛ-Monotonicˡ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>⊔-Monotonicʳ<span class="w"> </span>to<span class="w"> </span>⊔ᵛ-Monotonicʳ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>⊔-idemp<span class="w"> </span>to<span class="w"> </span>⊔ᵛ-idemp<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">open</span><span class="w"> </span>Lattice.FiniteValueMap.IterProdIsomorphism<span class="w"> </span>_≟ˢ_<span class="w"> </span>isLatticeˡ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">using</span><span class="w"> </span><span class="o">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">renaming</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span><span class="w"> </span>Provenance-union<span class="w"> </span>to<span class="w"> </span>Provenance-unionᵐ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">open</span><span class="w"> </span>Lattice.FiniteValueMap.IterProdIsomorphism.WithUniqueKeysAndFixedHeight<span class="w"> </span>_≟ˢ_<span class="w"> </span>isLatticeˡ<span class="w"> </span>vars-Unique<span class="w"> </span>≈ˡ-dec<span class="w"> </span>_<span class="w"> </span>fixedHeightˡ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">using</span><span class="w"> </span><span class="o">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">renaming</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span><span class="w"> </span>isFiniteHeightLattice<span class="w"> </span>to<span class="w"> </span>isFiniteHeightLatticeᵛ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>⊥-contains-bottoms<span class="w"> </span>to<span class="w"> </span>⊥ᵛ-contains-bottoms<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>≈ᵛ-dec<span class="w"> </span><span class="ow">=</span><span class="w"> </span>≈ˡ-dec⇒≈ᵛ-dec<span class="w"> </span>≈ˡ-dec<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>joinSemilatticeᵛ<span class="w"> </span><span class="ow">=</span><span class="w"> </span>IsFiniteHeightLattice.joinSemilattice<span class="w"> </span>isFiniteHeightLatticeᵛ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>fixedHeightᵛ<span class="w"> </span><span class="ow">=</span><span class="w"> </span>IsFiniteHeightLattice.fixedHeight<span class="w"> </span>isFiniteHeightLatticeᵛ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⊥ᵛ<span class="w"> </span><span class="ow">=</span><span class="w"> </span>Chain.Height.⊥<span class="w"> </span>fixedHeightᵛ</span></span></code></pre></td></tr></table> </div> </div> </div> </details> <p>I then used this lattice as an argument to the map module again, to construct the top-level \(\text{Info}\) lattice (in Agda: <code>StateVariablesFiniteMap</code>). This also required a fair bit of code, most of it to do with renaming.</p> <details><summary><strong>(Click here to expand the module uses for the top-level lattice)</strong></summary><div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Forward.agda" data-first-line="85" data-last-line="112" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Forward.agda#L85-L112">Forward.agda</a>, lines 85 through 112</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 85 </span><span class="lnt"> 86 </span><span class="lnt"> 87 </span><span class="lnt"> 88 </span><span class="lnt"> 89 </span><span class="lnt"> 90 </span><span class="lnt"> 91 </span><span class="lnt"> 92 </span><span class="lnt"> 93 </span><span class="lnt"> 94 </span><span class="lnt"> 95 </span><span class="lnt"> 96 </span><span class="lnt"> 97 </span><span class="lnt"> 98 </span><span class="lnt"> 99 </span><span class="lnt">100 </span><span class="lnt">101 </span><span class="lnt">102 </span><span class="lnt">103 </span><span class="lnt">104 </span><span class="lnt">105 </span><span class="lnt">106 </span><span class="lnt">107 </span><span class="lnt">108 </span><span class="lnt">109 </span><span class="lnt">110 </span><span class="lnt">111 </span><span class="lnt">112 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="kr">module</span><span class="w"> </span><span class="n">StateVariablesFiniteMap</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>Lattice.FiniteValueMap.WithKeys<span class="w"> </span>_≟_<span class="w"> </span>isLatticeᵛ<span class="w"> </span>states<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">open</span><span class="w"> </span>StateVariablesFiniteMap<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">using</span><span class="w"> </span><span class="o">(</span>_[_];<span class="w"> </span>[]-∈;<span class="w"> </span>m₁≼m₂⇒m₁[ks]≼m₂[ks];<span class="w"> </span>m₁≈m₂⇒k∈m₁⇒k∈km₂⇒v₁≈v₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">renaming</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span><span class="w"> </span>FiniteMap<span class="w"> </span>to<span class="w"> </span>StateVariables<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>isLattice<span class="w"> </span>to<span class="w"> </span>isLatticeᵐ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>_≈_<span class="w"> </span>to<span class="w"> </span>_≈ᵐ_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>_∈_<span class="w"> </span>to<span class="w"> </span>_∈ᵐ_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>_∈k_<span class="w"> </span>to<span class="w"> </span>_∈kᵐ_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>locate<span class="w"> </span>to<span class="w"> </span>locateᵐ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>_≼_<span class="w"> </span>to<span class="w"> </span>_≼ᵐ_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>≈₂-dec⇒≈-dec<span class="w"> </span>to<span class="w"> </span>≈ᵛ-dec⇒≈ᵐ-dec<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>m₁≼m₂⇒m₁[k]≼m₂[k]<span class="w"> </span>to<span class="w"> </span>m₁≼m₂⇒m₁[k]ᵐ≼m₂[k]ᵐ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>public<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">open</span><span class="w"> </span>Lattice.FiniteValueMap.IterProdIsomorphism.WithUniqueKeysAndFixedHeight<span class="w"> </span>_≟_<span class="w"> </span>isLatticeᵛ<span class="w"> </span>states-Unique<span class="w"> </span>≈ᵛ-dec<span class="w"> </span>_<span class="w"> </span>fixedHeightᵛ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">using</span><span class="w"> </span><span class="o">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">renaming</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span><span class="w"> </span>isFiniteHeightLattice<span class="w"> </span>to<span class="w"> </span>isFiniteHeightLatticeᵐ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">open</span><span class="w"> </span>IsFiniteHeightLattice<span class="w"> </span>isFiniteHeightLatticeᵐ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">using</span><span class="w"> </span><span class="o">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">renaming</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span><span class="w"> </span>≈-sym<span class="w"> </span>to<span class="w"> </span>≈ᵐ-sym<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>≈ᵐ-dec<span class="w"> </span><span class="ow">=</span><span class="w"> </span>≈ᵛ-dec⇒≈ᵐ-dec<span class="w"> </span>≈ᵛ-dec<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>fixedHeightᵐ<span class="w"> </span><span class="ow">=</span><span class="w"> </span>IsFiniteHeightLattice.fixedHeight<span class="w"> </span>isFiniteHeightLatticeᵐ</span></span></code></pre></td></tr></table> </div> </div> </div> </details> <a href="#constructing-a-monotone-function"> <h3 id="constructing-a-monotone-function">Constructing a Monotone Function</h3> </a> <p>We now have a lattice in hand; the next step is to define a function over this lattice. For us to be able to use the fixed-point algorithm on this function, it will need to be <a href="https://danilafe.com/blog/01_spa_agda_lattices/#define-monotonicity">monotonic</a>.</p> <p>Our goal with static analysis is to compute information about our program; that&rsquo;s what we want the function to do. When the lattice we&rsquo;re using is the sign lattice, we&rsquo;re trying to determine the signs of each of the variables in various parts of the program. How do we go about this?</p> <p id="general-evaluator">Each piece of code in the program might change a variable&rsquo;s sign. For instance, if <code>x</code> has sign \(0\), and we run the statement <code>x = x - 1</code>, the sign of <code>x</code> will be \(-\). If we have an expression <code>y + z</code>, we can use the signs of <code>y</code> and <code>z</code> to compute the sign of the whole thing. This is a form of <a href="https://en.wikipedia.org/wiki/Abstract_interpretation"class="external-link">abstract interpretation<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, in which we almost-run the program, but forget some details (e.g., the exact values of <code>x</code>, <code>y</code>, and <code>z</code>, leaving only their signs). The exact details of how this partial evaluation is done are analysis-specific; in general, we simply require an analysis to provide an evaluator. We will define <a href="#instantiating-with-the-sign-lattice"class="same-page-link">an evaluator for the sign lattice below<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#arrow-up"/> </svg></a>.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Forward.agda" data-first-line="166" data-last-line="167" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Forward.agda#L166-L167">Forward.agda</a>, lines 166 through 167</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">166 </span><span class="lnt">167 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="kr">module</span><span class="w"> </span><span class="n">WithEvaluator</span><span class="w"> </span><span class="o">(</span>eval<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">→</span><span class="w"> </span>VariableValues<span class="w"> </span><span class="ow">→</span><span class="w"> </span>L<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>eval-Mono<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>e<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Monotonic<span class="w"> </span>_≼ᵛ_<span class="w"> </span>_≼ˡ_<span class="w"> </span><span class="o">(</span>eval<span class="w"> </span>e<span class="o">))</span><span class="w"> </span><span class="kr">where</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>From this, we know how each statement and basic block will change variables in the function. But we have described them process as &ldquo;if a variable has sign X, it becomes sign Y&rdquo; &ndash; how do we know what sign a variable has <em>before</em> the code runs? Fortunately, the Control Flow Graph tells us exactly what code could be executed before any given basic block. Recall that edges in the graph describe all possible jumps that could occur; thus, for any node, the incoming edges describe all possible blocks that can precede it. This is why we spent all that time <a href="https://danilafe.com/blog/06_spa_agda_cfg/#additional-functions">defining the <code>predecessors</code> function</a>.</p> <p id="join-preds">We proceed as follows: for any given node, find its predecessors. By accessing our \(\text{Info}\) map for each predecessor, we can determine our current best guess of variable signs at that point, in the form of a \(\text{Variable} \to \text{Sign}\) map (more generally, \(\text{Variable} \to L\) map in an arbitrary analysis). We know that any of these predecessors could&rsquo;ve been the previous point of execution; if a variable <code>x</code> has sign \(+\) in one predecessor and \(-\) in another, it can be either one or the other when we start executing the current block. Early on, we saw that <a href="https://danilafe.com/blog/01_spa_agda_lattices/#lub-glub-or-and">the \((\sqcup)\) operator models disjunction (&ldquo;A or B&rdquo;)</a>. So, we apply \((\sqcup)\) to the variable-sign maps of all predecessors. The <a href="https://cs.au.dk/~amoeller/spa/"class="external-link">reference <em>Static Program Analysis</em> text<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> calls this operation \(\text{JOIN}\):</p> $$ \textit{JOIN}(v) = \bigsqcup_{w \in \textit{pred}(v)} \llbracket w \rrbracket $$ <p>The Agda implementation uses a <code>foldr</code>:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Forward.agda" data-first-line="139" data-last-line="140" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Forward.agda#L139-L140">Forward.agda</a>, lines 139 through 140</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">139 </span><span class="lnt">140 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">joinForKey</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>State<span class="w"> </span><span class="ow">→</span><span class="w"> </span>StateVariables<span class="w"> </span><span class="ow">→</span><span class="w"> </span>VariableValues<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>joinForKey<span class="w"> </span>k<span class="w"> </span>states<span class="w"> </span><span class="ow">=</span><span class="w"> </span>foldr<span class="w"> </span>_⊔ᵛ_<span class="w"> </span>⊥ᵛ<span class="w"> </span><span class="o">(</span>states<span class="w"> </span>[<span class="w"> </span>incoming<span class="w"> </span>k<span class="w"> </span>]<span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Computing the &ldquo;combined incoming states&rdquo; for any node is a monotonic function. This follows from the monotonicity of \((\sqcup)\) &mdash; in both arguments &mdash; and the definition of <code>foldr</code>.</p> <details><summary><strong>(Click here to expand the general proof)</strong></summary><div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice.agda" data-first-line="143" data-last-line="151" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice.agda#L143-L151">Lattice.agda</a>, lines 143 through 151</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">143 </span><span class="lnt">144 </span><span class="lnt">145 </span><span class="lnt">146 </span><span class="lnt">147 </span><span class="lnt">148 </span><span class="lnt">149 </span><span class="lnt">150 </span><span class="lnt">151 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">foldr-Mono</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>l₁<span class="w"> </span>l₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>List<span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="o">(</span>f<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="o">)</span><span class="w"> </span><span class="o">(</span>b₁<span class="w"> </span>b₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>Pairwise<span class="w"> </span>_≼₁_<span class="w"> </span>l₁<span class="w"> </span>l₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>b₁<span class="w"> </span>≼₂<span class="w"> </span>b₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span><span class="ow">∀</span><span class="w"> </span>b<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Monotonic<span class="w"> </span>_≼₁_<span class="w"> </span>_≼₂_<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>a<span class="w"> </span><span class="ow">→</span><span class="w"> </span>f<span class="w"> </span>a<span class="w"> </span>b<span class="o">))</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span><span class="ow">∀</span><span class="w"> </span>a<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Monotonic<span class="w"> </span>_≼₂_<span class="w"> </span>_≼₂_<span class="w"> </span><span class="o">(</span>f<span class="w"> </span>a<span class="o">))</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>foldr<span class="w"> </span>f<span class="w"> </span>b₁<span class="w"> </span>l₁<span class="w"> </span>≼₂<span class="w"> </span>foldr<span class="w"> </span>f<span class="w"> </span>b₂<span class="w"> </span>l₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>foldr-Mono<span class="w"> </span>List.[]<span class="w"> </span>List.[]<span class="w"> </span>f<span class="w"> </span>b₁<span class="w"> </span>b₂<span class="w"> </span>_<span class="w"> </span>b₁≼b₂<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>b₁≼b₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>foldr-Mono<span class="w"> </span><span class="o">(</span>x<span class="w"> </span>∷<span class="w"> </span>xs<span class="o">)</span><span class="w"> </span><span class="o">(</span>y<span class="w"> </span>∷<span class="w"> </span>ys<span class="o">)</span><span class="w"> </span>f<span class="w"> </span>b₁<span class="w"> </span>b₂<span class="w"> </span><span class="o">(</span>x≼y<span class="w"> </span>∷<span class="w"> </span>xs≼ys<span class="o">)</span><span class="w"> </span>b₁≼b₂<span class="w"> </span>f-Mono₁<span class="w"> </span>f-Mono₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>≼₂-trans<span class="w"> </span><span class="o">(</span>f-Mono₁<span class="w"> </span><span class="o">(</span>foldr<span class="w"> </span>f<span class="w"> </span>b₁<span class="w"> </span>xs<span class="o">)</span><span class="w"> </span>x≼y<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>f-Mono₂<span class="w"> </span>y<span class="w"> </span><span class="o">(</span>foldr-Mono<span class="w"> </span>xs<span class="w"> </span>ys<span class="w"> </span>f<span class="w"> </span>b₁<span class="w"> </span>b₂<span class="w"> </span>xs≼ys<span class="w"> </span>b₁≼b₂<span class="w"> </span>f-Mono₁<span class="w"> </span>f-Mono₂<span class="o">))</span></span></span></code></pre></td></tr></table> </div> </div> </div> </details> <p>From this, we can formally state that \(\text{JOIN}\) is monotonic. Note that the input and output lattices are different: the input lattice is the lattice of variable states at each block, and the output lattice is a single variable-sign map, representing the combined preceding state at a given node.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Forward.agda" data-first-line="145" data-last-line="149" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Forward.agda#L145-L149">Forward.agda</a>, lines 145 through 149</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">145 </span><span class="lnt">146 </span><span class="lnt">147 </span><span class="lnt">148 </span><span class="lnt">149 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">joinForKey-Mono</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>k<span class="w"> </span><span class="ow">:</span><span class="w"> </span>State<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Monotonic<span class="w"> </span>_≼ᵐ_<span class="w"> </span>_≼ᵛ_<span class="w"> </span><span class="o">(</span>joinForKey<span class="w"> </span>k<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>joinForKey-Mono<span class="w"> </span>k<span class="w"> </span><span class="o">{</span>fm₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>fm₂<span class="o">}</span><span class="w"> </span>fm₁≼fm₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>foldr-Mono<span class="w"> </span>joinSemilatticeᵛ<span class="w"> </span>joinSemilatticeᵛ<span class="w"> </span><span class="o">(</span>fm₁<span class="w"> </span>[<span class="w"> </span>incoming<span class="w"> </span>k<span class="w"> </span>]<span class="o">)</span><span class="w"> </span><span class="o">(</span>fm₂<span class="w"> </span>[<span class="w"> </span>incoming<span class="w"> </span>k<span class="w"> </span>]<span class="o">)</span><span class="w"> </span>_⊔ᵛ_<span class="w"> </span>⊥ᵛ<span class="w"> </span>⊥ᵛ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>m₁≼m₂⇒m₁[ks]≼m₂[ks]<span class="w"> </span>fm₁<span class="w"> </span>fm₂<span class="w"> </span><span class="o">(</span>incoming<span class="w"> </span>k<span class="o">)</span><span class="w"> </span>fm₁≼fm₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>⊔ᵛ-idemp<span class="w"> </span>⊥ᵛ<span class="o">)</span><span class="w"> </span>⊔ᵛ-Monotonicʳ<span class="w"> </span>⊔ᵛ-Monotonicˡ</span></span></code></pre></td></tr></table> </div> </div> </div> <p id="less-than-lemma">Above, the <code>m₁≼m₂⇒m₁[ks]≼m₂[ks]</code> lemma states that for two maps with the same keys, where one map is less than another, all the values for any subset of keys <code>ks</code> are pairwise less than each other (i.e. <code>m₁[k]≼m₂[k]</code>, and <code>m₁[l]≼m₂[l]</code>, etc.). This follows from the definition of &ldquo;less than&rdquo; for maps.</p> <p>So those are the two pieces: first, join all the preceding states, then use the abstract interpretation function. I opted to do both of these in bulk:</p> <ol> <li>Take an initial \(\text{Info}\) map, and update every basic block&rsquo;s entry to be the join of its predecessors.</li> <li>In the new joined map, each key now contains the variable state at the beginning of the block; so, apply the abstract interpretation function via <code>eval</code> to each key, computing the state at the end of the block.</li> </ol> <p>I chose to do these in bulk because this way, after each application of the function, we have updated each block with exactly one round of information. The alternative &mdash; which is specified in the reference text &mdash; is to update one key at a time. The difference there is that updates to later keys might be &ldquo;tainted&rdquo; by updates to keys that came before them. This is probably fine (and perhaps more efficient, in that it &ldquo;moves faster&rdquo;), but it&rsquo;s harder to reason about.</p> <a href="#generalized-update"> <h4 id="generalized-update">Generalized Update</h4> </a> <p>To implement bulk assignment, I needed to implement the source text&rsquo;s Exercise 4.26:</p> <blockquote> <p><strong>Exercise 4.26</strong>: Recall that \(f[a \leftarrow x]\) denotes the function that is identical to \(f\) except that it maps \(a\) to \(x\). Assume \(f : L_1 \to (A \to L_2)\) and \(g : L_1 \to L_2\) are monotone functions where \(L_1\) and \(L_2\) are lattices and \(A\) is a set, and let \(a \in A\). (Note that the codomain of \(f\) is a map lattice.)</p> <p>Show that the function \(h : L_1 \to (A \to L_2)\) defined by \(h(x) = f(x)[a \leftarrow g(x)]\) is monotone.</p> </blockquote> <p>In fact, I generalized this statement to update several keys at once, as follows:</p> $$ h(x) = f(x)[a_1 \leftarrow g(a_1, x),\ ...,\ a_n \leftarrow g(a_n, x)] $$ <p>I called this operation &ldquo;generalized update&rdquo;.</p> <p>At first, the exercise may not obviously correspond to the bulk operation I&rsquo;ve described. Particularly confusing is the fact that it has two lattices, \(L_1\) and \(L_2\). In fact, the exercise results in a very general theorem; we can exploit a more concrete version of the theorem by setting \(L_1 \triangleq A \to L_2\), resulting in an overall signature for \(f\) and \(h\):</p> $$ f : (A \to L_2) \to (A \to L_2) $$ <p>In other words, if we give the entire operation in Exercise 4.26 a type, it would look like this:</p> $$ \text{ex}_{4.26} : \underbrace{K}_{\text{value of}\ a} \to \underbrace{(\text{Map} \to V)}_{\text{updater}} \to \underbrace{\text{Map} \to \text{Map}}_{f} \to \underbrace{\text{Map} \to \text{Map}}_{h} $$ <p>That&rsquo;s still more general than we need it. This here allows us to modify any map-to-map function by updating a certain key in that function. If we <em>just</em> want to update keys (as we do for the purposes of static analysis), we can recover a simpler version by setting \(f \triangleq id\), which results in an updater \(h(x) = x[a \leftarrow g(x)]\), and a signature for the exercise:</p> $$ \text{ex}_{4.26} : \underbrace{K}_{\text{value of}\ a} \to \underbrace{(\text{Map} \to V)}_{\text{updater}\ g} \to \underbrace{\text{Map}}_{\text{old map}} \to \underbrace{\text{Map}}_{\text{updated map}} $$ <p>This looks just like Haskell&rsquo;s <a href="https://hackage.haskell.org/package/containers-0.4.0.0/docs/src/Data-Map.html#adjust"class="external-link"><code>Data.Map.adjust</code> function<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, except that it can take the entire map into consideration when updating a key.</p> <p>My generalized version takes in a list of keys to update, and makes the updater accept a key so that its behavior can be specialized for each entry it changes. The sketch of the implementation is in the <code>_updating_via_</code> function from the <code>Map</code> module, and its helper <code>transform</code>. Here, I collapse its definition, since it&rsquo;s not particularly important.</p> <details><summary><strong>(Click here to see the definition of <code>transform</code>)</strong></summary><div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Map.agda" data-first-line="926" data-last-line="931" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Map.agda#L926-L931">Map.agda</a>, lines 926 through 931</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">926 </span><span class="lnt">927 </span><span class="lnt">928 </span><span class="lnt">929 </span><span class="lnt">930 </span><span class="lnt">931 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">transform</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>List<span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>transform<span class="w"> </span>[]<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>[]<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>transform<span class="w"> </span><span class="o">((</span>k<span class="w"> </span>,<span class="w"> </span>v<span class="o">)</span><span class="w"> </span>∷<span class="w"> </span>xs<span class="o">)</span><span class="w"> </span>ks<span class="w"> </span>f<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">with</span><span class="w"> </span>k∈-dec<span class="w"> </span>k<span class="w"> </span>ks<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>yes<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>k<span class="w"> </span>,<span class="w"> </span>f<span class="w"> </span>k<span class="o">)</span><span class="w"> </span>∷<span class="w"> </span>transform<span class="w"> </span>xs<span class="w"> </span>ks<span class="w"> </span>f<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>no<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>k<span class="w"> </span>,<span class="w"> </span>v<span class="o">)</span><span class="w"> </span>∷<span class="w"> </span>transform<span class="w"> </span>xs<span class="w"> </span>ks<span class="w"> </span>f</span></span></code></pre></td></tr></table> </div> </div> </div> </details> <p>The proof of monotonicity &mdash; which is the solution to the exercise &mdash; is actually quite complicated. I will omit its description, and show it here in another collapsed block.</p> <details><summary><strong>(Click here to see the proof of monotonicity of \(h\))</strong></summary><div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Map.agda" data-first-line="1042" data-last-line="1105" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Map.agda#L1042-L1105">Map.agda</a>, lines 1042 through 1105</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1042 </span><span class="lnt">1043 </span><span class="lnt">1044 </span><span class="lnt">1045 </span><span class="lnt">1046 </span><span class="lnt">1047 </span><span class="lnt">1048 </span><span class="lnt">1049 </span><span class="lnt">1050 </span><span class="lnt">1051 </span><span class="lnt">1052 </span><span class="lnt">1053 </span><span class="lnt">1054 </span><span class="lnt">1055 </span><span class="lnt">1056 </span><span class="lnt">1057 </span><span class="lnt">1058 </span><span class="lnt">1059 </span><span class="lnt">1060 </span><span class="lnt">1061 </span><span class="lnt">1062 </span><span class="lnt">1063 </span><span class="lnt">1064 </span><span class="lnt">1065 </span><span class="lnt">1066 </span><span class="lnt">1067 </span><span class="lnt">1068 </span><span class="lnt">1069 </span><span class="lnt">1070 </span><span class="lnt">1071 </span><span class="lnt">1072 </span><span class="lnt">1073 </span><span class="lnt">1074 </span><span class="lnt">1075 </span><span class="lnt">1076 </span><span class="lnt">1077 </span><span class="lnt">1078 </span><span class="lnt">1079 </span><span class="lnt">1080 </span><span class="lnt">1081 </span><span class="lnt">1082 </span><span class="lnt">1083 </span><span class="lnt">1084 </span><span class="lnt">1085 </span><span class="lnt">1086 </span><span class="lnt">1087 </span><span class="lnt">1088 </span><span class="lnt">1089 </span><span class="lnt">1090 </span><span class="lnt">1091 </span><span class="lnt">1092 </span><span class="lnt">1093 </span><span class="lnt">1094 </span><span class="lnt">1095 </span><span class="lnt">1096 </span><span class="lnt">1097 </span><span class="lnt">1098 </span><span class="lnt">1099 </span><span class="lnt">1100 </span><span class="lnt">1101 </span><span class="lnt">1102 </span><span class="lnt">1103 </span><span class="lnt">1104 </span><span class="lnt">1105 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">f&#39;-Monotonic</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Monotonic<span class="w"> </span>_≼ˡ_<span class="w"> </span>_≼_<span class="w"> </span>f&#39;<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>f&#39;-Monotonic<span class="w"> </span><span class="o">{</span>l₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>l₂<span class="o">}</span><span class="w"> </span>l₁≼l₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>f&#39;l₁f&#39;l₂⊆f&#39;l₂<span class="w"> </span>,<span class="w"> </span>f&#39;l₂⊆f&#39;l₁f&#39;l₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>fl₁fl₂⊆fl₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>proj₁<span class="w"> </span><span class="o">(</span>f-Monotonic<span class="w"> </span>l₁≼l₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>fl₂⊆fl₁fl₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>proj₂<span class="w"> </span><span class="o">(</span>f-Monotonic<span class="w"> </span>l₁≼l₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">f&#39;l₁f&#39;l₂⊆f&#39;l₂</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="o">((</span>f&#39;<span class="w"> </span>l₁<span class="o">)</span><span class="w"> </span>⊔<span class="w"> </span><span class="o">(</span>f&#39;<span class="w"> </span>l₂<span class="o">))</span><span class="w"> </span>⊆<span class="w"> </span>f&#39;<span class="w"> </span>l₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>f&#39;l₁f&#39;l₂⊆f&#39;l₂<span class="w"> </span>k<span class="w"> </span>v<span class="w"> </span>k,v∈f&#39;l₁f&#39;l₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">with</span><span class="w"> </span>Expr-Provenance-≡<span class="w"> </span><span class="o">((</span>`<span class="w"> </span><span class="o">(</span>f&#39;<span class="w"> </span>l₁<span class="o">))</span><span class="w"> </span>∪<span class="w"> </span><span class="o">(</span>`<span class="w"> </span><span class="o">(</span>f&#39;<span class="w"> </span>l₂<span class="o">)))</span><span class="w"> </span>k,v∈f&#39;l₁f&#39;l₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span><span class="kr">in</span>₁<span class="w"> </span><span class="o">(</span>single<span class="w"> </span>k,v∈f&#39;l₁<span class="o">)</span><span class="w"> </span>k∉kf&#39;l₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">let</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>k∈kfl₁<span class="w"> </span><span class="ow">=</span><span class="w"> </span>updating-via-∈k-backward<span class="w"> </span><span class="o">(</span>f<span class="w"> </span>l₁<span class="o">)</span><span class="w"> </span>ks<span class="w"> </span><span class="o">(</span>updater<span class="w"> </span>l₁<span class="o">)</span><span class="w"> </span><span class="o">(</span>forget<span class="w"> </span>k,v∈f&#39;l₁<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>k∈kfl₁fl₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>union-preserves-∈k₁<span class="w"> </span><span class="o">{</span>l₁<span class="w"> </span><span class="ow">=</span><span class="w"> </span>proj₁<span class="w"> </span><span class="o">(</span>f<span class="w"> </span>l₁<span class="o">)}</span><span class="w"> </span><span class="o">{</span>l₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>proj₁<span class="w"> </span><span class="o">(</span>f<span class="w"> </span>l₂<span class="o">)}</span><span class="w"> </span>k∈kfl₁<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>v&#39;<span class="w"> </span>,<span class="w"> </span>k,v&#39;∈fl₁l₂<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>locate<span class="w"> </span><span class="o">{</span>m<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>f<span class="w"> </span>l₁<span class="w"> </span>⊔<span class="w"> </span>f<span class="w"> </span>l₂<span class="o">)}</span><span class="w"> </span>k∈kfl₁fl₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>v&#39;&#39;<span class="w"> </span>,<span class="w"> </span><span class="o">(</span>v&#39;≈v&#39;&#39;<span class="w"> </span>,<span class="w"> </span>k,v&#39;&#39;∈fl₂<span class="o">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>fl₁fl₂⊆fl₂<span class="w"> </span>k<span class="w"> </span>v&#39;<span class="w"> </span>k,v&#39;∈fl₁l₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>k∈kf&#39;l₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>updating-via-∈k-forward<span class="w"> </span><span class="o">(</span>f<span class="w"> </span>l₂<span class="o">)</span><span class="w"> </span>ks<span class="w"> </span><span class="o">(</span>updater<span class="w"> </span>l₂<span class="o">)</span><span class="w"> </span><span class="o">(</span>forget<span class="w"> </span>k,v&#39;&#39;∈fl₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">in</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⊥-elim<span class="w"> </span><span class="o">(</span>k∉kf&#39;l₂<span class="w"> </span>k∈kf&#39;l₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span><span class="kr">in</span>₂<span class="w"> </span>k∉kf&#39;l₁<span class="w"> </span><span class="o">(</span>single<span class="w"> </span>k,v&#39;∈f&#39;l₂<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>v<span class="w"> </span>,<span class="w"> </span><span class="o">(</span>IsLattice.≈-refl<span class="w"> </span>lB<span class="w"> </span>,<span class="w"> </span>k,v&#39;∈f&#39;l₂<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>bothᵘ<span class="w"> </span><span class="o">(</span>single<span class="w"> </span><span class="o">{</span>v₁<span class="o">}</span><span class="w"> </span>k,v₁∈f&#39;l₁<span class="o">)</span><span class="w"> </span><span class="o">(</span>single<span class="w"> </span><span class="o">{</span>v₂<span class="o">}</span><span class="w"> </span>k,v₂∈f&#39;l₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">with</span><span class="w"> </span>k∈-dec<span class="w"> </span>k<span class="w"> </span>ks<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>yes<span class="w"> </span>k∈ks<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">with</span><span class="w"> </span>refl<span class="w"> </span>←<span class="w"> </span>updating-via-k∈ks-≡<span class="w"> </span><span class="o">(</span>f<span class="w"> </span>l₁<span class="o">)</span><span class="w"> </span><span class="o">(</span>updater<span class="w"> </span>l₁<span class="o">)</span><span class="w"> </span>k∈ks<span class="w"> </span>k,v₁∈f&#39;l₁<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">with</span><span class="w"> </span>refl<span class="w"> </span>←<span class="w"> </span>updating-via-k∈ks-≡<span class="w"> </span><span class="o">(</span>f<span class="w"> </span>l₂<span class="o">)</span><span class="w"> </span><span class="o">(</span>updater<span class="w"> </span>l₂<span class="o">)</span><span class="w"> </span>k∈ks<span class="w"> </span>k,v₂∈f&#39;l₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>updater<span class="w"> </span>l₂<span class="w"> </span>k<span class="w"> </span>,<span class="w"> </span><span class="o">(</span>g-Monotonicʳ<span class="w"> </span>k<span class="w"> </span>l₁≼l₂<span class="w"> </span>,<span class="w"> </span>k,v₂∈f&#39;l₂<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>no<span class="w"> </span>k∉ks<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">let</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>k,v₁∈fl₁<span class="w"> </span><span class="ow">=</span><span class="w"> </span>updating-via-k∉ks-backward<span class="w"> </span><span class="o">(</span>f<span class="w"> </span>l₁<span class="o">)</span><span class="w"> </span><span class="o">(</span>updater<span class="w"> </span>l₁<span class="o">)</span><span class="w"> </span>k∉ks<span class="w"> </span>k,v₁∈f&#39;l₁<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>k,v₂∈fl₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>updating-via-k∉ks-backward<span class="w"> </span><span class="o">(</span>f<span class="w"> </span>l₂<span class="o">)</span><span class="w"> </span><span class="o">(</span>updater<span class="w"> </span>l₂<span class="o">)</span><span class="w"> </span>k∉ks<span class="w"> </span>k,v₂∈f&#39;l₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>k,v₁v₂∈fl₁fl₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊔-combines<span class="w"> </span><span class="o">{</span>m₁<span class="w"> </span><span class="ow">=</span><span class="w"> </span>f<span class="w"> </span>l₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>m₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>f<span class="w"> </span>l₂<span class="o">}</span><span class="w"> </span>k,v₁∈fl₁<span class="w"> </span>k,v₂∈fl₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>v&#39;<span class="w"> </span>,<span class="w"> </span><span class="o">(</span>v&#39;≈v₁v₂<span class="w"> </span>,<span class="w"> </span>k,v&#39;∈fl₂<span class="o">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>fl₁fl₂⊆fl₂<span class="w"> </span>k<span class="w"> </span>_<span class="w"> </span>k,v₁v₂∈fl₁fl₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>k,v&#39;∈f&#39;l₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>updating-via-k∉ks-forward<span class="w"> </span><span class="o">(</span>f<span class="w"> </span>l₂<span class="o">)</span><span class="w"> </span><span class="o">(</span>updater<span class="w"> </span>l₂<span class="o">)</span><span class="w"> </span>k∉ks<span class="w"> </span>k,v&#39;∈fl₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">in</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>v&#39;<span class="w"> </span>,<span class="w"> </span><span class="o">(</span>v&#39;≈v₁v₂<span class="w"> </span>,<span class="w"> </span>k,v&#39;∈f&#39;l₂<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">f&#39;l₂⊆f&#39;l₁f&#39;l₂</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>f&#39;<span class="w"> </span>l₂<span class="w"> </span>⊆<span class="w"> </span><span class="o">((</span>f&#39;<span class="w"> </span>l₁<span class="o">)</span><span class="w"> </span>⊔<span class="w"> </span><span class="o">(</span>f&#39;<span class="w"> </span>l₂<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>f&#39;l₂⊆f&#39;l₁f&#39;l₂<span class="w"> </span>k<span class="w"> </span>v<span class="w"> </span>k,v∈f&#39;l₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">with</span><span class="w"> </span>k∈kfl₂<span class="w"> </span>←<span class="w"> </span>updating-via-∈k-backward<span class="w"> </span><span class="o">(</span>f<span class="w"> </span>l₂<span class="o">)</span><span class="w"> </span>ks<span class="w"> </span><span class="o">(</span>updater<span class="w"> </span>l₂<span class="o">)</span><span class="w"> </span><span class="o">(</span>forget<span class="w"> </span>k,v∈f&#39;l₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">with</span><span class="w"> </span><span class="o">(</span>v&#39;<span class="w"> </span>,<span class="w"> </span>k,v&#39;∈fl₂<span class="o">)</span><span class="w"> </span>←<span class="w"> </span>locate<span class="w"> </span><span class="o">{</span>m<span class="w"> </span><span class="ow">=</span><span class="w"> </span>f<span class="w"> </span>l₂<span class="o">}</span><span class="w"> </span>k∈kfl₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">with</span><span class="w"> </span><span class="o">(</span>v&#39;&#39;<span class="w"> </span>,<span class="w"> </span><span class="o">(</span>v&#39;≈v&#39;&#39;<span class="w"> </span>,<span class="w"> </span>k,v&#39;&#39;∈fl₁fl₂<span class="o">))</span><span class="w"> </span>←<span class="w"> </span>fl₂⊆fl₁fl₂<span class="w"> </span>k<span class="w"> </span>v&#39;<span class="w"> </span>k,v&#39;∈fl₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">with</span><span class="w"> </span>Expr-Provenance-≡<span class="w"> </span><span class="o">((</span>`<span class="w"> </span><span class="o">(</span>f<span class="w"> </span>l₁<span class="o">))</span><span class="w"> </span>∪<span class="w"> </span><span class="o">(</span>`<span class="w"> </span><span class="o">(</span>f<span class="w"> </span>l₂<span class="o">)))</span><span class="w"> </span>k,v&#39;&#39;∈fl₁fl₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span><span class="kr">in</span>₁<span class="w"> </span><span class="o">(</span>single<span class="w"> </span>k,v&#39;&#39;∈fl₁<span class="o">)</span><span class="w"> </span>k∉kfl₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥-elim<span class="w"> </span><span class="o">(</span>k∉kfl₂<span class="w"> </span>k∈kfl₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span><span class="kr">in</span>₂<span class="w"> </span>k∉kfl₁<span class="w"> </span><span class="o">(</span>single<span class="w"> </span>k,v&#39;&#39;∈fl₂<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">let</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>k∉kf&#39;l₁<span class="w"> </span><span class="ow">=</span><span class="w"> </span>updating-via-∉k-forward<span class="w"> </span><span class="o">(</span>f<span class="w"> </span>l₁<span class="o">)</span><span class="w"> </span>ks<span class="w"> </span><span class="o">(</span>updater<span class="w"> </span>l₁<span class="o">)</span><span class="w"> </span>k∉kfl₁<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">in</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>v<span class="w"> </span>,<span class="w"> </span><span class="o">(</span>IsLattice.≈-refl<span class="w"> </span>lB<span class="w"> </span>,<span class="w"> </span>union-preserves-∈₂<span class="w"> </span>k∉kf&#39;l₁<span class="w"> </span>k,v∈f&#39;l₂<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>bothᵘ<span class="w"> </span><span class="o">(</span>single<span class="w"> </span><span class="o">{</span>v₁<span class="o">}</span><span class="w"> </span>k,v₁∈fl₁<span class="o">)</span><span class="w"> </span><span class="o">(</span>single<span class="w"> </span><span class="o">{</span>v₂<span class="o">}</span><span class="w"> </span>k,v₂∈fl₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">with</span><span class="w"> </span>k∈-dec<span class="w"> </span>k<span class="w"> </span>ks<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>yes<span class="w"> </span>k∈ks<span class="w"> </span><span class="kr">with</span><span class="w"> </span>refl<span class="w"> </span>←<span class="w"> </span>updating-via-k∈ks-≡<span class="w"> </span><span class="o">(</span>f<span class="w"> </span>l₂<span class="o">)</span><span class="w"> </span><span class="o">(</span>updater<span class="w"> </span>l₂<span class="o">)</span><span class="w"> </span>k∈ks<span class="w"> </span>k,v∈f&#39;l₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">let</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>k,uv₁∈f&#39;l₁<span class="w"> </span><span class="ow">=</span><span class="w"> </span>updating-via-k∈ks-forward<span class="w"> </span><span class="o">(</span>f<span class="w"> </span>l₁<span class="o">)</span><span class="w"> </span><span class="o">(</span>updater<span class="w"> </span>l₁<span class="o">)</span><span class="w"> </span>k∈ks<span class="w"> </span><span class="o">(</span>forget<span class="w"> </span>k,v₁∈fl₁<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>k,uv₂∈f&#39;l₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>updating-via-k∈ks-forward<span class="w"> </span><span class="o">(</span>f<span class="w"> </span>l₂<span class="o">)</span><span class="w"> </span><span class="o">(</span>updater<span class="w"> </span>l₂<span class="o">)</span><span class="w"> </span>k∈ks<span class="w"> </span><span class="o">(</span>forget<span class="w"> </span>k,v₂∈fl₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>k,uv₁uv₂∈f&#39;l₁f&#39;l₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊔-combines<span class="w"> </span><span class="o">{</span>m₁<span class="w"> </span><span class="ow">=</span><span class="w"> </span>f&#39;<span class="w"> </span>l₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>m₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>f&#39;<span class="w"> </span>l₂<span class="o">}</span><span class="w"> </span>k,uv₁∈f&#39;l₁<span class="w"> </span>k,uv₂∈f&#39;l₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">in</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>updater<span class="w"> </span>l₁<span class="w"> </span>k<span class="w"> </span>⊔₂<span class="w"> </span>updater<span class="w"> </span>l₂<span class="w"> </span>k<span class="w"> </span>,<span class="w"> </span><span class="o">(</span>IsLattice.≈-sym<span class="w"> </span>lB<span class="w"> </span><span class="o">(</span>g-Monotonicʳ<span class="w"> </span>k<span class="w"> </span>l₁≼l₂<span class="o">)</span><span class="w"> </span>,<span class="w"> </span>k,uv₁uv₂∈f&#39;l₁f&#39;l₂<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>no<span class="w"> </span>k∉ks<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">with</span><span class="w"> </span>k,v₁∈f&#39;l₁<span class="w"> </span>←<span class="w"> </span>updating-via-k∉ks-forward<span class="w"> </span><span class="o">(</span>f<span class="w"> </span>l₁<span class="o">)</span><span class="w"> </span><span class="o">(</span>updater<span class="w"> </span>l₁<span class="o">)</span><span class="w"> </span>k∉ks<span class="w"> </span>k,v₁∈fl₁<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">with</span><span class="w"> </span>k,v₂∈f&#39;l₂<span class="w"> </span>←<span class="w"> </span>updating-via-k∉ks-forward<span class="w"> </span><span class="o">(</span>f<span class="w"> </span>l₂<span class="o">)</span><span class="w"> </span><span class="o">(</span>updater<span class="w"> </span>l₂<span class="o">)</span><span class="w"> </span>k∉ks<span class="w"> </span>k,v₂∈fl₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">with</span><span class="w"> </span>k,v₁v₂∈f&#39;l₁f&#39;l₂<span class="w"> </span>←<span class="w"> </span>⊔-combines<span class="w"> </span><span class="o">{</span>m₁<span class="w"> </span><span class="ow">=</span><span class="w"> </span>f&#39;<span class="w"> </span>l₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>m₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>f&#39;<span class="w"> </span>l₂<span class="o">}</span><span class="w"> </span>k,v₁∈f&#39;l₁<span class="w"> </span>k,v₂∈f&#39;l₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">with</span><span class="w"> </span>refl<span class="w"> </span>←<span class="w"> </span>Map-functional<span class="w"> </span><span class="o">{</span>m<span class="w"> </span><span class="ow">=</span><span class="w"> </span>f&#39;<span class="w"> </span>l₂<span class="o">}</span><span class="w"> </span>k,v∈f&#39;l₂<span class="w"> </span>k,v₂∈f&#39;l₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">with</span><span class="w"> </span>refl<span class="w"> </span>←<span class="w"> </span>Map-functional<span class="w"> </span><span class="o">{</span>m<span class="w"> </span><span class="ow">=</span><span class="w"> </span>f<span class="w"> </span>l₂<span class="o">}</span><span class="w"> </span>k,v&#39;∈fl₂<span class="w"> </span>k,v₂∈fl₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>v₁<span class="w"> </span>⊔₂<span class="w"> </span>v<span class="w"> </span>,<span class="w"> </span><span class="o">(</span>v&#39;≈v&#39;&#39;<span class="w"> </span>,<span class="w"> </span>k,v₁v₂∈f&#39;l₁f&#39;l₂<span class="o">))</span></span></span></code></pre></td></tr></table> </div> </div> </div> </details> <p>Given a proof of the exercise, all that&rsquo;s left is to instantiate the theorem with the argument I described. Specifically:</p> <ul> <li>\(L_1 \triangleq \text{Info} \triangleq \text{NodeId} \to (\text{Variable} \to \text{Sign})\)</li> <li>\(L_2 \triangleq \text{Variable} \to \text{Sign} \)</li> <li>\(A \triangleq \text{NodeId}\)</li> <li>\(f \triangleq \text{id} \triangleq x \mapsto x\)</li> <li>\(g(k, m) = \text{JOIN}(k, m)\)</li> </ul> <p>In the equation for \(g\), I explicitly insert the map \(m\) instead of leaving it implicit as the textbook does. In Agda, this instantiation for joining all predecessor looks like this (using <code>states</code> as the list of keys to update, indicating that we should update <em>every</em> key):</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Forward.agda" data-first-line="152" data-last-line="157" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Forward.agda#L152-L157">Forward.agda</a>, lines 152 through 157</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">152 </span><span class="lnt">153 </span><span class="lnt">154 </span><span class="lnt">155 </span><span class="lnt">156 </span><span class="lnt">157 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="kr">open</span><span class="w"> </span>StateVariablesFiniteMap.GeneralizedUpdate<span class="w"> </span>states<span class="w"> </span>isLatticeᵐ<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>x<span class="w"> </span><span class="ow">→</span><span class="w"> </span>x<span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>a₁≼a₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>a₁≼a₂<span class="o">)</span><span class="w"> </span>joinForKey<span class="w"> </span>joinForKey-Mono<span class="w"> </span>states<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">renaming</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span><span class="w"> </span>f&#39;<span class="w"> </span>to<span class="w"> </span>joinAll<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>f&#39;-Monotonic<span class="w"> </span>to<span class="w"> </span>joinAll-Mono<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>f&#39;-k∈ks-≡<span class="w"> </span>to<span class="w"> </span>joinAll-k∈ks-≡<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>And the one for evaluating all programs looks like this:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Forward.agda" data-first-line="215" data-last-line="220" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Forward.agda#L215-L220">Forward.agda</a>, lines 215 through 220</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">215 </span><span class="lnt">216 </span><span class="lnt">217 </span><span class="lnt">218 </span><span class="lnt">219 </span><span class="lnt">220 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="kr">open</span><span class="w"> </span>StateVariablesFiniteMap.GeneralizedUpdate<span class="w"> </span>states<span class="w"> </span>isLatticeᵐ<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>x<span class="w"> </span><span class="ow">→</span><span class="w"> </span>x<span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>a₁≼a₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>a₁≼a₂<span class="o">)</span><span class="w"> </span>updateVariablesForState<span class="w"> </span>updateVariablesForState-Monoʳ<span class="w"> </span>states<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">renaming</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span><span class="w"> </span>f&#39;<span class="w"> </span>to<span class="w"> </span>updateAll<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>f&#39;-Monotonic<span class="w"> </span>to<span class="w"> </span>updateAll-Mono<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>f&#39;-k∈ks-≡<span class="w"> </span>to<span class="w"> </span>updateAll-k∈ks-≡<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p id="define-updateVariablesFromStmt">Actually, we haven&rsquo;t yet seen that <code>updateVariablesFromStmt</code>. This is a function that we can define using the user-provided abtract interpretation <code>eval</code>. Specifically, it handles the job of updating the sign of a variable once it has been assigned to (or doing nothing if the statement is a no-op).</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Forward.agda" data-first-line="191" data-last-line="193" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Forward.agda#L191-L193">Forward.agda</a>, lines 191 through 193</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">191 </span><span class="lnt">192 </span><span class="lnt">193 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">updateVariablesFromStmt</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>BasicStmt<span class="w"> </span><span class="ow">→</span><span class="w"> </span>VariableValues<span class="w"> </span><span class="ow">→</span><span class="w"> </span>VariableValues<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>updateVariablesFromStmt<span class="w"> </span><span class="o">(</span>k<span class="w"> </span>←<span class="w"> </span>e<span class="o">)</span><span class="w"> </span>vs<span class="w"> </span><span class="ow">=</span><span class="w"> </span>updateVariablesFromExpression<span class="w"> </span>k<span class="w"> </span>e<span class="w"> </span>vs<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>updateVariablesFromStmt<span class="w"> </span>noop<span class="w"> </span>vs<span class="w"> </span><span class="ow">=</span><span class="w"> </span>vs</span></span></code></pre></td></tr></table> </div> </div> </div> <p>The <code>updateVariablesFromExpression</code> is now new, and it is yet another map update, which changes the sign of a variable <code>k</code> to be the one we get from running <code>eval</code> on it. Map updates are instances of the generalized update; this time, the updater \(g\) is <code>eval</code>. The exercise requires the updater to be monotonic, which constrains the user-provided evaluation function to be monotonic too.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Forward.agda" data-first-line="173" data-last-line="181" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Forward.agda#L173-L181">Forward.agda</a>, lines 173 through 181</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">173 </span><span class="lnt">174 </span><span class="lnt">175 </span><span class="lnt">176 </span><span class="lnt">177 </span><span class="lnt">178 </span><span class="lnt">179 </span><span class="lnt">180 </span><span class="lnt">181 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="kr">private</span><span class="w"> </span><span class="kr">module</span><span class="w"> </span>_ (<span class="n">k</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>String<span class="o">)</span><span class="w"> </span><span class="o">(</span>e<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="o">)</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">open</span><span class="w"> </span>VariableValuesFiniteMap.GeneralizedUpdate<span class="w"> </span>vars<span class="w"> </span>isLatticeᵛ<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>x<span class="w"> </span><span class="ow">→</span><span class="w"> </span>x<span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>a₁≼a₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>a₁≼a₂<span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>_<span class="w"> </span><span class="ow">→</span><span class="w"> </span>eval<span class="w"> </span>e<span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>_<span class="w"> </span><span class="o">{</span>vs₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>vs₂<span class="o">}</span><span class="w"> </span>vs₁≼vs₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>eval-Mono<span class="w"> </span>e<span class="w"> </span><span class="o">{</span>vs₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>vs₂<span class="o">}</span><span class="w"> </span>vs₁≼vs₂<span class="o">)</span><span class="w"> </span><span class="o">(</span>k<span class="w"> </span>∷<span class="w"> </span>[]<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">renaming</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span><span class="w"> </span>f&#39;<span class="w"> </span>to<span class="w"> </span>updateVariablesFromExpression<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>f&#39;-Monotonic<span class="w"> </span>to<span class="w"> </span>updateVariablesFromExpression-Mono<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>f&#39;-k∈ks-≡<span class="w"> </span>to<span class="w"> </span>updateVariablesFromExpression-k∈ks-≡<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>f&#39;-k∉ks-backward<span class="w"> </span>to<span class="w"> </span>updateVariablesFromExpression-k∉ks-backward<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>public</span></span></code></pre></td></tr></table> </div> </div> </div> <p>We finally write the <code>analyze</code> function as the composition of the two bulk updates:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Forward.agda" data-first-line="226" data-last-line="232" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Forward.agda#L226-L232">Forward.agda</a>, lines 226 through 232</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">226 </span><span class="lnt">227 </span><span class="lnt">228 </span><span class="lnt">229 </span><span class="lnt">230 </span><span class="lnt">231 </span><span class="lnt">232 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">analyze</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>StateVariables<span class="w"> </span><span class="ow">→</span><span class="w"> </span>StateVariables<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>analyze<span class="w"> </span><span class="ow">=</span><span class="w"> </span>updateAll<span class="w"> </span>∘<span class="w"> </span>joinAll<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">analyze-Mono</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Monotonic<span class="w"> </span>_≼ᵐ_<span class="w"> </span>_≼ᵐ_<span class="w"> </span>analyze<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>analyze-Mono<span class="w"> </span><span class="o">{</span>sv₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>sv₂<span class="o">}</span><span class="w"> </span>sv₁≼sv₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>updateAll-Mono<span class="w"> </span><span class="o">{</span>joinAll<span class="w"> </span>sv₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>joinAll<span class="w"> </span>sv₂<span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>joinAll-Mono<span class="w"> </span><span class="o">{</span>sv₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>sv₂<span class="o">}</span><span class="w"> </span>sv₁≼sv₂<span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#instantiating-with-the-sign-lattice"> <h3 id="instantiating-with-the-sign-lattice">Instantiating with the Sign Lattice</h3> </a> <p>Thus far, I&rsquo;ve been talking about the sign lattice throughout, but implementing the Agda code in terms of a general lattice <code>L</code> and evaluation function <code>eval</code>. In order to actually run the Agda code, we do need to provide an <code>eval</code> function, which implements the logic we used above, in which a zero-sign variable \(x\) minus one was determined to be negative. For binary operators specifically, I&rsquo;ve used the table provided in the textbook; here they are:</p> <figure><img src="https://danilafe.com/blog/08_spa_agda_forward/plusminus.png" alt="Cayley tables for abstract interpretation of plus and minus"><figcaption> <p>Cayley tables for abstract interpretation of plus and minus</p> </figcaption> </figure> <p>These are pretty much common sense:</p> <ul> <li>A positive plus a positive is still positive, so \(+\ \hat{+}\ + = +\)</li> <li>A positive plus any sign could be any sign still, so \(+\ \hat{+}\ \top = \top\)</li> <li>Any sign plus &ldquo;impossible&rdquo; is impossible, so \(\top\ \hat{+} \bot = \bot\).</li> <li>etc.</li> </ul> <p>The Agda encoding for the plus function is as follows, and the one for minus is similar.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Sign.agda" data-first-line="76" data-last-line="94" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Sign.agda#L76-L94">Sign.agda</a>, lines 76 through 94</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span><span class="lnt">79 </span><span class="lnt">80 </span><span class="lnt">81 </span><span class="lnt">82 </span><span class="lnt">83 </span><span class="lnt">84 </span><span class="lnt">85 </span><span class="lnt">86 </span><span class="lnt">87 </span><span class="lnt">88 </span><span class="lnt">89 </span><span class="lnt">90 </span><span class="lnt">91 </span><span class="lnt">92 </span><span class="lnt">93 </span><span class="lnt">94 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="nf">plus</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>SignLattice<span class="w"> </span><span class="ow">→</span><span class="w"> </span>SignLattice<span class="w"> </span><span class="ow">→</span><span class="w"> </span>SignLattice<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus<span class="w"> </span>⊥ᵍ<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥ᵍ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus<span class="w"> </span>_<span class="w"> </span>⊥ᵍ<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥ᵍ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus<span class="w"> </span>⊤ᵍ<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊤ᵍ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus<span class="w"> </span>_<span class="w"> </span>⊤ᵍ<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊤ᵍ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus<span class="w"> </span>[<span class="w"> </span>+<span class="w"> </span>]ᵍ<span class="w"> </span>[<span class="w"> </span>+<span class="w"> </span>]ᵍ<span class="w"> </span><span class="ow">=</span><span class="w"> </span>[<span class="w"> </span>+<span class="w"> </span>]ᵍ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus<span class="w"> </span>[<span class="w"> </span>+<span class="w"> </span>]ᵍ<span class="w"> </span>[<span class="w"> </span>-<span class="w"> </span>]ᵍ<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊤ᵍ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus<span class="w"> </span>[<span class="w"> </span>+<span class="w"> </span>]ᵍ<span class="w"> </span>[<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span>]ᵍ<span class="w"> </span><span class="ow">=</span><span class="w"> </span>[<span class="w"> </span>+<span class="w"> </span>]ᵍ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus<span class="w"> </span>[<span class="w"> </span>-<span class="w"> </span>]ᵍ<span class="w"> </span>[<span class="w"> </span>+<span class="w"> </span>]ᵍ<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊤ᵍ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus<span class="w"> </span>[<span class="w"> </span>-<span class="w"> </span>]ᵍ<span class="w"> </span>[<span class="w"> </span>-<span class="w"> </span>]ᵍ<span class="w"> </span><span class="ow">=</span><span class="w"> </span>[<span class="w"> </span>-<span class="w"> </span>]ᵍ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus<span class="w"> </span>[<span class="w"> </span>-<span class="w"> </span>]ᵍ<span class="w"> </span>[<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span>]ᵍ<span class="w"> </span><span class="ow">=</span><span class="w"> </span>[<span class="w"> </span>-<span class="w"> </span>]ᵍ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus<span class="w"> </span>[<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span>]ᵍ<span class="w"> </span>[<span class="w"> </span>+<span class="w"> </span>]ᵍ<span class="w"> </span><span class="ow">=</span><span class="w"> </span>[<span class="w"> </span>+<span class="w"> </span>]ᵍ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus<span class="w"> </span>[<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span>]ᵍ<span class="w"> </span>[<span class="w"> </span>-<span class="w"> </span>]ᵍ<span class="w"> </span><span class="ow">=</span><span class="w"> </span>[<span class="w"> </span>-<span class="w"> </span>]ᵍ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus<span class="w"> </span>[<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span>]ᵍ<span class="w"> </span>[<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span>]ᵍ<span class="w"> </span><span class="ow">=</span><span class="w"> </span>[<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span>]ᵍ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">-- this is incredibly tedious: 125 cases per monotonicity proof, and tactics</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">-- are hard. postulate for now.</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kr">postulate</span><span class="w"> </span>plus-Monoˡ<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>s₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>SignLattice<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Monotonic<span class="w"> </span>_≼ᵍ_<span class="w"> </span>_≼ᵍ_<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>s₁<span class="w"> </span><span class="ow">→</span><span class="w"> </span>plus<span class="w"> </span>s₁<span class="w"> </span>s₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kr">postulate</span><span class="w"> </span>plus-Monoʳ<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>s₁<span class="w"> </span><span class="ow">:</span><span class="w"> </span>SignLattice<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Monotonic<span class="w"> </span>_≼ᵍ_<span class="w"> </span>_≼ᵍ_<span class="w"> </span><span class="o">(</span>plus<span class="w"> </span>s₁<span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>As the comment in the block says, it would be incredibly tedious to verify the monotonicity of these tables, since you would have to consider roughly 125 cases <em>per argument</em>: for each (fixed) sign \(s\) and two other signs \(s_1 \le s_2\), we&rsquo;d need to show that \(s\ \hat{+}\ s_1 \le s\ \hat{+}\ s_2\). I therefore commit the <em>faux pas</em> of using <code>postulate</code>. Fortunately, the proof of monotonicity is not used for the execution of the program, so we will get away with this, barring any meddling kids.</p> <p>From this, all that&rsquo;s left is to show that for any expression <code>e</code>, the evaluation function:</p> $$ \text{eval} : \text{Expr} \to (\text{Variable} \to \text{Sign}) \to \text{Sign} $$ <p>is monotonic. It&rsquo;s defined straightforwardly and very much like an evaluator / interpreter, suggesting that &ldquo;abstract interpretation&rdquo; is the correct term here.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Sign.agda" data-first-line="176" data-last-line="184" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Sign.agda#L176-L184">Sign.agda</a>, lines 176 through 184</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">176 </span><span class="lnt">177 </span><span class="lnt">178 </span><span class="lnt">179 </span><span class="lnt">180 </span><span class="lnt">181 </span><span class="lnt">182 </span><span class="lnt">183 </span><span class="lnt">184 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">eval</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>e<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>VariableValues<span class="w"> </span><span class="ow">→</span><span class="w"> </span>SignLattice<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>eval<span class="w"> </span><span class="o">(</span>e₁<span class="w"> </span>+<span class="w"> </span>e₂<span class="o">)</span><span class="w"> </span>vs<span class="w"> </span><span class="ow">=</span><span class="w"> </span>plus<span class="w"> </span><span class="o">(</span>eval<span class="w"> </span>e₁<span class="w"> </span>vs<span class="o">)</span><span class="w"> </span><span class="o">(</span>eval<span class="w"> </span>e₂<span class="w"> </span>vs<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>eval<span class="w"> </span><span class="o">(</span>e₁<span class="w"> </span>-<span class="w"> </span>e₂<span class="o">)</span><span class="w"> </span>vs<span class="w"> </span><span class="ow">=</span><span class="w"> </span>minus<span class="w"> </span><span class="o">(</span>eval<span class="w"> </span>e₁<span class="w"> </span>vs<span class="o">)</span><span class="w"> </span><span class="o">(</span>eval<span class="w"> </span>e₂<span class="w"> </span>vs<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>eval<span class="w"> </span><span class="o">(</span>`<span class="w"> </span>k<span class="o">)</span><span class="w"> </span>vs<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">with</span><span class="w"> </span>∈k-decᵛ<span class="w"> </span>k<span class="w"> </span><span class="o">(</span>proj₁<span class="w"> </span><span class="o">(</span>proj₁<span class="w"> </span>vs<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>yes<span class="w"> </span>k∈vs<span class="w"> </span><span class="ow">=</span><span class="w"> </span>proj₁<span class="w"> </span><span class="o">(</span>locateᵛ<span class="w"> </span><span class="o">{</span>k<span class="o">}</span><span class="w"> </span><span class="o">{</span>vs<span class="o">}</span><span class="w"> </span>k∈vs<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>no<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊤ᵍ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>eval<span class="w"> </span><span class="o">(</span>#<span class="w"> </span><span class="mi">0</span><span class="o">)</span><span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>[<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span>]ᵍ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>eval<span class="w"> </span><span class="o">(</span>#<span class="w"> </span><span class="o">(</span>suc<span class="w"> </span>n&#39;<span class="o">))</span><span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>[<span class="w"> </span>+<span class="w"> </span>]ᵍ</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Thought it won&rsquo;t happen, it was easier to just handle the case where there&rsquo;s an undefined variable; I give it &ldquo;any sign&rdquo;. Otherwise, the function simply consults the sign tables for <code>+</code> or <code>-</code>, as well as the known signs of the variables. For natural number literals, it assigns <code>0</code> the &ldquo;zero&rdquo; sign, and any other natural number the &ldquo;\(+\)&rdquo;.</p> <p>To prove monotonicity, we need to consider two variable maps (one less than the other), and show that the abstract interpretation respects that ordering. This boils down to the fact that the <code>plus</code> and <code>minus</code> tables are monotonic in both arguments (thus, if their sub-expressions are evaluated monotonically given an environment, then so is the whole addition or subtraction), and to the fact that for two maps <code>m₁ ≼ m₂</code>, the values at corresponding keys are similarly ordered: <code>m₁[k] ≼ m₂[k]</code>. We <a href="#less-than-lemma"class="same-page-link">saw that above</a>.</p> <details><summary><strong>(Click to expand the proof that the evaluation function for signs is monotonic)</strong></summary><div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Sign.agda" data-first-line="186" data-last-line="223" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Sign.agda#L186-L223">Sign.agda</a>, lines 186 through 223</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">186 </span><span class="lnt">187 </span><span class="lnt">188 </span><span class="lnt">189 </span><span class="lnt">190 </span><span class="lnt">191 </span><span class="lnt">192 </span><span class="lnt">193 </span><span class="lnt">194 </span><span class="lnt">195 </span><span class="lnt">196 </span><span class="lnt">197 </span><span class="lnt">198 </span><span class="lnt">199 </span><span class="lnt">200 </span><span class="lnt">201 </span><span class="lnt">202 </span><span class="lnt">203 </span><span class="lnt">204 </span><span class="lnt">205 </span><span class="lnt">206 </span><span class="lnt">207 </span><span class="lnt">208 </span><span class="lnt">209 </span><span class="lnt">210 </span><span class="lnt">211 </span><span class="lnt">212 </span><span class="lnt">213 </span><span class="lnt">214 </span><span class="lnt">215 </span><span class="lnt">216 </span><span class="lnt">217 </span><span class="lnt">218 </span><span class="lnt">219 </span><span class="lnt">220 </span><span class="lnt">221 </span><span class="lnt">222 </span><span class="lnt">223 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">eval-Mono</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>e<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Monotonic<span class="w"> </span>_≼ᵛ_<span class="w"> </span>_≼ᵍ_<span class="w"> </span><span class="o">(</span>eval<span class="w"> </span>e<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>eval-Mono<span class="w"> </span><span class="o">(</span>e₁<span class="w"> </span>+<span class="w"> </span>e₂<span class="o">)</span><span class="w"> </span><span class="o">{</span>vs₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>vs₂<span class="o">}</span><span class="w"> </span>vs₁≼vs₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">let</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">-- TODO: can this be done with less boilerplate?</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>g₁vs₁<span class="w"> </span><span class="ow">=</span><span class="w"> </span>eval<span class="w"> </span>e₁<span class="w"> </span>vs₁<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>g₂vs₁<span class="w"> </span><span class="ow">=</span><span class="w"> </span>eval<span class="w"> </span>e₂<span class="w"> </span>vs₁<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>g₁vs₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>eval<span class="w"> </span>e₁<span class="w"> </span>vs₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>g₂vs₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>eval<span class="w"> </span>e₂<span class="w"> </span>vs₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">in</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>≼ᵍ-trans<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span>plus<span class="w"> </span>g₁vs₁<span class="w"> </span>g₂vs₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>plus<span class="w"> </span>g₁vs₂<span class="w"> </span>g₂vs₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>plus<span class="w"> </span>g₁vs₂<span class="w"> </span>g₂vs₂<span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>plus-Monoˡ<span class="w"> </span>g₂vs₁<span class="w"> </span><span class="o">{</span>g₁vs₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>g₁vs₂<span class="o">}</span><span class="w"> </span><span class="o">(</span>eval-Mono<span class="w"> </span>e₁<span class="w"> </span><span class="o">{</span>vs₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>vs₂<span class="o">}</span><span class="w"> </span>vs₁≼vs₂<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>plus-Monoʳ<span class="w"> </span>g₁vs₂<span class="w"> </span><span class="o">{</span>g₂vs₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>g₂vs₂<span class="o">}</span><span class="w"> </span><span class="o">(</span>eval-Mono<span class="w"> </span>e₂<span class="w"> </span><span class="o">{</span>vs₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>vs₂<span class="o">}</span><span class="w"> </span>vs₁≼vs₂<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>eval-Mono<span class="w"> </span><span class="o">(</span>e₁<span class="w"> </span>-<span class="w"> </span>e₂<span class="o">)</span><span class="w"> </span><span class="o">{</span>vs₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>vs₂<span class="o">}</span><span class="w"> </span>vs₁≼vs₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">let</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">-- TODO: here too -- can this be done with less boilerplate?</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>g₁vs₁<span class="w"> </span><span class="ow">=</span><span class="w"> </span>eval<span class="w"> </span>e₁<span class="w"> </span>vs₁<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>g₂vs₁<span class="w"> </span><span class="ow">=</span><span class="w"> </span>eval<span class="w"> </span>e₂<span class="w"> </span>vs₁<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>g₁vs₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>eval<span class="w"> </span>e₁<span class="w"> </span>vs₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>g₂vs₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>eval<span class="w"> </span>e₂<span class="w"> </span>vs₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">in</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>≼ᵍ-trans<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span>minus<span class="w"> </span>g₁vs₁<span class="w"> </span>g₂vs₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>minus<span class="w"> </span>g₁vs₂<span class="w"> </span>g₂vs₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>minus<span class="w"> </span>g₁vs₂<span class="w"> </span>g₂vs₂<span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>minus-Monoˡ<span class="w"> </span>g₂vs₁<span class="w"> </span><span class="o">{</span>g₁vs₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>g₁vs₂<span class="o">}</span><span class="w"> </span><span class="o">(</span>eval-Mono<span class="w"> </span>e₁<span class="w"> </span><span class="o">{</span>vs₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>vs₂<span class="o">}</span><span class="w"> </span>vs₁≼vs₂<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>minus-Monoʳ<span class="w"> </span>g₁vs₂<span class="w"> </span><span class="o">{</span>g₂vs₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>g₂vs₂<span class="o">}</span><span class="w"> </span><span class="o">(</span>eval-Mono<span class="w"> </span>e₂<span class="w"> </span><span class="o">{</span>vs₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>vs₂<span class="o">}</span><span class="w"> </span>vs₁≼vs₂<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>eval-Mono<span class="w"> </span><span class="o">(</span>`<span class="w"> </span>k<span class="o">)</span><span class="w"> </span><span class="o">{</span>vs₁@<span class="o">((</span>kvs₁<span class="w"> </span>,<span class="w"> </span>_<span class="o">)</span><span class="w"> </span>,<span class="w"> </span>_<span class="o">)}</span><span class="w"> </span><span class="o">{</span>vs₂@<span class="o">((</span>kvs₂<span class="w"> </span>,<span class="w"> </span>_<span class="o">)</span>,<span class="w"> </span>_<span class="o">)}</span><span class="w"> </span>vs₁≼vs₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">with</span><span class="w"> </span>∈k-decᵛ<span class="w"> </span>k<span class="w"> </span>kvs₁<span class="w"> </span><span class="ow">|</span><span class="w"> </span>∈k-decᵛ<span class="w"> </span>k<span class="w"> </span>kvs₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>yes<span class="w"> </span>k∈kvs₁<span class="w"> </span><span class="ow">|</span><span class="w"> </span>yes<span class="w"> </span>k∈kvs₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">let</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>v₁<span class="w"> </span>,<span class="w"> </span>k,v₁∈vs₁<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>locateᵛ<span class="w"> </span><span class="o">{</span>k<span class="o">}</span><span class="w"> </span><span class="o">{</span>vs₁<span class="o">}</span><span class="w"> </span>k∈kvs₁<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>v₂<span class="w"> </span>,<span class="w"> </span>k,v₂∈vs₂<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>locateᵛ<span class="w"> </span><span class="o">{</span>k<span class="o">}</span><span class="w"> </span><span class="o">{</span>vs₂<span class="o">}</span><span class="w"> </span>k∈kvs₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">in</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>m₁≼m₂⇒m₁[k]ᵛ≼m₂[k]ᵛ<span class="w"> </span>vs₁<span class="w"> </span>vs₂<span class="w"> </span>vs₁≼vs₂<span class="w"> </span>k,v₁∈vs₁<span class="w"> </span>k,v₂∈vs₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>yes<span class="w"> </span>k∈kvs₁<span class="w"> </span><span class="ow">|</span><span class="w"> </span>no<span class="w"> </span>k∉kvs₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥-elim<span class="w"> </span><span class="o">(</span>k∉kvs₂<span class="w"> </span><span class="o">(</span>subst<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>l<span class="w"> </span><span class="ow">→</span><span class="w"> </span>k<span class="w"> </span>∈ˡ<span class="w"> </span>l<span class="o">)</span><span class="w"> </span><span class="o">(</span>all-equal-keysᵛ<span class="w"> </span>vs₁<span class="w"> </span>vs₂<span class="o">)</span><span class="w"> </span>k∈kvs₁<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>no<span class="w"> </span>k∉kvs₁<span class="w"> </span><span class="ow">|</span><span class="w"> </span>yes<span class="w"> </span>k∈kvs₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥-elim<span class="w"> </span><span class="o">(</span>k∉kvs₁<span class="w"> </span><span class="o">(</span>subst<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>l<span class="w"> </span><span class="ow">→</span><span class="w"> </span>k<span class="w"> </span>∈ˡ<span class="w"> </span>l<span class="o">)</span><span class="w"> </span><span class="o">(</span>all-equal-keysᵛ<span class="w"> </span>vs₂<span class="w"> </span>vs₁<span class="o">)</span><span class="w"> </span>k∈kvs₂<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>no<span class="w"> </span>k∉kvs₁<span class="w"> </span><span class="ow">|</span><span class="w"> </span>no<span class="w"> </span>k∉kvs₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>IsLattice.≈-refl<span class="w"> </span>isLatticeᵍ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>eval-Mono<span class="w"> </span><span class="o">(</span>#<span class="w"> </span><span class="mi">0</span><span class="o">)</span><span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>≈ᵍ-refl<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>eval-Mono<span class="w"> </span><span class="o">(</span>#<span class="w"> </span><span class="o">(</span>suc<span class="w"> </span>n&#39;<span class="o">))</span><span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>≈ᵍ-refl</span></span></code></pre></td></tr></table> </div> </div> </div> </details> <p>That&rsquo;s all we need. With this, I just instantiate the <code>Forward</code> module we have been working with, and make use of the <code>result</code>. I also used a <code>show</code> function (which I defined) to stringify that output.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Sign.agda" data-first-line="225" data-last-line="229" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Sign.agda#L225-L229">Sign.agda</a>, lines 225 through 229</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">225 </span><span class="lnt">226 </span><span class="lnt">227 </span><span class="lnt">228 </span><span class="lnt">229 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="kr">module</span><span class="w"> </span><span class="n">ForwardWithEval</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>ForwardWithProg.WithEvaluator<span class="w"> </span>eval<span class="w"> </span>eval-Mono<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">open</span><span class="w"> </span>ForwardWithEval<span class="w"> </span><span class="kr">using</span><span class="w"> </span><span class="o">(</span>result<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">-- For debugging purposes, print out the result.</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>output<span class="w"> </span><span class="ow">=</span><span class="w"> </span>show<span class="w"> </span>result</span></span></code></pre></td></tr></table> </div> </div> </div> <p>But wait, <code>result</code>? We haven&rsquo;t seen a result just yet. That&rsquo;s the last piece, and it involves finally making use of the fixed-point algorithm.</p> <a href="#invoking-the-fixed-point-algorithm"> <h3 id="invoking-the-fixed-point-algorithm">Invoking the Fixed Point Algorithm</h3> </a> <p>Our \(\text{Info}\) lattice is of finite height, and the function we have defined is monotonic (by virtue of being constructed only from map updates, which are monotonic by Exercise 4.26, and from function composition, which preserves monotonicity). We can therefore apply the fixed-point-algorithm, and compute the least fixed point:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Forward.agda" data-first-line="235" data-last-line="238" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Forward.agda#L235-L238">Forward.agda</a>, lines 235 through 238</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">235 </span><span class="lnt">236 </span><span class="lnt">237 </span><span class="lnt">238 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="kr">open</span><span class="w"> </span><span class="kr">import</span><span class="w"> </span><span class="n">Fixedpoint</span><span class="w"> </span>≈ᵐ-dec<span class="w"> </span>isFiniteHeightLatticeᵐ<span class="w"> </span>analyze<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span><span class="o">{</span>m₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>m₂<span class="o">}</span><span class="w"> </span>m₁≼m₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>analyze-Mono<span class="w"> </span><span class="o">{</span>m₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>m₂<span class="o">}</span><span class="w"> </span>m₁≼m₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">using</span><span class="w"> </span><span class="o">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">renaming</span><span class="w"> </span><span class="o">(</span>aᶠ<span class="w"> </span>to<span class="w"> </span>result;<span class="w"> </span>aᶠ≈faᶠ<span class="w"> </span>to<span class="w"> </span>result≈analyze-result<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>public</span></span></code></pre></td></tr></table> </div> </div> </div> <p>With this, <code>analyze</code> is the result of our forward analysis!</p> <p>In a <code>Main.agda</code> file, I invoked this analysis on a sample program:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">testCode</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Stmt<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>testCode<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⟨<span class="w"> </span><span class="s">&#34;zero&#34;</span><span class="w"> </span>←<span class="w"> </span><span class="o">(</span>#<span class="w"> </span><span class="mi">0</span><span class="o">)</span><span class="w"> </span>⟩<span class="w"> </span>then<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⟨<span class="w"> </span><span class="s">&#34;pos&#34;</span><span class="w"> </span>←<span class="w"> </span><span class="o">((</span>`<span class="w"> </span><span class="s">&#34;zero&#34;</span><span class="o">)</span><span class="w"> </span>Expr.+<span class="w"> </span><span class="o">(</span>#<span class="w"> </span><span class="mi">1</span><span class="o">))</span><span class="w"> </span>⟩<span class="w"> </span>then<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⟨<span class="w"> </span><span class="s">&#34;neg&#34;</span><span class="w"> </span>←<span class="w"> </span><span class="o">((</span>`<span class="w"> </span><span class="s">&#34;zero&#34;</span><span class="o">)</span><span class="w"> </span>Expr.-<span class="w"> </span><span class="o">(</span>#<span class="w"> </span><span class="mi">1</span><span class="o">))</span><span class="w"> </span>⟩<span class="w"> </span>then<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⟨<span class="w"> </span><span class="s">&#34;unknown&#34;</span><span class="w"> </span>←<span class="w"> </span><span class="o">((</span>`<span class="w"> </span><span class="s">&#34;pos&#34;</span><span class="o">)</span><span class="w"> </span>Expr.+<span class="w"> </span><span class="o">(</span>`<span class="w"> </span><span class="s">&#34;neg&#34;</span><span class="o">))</span><span class="w"> </span>⟩<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nf">testProgram</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Program<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>testProgram<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">record</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span><span class="w"> </span>rootStmt<span class="w"> </span><span class="ow">=</span><span class="w"> </span>testCode<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kr">open</span><span class="w"> </span>WithProg<span class="w"> </span>testProgram<span class="w"> </span><span class="kr">using</span><span class="w"> </span><span class="o">(</span>output;<span class="w"> </span>analyze-correct<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>main<span class="w"> </span><span class="ow">=</span><span class="w"> </span>run<span class="w"> </span><span class="o">{</span><span class="mi">0</span>ℓ<span class="o">}</span><span class="w"> </span><span class="o">(</span>putStrLn<span class="w"> </span>output<span class="o">)</span><span class="w"> </span></span></span></code></pre></div><p>The result is verbose, since it shows variable signs for each statement in the program. However, the key is the last basic block, which shows the variables at the end of the program. It reads:</p> <pre tabindex="0"><code>{&#34;neg&#34; ↦ -, &#34;pos&#34; ↦ +, &#34;unknown&#34; ↦ ⊤, &#34;zero&#34; ↦ 0, } </code></pre><a href="#verifying-the-analysis"> <h3 id="verifying-the-analysis">Verifying the Analysis</h3> </a> <p>We now have a general framework for running forward analyses: you provide an abstract interpretation function for expressions, as well as a proof that this function is monotonic, and you get an Agda function that takes a program and tells you the variable states at every point. If your abstract interpretation function is for determining the signs of expressions, the final result is an analysis that determines all possible signs for all variables, anywhere in the code. It&rsquo;s pretty easy to instantiate this framework with another type of forward analysis &mdash; in fact, by switching the <code>plus</code> function to one that uses <code>AboveBelow ℤ</code>, rather than <code>AboveBelow Sign</code>:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">plus</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>ConstLattice<span class="w"> </span><span class="ow">→</span><span class="w"> </span>ConstLattice<span class="w"> </span><span class="ow">→</span><span class="w"> </span>ConstLattice<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus<span class="w"> </span>⊥ᶜ<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥ᶜ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus<span class="w"> </span>_<span class="w"> </span>⊥ᶜ<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥ᶜ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus<span class="w"> </span>⊤ᶜ<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊤ᶜ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus<span class="w"> </span>_<span class="w"> </span>⊤ᶜ<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊤ᶜ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>plus<span class="w"> </span>[<span class="w"> </span>z₁<span class="w"> </span>]ᶜ<span class="w"> </span>[<span class="w"> </span>z₂<span class="w"> </span>]ᶜ<span class="w"> </span><span class="ow">=</span><span class="w"> </span>[<span class="w"> </span>z₁<span class="w"> </span>Int.+<span class="w"> </span>z₂<span class="w"> </span>]ᶜ<span class="w"> </span></span></span></code></pre></div><p>we can defined a constant-propagation analysis.</p> <pre tabindex="0"><code>{&#34;neg&#34; ↦ -1, &#34;pos&#34; ↦ 1, &#34;unknown&#34; ↦ 0, &#34;zero&#34; ↦ 0, } </code></pre><p>However, we haven&rsquo;t proved our analysis correct, and we haven&rsquo;t yet made use of the CFG-semantics equivalence that we <a href="https://danilafe.com/blog/07_spa_agda_semantics_and_cfg/">proved in the previous section</a>. I was hoping to get to it in this post, but there was just too much to cover. So, I will get to that in the next post, where we will make use of the remaining machinery to demonstrate that the output of our analyzer matches reality.</p> Implementing and Verifying "Static Program Analysis" in Agda, Part 7: Connecting Semantics and Control Flow Graphs https://danilafe.com/blog/07_spa_agda_semantics_and_cfg/ Thu, 28 Nov 2024 20:32:00 -0700 https://danilafe.com/blog/07_spa_agda_semantics_and_cfg/ <p>In the previous two posts, I covered two ways of looking at programs in my little toy language:</p> <ul> <li> <p><a href="https://danilafe.com/blog/05_spa_agda_semantics/">In part 5</a>, I covered the <strong>formal semantics</strong> of the programming language. These are precise rules that describe how programs are executed. These serve as the source of truth for what each statement and expression does.</p> <p>Because they are the source of truth, they capture all information about how programs are executed. To determine that a program starts in one environment and ends in another (getting a judgement \(\rho_1, s \Rightarrow \rho_2\)), we need to actually run the program. In fact, our Agda definitions encoding the semantics actually produce proof trees, which contain every single step of the program&rsquo;s execution.</p> </li> <li> <p><a href="https://danilafe.com/blog/06_spa_agda_cfg/">In part 6</a>, I covered <strong>Control Flow Graphs</strong> (CFGs), which in short arranged code into a structure that represents how execution moves from one statement or expression to the next.</p> <p>Unlike the semantics, CFGs do not capture a program&rsquo;s entire execution; they merely contain the possible orders in which statements can be evaluated. Instead of capturing the exact number of iterations performed by a <code>while</code> loop, they encode repetition as cycles in the graph. Because they are missing some information, they&rsquo;re more of an approximation of a program&rsquo;s behavior.</p> </li> </ul> <p>Our analyses operate on CFGs, but it is our semantics that actually determine how a program behaves. In order for our analyses to be able to produce correct results, we need to make sure that there isn&rsquo;t a disconnect between the approximation and the truth. In the previous post, I stated the property I will use to establish the connection between the two perspectives:</p> <blockquote> <p>For each possible execution of a program according to its semantics, there exists a corresponding path through the graph.</p> </blockquote> <p>By ensuring this property, we will guarantee that our Control Flow Graphs account for anything that might happen. Thus, a correct analysis built on top of the graphs will produce results that match reality.</p> <a href="#traces-paths-through-a-graph"> <h3 id="traces-paths-through-a-graph">Traces: Paths Through a Graph</h3> </a> <p>A CFG contains each &ldquo;basic&rdquo; statement in our program, by definition; when we&rsquo;re executing the program, we are therefore running code in one of the CFG&rsquo;s nodes. When we switch from one node to another, there ought to be an edge between the two, since edges in the CFG encode possible control flow. We keep doing this until the program terminates (if ever).</p> <p>Now, I said that there &ldquo;ought to be edges&rdquo; in the graph that correspond to our program&rsquo;s execution. Moreover, the endpoints of these edges have to line up, since we can only switch which basic block / node we&rsquo;re executing by following an edge. As a result, if our CFG is correct, then for every program execution, there is a path between the CFG&rsquo;s nodes that matches the statements that we were executing.</p> <p>Take the following program and CFG from the previous post as an example.</p> <div class="side-by-side"> <div class="side-by-side-item" style="flex-grow: 0.55;" > <pre tabindex="0"><code>x = 2; while x { x = x - 1; } y = x; </code></pre> </div> <div class="side-by-side-item" style="flex-grow: 0.5;" > <figure class="small"><img src="https://danilafe.com/blog/07_spa_agda_semantics_and_cfg/while-cfg.png"> </figure> </div> </div> <p>We start by executing <code>x = 2</code>, which is the top node in the CFG. Then, we execute the condition of the loop, <code>x</code>. This condition is in the second node from the top; fortunately, there exists an edge between <code>x = 2</code> and <code>x</code> that allows for this possibility. Once we computed <code>x</code>, we know that it&rsquo;s nonzero, and therefore we proceed to the loop body. This is the statement <code>x = x - 1</code>, contained in the bottom left node in the CFG. There is once again an edge between <code>x</code> and that node; so far, so good. Once we&rsquo;re done executing the statement, we go back to the top of the loop again, following the edge back to the middle node. We then execute the condition, loop body, and condition again. At that point we have reduced <code>x</code> to zero, so the condition produces a falsey value. We exit the loop and execute <code>y = x</code>, which is allowed by the edge from the middle node to the bottom right node.</p> <p>We will want to show that every possible execution of the program (e.g., with different variable assignments) corresponds to a path in the CFG. If one doesn&rsquo;t, then our program can do something that our CFG doesn&rsquo;t account for, which means that our analyses will not be correct.</p> <p>I will define a <code>Trace</code> datatype, which will be an embellished path through the graph. At its core, a path is simply a list of indices together with edges that connect them. Viewed another way, it&rsquo;s a list of edges, where each edge&rsquo;s endpoint is the next edge&rsquo;s starting point. We want to make illegal states unrepresentable, and therefore use the type system to assert that the edges are compatible. The easiest way to do this is by making our <code>Trace</code> indexed by its start and end points. An empty trace, containing no edges, will start and end in the same node; the <code>::</code> equivalent for the trace will allow prepending one edge, starting at node <code>i1</code> and ending in <code>i2</code>, to another trace which starts in <code>i2</code> and ends in some arbitrary <code>i3</code>. Here&rsquo;s an initial stab at that:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="kr">module</span><span class="w"> </span>_ {<span class="n">g</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Graph<span class="o">}</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">open</span><span class="w"> </span>Graph<span class="w"> </span>g<span class="w"> </span><span class="kr">using</span><span class="w"> </span><span class="o">(</span>Index;<span class="w"> </span>edges;<span class="w"> </span>inputs;<span class="w"> </span>outputs<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">data</span><span class="w"> </span>Trace<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Index<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Index<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">Trace-single</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>idx<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Index<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Trace<span class="w"> </span>idx<span class="w"> </span>idx<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">Trace-edge</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>idx₁<span class="w"> </span>idx₂<span class="w"> </span>idx₃<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Index<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>idx₁<span class="w"> </span>,<span class="w"> </span>idx₂<span class="o">)</span><span class="w"> </span>∈<span class="w"> </span>edges<span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>Trace<span class="w"> </span>idx₂<span class="w"> </span>idx₃<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Trace<span class="w"> </span>idx₁<span class="w"> </span>idx₃<span class="w"> </span></span></span></code></pre></div><p>This isn&rsquo;t enough, though. Suppose you had a function that takes an evaluation judgement and produces a trace, resulting in a signature like this:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">buildCfg-sufficient</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>s<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Stmt<span class="o">}</span><span class="w"> </span><span class="o">{</span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>ρ₁<span class="w"> </span>,<span class="w"> </span>s<span class="w"> </span>⇒ˢ<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">let</span><span class="w"> </span>g<span class="w"> </span><span class="ow">=</span><span class="w"> </span>buildCfg<span class="w"> </span>s<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">in</span><span class="w"> </span>Σ<span class="w"> </span><span class="o">(</span>Index<span class="w"> </span>g<span class="w"> </span>×<span class="w"> </span>Index<span class="w"> </span>g<span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span><span class="o">(</span>idx₁<span class="w"> </span>,<span class="w"> </span>idx₂<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Trace<span class="w"> </span><span class="o">{</span>g<span class="o">}</span><span class="w"> </span>idx₁<span class="w"> </span>idx₂<span class="o">)</span><span class="w"> </span></span></span></code></pre></div><p>What&rsquo;s stopping this function from returning <em>any</em> trace through the graph, including one that doesn&rsquo;t even include the statements in our program <code>s</code>? We need to narrow the type somewhat to require that the nodes it visits have some relation to the program execution in question.</p> <p>We could do this by indexing the <code>Trace</code> data type by a list of statements that we expect it to match, and requiring that for each constructor, the statements of the starting node be at the front of that list. We could compute the list of executed statements in order using <span class="sidenote"> <label class="sidenote-label" for="full-execution=note">a recursive function on the <code>_,_⇒ˢ_</code> data type.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="full-execution=note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> I mentioned earlier that our encoding of the semantics is actually defining a proof tree, which includes every step of the computation. That's why we can write a function that takes the proof tree and extracts the executed statements. <span class="sidenote-delimiter">]</span> </span> </span> </p> <p>That would work, but it loses a bit of information. The execution judgement contains not only each statement that was evaluated, but also the environments before and after evaluating it. Keeping those around will be useful: eventually, we&rsquo;d like to state the invariant that at every CFG node, the results of our analysis match the current program environment. Thus, instead of indexing simply by the statements of code, I chose to index my <code>Trace</code> by the starting and ending environment, and to require it to contain evaluation judgements for each node&rsquo;s code. The judgements include the statements that were evaluated, which we can match against the code in the CFG node. However, they also assert that the environments before and after are connected by that code in the language&rsquo;s formal semantics. The resulting definition is as follows:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Traces.agda" data-first-line="10" data-last-line="18" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Traces.agda#L10-L18">Traces.agda</a>, lines 10 through 18</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="kr">module</span><span class="w"> </span>_ {<span class="n">g</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Graph<span class="o">}</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">open</span><span class="w"> </span>Graph<span class="w"> </span>g<span class="w"> </span><span class="kr">using</span><span class="w"> </span><span class="o">(</span>Index;<span class="w"> </span>edges;<span class="w"> </span>inputs;<span class="w"> </span>outputs<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">data</span><span class="w"> </span>Trace<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Index<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Index<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Env<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Env<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">Trace-single</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="o">}</span><span class="w"> </span><span class="o">{</span>idx<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Index<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>ρ₁<span class="w"> </span>,<span class="w"> </span><span class="o">(</span>g<span class="w"> </span>[<span class="w"> </span>idx<span class="w"> </span>]<span class="o">)</span><span class="w"> </span>⇒ᵇˢ<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Trace<span class="w"> </span>idx<span class="w"> </span>idx<span class="w"> </span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">Trace-edge</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span>ρ₃<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="o">}</span><span class="w"> </span><span class="o">{</span>idx₁<span class="w"> </span>idx₂<span class="w"> </span>idx₃<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Index<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>ρ₁<span class="w"> </span>,<span class="w"> </span><span class="o">(</span>g<span class="w"> </span>[<span class="w"> </span>idx₁<span class="w"> </span>]<span class="o">)</span><span class="w"> </span>⇒ᵇˢ<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>idx₁<span class="w"> </span>,<span class="w"> </span>idx₂<span class="o">)</span><span class="w"> </span>∈<span class="w"> </span>edges<span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>Trace<span class="w"> </span>idx₂<span class="w"> </span>idx₃<span class="w"> </span>ρ₂<span class="w"> </span>ρ₃<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Trace<span class="w"> </span>idx₁<span class="w"> </span>idx₃<span class="w"> </span>ρ₁<span class="w"> </span>ρ₃</span></span></code></pre></td></tr></table> </div> </div> </div> <p>The <code>g [ idx ]</code> and <code>g [ idx₁ ]</code> represent accessing the basic block code at indices <code>idx</code> and <code>idx₁</code> in graph <code>g</code>.</p> <a href="#trace-preservation-by-graph-operations"> <h3 id="trace-preservation-by-graph-operations">Trace Preservation by Graph Operations</h3> </a> <p>Our proofs of trace existence will have the same &ldquo;shape&rdquo; as the functions that build the graph. To prove the trace property, we&rsquo;ll assume that evaluations of sub-statements correspond to traces in the sub-graphs, and use that to prove that the full statements have corresponding traces in the full graph. We built up graphs by combining sub-graphs for sub-statements, using <code>_∙_</code> (overlaying two graphs), <code>_↦_</code> (sequencing two graphs) and <code>loop</code> (creating a zero-or-more loop in the graph). Thus, to make the jump from sub-graphs to full graphs, we&rsquo;ll need to prove that traces persist through overlaying, sequencing, and looping.</p> <p>Take <code>_∙_</code>, for instance; we want to show that if a trace exists in the left operand of overlaying, it also exists in the final graph. This leads to the following statement and proof:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Properties.agda" data-first-line="88" data-last-line="97" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Properties.agda#L88-L97">Properties.agda</a>, lines 88 through 97</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">88 </span><span class="lnt">89 </span><span class="lnt">90 </span><span class="lnt">91 </span><span class="lnt">92 </span><span class="lnt">93 </span><span class="lnt">94 </span><span class="lnt">95 </span><span class="lnt">96 </span><span class="lnt">97 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">Trace-∙ˡ</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>g₁<span class="w"> </span>g₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Graph<span class="o">}</span><span class="w"> </span><span class="o">{</span>idx₁<span class="w"> </span>idx₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Graph.Index<span class="w"> </span>g₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>Trace<span class="w"> </span><span class="o">{</span>g₁<span class="o">}</span><span class="w"> </span>idx₁<span class="w"> </span>idx₂<span class="w"> </span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>Trace<span class="w"> </span><span class="o">{</span>g₁<span class="w"> </span>∙<span class="w"> </span>g₂<span class="o">}</span><span class="w"> </span><span class="o">(</span>idx₁<span class="w"> </span>Fin.↑ˡ<span class="w"> </span>Graph.size<span class="w"> </span>g₂<span class="o">)</span><span class="w"> </span><span class="o">(</span>idx₂<span class="w"> </span>Fin.↑ˡ<span class="w"> </span>Graph.size<span class="w"> </span>g₂<span class="o">)</span><span class="w"> </span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>Trace-∙ˡ<span class="w"> </span><span class="o">{</span>g₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>g₂<span class="o">}</span><span class="w"> </span><span class="o">{</span>idx₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>idx₁<span class="o">}</span><span class="w"> </span><span class="o">(</span>Trace-single<span class="w"> </span>ρ₁⇒ρ₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">rewrite</span><span class="w"> </span>sym<span class="w"> </span><span class="o">(</span>lookup-++ˡ<span class="w"> </span><span class="o">(</span>Graph.nodes<span class="w"> </span>g₁<span class="o">)</span><span class="w"> </span><span class="o">(</span>Graph.nodes<span class="w"> </span>g₂<span class="o">)</span><span class="w"> </span>idx₁<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>Trace-single<span class="w"> </span>ρ₁⇒ρ₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>Trace-∙ˡ<span class="w"> </span><span class="o">{</span>g₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>g₂<span class="o">}</span><span class="w"> </span><span class="o">{</span>idx₁<span class="o">}</span><span class="w"> </span><span class="o">(</span>Trace-edge<span class="w"> </span>ρ₁⇒ρ<span class="w"> </span>idx₁→idx<span class="w"> </span>tr&#39;<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">rewrite</span><span class="w"> </span>sym<span class="w"> </span><span class="o">(</span>lookup-++ˡ<span class="w"> </span><span class="o">(</span>Graph.nodes<span class="w"> </span>g₁<span class="o">)</span><span class="w"> </span><span class="o">(</span>Graph.nodes<span class="w"> </span>g₂<span class="o">)</span><span class="w"> </span>idx₁<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>Trace-edge<span class="w"> </span>ρ₁⇒ρ<span class="w"> </span><span class="o">(</span>ListMemProp.∈-++⁺ˡ<span class="w"> </span><span class="o">(</span>x∈xs⇒fx∈fxs<span class="w"> </span><span class="o">(</span>_↑ˡ<span class="w"> </span>Graph.size<span class="w"> </span>g₂<span class="o">)</span><span class="w"> </span>idx₁→idx<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>Trace-∙ˡ<span class="w"> </span>tr&#39;<span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>There are some details there to discuss.</p> <ul> <li> <p>First, we have to change the indices of the returned <code>Trace</code>. That&rsquo;s because they start out as indices into the graph <code>g₁</code>, but become indices into the graph <code>g₁ ∙ g₂</code>. To take care of this re-indexing, we have to make use of the <code>↑ˡ</code> operators, which I described in <a href="https://danilafe.com/blog/06_spa_agda_cfg/#fin-reindexing">this section of the previous post</a>.</p> </li> <li> <p>Next, in either case, we need to show that the new index acquired via <code>↑ˡ</code> returns the same basic block in the new graph as the old index returned in the original graph. Fortunately, the Agda standard library provides a proof of this, <code>lookup-++ˡ</code>. The resulting equality is the following:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl">g₁<span class="w"> </span>[<span class="w"> </span>idx₁<span class="w"> </span>]<span class="w"> </span>≡<span class="w"> </span><span class="o">(</span>g₁<span class="w"> </span>∙<span class="w"> </span>g₂<span class="o">)</span><span class="w"> </span>[<span class="w"> </span>idx₁<span class="w"> </span>↑ˡ<span class="w"> </span>Graph.size<span class="w"> </span>g₂<span class="w"> </span>]<span class="w"> </span></span></span></code></pre></div><p>This allows us to use the evaluation judgement in each constructor for traces in the output of the function.</p> </li> <li> <p>Lastly, in the <code>Trace-edge</code> case, we have to additionally return a proof that the edge used by the trace still exists in the output graph. This follows from the fact that we include the edges from <code>g₁</code> after re-indexing them.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>edges<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>Graph.edges<span class="w"> </span>g₁<span class="w"> </span>↑ˡᵉ<span class="w"> </span>Graph.size<span class="w"> </span>g₂<span class="o">)</span><span class="w"> </span>List.++<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>Graph.size<span class="w"> </span>g₁<span class="w"> </span>↑ʳᵉ<span class="w"> </span>Graph.edges<span class="w"> </span>g₂<span class="o">)</span><span class="w"> </span></span></span></code></pre></div><p>The <code>↑ˡᵉ</code> function is just a list <code>map</code> with <code>↑ˡ</code>. Thus, if a pair of edges is in the original list (<code>Graph.edges g₁</code>), as is evidenced by <code>idx₁→idx</code>, then its re-indexing is in the mapped list. To show this, I use the utility lemma <code>x∈xs⇒fx∈fxs</code>. The mapped list is the left-hand-side of a <code>List.++</code> operator, so I additionally use the lemma <code>∈-++⁺ˡ</code> that shows membership is preserved by list concatenation.</p> </li> </ul> <p>The proof of <code>Trace-∙ʳ</code>, the same property but for the right-hand operand <code>g₂</code>, is very similar, as are the proofs for sequencing. I give their statements, but not their proofs, below.</p> <p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Properties.agda" data-first-line="99" data-last-line="101" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Properties.agda#L99-L101">Properties.agda</a>, lines 99 through 101</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 99 </span><span class="lnt">100 </span><span class="lnt">101 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">Trace-∙ʳ</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>g₁<span class="w"> </span>g₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Graph<span class="o">}</span><span class="w"> </span><span class="o">{</span>idx₁<span class="w"> </span>idx₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Graph.Index<span class="w"> </span>g₂<span class="o">}</span><span class="w"> </span><span class="o">{</span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>Trace<span class="w"> </span><span class="o">{</span>g₂<span class="o">}</span><span class="w"> </span>idx₁<span class="w"> </span>idx₂<span class="w"> </span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>Trace<span class="w"> </span><span class="o">{</span>g₁<span class="w"> </span>∙<span class="w"> </span>g₂<span class="o">}</span><span class="w"> </span><span class="o">(</span>Graph.size<span class="w"> </span>g₁<span class="w"> </span>Fin.↑ʳ<span class="w"> </span>idx₁<span class="o">)</span><span class="w"> </span><span class="o">(</span>Graph.size<span class="w"> </span>g₁<span class="w"> </span>Fin.↑ʳ<span class="w"> </span>idx₂<span class="o">)</span><span class="w"> </span>ρ₁<span class="w"> </span>ρ₂</span></span></code></pre></td></tr></table> </div> </div> </div> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Properties.agda" data-first-line="139" data-last-line="141" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Properties.agda#L139-L141">Properties.agda</a>, lines 139 through 141</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">139 </span><span class="lnt">140 </span><span class="lnt">141 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">Trace-↦ˡ</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>g₁<span class="w"> </span>g₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Graph<span class="o">}</span><span class="w"> </span><span class="o">{</span>idx₁<span class="w"> </span>idx₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Graph.Index<span class="w"> </span>g₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>Trace<span class="w"> </span><span class="o">{</span>g₁<span class="o">}</span><span class="w"> </span>idx₁<span class="w"> </span>idx₂<span class="w"> </span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>Trace<span class="w"> </span><span class="o">{</span>g₁<span class="w"> </span>↦<span class="w"> </span>g₂<span class="o">}</span><span class="w"> </span><span class="o">(</span>idx₁<span class="w"> </span>Fin.↑ˡ<span class="w"> </span>Graph.size<span class="w"> </span>g₂<span class="o">)</span><span class="w"> </span><span class="o">(</span>idx₂<span class="w"> </span>Fin.↑ˡ<span class="w"> </span>Graph.size<span class="w"> </span>g₂<span class="o">)</span><span class="w"> </span>ρ₁<span class="w"> </span>ρ₂</span></span></code></pre></td></tr></table> </div> </div> </div> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Properties.agda" data-first-line="150" data-last-line="152" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Properties.agda#L150-L152">Properties.agda</a>, lines 150 through 152</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">150 </span><span class="lnt">151 </span><span class="lnt">152 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">Trace-↦ʳ</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>g₁<span class="w"> </span>g₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Graph<span class="o">}</span><span class="w"> </span><span class="o">{</span>idx₁<span class="w"> </span>idx₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Graph.Index<span class="w"> </span>g₂<span class="o">}</span><span class="w"> </span><span class="o">{</span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>Trace<span class="w"> </span><span class="o">{</span>g₂<span class="o">}</span><span class="w"> </span>idx₁<span class="w"> </span>idx₂<span class="w"> </span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>Trace<span class="w"> </span><span class="o">{</span>g₁<span class="w"> </span>↦<span class="w"> </span>g₂<span class="o">}</span><span class="w"> </span><span class="o">(</span>Graph.size<span class="w"> </span>g₁<span class="w"> </span>Fin.↑ʳ<span class="w"> </span>idx₁<span class="o">)</span><span class="w"> </span><span class="o">(</span>Graph.size<span class="w"> </span>g₁<span class="w"> </span>Fin.↑ʳ<span class="w"> </span>idx₂<span class="o">)</span><span class="w"> </span>ρ₁<span class="w"> </span>ρ₂</span></span></code></pre></td></tr></table> </div> </div> </div> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Properties.agda" data-first-line="175" data-last-line="176" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Properties.agda#L175-L176">Properties.agda</a>, lines 175 through 176</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">175 </span><span class="lnt">176 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">Trace-loop</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>g<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Graph<span class="o">}</span><span class="w"> </span><span class="o">{</span>idx₁<span class="w"> </span>idx₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Graph.Index<span class="w"> </span>g<span class="o">}</span><span class="w"> </span><span class="o">{</span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>Trace<span class="w"> </span><span class="o">{</span>g<span class="o">}</span><span class="w"> </span>idx₁<span class="w"> </span>idx₂<span class="w"> </span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Trace<span class="w"> </span><span class="o">{</span>loop<span class="w"> </span>g<span class="o">}</span><span class="w"> </span><span class="o">(</span><span class="mi">2</span><span class="w"> </span>Fin.↑ʳ<span class="w"> </span>idx₁<span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="mi">2</span><span class="w"> </span>Fin.↑ʳ<span class="w"> </span>idx₂<span class="o">)</span><span class="w"> </span>ρ₁<span class="w"> </span>ρ₂</span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>Preserving traces is unfortunately not quite enough. The thing that we&rsquo;re missing is looping: the same sub-graph can be re-traversed several times as part of execution, which suggests that we ought to be able to combine multiple traces through a loop graph into one. Using our earlier concrete example, we might have traces for evaluating <code>x</code> then <code>x = x -1</code> with the variable <code>x</code> being mapped first to <code>2</code> and then to <code>1</code>. These traces occur back-to-back, so we will put them together into a single trace. To prove some properties about this, I&rsquo;ll define a more precise type of trace.</p> <a href="#end-to-end-traces"> <h3 id="end-to-end-traces">End-To-End Traces</h3> </a> <p>The key way that traces through a loop graph are combined is through the back-edges. Specifically, our <code>loop</code> graphs have edges from each of the <code>output</code> nodes to each of the <code>input</code> nodes. Thus, if we have two paths, both starting at the beginning of the graph and ending at the end, we know that the first path&rsquo;s end has an edge to the second path&rsquo;s beginning. This is enough to combine them.</p> <p>This logic doesn&rsquo;t work if one of the paths ends in the middle of the graph, and not on one of the <code>output</code>s. That&rsquo;s because there is no guarantee that there is a connecting edge.</p> <p>To make things easier, I defined a new data type of &ldquo;end-to-end&rdquo; traces, whose first nodes are one of the graph&rsquo;s <code>input</code>s, and whose last nodes are one of the graph&rsquo;s <code>output</code>s.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Traces.agda" data-first-line="27" data-last-line="36" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Traces.agda#L27-L36">Traces.agda</a>, lines 27 through 36</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="kr">record</span><span class="w"> </span>EndToEndTrace<span class="w"> </span><span class="o">(</span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="o">)</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">constructor</span><span class="w"> </span>MkEndToEndTrace<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">field</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">idx₁</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Index<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">idx₁∈inputs</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>idx₁<span class="w"> </span>∈<span class="w"> </span>inputs<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">idx₂</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Index<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">idx₂∈outputs</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>idx₂<span class="w"> </span>∈<span class="w"> </span>outputs<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">trace</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Trace<span class="w"> </span>idx₁<span class="w"> </span>idx₂<span class="w"> </span>ρ₁<span class="w"> </span>ρ₂</span></span></code></pre></td></tr></table> </div> </div> </div> <p>We can trivially lift the proofs from the previous section to end-to-end traces. For example, here&rsquo;s the lifted version of the first property we proved:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Properties.agda" data-first-line="110" data-last-line="121" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Properties.agda#L110-L121">Properties.agda</a>, lines 110 through 121</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">110 </span><span class="lnt">111 </span><span class="lnt">112 </span><span class="lnt">113 </span><span class="lnt">114 </span><span class="lnt">115 </span><span class="lnt">116 </span><span class="lnt">117 </span><span class="lnt">118 </span><span class="lnt">119 </span><span class="lnt">120 </span><span class="lnt">121 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">EndToEndTrace-∙ˡ</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>g₁<span class="w"> </span>g₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Graph<span class="o">}</span><span class="w"> </span><span class="o">{</span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>EndToEndTrace<span class="w"> </span><span class="o">{</span>g₁<span class="o">}</span><span class="w"> </span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>EndToEndTrace<span class="w"> </span><span class="o">{</span>g₁<span class="w"> </span>∙<span class="w"> </span>g₂<span class="o">}</span><span class="w"> </span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>EndToEndTrace-∙ˡ<span class="w"> </span><span class="o">{</span>g₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>g₂<span class="o">}</span><span class="w"> </span>etr<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">record</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span><span class="w"> </span>idx₁<span class="w"> </span><span class="ow">=</span><span class="w"> </span>EndToEndTrace.idx₁<span class="w"> </span>etr<span class="w"> </span>Fin.↑ˡ<span class="w"> </span>Graph.size<span class="w"> </span>g₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>idx₁∈inputs<span class="w"> </span><span class="ow">=</span><span class="w"> </span>ListMemProp.∈-++⁺ˡ<span class="w"> </span><span class="o">(</span>x∈xs⇒fx∈fxs<span class="w"> </span><span class="o">(</span>Fin._↑ˡ<span class="w"> </span>Graph.size<span class="w"> </span>g₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>EndToEndTrace.idx₁∈inputs<span class="w"> </span>etr<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>idx₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>EndToEndTrace.idx₂<span class="w"> </span>etr<span class="w"> </span>Fin.↑ˡ<span class="w"> </span>Graph.size<span class="w"> </span>g₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>idx₂∈outputs<span class="w"> </span><span class="ow">=</span><span class="w"> </span>ListMemProp.∈-++⁺ˡ<span class="w"> </span><span class="o">(</span>x∈xs⇒fx∈fxs<span class="w"> </span><span class="o">(</span>Fin._↑ˡ<span class="w"> </span>Graph.size<span class="w"> </span>g₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>EndToEndTrace.idx₂∈outputs<span class="w"> </span>etr<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>trace<span class="w"> </span><span class="ow">=</span><span class="w"> </span>Trace-∙ˡ<span class="w"> </span><span class="o">(</span>EndToEndTrace.trace<span class="w"> </span>etr<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The other lifted properties are similar.</p> <p>For looping, the proofs get far more tedious, because of just how many sources of edges there are in the output graph &mdash; they span four lines:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Graphs.agda" data-first-line="84" data-last-line="94" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Graphs.agda#L84-L94">Graphs.agda</a>, lines 84 through 94</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">84 </span><span class="lnt">85 </span><span class="lnt">86 </span><span class="lnt">87 </span><span class="hl"><span class="lnt">88 </span></span><span class="hl"><span class="lnt">89 </span></span><span class="hl"><span class="lnt">90 </span></span><span class="hl"><span class="lnt">91 </span></span><span class="lnt">92 </span><span class="lnt">93 </span><span class="lnt">94 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">loop</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Graph<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Graph<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>loop<span class="w"> </span>g<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">record</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span><span class="w"> </span>size<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span>Nat.+<span class="w"> </span>Graph.size<span class="w"> </span>g<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>nodes<span class="w"> </span><span class="ow">=</span><span class="w"> </span>[]<span class="w"> </span>∷<span class="w"> </span>[]<span class="w"> </span>∷<span class="w"> </span>Graph.nodes<span class="w"> </span>g<span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span>;<span class="w"> </span>edges<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span><span class="mi">2</span><span class="w"> </span>↑ʳᵉ<span class="w"> </span>Graph.edges<span class="w"> </span>g<span class="o">)</span><span class="w"> </span>List.++<span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span>List.map<span class="w"> </span><span class="o">(</span>zero<span class="w"> </span>,_<span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="mi">2</span><span class="w"> </span>↑ʳⁱ<span class="w"> </span>Graph.inputs<span class="w"> </span>g<span class="o">)</span><span class="w"> </span>List.++<span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span>List.map<span class="w"> </span><span class="o">(</span>_,<span class="w"> </span>suc<span class="w"> </span>zero<span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="mi">2</span><span class="w"> </span>↑ʳⁱ<span class="w"> </span>Graph.outputs<span class="w"> </span>g<span class="o">)</span><span class="w"> </span>List.++<span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span><span class="o">((</span>suc<span class="w"> </span>zero<span class="w"> </span>,<span class="w"> </span>zero<span class="o">)</span><span class="w"> </span>∷<span class="w"> </span><span class="o">(</span>zero<span class="w"> </span>,<span class="w"> </span>suc<span class="w"> </span>zero<span class="o">)</span><span class="w"> </span>∷<span class="w"> </span>[]<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>inputs<span class="w"> </span><span class="ow">=</span><span class="w"> </span>zero<span class="w"> </span>∷<span class="w"> </span>[]<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>outputs<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>suc<span class="w"> </span>zero<span class="o">)</span><span class="w"> </span>∷<span class="w"> </span>[]<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>I therefore made use of two helper lemmas. The first is about list membership under concatenation. Simply put, if you concatenate a bunch of lists, and one of them (<code>l</code>) contains some element <code>x</code>, then the concatenation contains <code>x</code> too.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Utils.agda" data-first-line="82" data-last-line="85" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Utils.agda#L82-L85">Utils.agda</a>, lines 82 through 85</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">82 </span><span class="lnt">83 </span><span class="lnt">84 </span><span class="lnt">85 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">concat-∈</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>a<span class="o">}</span><span class="w"> </span><span class="o">{</span>A<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">}</span><span class="w"> </span><span class="o">{</span>x<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">}</span><span class="w"> </span><span class="o">{</span>l<span class="w"> </span><span class="ow">:</span><span class="w"> </span>List<span class="w"> </span>A<span class="o">}</span><span class="w"> </span><span class="o">{</span>ls<span class="w"> </span><span class="ow">:</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>List<span class="w"> </span>A<span class="o">)}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>x<span class="w"> </span>∈<span class="w"> </span>l<span class="w"> </span><span class="ow">→</span><span class="w"> </span>l<span class="w"> </span>∈<span class="w"> </span>ls<span class="w"> </span><span class="ow">→</span><span class="w"> </span>x<span class="w"> </span>∈<span class="w"> </span>foldr<span class="w"> </span>_++_<span class="w"> </span>[]<span class="w"> </span>ls<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>concat-∈<span class="w"> </span>x∈l<span class="w"> </span><span class="o">(</span>here<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>ListMemProp.∈-++⁺ˡ<span class="w"> </span>x∈l<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>concat-∈<span class="w"> </span><span class="o">{</span>ls<span class="w"> </span><span class="ow">=</span><span class="w"> </span>l&#39;<span class="w"> </span>∷<span class="w"> </span>ls&#39;<span class="o">}</span><span class="w"> </span>x∈l<span class="w"> </span><span class="o">(</span>there<span class="w"> </span>l∈ls&#39;<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>ListMemProp.∈-++⁺ʳ<span class="w"> </span>l&#39;<span class="w"> </span><span class="o">(</span>concat-∈<span class="w"> </span>x∈l<span class="w"> </span>l∈ls&#39;<span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>I then specialized this lemma for concatenated groups of edges.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Properties.agda" data-first-line="162" data-last-line="172" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Properties.agda#L162-L172">Properties.agda</a>, lines 162 through 172</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">162 </span><span class="lnt">163 </span><span class="lnt">164 </span><span class="lnt">165 </span><span class="lnt">166 </span><span class="lnt">167 </span><span class="lnt">168 </span><span class="lnt">169 </span><span class="hl"><span class="lnt">170 </span></span><span class="hl"><span class="lnt">171 </span></span><span class="hl"><span class="lnt">172 </span></span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">loop-edge-groups</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>g<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Graph<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>List<span class="w"> </span><span class="o">(</span>Graph.Edge<span class="w"> </span><span class="o">(</span>loop<span class="w"> </span>g<span class="o">)))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>loop-edge-groups<span class="w"> </span>g<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span><span class="mi">2</span><span class="w"> </span>↑ʳᵉ<span class="w"> </span>Graph.edges<span class="w"> </span>g<span class="o">)</span><span class="w"> </span>∷<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>List.map<span class="w"> </span><span class="o">(</span>zero<span class="w"> </span>,_<span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="mi">2</span><span class="w"> </span>↑ʳⁱ<span class="w"> </span>Graph.inputs<span class="w"> </span>g<span class="o">))</span><span class="w"> </span>∷<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>List.map<span class="w"> </span><span class="o">(</span>_,<span class="w"> </span>suc<span class="w"> </span>zero<span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="mi">2</span><span class="w"> </span>↑ʳⁱ<span class="w"> </span>Graph.outputs<span class="w"> </span>g<span class="o">))</span><span class="w"> </span>∷<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">((</span>suc<span class="w"> </span>zero<span class="w"> </span>,<span class="w"> </span>zero<span class="o">)</span><span class="w"> </span>∷<span class="w"> </span><span class="o">(</span>zero<span class="w"> </span>,<span class="w"> </span>suc<span class="w"> </span>zero<span class="o">)</span><span class="w"> </span>∷<span class="w"> </span>[]<span class="o">)</span><span class="w"> </span>∷<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>[]<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"></span><span class="nf">loop-edge-help</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>g<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Graph<span class="o">)</span><span class="w"> </span><span class="o">{</span>l<span class="w"> </span><span class="ow">:</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>Graph.Edge<span class="w"> </span><span class="o">(</span>loop<span class="w"> </span>g<span class="o">))}</span><span class="w"> </span><span class="o">{</span>e<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Graph.Edge<span class="w"> </span><span class="o">(</span>loop<span class="w"> </span>g<span class="o">)}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span>e<span class="w"> </span>ListMem.∈<span class="w"> </span>l<span class="w"> </span><span class="ow">→</span><span class="w"> </span>l<span class="w"> </span>ListMem.∈<span class="w"> </span>loop-edge-groups<span class="w"> </span>g<span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span>e<span class="w"> </span>ListMem.∈<span class="w"> </span>Graph.edges<span class="w"> </span><span class="o">(</span>loop<span class="w"> </span>g<span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Now we can finally prove end-to-end properties of loop graphs. The simplest one is that they allow the code within them to be entirely bypassed (as when the loop body is evaluated zero times). I called this <code>EndToEndTrace-loop⁰</code>. The &ldquo;input&rdquo; node of the loop graph is index <code>zero</code>, while the &ldquo;output&rdquo; node of the loop graph is index <code>suc zero</code>. Thus, the key step is to show that an edge between these two indices exists:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Properties.agda" data-first-line="227" data-last-line="240" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Properties.agda#L227-L240">Properties.agda</a>, lines 227 through 240</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">227 </span><span class="lnt">228 </span><span class="lnt">229 </span><span class="lnt">230 </span><span class="hl"><span class="lnt">231 </span></span><span class="hl"><span class="lnt">232 </span></span><span class="lnt">233 </span><span class="lnt">234 </span><span class="lnt">235 </span><span class="lnt">236 </span><span class="lnt">237 </span><span class="lnt">238 </span><span class="lnt">239 </span><span class="lnt">240 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">EndToEndTrace-loop⁰</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>g<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Graph<span class="o">}</span><span class="w"> </span><span class="o">{</span>ρ<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>EndToEndTrace<span class="w"> </span><span class="o">{</span>loop<span class="w"> </span>g<span class="o">}</span><span class="w"> </span>ρ<span class="w"> </span>ρ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>EndToEndTrace-loop⁰<span class="w"> </span><span class="o">{</span>g<span class="o">}</span><span class="w"> </span><span class="o">{</span>ρ<span class="o">}</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">let</span><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span>zero→suc<span class="w"> </span><span class="ow">=</span><span class="w"> </span>loop-edge-help<span class="w"> </span>g<span class="w"> </span><span class="o">(</span>there<span class="w"> </span><span class="o">(</span>here<span class="w"> </span>refl<span class="o">))</span><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span><span class="o">(</span>there<span class="w"> </span><span class="o">(</span>there<span class="w"> </span><span class="o">(</span>there<span class="w"> </span><span class="o">(</span>here<span class="w"> </span>refl<span class="o">))))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">in</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">record</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span><span class="w"> </span>idx₁<span class="w"> </span><span class="ow">=</span><span class="w"> </span>zero<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>idx₁∈inputs<span class="w"> </span><span class="ow">=</span><span class="w"> </span>here<span class="w"> </span>refl<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>idx₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>suc<span class="w"> </span>zero<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>idx₂∈outputs<span class="w"> </span><span class="ow">=</span><span class="w"> </span>here<span class="w"> </span>refl<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>trace<span class="w"> </span><span class="ow">=</span><span class="w"> </span>Trace-single<span class="w"> </span>[]<span class="w"> </span>++⟨<span class="w"> </span>zero→suc<span class="w"> </span>⟩<span class="w"> </span>Trace-single<span class="w"> </span>[]<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The only remaining novelty is the <code>trace</code> field of the returned <code>EndToEndTrace</code>. It uses the trace concatenation operation <code>++⟨_⟩</code>. This operator allows concatenating two traces, which start and end at distinct nodes, as long as there&rsquo;s an edge that connects them:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Traces.agda" data-first-line="21" data-last-line="25" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Traces.agda#L21-L25">Traces.agda</a>, lines 21 through 25</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">_++⟨_⟩_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>idx₁<span class="w"> </span>idx₂<span class="w"> </span>idx₃<span class="w"> </span>idx₄<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Index<span class="o">}</span><span class="w"> </span><span class="o">{</span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span>ρ₃<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>Trace<span class="w"> </span>idx₁<span class="w"> </span>idx₂<span class="w"> </span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>idx₂<span class="w"> </span>,<span class="w"> </span>idx₃<span class="o">)</span><span class="w"> </span>∈<span class="w"> </span>edges<span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>Trace<span class="w"> </span>idx₃<span class="w"> </span>idx₄<span class="w"> </span>ρ₂<span class="w"> </span>ρ₃<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Trace<span class="w"> </span>idx₁<span class="w"> </span>idx₄<span class="w"> </span>ρ₁<span class="w"> </span>ρ₃<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>_++⟨_⟩_<span class="w"> </span><span class="o">(</span>Trace-single<span class="w"> </span>ρ₁⇒ρ₂<span class="o">)</span><span class="w"> </span>idx₂→idx₃<span class="w"> </span>tr<span class="w"> </span><span class="ow">=</span><span class="w"> </span>Trace-edge<span class="w"> </span>ρ₁⇒ρ₂<span class="w"> </span>idx₂→idx₃<span class="w"> </span>tr<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>_++⟨_⟩_<span class="w"> </span><span class="o">(</span>Trace-edge<span class="w"> </span>ρ₁⇒ρ₂<span class="w"> </span>idx₁→idx&#39;<span class="w"> </span>tr&#39;<span class="o">)</span><span class="w"> </span>idx₂→idx₃<span class="w"> </span>tr<span class="w"> </span><span class="ow">=</span><span class="w"> </span>Trace-edge<span class="w"> </span>ρ₁⇒ρ₂<span class="w"> </span>idx₁→idx&#39;<span class="w"> </span><span class="o">(</span>tr&#39;<span class="w"> </span>++⟨<span class="w"> </span>idx₂→idx₃<span class="w"> </span>⟩<span class="w"> </span>tr<span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The expression on line 239 of <code>Properties.agda</code> is simply the single-edge trace constructed from the edge <code>0 -&gt; 1</code> that connects the start and end nodes of the loop graph. Both of those nodes is empty, so no code is evaluated in that case.</p> <p>The proof for combining several traces through a loop follows a very similar pattern. However, instead of constructing a single-edge trace as we did above, it concatenates two traces from its arguments. Also, instead of using the edge from the first node to the last, it instead uses an edge from the last to the first, as I described at the very beginning of this section.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Properties.agda" data-first-line="209" data-last-line="225" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Properties.agda#L209-L225">Properties.agda</a>, lines 209 through 225</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">209 </span><span class="lnt">210 </span><span class="lnt">211 </span><span class="lnt">212 </span><span class="lnt">213 </span><span class="lnt">214 </span><span class="lnt">215 </span><span class="hl"><span class="lnt">216 </span></span><span class="hl"><span class="lnt">217 </span></span><span class="lnt">218 </span><span class="lnt">219 </span><span class="lnt">220 </span><span class="lnt">221 </span><span class="lnt">222 </span><span class="lnt">223 </span><span class="lnt">224 </span><span class="lnt">225 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">EndToEndTrace-loop²</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>g<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Graph<span class="o">}</span><span class="w"> </span><span class="o">{</span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span>ρ₃<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>EndToEndTrace<span class="w"> </span><span class="o">{</span>loop<span class="w"> </span>g<span class="o">}</span><span class="w"> </span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>EndToEndTrace<span class="w"> </span><span class="o">{</span>loop<span class="w"> </span>g<span class="o">}</span><span class="w"> </span>ρ₂<span class="w"> </span>ρ₃<span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>EndToEndTrace<span class="w"> </span><span class="o">{</span>loop<span class="w"> </span>g<span class="o">}</span><span class="w"> </span>ρ₁<span class="w"> </span>ρ₃<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>EndToEndTrace-loop²<span class="w"> </span><span class="o">{</span>g<span class="o">}</span><span class="w"> </span><span class="o">(</span>MkEndToEndTrace<span class="w"> </span>zero<span class="w"> </span><span class="o">(</span>here<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span><span class="o">(</span>suc<span class="w"> </span>zero<span class="o">)</span><span class="w"> </span><span class="o">(</span>here<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span>tr₁<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>MkEndToEndTrace<span class="w"> </span>zero<span class="w"> </span><span class="o">(</span>here<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span><span class="o">(</span>suc<span class="w"> </span>zero<span class="o">)</span><span class="w"> </span><span class="o">(</span>here<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span>tr₂<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">let</span><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span>suc→zero<span class="w"> </span><span class="ow">=</span><span class="w"> </span>loop-edge-help<span class="w"> </span>g<span class="w"> </span><span class="o">(</span>here<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span><span class="o">(</span>there<span class="w"> </span><span class="o">(</span>there<span class="w"> </span><span class="o">(</span>there<span class="w"> </span><span class="o">(</span>here<span class="w"> </span>refl<span class="o">))))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">in</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">record</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span><span class="w"> </span>idx₁<span class="w"> </span><span class="ow">=</span><span class="w"> </span>zero<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>idx₁∈inputs<span class="w"> </span><span class="ow">=</span><span class="w"> </span>here<span class="w"> </span>refl<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>idx₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>suc<span class="w"> </span>zero<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>idx₂∈outputs<span class="w"> </span><span class="ow">=</span><span class="w"> </span>here<span class="w"> </span>refl<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>trace<span class="w"> </span><span class="ow">=</span><span class="w"> </span>tr₁<span class="w"> </span>++⟨<span class="w"> </span>suc→zero<span class="w"> </span>⟩<span class="w"> </span>tr₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#proof-of-sufficiency"> <h3 id="proof-of-sufficiency">Proof of Sufficiency</h3> </a> <p>We now have all the pieces to show each execution of our program has a corresponding trace through a graph. Here is the whole proof:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Properties.agda" data-first-line="281" data-last-line="296" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Properties.agda#L281-L296">Properties.agda</a>, lines 281 through 296</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">281 </span><span class="lnt">282 </span><span class="lnt">283 </span><span class="lnt">284 </span><span class="lnt">285 </span><span class="lnt">286 </span><span class="lnt">287 </span><span class="lnt">288 </span><span class="lnt">289 </span><span class="lnt">290 </span><span class="lnt">291 </span><span class="lnt">292 </span><span class="lnt">293 </span><span class="lnt">294 </span><span class="lnt">295 </span><span class="lnt">296 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">buildCfg-sufficient</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>s<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Stmt<span class="o">}</span><span class="w"> </span><span class="o">{</span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>ρ₁<span class="w"> </span>,<span class="w"> </span>s<span class="w"> </span>⇒ˢ<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>EndToEndTrace<span class="w"> </span><span class="o">{</span>buildCfg<span class="w"> </span>s<span class="o">}</span><span class="w"> </span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>buildCfg-sufficient<span class="w"> </span><span class="o">(</span>⇒ˢ-⟨⟩<span class="w"> </span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span>bs<span class="w"> </span>ρ₁,bs⇒ρ₂<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>EndToEndTrace-singleton<span class="w"> </span><span class="o">(</span>ρ₁,bs⇒ρ₂<span class="w"> </span>∷<span class="w"> </span>[]<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>buildCfg-sufficient<span class="w"> </span><span class="o">(</span>⇒ˢ-then<span class="w"> </span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span>ρ₃<span class="w"> </span>s₁<span class="w"> </span>s₂<span class="w"> </span>ρ₁,s₁⇒ρ₂<span class="w"> </span>ρ₂,s₂⇒ρ₃<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>buildCfg-sufficient<span class="w"> </span>ρ₁,s₁⇒ρ₂<span class="w"> </span>++<span class="w"> </span>buildCfg-sufficient<span class="w"> </span>ρ₂,s₂⇒ρ₃<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>buildCfg-sufficient<span class="w"> </span><span class="o">(</span>⇒ˢ-if-true<span class="w"> </span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span>s₁<span class="w"> </span>s₂<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span>ρ₁,s₁⇒ρ₂<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>EndToEndTrace-∙ˡ<span class="w"> </span><span class="o">(</span>buildCfg-sufficient<span class="w"> </span>ρ₁,s₁⇒ρ₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>buildCfg-sufficient<span class="w"> </span><span class="o">(</span>⇒ˢ-if-false<span class="w"> </span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span>_<span class="w"> </span>s₁<span class="w"> </span>s₂<span class="w"> </span>_<span class="w"> </span>ρ₁,s₂⇒ρ₂<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>EndToEndTrace-∙ʳ<span class="w"> </span><span class="o">{</span>buildCfg<span class="w"> </span>s₁<span class="o">}</span><span class="w"> </span><span class="o">(</span>buildCfg-sufficient<span class="w"> </span>ρ₁,s₂⇒ρ₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>buildCfg-sufficient<span class="w"> </span><span class="o">(</span>⇒ˢ-while-true<span class="w"> </span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span>ρ₃<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span>s<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span>ρ₁,s⇒ρ₂<span class="w"> </span>ρ₂,ws⇒ρ₃<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>EndToEndTrace-loop²<span class="w"> </span><span class="o">{</span>buildCfg<span class="w"> </span>s<span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>EndToEndTrace-loop<span class="w"> </span><span class="o">{</span>buildCfg<span class="w"> </span>s<span class="o">}</span><span class="w"> </span><span class="o">(</span>buildCfg-sufficient<span class="w"> </span>ρ₁,s⇒ρ₂<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>buildCfg-sufficient<span class="w"> </span>ρ₂,ws⇒ρ₃<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>buildCfg-sufficient<span class="w"> </span><span class="o">(</span>⇒ˢ-while-false<span class="w"> </span>ρ<span class="w"> </span>_<span class="w"> </span>s<span class="w"> </span>_<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>EndToEndTrace-loop⁰<span class="w"> </span><span class="o">{</span>buildCfg<span class="w"> </span>s<span class="o">}</span><span class="w"> </span><span class="o">{</span>ρ<span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We proceed by <span class="sidenote"> <label class="sidenote-label" for="derivation-note">checking what inference rule was used to execute a particular statement,</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="derivation-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Precisely, we proceed by induction on the derivation of \(\rho_1, s \Rightarrow \rho_2\). <span class="sidenote-delimiter">]</span> </span> </span> because that&rsquo;s what tells us what the program did in that particular moment.</p> <ul> <li> <p>When executing a basic statement, we know that we constructed a singleton graph that contains one node with that statement. Thus, we can trivially construct a single-step trace without any edges.</p> </li> <li> <p>When executing a sequence of statements, we have two induction hypotheses. These state that the sub-graphs we construct for the first and second statement have the trace property. We also have two evaluation judgements (one for each statement), which means that we can apply that property to get traces. The <code>buildCfg</code> function sequences the two graphs, and we can sequence the two traces through them, resulting in a trace through the final output.</p> </li> <li> <p>For both the <code>then</code> and <code>else</code> cases of evaluating an <code>if</code> statement, we observe that <code>buildCfg</code> overlays the sub-graphs of the two branches using <code>_∙_</code>. We also know that the two sub-graphs have the trace property.</p> <ul> <li>In the <code>then</code> case, since we have an evaluation judgement for <code>s₁</code> (in variable <code>ρ₁,s₁⇒ρ₂</code>), we conclude that there&rsquo;s a correct trace through the <code>then</code> sub-graph. Since that graph is the left operand of <code>_∙_</code>, we use <code>EndToEndTrace-∙ˡ</code> to show that the trace is preserved in the full graph.</li> <li>In the <code>else</code> case things are symmetric. We are evaluating <code>s₂</code>, with a judgement given by <code>ρ₁,s₂⇒ρ₂</code>. We use that to conclude that there&rsquo;s a trace through the graph built from <code>s₂</code>. Since this sub-graph is the right operand of <code>_∙_</code>, we use <code>EndToEndTrace-∙ʳ</code> to show that it&rsquo;s preserved in the full graph.</li> </ul> </li> <li> <p>For the <code>true</code> case of <code>while</code>, we have two evaluation judgements: one for the body and one for the loop again, this time in a new environment. They are stored in <code>ρ₁,s⇒ρ₂</code> and <code>ρ₂,ws⇒ρ₃</code>, respectively. The statement being evaluated by <code>ρ₂,ws⇒ρ₃</code> is actually the exact same statement that&rsquo;s being evaluated at the top level of the proof. Thus, we can use <code>EndToEndTrace-loop²</code>, which sequences two traces through the same graph.</p> <p>We also use <code>EndToEndTrace-loop</code> to lift the trace through <code>buildCfg s</code> into a trace through <code>buildCfg (while e s)</code>.</p> </li> <li> <p>For the <code>false</code> case of the <code>while</code>, we don&rsquo;t execute any instructions, and finish evaluating right away. This corresponds to the do-nothing trace, which we have established exists using <code>EndToEndTrace-loop⁰</code>.</p> </li> </ul> <p>That&rsquo;s it! We have now validated that the Control Flow Graphs we construct match the semantics of the programming language, which makes them a good input to our static program analyses. We can finally start writing those!</p> <a href="#defining-and-verifying-static-program-analyses"> <h3 id="defining-and-verifying-static-program-analyses">Defining and Verifying Static Program Analyses</h3> </a> <p>We have all the pieces we need to define a formally-verified forward analysis:</p> <ul> <li>We have used the framework of lattices to encode the precision of program analysis outputs. Smaller elements in a lattice are more specific, meaning more useful information.</li> <li>We have <a href="https://danilafe.com/blog/04_spa_agda_fixedpoint/">implemented fixed-point algorithm</a>, which finds the smallest solutions to equations in the form \(f(x) = x\) for monotonic functions over lattices. By defining our analysis as such a function, we can apply the algorithm to find the most precise steady-state description of our program.</li> <li>We have defined how our programs are executed, which is crucial for defining &ldquo;correctness&rdquo;.</li> </ul> <p>Here&rsquo;s how these pieces will fit together. We will construct a finite-height lattice. Every single element of this lattice will contain information about each variable at each node in the Control Flow Graph. We will then define a monotonic function that updates this information using the structure encoded in the CFG&rsquo;s edges and nodes. Then, using the fixed-point algorithm, we will find the least element of the lattice, which will give us a precise description of all program variables at all points in the program. Because we have just validated our CFGs to be faithful to the language&rsquo;s semantics, we&rsquo;ll be able to prove that our algorithm produces accurate results.</p> <p>The next post or two will be the last stretch; I hope to see you there!</p> Implementing and Verifying "Static Program Analysis" in Agda, Part 6: Control Flow Graphs https://danilafe.com/blog/06_spa_agda_cfg/ Wed, 27 Nov 2024 16:26:42 -0700 https://danilafe.com/blog/06_spa_agda_cfg/ <p>In the previous section, I&rsquo;ve given a formal definition of the programming language that I&rsquo;ve been trying to analyze. This formal definition serves as the &ldquo;ground truth&rdquo; for how our little imperative programs are executed; however, program analyses (especially in practice) seldom take the formal semantics as input. Instead, they focus on more pragmatic program representations from the world of compilers. One such representation are <em>Control Flow Graphs (CFGs)</em>. That&rsquo;s what I want to discuss in this post.</p> <p>Let&rsquo;s start by building some informal intuition. CFGs are pretty much what their name suggests: they are a type of <a href="https://en.wikipedia.org/wiki/Graph_%28discrete_mathematics%29"class="external-link">graph<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>; their edges show how execution might jump from one piece of code to another (how control might flow).</p> <p>For example, take the below program.</p> <pre tabindex="0"><code>x = ...; if x { x = 1; } else { x = 0; } y = x; </code></pre><p>The CFG might look like this:</p> <figure class="small"><img src="https://danilafe.com/blog/06_spa_agda_cfg/if-cfg.png"> </figure> <p>Here, the initialization of <code>x</code> with <code>...</code>, as well as the <code>if</code> condition (just <code>x</code>), are guaranteed to execute one after another, so they occupy a single node. From there, depending on the condition, the control flow can jump to one of the branches of the <code>if</code> statement: the &ldquo;then&rdquo; branch if the condition is truthy, and the &ldquo;else&rdquo; branch if the condition is falsy. As a result, there are two arrows coming out of the initial node. Once either branch is executed, control always jumps to the code right after the <code>if</code> statement (the <code>y = x</code>). Thus, both the <code>x = 1</code> and <code>x = 0</code> nodes have a single arrow to the <code>y = x</code> node.</p> <p>As another example, if you had a loop:</p> <pre tabindex="0"><code>x = ...; while x { x = x - 1; } y = x; </code></pre><p>The CFG would look like this:</p> <figure class="small"><img src="https://danilafe.com/blog/06_spa_agda_cfg/while-cfg.png"> </figure> <p>Here, the condition of the loop (<code>x</code>) is not always guaranteed to execute together with the code that initializes <code>x</code>. That&rsquo;s because the condition of the loop is checked after every iteration, whereas the code before the loop is executed only once. As a result, <code>x = ...</code> and <code>x</code> occupy distinct CFG nodes. From there, the control flow can proceed in two different ways, depending on the value of <code>x</code>. If <code>x</code> is truthy, the program will proceed to the loop body (decrementing <code>x</code>). If <code>x</code> is falsy, the program will skip the loop body altogether, and go to the code right after the loop (<code>y = x</code>). This is indicated by the two arrows going out of the <code>x</code> node. After executing the body, we return to the condition of the loop to see if we need to run another iteration. Because of this, the decrementing node has an arrow back to the loop condition.</p> <p>Now, let&rsquo;s be a bit more precise. Control Flow Graphs are defined as follows:</p> <ul> <li> <p><strong>The nodes</strong> are <a href="https://en.wikipedia.org/wiki/Basic_block"class="external-link"><em>basic blocks</em><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. Paraphrasing Wikipedia&rsquo;s definition, a basic block is a piece of code that has only one entry point and one exit point.</p> <p>The one-entry-point rule means that it&rsquo;s not possible to jump into the middle of the basic block, executing only half of its instructions. The execution of a basic block always begins at the top. Symmetrically, the one-exit-point rule means that you can&rsquo;t jump away to other code, skipping some instructions. The execution of a basic block always ends at the bottom.</p> <p>As a result of these constraints, when running a basic block, you are guaranteed to execute every instruction in exactly the order they occur in, and execute each instruction exactly once.</p> </li> <li> <p><strong>The edges</strong> are jumps between basic blocks. We&rsquo;ve already seen how <code>if</code> and <code>while</code> statements introduce these jumps.</p> </li> </ul> <p id="list-basic-stmts">Basic blocks can only be made of code that doesn&rsquo;t jump (otherwise, we violate the single-exit-point policy). In the previous post, we defined exactly this kind of code as <a href="https://danilafe.com/blog/05_spa_agda_semantics/#introduce-simple-statements">simple statements</a>. So, in our control flow graph, nodes will be sequences of simple statements.</p> <a href="#control-flow-graphs-in-agda"> <h3 id="control-flow-graphs-in-agda">Control Flow Graphs in Agda</h3> </a> <a href="#basic-definition"> <h4 id="basic-definition">Basic Definition</h4> </a> <p>At an abstract level, it&rsquo;s easy to say &ldquo;it&rsquo;s just a graph where X is Y&rdquo; about anything. It&rsquo;s much harder to give a precise definition of such a graph, particularly if you want to rule out invalid graphs (e.g., ones with edges pointing nowhere). In Agda, I chose the represent a CFG with two lists: one of nodes, and one of edges. Each node is simply a list of <code>BasicStmt</code>s, as I described in a preceding paragraph. An edge is simply a pair of numbers, each number encoding the index of the node connected by the edge.</p> <p>Here&rsquo;s where it gets a little complicated. I don&rsquo;t want to use plain natural numbers for indices, because that means you can easily introduce &ldquo;broken&rdquo; edge. For example, what if you have 4 nodes, and you have an edge <code>(5, 5)</code>? To avoid this, I picked the finite natural numbers represented by <a href="https://agda.github.io/agda-stdlib/v2.0/Data.Fin.Base.html#1154"class="external-link"><code>Fin</code><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> as endpoints for edges.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="kr">data</span><span class="w"> </span>Fin<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">zero</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Fin<span class="w"> </span><span class="o">(</span>suc<span class="w"> </span>n<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">suc</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="o">(</span>i<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Fin<span class="w"> </span>n<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Fin<span class="w"> </span><span class="o">(</span>suc<span class="w"> </span>n<span class="o">)</span><span class="w"> </span></span></span></code></pre></div><p>Specifically, <code>Fin n</code> is the type of natural numbers less than <code>n</code>. Following this definition, <code>Fin 3</code> represents the numbers <code>0</code>, <code>1</code> and <code>2</code>. These are represented using the same constructors as <code>Nat</code>: <code>zero</code> and <code>suc</code>. The type of <code>zero</code> is <code>Fin (suc n)</code> for any <code>n</code>; this makes sense because zero is less than any number plus one. For <code>suc</code>, the bound <code>n</code> of the input <code>i</code> is incremented by one, leading to another <code>suc n</code> in the final type. This makes sense because if <code>i &lt; n</code>, then <code>i + 1 &lt; n + 1</code>. I&rsquo;ve previously explained this data type <a href="https://danilafe.com/blog/01_aoc_coq/#aside-vectors-and-finite-mathbbn">in another post on this site</a>.</p> <p>Here&rsquo;s my definition of <code>Graph</code>s written using <code>Fin</code>:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Graphs.agda" data-first-line="24" data-last-line="39" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Graphs.agda#L24-L39">Graphs.agda</a>, lines 24 through 39</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="kr">constructor</span><span class="w"> </span>MkGraph<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">field</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">size</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">Index</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>Index<span class="w"> </span><span class="ow">=</span><span class="w"> </span>Fin<span class="w"> </span>size<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">Edge</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>Edge<span class="w"> </span><span class="ow">=</span><span class="w"> </span>Index<span class="w"> </span>×<span class="w"> </span>Index<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">field</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">nodes</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Vec<span class="w"> </span><span class="o">(</span>List<span class="w"> </span>BasicStmt<span class="o">)</span><span class="w"> </span>size<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">edges</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>List<span class="w"> </span>Edge<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">inputs</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>List<span class="w"> </span>Index<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">outputs</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>List<span class="w"> </span>Index<span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>I explicitly used a <code>size</code> field, which determines how many nodes are in the graph, and serves as the upper bound for the edge indices. From there, an index <code>Index</code> into the node list is <span class="sidenote"> <label class="sidenote-label" for="size-note">just a natural number less than <code>size</code>,</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="size-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Ther are <code>size</code> natural numbers less than <code>size</code>:<br> <code>0, 1, ..., size - 1</code>. <span class="sidenote-delimiter">]</span> </span> </span> and an edge is just a pair of indices. The graph then contains a vector (exact-length list) <code>nodes</code> of all the basic blocks, and then a list of edges <code>edges</code>.</p> <p>There are two fields here that I have not yet said anything about: <code>inputs</code> and <code>outputs</code>. When we have a complete CFG for our programs, these fields are totally unnecessary. However, as we are <em>building</em> the CFG, these will come in handy, by telling us how to stitch together smaller sub-graphs that we&rsquo;ve already built. Let&rsquo;s talk about that next.</p> <a href="#combining-graphs"> <h4 id="combining-graphs">Combining Graphs</h4> </a> <p>Suppose you&rsquo;re building a CFG for a program in the following form:</p> <pre tabindex="0"><code>code1; code2; </code></pre><p>Where <code>code1</code> and <code>code2</code> are arbitrary pieces of code, which could include statements, loops, and pretty much anything else. Besides the fact that they occur one after another, these pieces of code are unrelated, and we can build CFGs for each one them independently. However, the fact that <code>code1</code> and <code>code2</code> are in sequence means that the full control flow graph for the above program should have edges going from the nodes in <code>code1</code> to the nodes in <code>code2</code>. Of course, not <em>every</em> node in <code>code1</code> should have such edges: that would mean that after executing any &ldquo;basic&rdquo; sequence of instructions, you could suddenly decide to skip the rest of <code>code1</code> and move on to executing <code>code2</code>.</p> <p>Thus, we need to be more precise about what edges we need to insert; we want to insert edges between the &ldquo;final&rdquo; nodes in <code>code1</code> (where control ends up after <code>code1</code> is finished executing) and the &ldquo;initial&rdquo; nodes in <code>code2</code> (where control would begin once we started executing <code>code2</code>). Those are the <code>outputs</code> and <code>inputs</code>, respectively. When stitching together sequenced control graphs, we will connect each of the outputs of one to each of the inputs of the other.</p> <p>This is defined by the operation <code>g₁ ↦ g₂</code>, which sequences two graphs <code>g₁</code> and <code>g₂</code>:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Graphs.agda" data-first-line="72" data-last-line="83" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Graphs.agda#L72-L83">Graphs.agda</a>, lines 72 through 83</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">72 </span><span class="lnt">73 </span><span class="lnt">74 </span><span class="lnt">75 </span><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span><span class="lnt">79 </span><span class="lnt">80 </span><span class="lnt">81 </span><span class="lnt">82 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">_↦_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Graph<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Graph<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Graph<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>_↦_<span class="w"> </span>g₁<span class="w"> </span>g₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">record</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span><span class="w"> </span>size<span class="w"> </span><span class="ow">=</span><span class="w"> </span>Graph.size<span class="w"> </span>g₁<span class="w"> </span>Nat.+<span class="w"> </span>Graph.size<span class="w"> </span>g₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>nodes<span class="w"> </span><span class="ow">=</span><span class="w"> </span>Graph.nodes<span class="w"> </span>g₁<span class="w"> </span>++<span class="w"> </span>Graph.nodes<span class="w"> </span>g₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>edges<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>Graph.edges<span class="w"> </span>g₁<span class="w"> </span>↑ˡᵉ<span class="w"> </span>Graph.size<span class="w"> </span>g₂<span class="o">)</span><span class="w"> </span>List.++<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>Graph.size<span class="w"> </span>g₁<span class="w"> </span>↑ʳᵉ<span class="w"> </span>Graph.edges<span class="w"> </span>g₂<span class="o">)</span><span class="w"> </span>List.++<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>List.cartesianProduct<span class="w"> </span><span class="o">(</span>Graph.outputs<span class="w"> </span>g₁<span class="w"> </span>↑ˡⁱ<span class="w"> </span>Graph.size<span class="w"> </span>g₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>Graph.size<span class="w"> </span>g₁<span class="w"> </span>↑ʳⁱ<span class="w"> </span>Graph.inputs<span class="w"> </span>g₂<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>inputs<span class="w"> </span><span class="ow">=</span><span class="w"> </span>Graph.inputs<span class="w"> </span>g₁<span class="w"> </span>↑ˡⁱ<span class="w"> </span>Graph.size<span class="w"> </span>g₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>outputs<span class="w"> </span><span class="ow">=</span><span class="w"> </span>Graph.size<span class="w"> </span>g₁<span class="w"> </span>↑ʳⁱ<span class="w"> </span>Graph.outputs<span class="w"> </span>g₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The definition starts out pretty innocuous, but gets a bit complicated by the end. The sum of the numbers of nodes in the two operands becomes the new graph size, and the nodes from the two graphs are all included in the result. Then, the definitions start making use of various operators like <code>↑ˡᵉ</code> and <code>↑ʳᵉ</code>; these deserve an explanation.</p> <p id="fin-reindexing">The tricky thing is that when we&rsquo;re concatenating lists of nodes, we are changing some of the indices of the elements within. For instance, in the lists <code>[x]</code> and <code>[y]</code>, the indices of both <code>x</code> and <code>y</code> are <code>0</code>; however, in the concatenated list <code>[x, y]</code>, the index of <code>x</code> is still <code>0</code>, but the index of <code>y</code> is <code>1</code>. More generally, when we concatenate two lists <code>l1</code> and <code>l2</code>, the indices into <code>l1</code> remain unchanged, whereas the indices <code>l2</code> are shifted by <code>length l1</code>.</p> <p>Actually, that&rsquo;s not all there is to it. The <em>values</em> of the indices into the left list don&rsquo;t change, but their types do! They start as <code>Fin (length l1)</code>, but for the whole list, these same indices will have type <code>Fin (length l1 + length l2))</code>.</p> <p>To help deal with this, Agda provides the operators <a href="https://agda.github.io/agda-stdlib/v2.0/Data.Fin.Base.html#2355"class="external-link"><code>↑ˡ</code><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> and <a href="https://agda.github.io/agda-stdlib/v2.0/Data.Fin.Base.html#2522"class="external-link"><code>↑ʳ</code><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> that implement this re-indexing and re-typing. The former implements &ldquo;re-indexing on the left&rdquo; &ndash; given an index into the left list <code>l1</code>, it changes its type by adding the other list&rsquo;s length to it, but keeps the index value itself unchanged. The latter implements &ldquo;re-indexing on the right&rdquo; &ndash; given an index into the right list <code>l2</code>, it adds the length of the first list to it (shifting it), and does the same to its type.</p> <p>The definition leads to the following equations:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">l1</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Vec<span class="w"> </span>A<span class="w"> </span>n<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nf">l2</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Vec<span class="w"> </span>A<span class="w"> </span>m<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nf">idx1</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Fin<span class="w"> </span>n<span class="w"> </span><span class="c1">-- index into l1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nf">idx2</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Fin<span class="w"> </span>m<span class="w"> </span><span class="c1">-- index into l2</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>l1<span class="w"> </span>[<span class="w"> </span>idx1<span class="w"> </span>]<span class="w"> </span>≡<span class="w"> </span><span class="o">(</span>l1<span class="w"> </span>++<span class="w"> </span>l2<span class="o">)</span><span class="w"> </span>[<span class="w"> </span>idx1<span class="w"> </span>↑ˡ<span class="w"> </span>m<span class="w"> </span>]<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>l2<span class="w"> </span>[<span class="w"> </span>idx2<span class="w"> </span>]<span class="w"> </span>≡<span class="w"> </span><span class="o">(</span>l1<span class="w"> </span>++<span class="w"> </span>l2<span class="o">)</span><span class="w"> </span>[<span class="w"> </span>n<span class="w"> </span>↑ʳ<span class="w"> </span>idx2<span class="w"> </span>]<span class="w"> </span></span></span></code></pre></div><p>The operators used in the definition above are just versions of the same re-indexing operators. The <code>↑ˡᵉ</code> operator applies <code>↑ˡ</code> to all the (<strong>e</strong>)dges in a graph, and the <code>↑ˡⁱ</code> applies it to all the (<strong>i</strong>)ndices in a list (like <code>inputs</code> and <code>outputs</code>).</p> <p>Given these definitions, hopefully the intent with the rest of the definition is not too hard to see. The edges in the new graph come from three places: the graph <code>g₁</code> and <code>g₂</code>, and from creating a new edge from each of the outputs of <code>g₁</code> to each of the inputs of <code>g₂</code>. We keep the inputs of <code>g₁</code> as the inputs of the whole graph (since <code>g₁</code> comes first), and symmetrically we keep the outputs of <code>g₂</code>. Of course, we do have to re-index them to keep them pointing at the right nodes.</p> <p>Another operation we will need is &ldquo;overlaying&rdquo; two graphs: this will be like placing them in parallel, without adding jumps between the two. We use this operation when combining the sub-CFGs of the &ldquo;if&rdquo; and &ldquo;else&rdquo; branches of an <code>if</code>/<code>else</code>, which both follow the condition, and both proceed to the code after the conditional.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Graphs.agda" data-first-line="59" data-last-line="70" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Graphs.agda#L59-L70">Graphs.agda</a>, lines 59 through 70</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span><span class="lnt">65 </span><span class="lnt">66 </span><span class="lnt">67 </span><span class="lnt">68 </span><span class="lnt">69 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">_∙_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Graph<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Graph<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Graph<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>_∙_<span class="w"> </span>g₁<span class="w"> </span>g₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">record</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span><span class="w"> </span>size<span class="w"> </span><span class="ow">=</span><span class="w"> </span>Graph.size<span class="w"> </span>g₁<span class="w"> </span>Nat.+<span class="w"> </span>Graph.size<span class="w"> </span>g₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>nodes<span class="w"> </span><span class="ow">=</span><span class="w"> </span>Graph.nodes<span class="w"> </span>g₁<span class="w"> </span>++<span class="w"> </span>Graph.nodes<span class="w"> </span>g₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>edges<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>Graph.edges<span class="w"> </span>g₁<span class="w"> </span>↑ˡᵉ<span class="w"> </span>Graph.size<span class="w"> </span>g₂<span class="o">)</span><span class="w"> </span>List.++<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>Graph.size<span class="w"> </span>g₁<span class="w"> </span>↑ʳᵉ<span class="w"> </span>Graph.edges<span class="w"> </span>g₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>inputs<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>Graph.inputs<span class="w"> </span>g₁<span class="w"> </span>↑ˡⁱ<span class="w"> </span>Graph.size<span class="w"> </span>g₂<span class="o">)</span><span class="w"> </span>List.++<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>Graph.size<span class="w"> </span>g₁<span class="w"> </span>↑ʳⁱ<span class="w"> </span>Graph.inputs<span class="w"> </span>g₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>outputs<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>Graph.outputs<span class="w"> </span>g₁<span class="w"> </span>↑ˡⁱ<span class="w"> </span>Graph.size<span class="w"> </span>g₂<span class="o">)</span><span class="w"> </span>List.++<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>Graph.size<span class="w"> </span>g₁<span class="w"> </span>↑ʳⁱ<span class="w"> </span>Graph.outputs<span class="w"> </span>g₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Everything here is just concatenation; we pool together the nodes, edges, inputs, and outputs, and the main source of complexity is the re-indexing.</p> <p>The one last operation, which we will use for <code>while</code> loops, is looping. This operation simply connects the outputs of a graph back to its inputs (allowing looping), and also allows the body to be skipped. This is slightly different from the graph for <code>while</code> loops I showed above; the reason for that is that I currently don&rsquo;t include the conditional expressions in my CFG. This is a limitation that I will address in future work.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Graphs.agda" data-first-line="85" data-last-line="95" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Graphs.agda#L85-L95">Graphs.agda</a>, lines 85 through 95</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">85 </span><span class="lnt">86 </span><span class="lnt">87 </span><span class="lnt">88 </span><span class="lnt">89 </span><span class="lnt">90 </span><span class="lnt">91 </span><span class="lnt">92 </span><span class="lnt">93 </span><span class="lnt">94 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl">loop<span class="w"> </span>g<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">record</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span><span class="w"> </span>size<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span>Nat.+<span class="w"> </span>Graph.size<span class="w"> </span>g<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>nodes<span class="w"> </span><span class="ow">=</span><span class="w"> </span>[]<span class="w"> </span>∷<span class="w"> </span>[]<span class="w"> </span>∷<span class="w"> </span>Graph.nodes<span class="w"> </span>g<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>edges<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span><span class="mi">2</span><span class="w"> </span>↑ʳᵉ<span class="w"> </span>Graph.edges<span class="w"> </span>g<span class="o">)</span><span class="w"> </span>List.++<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>List.map<span class="w"> </span><span class="o">(</span>zero<span class="w"> </span>,_<span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="mi">2</span><span class="w"> </span>↑ʳⁱ<span class="w"> </span>Graph.inputs<span class="w"> </span>g<span class="o">)</span><span class="w"> </span>List.++<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>List.map<span class="w"> </span><span class="o">(</span>_,<span class="w"> </span>suc<span class="w"> </span>zero<span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="mi">2</span><span class="w"> </span>↑ʳⁱ<span class="w"> </span>Graph.outputs<span class="w"> </span>g<span class="o">)</span><span class="w"> </span>List.++<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">((</span>suc<span class="w"> </span>zero<span class="w"> </span>,<span class="w"> </span>zero<span class="o">)</span><span class="w"> </span>∷<span class="w"> </span><span class="o">(</span>zero<span class="w"> </span>,<span class="w"> </span>suc<span class="w"> </span>zero<span class="o">)</span><span class="w"> </span>∷<span class="w"> </span>[]<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>inputs<span class="w"> </span><span class="ow">=</span><span class="w"> </span>zero<span class="w"> </span>∷<span class="w"> </span>[]<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>outputs<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>suc<span class="w"> </span>zero<span class="o">)</span><span class="w"> </span>∷<span class="w"> </span>[]<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Given these thee operations, I construct Control Flow Graphs as follows, where <code>singleton</code> creates a new CFG node with the given list of simple statements:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Graphs.agda" data-first-line="122" data-last-line="126" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Graphs.agda#L122-L126">Graphs.agda</a>, lines 122 through 126</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">122 </span><span class="lnt">123 </span><span class="lnt">124 </span><span class="lnt">125 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl">buildCfg<span class="w"> </span>⟨<span class="w"> </span>bs₁<span class="w"> </span>⟩<span class="w"> </span><span class="ow">=</span><span class="w"> </span>singleton<span class="w"> </span><span class="o">(</span>bs₁<span class="w"> </span>∷<span class="w"> </span>[]<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>buildCfg<span class="w"> </span><span class="o">(</span>s₁<span class="w"> </span>then<span class="w"> </span>s₂<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>buildCfg<span class="w"> </span>s₁<span class="w"> </span>↦<span class="w"> </span>buildCfg<span class="w"> </span>s₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>buildCfg<span class="w"> </span><span class="o">(</span>if<span class="w"> </span>_<span class="w"> </span>then<span class="w"> </span>s₁<span class="w"> </span>else<span class="w"> </span>s₂<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>buildCfg<span class="w"> </span>s₁<span class="w"> </span>∙<span class="w"> </span>buildCfg<span class="w"> </span>s₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>buildCfg<span class="w"> </span><span class="o">(</span>while<span class="w"> </span>_<span class="w"> </span>repeat<span class="w"> </span>s<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>loop<span class="w"> </span><span class="o">(</span>buildCfg<span class="w"> </span>s<span class="o">)</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Throughout this, I&rsquo;ve been liberal to include empty CFG nodes as was convenient. This is a departure from the formal definition I gave above, but it makes things much simpler.</p> <a href="#additional-functions"> <h3 id="additional-functions">Additional Functions</h3> </a> <p>To integrate Control Flow Graphs into our lattice-based program analyses, we&rsquo;ll need to do a couple of things. First, upon reading the <a href="https://cs.au.dk/~amoeller/spa/"class="external-link">reference <em>Static Program Analysis</em> text<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, one sees a lot of quantification over the predecessors or successors of a given CFG node. For example, the following equation is from Chapter 5:</p> $$ \textit{JOIN}(v) = \bigsqcup_{w \in \textit{pred}(v)} \llbracket w \rrbracket $$ <p>To compute the \(\textit{JOIN}\) function (which we have not covered yet) for a given CFG node, we need to iterate over all of its predecessors, and combine their static information using \(\sqcup\), which I first <a href="https://danilafe.com/blog/01_spa_agda_lattices/#least-upper-bound">explained several posts ago</a>. To be able to iterate over them, we need to be able to retrieve the predecessors of a node from a graph!</p> <p>Our encoding does not make computing the predecessors particularly easy; to check if two nodes are connected, we need to check if an <code>Index</code>-<code>Index</code> pair corresponding to the nodes is present in the <code>edges</code> list. To this end, we need to be able to compare edges for equality. Fortunately, it&rsquo;s relatively straightforward to show that our edges can be compared in such a way; after all, they are just pairs of <code>Fin</code>s, and <code>Fin</code>s and products support these comparisons.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Graphs.agda" data-first-line="149" data-last-line="152" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Graphs.agda#L149-L152">Graphs.agda</a>, lines 149 through 152</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">149 </span><span class="lnt">150 </span><span class="lnt">151 </span><span class="lnt">152 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="kr">module</span><span class="w"> </span>_ (<span class="n">g</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Graph<span class="o">)</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">open</span><span class="w"> </span><span class="kr">import</span><span class="w"> </span><span class="n">Data.Product.Properties</span><span class="w"> </span>as<span class="w"> </span>ProdProp<span class="w"> </span><span class="kr">using</span><span class="w"> </span><span class="o">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">private</span><span class="w"> </span>_≟_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>ProdProp.≡-dec<span class="w"> </span><span class="o">(</span>FinProp._≟_<span class="w"> </span><span class="o">{</span>Graph.size<span class="w"> </span>g<span class="o">})</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>FinProp._≟_<span class="w"> </span><span class="o">{</span>Graph.size<span class="w"> </span>g<span class="o">})</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Next, if we can compare edges for equality, we can check if an edge is in a list. Agda provides a built-in function for this:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Graphs.agda" data-first-line="154" data-last-line="154" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Graphs.agda#L154-L154">Graphs.agda</a>, line 154</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">154 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="kr">open</span><span class="w"> </span><span class="kr">import</span><span class="w"> </span><span class="n">Data.List.Membership.DecPropositional</span><span class="w"> </span><span class="o">(</span>_≟_<span class="o">)</span><span class="w"> </span><span class="kr">using</span><span class="w"> </span><span class="o">(</span>_∈?_<span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>To find the predecessors of a particular node, we go through all other nodes in the graph and see if there&rsquo;s an edge there between those nodes and the current one. This is preferable to simply iterating over the edges because we may have duplicates in that list (why not?).</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Graphs.agda" data-first-line="165" data-last-line="166" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Graphs.agda#L165-L166">Graphs.agda</a>, lines 165 through 166</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">165 </span><span class="lnt">166 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">predecessors</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="o">(</span>Graph.Index<span class="w"> </span>g<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>Graph.Index<span class="w"> </span>g<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>predecessors<span class="w"> </span>idx<span class="w"> </span><span class="ow">=</span><span class="w"> </span>List.filter<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>idx&#39;<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>idx&#39;<span class="w"> </span>,<span class="w"> </span>idx<span class="o">)</span><span class="w"> </span>∈?<span class="w"> </span><span class="o">(</span>Graph.edges<span class="w"> </span>g<span class="o">))</span><span class="w"> </span>indices</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Above, <code>indices</code> is a list of all the node identifiers in the graph. Since the graph has <code>size</code> nodes, the indices of all these nodes are simply the values <code>0</code>, <code>1</code>, &hellip;, <code>size - 1</code>. I defined a special function <code>finValues</code> to compute this list, together with a proof that this list is unique.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Graphs.agda" data-first-line="127" data-last-line="143" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Graphs.agda#L127-L143">Graphs.agda</a>, lines 127 through 143</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">127 </span><span class="lnt">128 </span><span class="lnt">129 </span><span class="lnt">130 </span><span class="lnt">131 </span><span class="lnt">132 </span><span class="lnt">133 </span><span class="lnt">134 </span><span class="lnt">135 </span><span class="lnt">136 </span><span class="lnt">137 </span><span class="lnt">138 </span><span class="lnt">139 </span><span class="lnt">140 </span><span class="lnt">141 </span><span class="lnt">142 </span><span class="lnt">143 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="kr">private</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">z≢sf</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>n<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="o">}</span><span class="w"> </span><span class="o">(</span>f<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Fin<span class="w"> </span>n<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>¬<span class="w"> </span><span class="o">(</span>zero<span class="w"> </span>≡<span class="w"> </span>suc<span class="w"> </span>f<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>z≢sf<span class="w"> </span>f<span class="w"> </span><span class="o">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">z≢mapsfs</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>n<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="o">}</span><span class="w"> </span><span class="o">(</span>fs<span class="w"> </span><span class="ow">:</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>Fin<span class="w"> </span>n<span class="o">))</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>All<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>sf<span class="w"> </span><span class="ow">→</span><span class="w"> </span>¬<span class="w"> </span>zero<span class="w"> </span>≡<span class="w"> </span>sf<span class="o">)</span><span class="w"> </span><span class="o">(</span>List.map<span class="w"> </span>suc<span class="w"> </span>fs<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>z≢mapsfs<span class="w"> </span>[]<span class="w"> </span><span class="ow">=</span><span class="w"> </span>[]<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>z≢mapsfs<span class="w"> </span><span class="o">(</span>f<span class="w"> </span>∷<span class="w"> </span>fs&#39;<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>z≢sf<span class="w"> </span>f<span class="w"> </span>∷<span class="w"> </span>z≢mapsfs<span class="w"> </span>fs&#39;<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">finValues</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>n<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Σ<span class="w"> </span><span class="o">(</span>List<span class="w"> </span><span class="o">(</span>Fin<span class="w"> </span>n<span class="o">))</span><span class="w"> </span>Unique<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>finValues<span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>[]<span class="w"> </span>,<span class="w"> </span>Utils.empty<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>finValues<span class="w"> </span><span class="o">(</span>suc<span class="w"> </span>n&#39;<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">let</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>inds&#39;<span class="w"> </span>,<span class="w"> </span>unids&#39;<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>finValues<span class="w"> </span>n&#39;<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">in</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span><span class="w"> </span>zero<span class="w"> </span>∷<span class="w"> </span>List.map<span class="w"> </span>suc<span class="w"> </span>inds&#39;<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>,<span class="w"> </span>push<span class="w"> </span><span class="o">(</span>z≢mapsfs<span class="w"> </span>inds&#39;<span class="o">)</span><span class="w"> </span><span class="o">(</span>Unique-map<span class="w"> </span>suc<span class="w"> </span>suc-injective<span class="w"> </span>unids&#39;<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Another important property of <code>finValues</code> is that each node identifier is present in the list, so that our computation written by traversing the node list do not &ldquo;miss&rdquo; nodes.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Graphs.agda" data-first-line="145" data-last-line="147" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Graphs.agda#L145-L147">Graphs.agda</a>, lines 145 through 147</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">145 </span><span class="lnt">146 </span><span class="lnt">147 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">finValues-complete</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>n<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="o">)</span><span class="w"> </span><span class="o">(</span>f<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Fin<span class="w"> </span>n<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>f<span class="w"> </span>ListMem.∈<span class="w"> </span><span class="o">(</span>proj₁<span class="w"> </span><span class="o">(</span>finValues<span class="w"> </span>n<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>finValues-complete<span class="w"> </span><span class="o">(</span>suc<span class="w"> </span>n&#39;<span class="o">)</span><span class="w"> </span>zero<span class="w"> </span><span class="ow">=</span><span class="w"> </span>RelAny.here<span class="w"> </span>refl<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>finValues-complete<span class="w"> </span><span class="o">(</span>suc<span class="w"> </span>n&#39;<span class="o">)</span><span class="w"> </span><span class="o">(</span>suc<span class="w"> </span>f&#39;<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>RelAny.there<span class="w"> </span><span class="o">(</span>x∈xs⇒fx∈fxs<span class="w"> </span>suc<span class="w"> </span><span class="o">(</span>finValues-complete<span class="w"> </span>n&#39;<span class="w"> </span>f&#39;<span class="o">))</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We can specialize these definitions for a particular graph <code>g</code>:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Graphs.agda" data-first-line="156" data-last-line="163" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Graphs.agda#L156-L163">Graphs.agda</a>, lines 156 through 163</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">156 </span><span class="lnt">157 </span><span class="lnt">158 </span><span class="lnt">159 </span><span class="lnt">160 </span><span class="lnt">161 </span><span class="lnt">162 </span><span class="lnt">163 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">indices</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>Graph.Index<span class="w"> </span>g<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>indices<span class="w"> </span><span class="ow">=</span><span class="w"> </span>proj₁<span class="w"> </span><span class="o">(</span>finValues<span class="w"> </span><span class="o">(</span>Graph.size<span class="w"> </span>g<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">indices-complete</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>idx<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="o">(</span>Graph.Index<span class="w"> </span>g<span class="o">))</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>idx<span class="w"> </span>ListMem.∈<span class="w"> </span>indices<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>indices-complete<span class="w"> </span><span class="ow">=</span><span class="w"> </span>finValues-complete<span class="w"> </span><span class="o">(</span>Graph.size<span class="w"> </span>g<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">indices-Unique</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Unique<span class="w"> </span>indices<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>indices-Unique<span class="w"> </span><span class="ow">=</span><span class="w"> </span>proj₂<span class="w"> </span><span class="o">(</span>finValues<span class="w"> </span><span class="o">(</span>Graph.size<span class="w"> </span>g<span class="o">))</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>To recap, we now have:</p> <ul> <li>A way to build control flow graphs from programs</li> <li>A list (unique&rsquo;d and complete) of all nodes in the control flow graph so that we can iterate over them when the algorithm demands.</li> <li>A &lsquo;predecessors&rsquo; function, which will be used by our static program analyses, implemented as an iteration over the list of nodes.</li> </ul> <p>All that&rsquo;s left is to connect our <code>predecessors</code> function to edges in the graph. The following definitions say that when an edge is in the graph, the starting node is listed as a predecessor of the ending node, and vise versa.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Graphs.agda" data-first-line="168" data-last-line="177" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Graphs.agda#L168-L177">Graphs.agda</a>, lines 168 through 177</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">168 </span><span class="lnt">169 </span><span class="lnt">170 </span><span class="lnt">171 </span><span class="lnt">172 </span><span class="lnt">173 </span><span class="lnt">174 </span><span class="lnt">175 </span><span class="lnt">176 </span><span class="lnt">177 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">edge⇒predecessor</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>idx₁<span class="w"> </span>idx₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Graph.Index<span class="w"> </span>g<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>idx₁<span class="w"> </span>,<span class="w"> </span>idx₂<span class="o">)</span><span class="w"> </span>ListMem.∈<span class="w"> </span><span class="o">(</span>Graph.edges<span class="w"> </span>g<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>idx₁<span class="w"> </span>ListMem.∈<span class="w"> </span><span class="o">(</span>predecessors<span class="w"> </span>idx₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>edge⇒predecessor<span class="w"> </span><span class="o">{</span>idx₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>idx₂<span class="o">}</span><span class="w"> </span>idx₁,idx₂∈es<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>∈-filter⁺<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>idx&#39;<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>idx&#39;<span class="w"> </span>,<span class="w"> </span>idx₂<span class="o">)</span><span class="w"> </span>∈?<span class="w"> </span><span class="o">(</span>Graph.edges<span class="w"> </span>g<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>indices-complete<span class="w"> </span>idx₁<span class="o">)</span><span class="w"> </span>idx₁,idx₂∈es<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">predecessor⇒edge</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>idx₁<span class="w"> </span>idx₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Graph.Index<span class="w"> </span>g<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>idx₁<span class="w"> </span>ListMem.∈<span class="w"> </span><span class="o">(</span>predecessors<span class="w"> </span>idx₂<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>idx₁<span class="w"> </span>,<span class="w"> </span>idx₂<span class="o">)</span><span class="w"> </span>ListMem.∈<span class="w"> </span><span class="o">(</span>Graph.edges<span class="w"> </span>g<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>predecessor⇒edge<span class="w"> </span><span class="o">{</span>idx₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>idx₂<span class="o">}</span><span class="w"> </span>idx₁∈pred<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>proj₂<span class="w"> </span><span class="o">(</span>∈-filter⁻<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>idx&#39;<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>idx&#39;<span class="w"> </span>,<span class="w"> </span>idx₂<span class="o">)</span><span class="w"> </span>∈?<span class="w"> </span><span class="o">(</span>Graph.edges<span class="w"> </span>g<span class="o">))</span><span class="w"> </span><span class="o">{</span>v<span class="w"> </span><span class="ow">=</span><span class="w"> </span>idx₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>xs<span class="w"> </span><span class="ow">=</span><span class="w"> </span>indices<span class="o">}</span><span class="w"> </span>idx₁∈pred<span class="w"> </span><span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#connecting-two-distinct-representations"> <h3 id="connecting-two-distinct-representations">Connecting Two Distinct Representations</h3> </a> <p>I&rsquo;ve described Control Flow Graphs as a compiler-centric representation of the program. Unlike the formal semantics from the previous post, CFGs do not reason about the dynamic behavior of the code. Instead, they capture the possible paths that execution can take through the instructions. In that sense, they are more of an approximation of what the program will do. This is good: because of <a href="https://en.wikipedia.org/wiki/Rice%27s_theorem"class="external-link">Rice&rsquo;s theorem<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, we can&rsquo;t do anything other than approximating without running the program.</p> <p>However, an incorrect approximation is of no use at all. Since the CFGs we build will be the core data type used by our program analyses, it&rsquo;s important that they are an accurate, if incomplete, representation. Specifically, because most of our analyses reason about possible outcomes &mdash; we report what sign each variable <strong>could</strong> have, for instance &mdash; it&rsquo;s important that we don&rsquo;t accidentally omit cases that can happen in practice from our CFGs. Formally, this means that for each possible execution of a program according to its semantics, <span class="sidenote"> <label class="sidenote-label" for="converse-note">there exists a corresponding path through the graph.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="converse-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> The converse is desirable too: that the graph has only paths that correspond to possible executions of the program. One graph that violates this property is the strongly-connected graph of all basic blocks in a program. Analyzing such a graph would give us an overly-conservative estimation; since anything can happen, most of our answers will likely be too general to be of any use. If, on the other hand, only the necessary graph connections exist, we can be more precise.<br> <br> However, proving this converse property (or even stating it precisely) is much harder, because our graphs are somewhat conservative already. There exist programs in which the condition of an <code>if</code>-statement is always evaluated to <code>false</code>, but our graphs always have edges for both the "then" and "else" cases. Determining whether a condition is always false (e.g.) is undecidable thanks to Rice's theorem (again), so we can't rule it out. Instead, we could broaden "all possible executions" to "all possible executions where branching conditions can produce arbitrary results", but this is something else entirely.<br> <br> For the time being, I will leave this converse property aside. As a result, our approximations might be "too careful". However, they will at the very least be sound. <span class="sidenote-delimiter">]</span> </span> </span> </p> <p>In the next post, I will prove that this property holds for the graphs shown here and the formal semantics I defined earlier. I hope to see you there!</p> Implementing and Verifying "Static Program Analysis" in Agda, Part 5: Our Programming Language https://danilafe.com/blog/05_spa_agda_semantics/ Sun, 03 Nov 2024 17:50:27 -0800 https://danilafe.com/blog/05_spa_agda_semantics/ <p>In the previous several posts, I&rsquo;ve formalized the notion of lattices, which are an essential ingredient to formalizing the analyses in Anders Møller&rsquo;s lecture notes. However, there can be no program analysis without a program to analyze! In this post, I will define the (very simple) language that we will be analyzing. An essential aspect of the language is its <a href="https://en.wikipedia.org/wiki/Semantics_%28computer_science%29"class="external-link">semantics<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, which simply speaking explains what each feature of the language does. At the end of the previous article, I gave the following <em>inference rule</em> which defined (partially) how the <code>if</code>-<code>else</code> statement in the language works.</p> $$ \frac{\rho_1, e \Downarrow z \quad \neg (z = 0) \quad \rho_1,s_1 \Downarrow \rho_2} {\rho_1, \textbf{if}\ e\ \textbf{then}\ s_1\ \textbf{else}\ s_2\ \Downarrow\ \rho_2} $$ <p>Like I mentioned then, this rule reads as follows:</p> <blockquote> <p>If the condition of an <code>if</code>-<code>else</code> statement evaluates to a nonzero value, then to evaluate the statement, you evaluate its <code>then</code> branch.</p> </blockquote> <p>Another similar &mdash; but crucially, not equivlalent &ndash; rule is the following:</p> $$ \frac{\rho_1, e \Downarrow z \quad z = 1 \quad \rho_1,s_1 \Downarrow \rho_2} {\rho_1, \textbf{if}\ e\ \textbf{then}\ s_1\ \textbf{else}\ s_2\ \Downarrow\ \rho_2} $$ <p>This time, the English interpretation of the rule is as follows:</p> <blockquote> <p>If the condition of an <code>if</code>-<code>else</code> statement evaluates to one, then to evaluate the statement, you evaluate its <code>then</code> branch.</p> </blockquote> <p>These rules are certainly not equivalent. For instance, the former allows the &ldquo;then&rdquo; branch to be executed when the condition is <code>2</code>; however, in the latter, the value of the conditional must be <code>1</code>. If our analysis were intelligent (our first few will not be), then this difference would change its output when determining the signs of the following program:</p> <pre tabindex="0"><code>x = 2 if x { y = - 1 } else { y = 1 } </code></pre><p>Using the first, more &ldquo;relaxed&rdquo; rule, the condition would be considered &ldquo;true&rdquo;, and the sign of <code>y</code> would be <code>-</code>. On the other hand, using the second, &ldquo;stricter&rdquo; rule, the sign of <code>y</code> would be <code>+</code>. I stress that in this case, I am showing a flow-sensitive analysis (one that can understand control flow and make more specific predictions); for our simplest analyses, we will not be aiming for flow-sensitivity. There is plenty of work to do even then.</p> <p>The point of showing these two distinct rules is that we need to be very precise about how the language will behave, because our analyses depend on that behavior.</p> <p>Let&rsquo;s not get ahead of ourselves, though. I&rsquo;ve motivated the need for semantics, but there is much groundwork to be laid before we delve into the precise rules of our language. After all, to define the language&rsquo;s semantics, we need to have a language.</p> <a href="#the-syntax-of-our-simple-language"> <h3 id="the-syntax-of-our-simple-language">The Syntax of Our Simple Language</h3> </a> <p>I&rsquo;ve shown a couple of examples our our language now, and there won&rsquo;t be that much more to it. We can start with <em>expressions</em>: things that evaluate to something. Some examples of expressions are <code>1</code>, <code>x</code>, and <code>2-(x+y)</code>. For our specific language, the precise set of possible expressions can be given by the following <a href="https://en.wikipedia.org/wiki/Context-free_grammar"class="external-link">Context-Free Grammar<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>:</p> $$ \begin{array}{rcll} e &amp;amp; ::= &amp;amp; x &amp;amp; \text{(variables)} \\ &amp;amp; | &amp;amp; z &amp;amp; \text{(integer literals)} \\ &amp;amp; | &amp;amp; e &#43; e &amp;amp; \text{(addition)} \\ &amp;amp; | &amp;amp; e - e &amp;amp; \text{(subtraction)} \end{array} $$ <p>The above can be read as follows:</p> <blockquote> <p>An expression \(e\) is one of the following things:</p> <ol> <li>Some variable \(x\) [importantly \(x\) is a placeholder for <em>any</em> variable, which could be <code>x</code> or <code>y</code> in our program code; specifically, \(x\) is a <a href="https://en.wikipedia.org/wiki/Metavariable"class="external-link"><em>metavariable</em><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>.]</li> <li>Some integer \(z\) [once again, \(z\) can be any integer, like 1, -42, etc.].</li> <li>The addition of two other expressions [which could themselves be additions etc.].</li> <li>The subtraction of two other expressions [which could also themselves be additions, subtractions, etc.].</li> </ol> </blockquote> <p>Since expressions can be nested within other expressions &mdash; which is necessary to allow complicated code like <code>2-(x+y)</code> above &mdash; they form a tree. Each node is one of the elements of the grammar above (variable, addition, etc.). If a node contains sub-expressions (like addition and subtraction do), then these sub-expressions form sub-trees of the given node. This data structure is called an <a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree"class="external-link">Abstract Syntax Tree<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>.</p> <p>Notably, though <code>2-(x+y)</code> has parentheses, our grammar above does not include include them as a case. The reason for this is that the structure of an abstract syntax tree is sufficient to encode the order in which the operations should be evaluated. Since I lack a nice way of drawing ASTs, I will use an ASCII drawing to show an example.</p> <pre tabindex="0"><code>Expression: 2 - (x+y) (-) / \ 2 (+) / \ x y Expression: (2-x) + y (+) / \ (-) y / \ 2 x </code></pre><p>Above, in the first AST, <code>(+)</code> is a child of the <code>(-)</code> node, which means that it&rsquo;s a sub-expression. As a result, that subexpression is evaluated first, before evaluating <code>(-)</code>, and so, the AST expresents <code>2-(x+y)</code>. In the other example, <code>(-)</code> is a child of <code>(+)</code>, and is therefore evaluated first. The resulting association encoded by that AST is <code>(2-x)+y</code>.</p> <p>To an Agda programmer, the one-of-four-things definition above should read quite similarly to the definition of an algebraic data type. Indeed, this is how we can encode the abstract syntax tree of expressions:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Base.agda" data-first-line="12" data-last-line="16" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Base.agda#L12-L16">Base.agda</a>, lines 12 through 16</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="kr">data</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">_+_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Expr<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">_-_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Expr<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">`_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>String<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Expr<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">#_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Expr</span></span></code></pre></td></tr></table> </div> </div> </div> <p>The only departure from the grammar above is that I had to invent constructors for the variable and integer cases, since Agda doesn&rsquo;t support implicit coercions. This adds a little bit of extra overhead, requiring, for example, that we write numbers as <code># 42</code> instead of <code>42</code>.</p> <p>Having defined expressions, the next thing on the menu is <em>statements</em>. Unlike expressions, which just produce values, statements &ldquo;do something&rdquo;; an example of a statement might be the following Python line:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Python" data-lang="Python"><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Hello, world!&#34;</span><span class="p">)</span> </span></span></code></pre></div><p>The <code>print</code> function doesn&rsquo;t produce any value, but it does perform an action; it prints its argument to the console!</p> <p id="introduce-simple-statements">For the formalization, it turns out to be convenient to separate &ldquo;simple&rdquo; statements from &ldquo;complex&rdquo; ones. Pragmatically speaking, the difference is that between the &ldquo;simple&rdquo; and the &ldquo;complex&rdquo; is control flow; simple statements will be guaranteed to always execute without any decisions or jumps. The reason for this will become clearer in subsequent posts; I will foreshadow a bit by saying that consecutive simple statements can be placed into a single <a href="https://en.wikipedia.org/wiki/Basic_block"class="external-link">basic block<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>.</p> <p>The following is a group of three simple statements:</p> <pre tabindex="0"><code>x = 1 y = x + 2 noop </code></pre><p>These will always be executed in the same order, exactly once. Here, <code>noop</code> is a convenient type of statement that simply does nothing.</p> <p>On the other hand, the following statement is not simple:</p> <pre tabindex="0"><code>while x { x = x - 1 } </code></pre><p>It&rsquo;s not simple because it makes decisions about how the code should be executed; if <code>x</code> is nonzero, it will try executing the statement in the body of the loop (<code>x = x - 1</code>). Otherwise, it would skip evaluating that statement, and carry on with subsequent code.</p> <p>I first define simple statements using the <code>BasicStmt</code> type:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Base.agda" data-first-line="18" data-last-line="20" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Base.agda#L18-L20">Base.agda</a>, lines 18 through 20</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="kr">data</span><span class="w"> </span>BasicStmt<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">_←_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>String<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">→</span><span class="w"> </span>BasicStmt<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">noop</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>BasicStmt</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Complex statements are just called <code>Stmt</code>; they include loops, conditionals and sequences &mdash; <span class="sidenote"> <label class="sidenote-label" for="then-note">\(s_1\ \text{then}\ s_2\)</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="then-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> The standard notation for sequencing in imperative languages is \(s_1; s_2\). However, Agda gives special meaning to the semicolon, and I couldn't find any passable symbolic alternatives. <span class="sidenote-delimiter">]</span> </span> </span> is a sequence where \(s_2\) is evaluated after \(s_1\). Complex statements subsume simple statements, which I model using the constructor <code>⟨_⟩</code>.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Base.agda" data-first-line="25" data-last-line="29" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Base.agda#L25-L29">Base.agda</a>, lines 25 through 29</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="kr">data</span><span class="w"> </span>Stmt<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⟨_⟩</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>BasicStmt<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Stmt<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">_then_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Stmt<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Stmt<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Stmt<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">if_then_else_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Stmt<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Stmt<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Stmt<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">while_repeat_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Stmt<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Stmt</span></span></code></pre></td></tr></table> </div> </div> </div> <p>For an example of using this encoding, take the following simple program:</p> <pre tabindex="0"><code>var = 1 if var { x = 1 } </code></pre><p>The Agda version is:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Main.agda" data-first-line="27" data-last-line="34" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Main.agda#L27-L34">Main.agda</a>, lines 27 through 34</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">testCodeCond₂</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Stmt<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>testCodeCond₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⟨<span class="w"> </span><span class="s">&#34;var&#34;</span><span class="w"> </span>←<span class="w"> </span><span class="o">(</span>#<span class="w"> </span><span class="mi">1</span><span class="o">)</span><span class="w"> </span>⟩<span class="w"> </span>then<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>if<span class="w"> </span><span class="o">(</span>`<span class="w"> </span><span class="s">&#34;var&#34;</span><span class="o">)</span><span class="w"> </span>then<span class="w"> </span><span class="o">(</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⟨<span class="w"> </span><span class="s">&#34;x&#34;</span><span class="w"> </span>←<span class="w"> </span><span class="o">(</span>#<span class="w"> </span><span class="mi">1</span><span class="o">)</span><span class="w"> </span>⟩<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">)</span><span class="w"> </span>else<span class="w"> </span><span class="o">(</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⟨<span class="w"> </span>noop<span class="w"> </span>⟩<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Notice how we used <code>noop</code> to express the fact that the <code>else</code> branch of the conditional does nothing.</p> <a href="#the-semantics-of-our-language"> <h3 id="the-semantics-of-our-language">The Semantics of Our Language</h3> </a> <p>We now have all the language constructs that I&rsquo;ll be showing off &mdash; because those are all the concepts that I&rsquo;ve formalized. What&rsquo;s left is to define how they behave. We will do this using a logical tool called <a href="https://en.wikipedia.org/wiki/Rule_of_inference"class="external-link"><em>inference rules</em><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. I&rsquo;ve written about them a number of times; they&rsquo;re ubiquitous, particularly in the sorts of things I like explore on this site. The <a href="https://danilafe.com/blog/01_aoc_coq/#inference-rules">section on inference rules</a> from my Advent of Code series is pretty relevant, and <a href="https://danilafe.com/blog/03_compiler_typechecking/#some-notation">the notation section from a post in my compiler series</a> says much the same thing; I won&rsquo;t be re-describing them here.</p> <p>There are three pieces which demand semantics: expressions, simple statements, and non-simple statements. The semantics of each of the three requires the semantics of the items that precede it. We will therefore start with expressions.</p> <a href="#expressions"> <h4 id="expressions">Expressions</h4> </a> <p>The trickiest thing about expression is that the value of an expression depends on the &ldquo;context&rdquo;: <code>x+1</code> can evaluate to <code>43</code> if <code>x</code> is <code>42</code>, or it can evaluate to <code>0</code> if <code>x</code> is <code>-1</code>. To evaluate an expression, we will therefore need to assign values to all of the variables in that expression. A mapping that assigns values to variables is typically called an <em>environment</em>. We will write \(\varnothing\) for &ldquo;empty environment&rdquo;, and \(\{\texttt{x} \mapsto 42, \texttt{y} \mapsto -1 \}\) for an environment that maps the variable \(\texttt{x}\) to 42, and the variable \(\texttt{y}\) to -1.</p> <p id="notation-for-environments">Now, a bit of notation. We will use the letter \(\rho\) to represent environments (and if several environments are involved, we will occasionally number them as \(\rho_1\), \(\rho_2\), etc.) We will use the letter \(e\) to stand for expressions, and the letter \(v\) to stand for values. Finally, we&rsquo;ll write \(\rho, e \Downarrow v\) to say that &ldquo;in an environment \(\rho\), expression \(e\) evaluates to value \(v\)&rdquo;. Our two previous examples of evaluating <code>x+1</code> can thus be written as follows:</p> $$ \{ \texttt{x} \mapsto 42 \}, \texttt{x}&#43;1 \Downarrow 43 \\ \{ \texttt{x} \mapsto -1 \}, \texttt{x}&#43;1 \Downarrow 0 \\ $$ <p>Now, on to the actual rules for how to evaluate expressions. Most simply, integer literals like <code>1</code> just evaluate to themselves.</p> $$ \frac{n \in \text{Int}}{\rho, n \Downarrow n} $$ <p>Note that the letter \(\rho\) is completely unused in the above rule. That&rsquo;s because no matter what values <em>variables</em> have, a number still evaluates to the same value. As we&rsquo;ve already established, the same is not true for a variable like \(\texttt{x}\). To evaluate such a variable, we need to retrieve the value it&rsquo;s mapped to in the current environment, which we will write as \(\rho(\texttt{x})\). This gives the following inference rule:</p> $$ \frac{\rho(x) = v}{\rho, x \Downarrow v} $$ <p>All that&rsquo;s left is to define addition and subtraction. For an expression in the form \(e_1+e_2\), we first need to evaluate the two subexpressions \(e_1\) and \(e_2\), and then add the two resulting numbers. As a result, the addition rule includes two additional premises, one for evaluating each summand.</p> $$ \frac {\rho, e_1 \Downarrow v_1 \quad \rho, e_2 \Downarrow v_2 \quad v_1 &#43; v_2 = v} {\rho, e_1&#43;e_2 \Downarrow v} $$ <p>The subtraction rule is similar. Below, I&rsquo;ve configured an instance of <a href="https://danilafe.com/blog/bergamot/">Bergamot</a> to interpret these exact rules. Try typing various expressions like <code>1</code>, <code>1+1</code>, etc. into the input box below to see them evaluate. If you click the &ldquo;Full Proof Tree&rdquo; button, you can also view the exact rules that were used in computing a particular value. The variables <code>x</code>, <code>y</code>, and <code>z</code> are pre-defined for your convenience.</p> <div id="expr-widget"></div> <script> window.addEventListener('load', function() { window.Bergamot.run(null, 'expr-widget', { "Expression": { "custom": "Expression" }, } , 'eval(extend(extend(extend(empty, x, 17), y, 42), z, 0), TERM, ?v)', '\nhidden section \u0022\u0022 {\n Eq @ eq(?x, ?x) \u003c-;\n}\nsection \u0022\u0022 {\n EvalNum @ eval(?rho, lit(?n), ?n) \u003c- int(?n);\n EvalVar @ eval(?rho, var(?x), ?v) \u003c- inenv(?x, ?v, ?rho);\n}\nsection \u0022\u0022 {\n EvalPlus @ eval(?rho, plus(?e_1, ?e_2), ?v) \u003c- eval(?rho, ?e_1, ?v_1), eval(?rho, ?e_2, ?v_2), add(?v_1, ?v_2, ?v);\n EvalMinus @ eval(?rho, minus(?e_1, ?e_2), ?v) \u003c- eval(?rho, ?e_1, ?v_1), eval(?rho, ?e_2, ?v_2), subtract(?v_1, ?v_2, ?v);\n}\nhidden section \u0022\u0022 {\n EnvTake @ inenv(?x, ?v, extend(?rho, ?x, ?v)) \u003c-;\n EnvSkip @ inenv(?x, ?v_1, extend(?rho, ?y, ?v_2)) \u003c- inenv(?x, ?v_1, ?rho), not(eq(?x, ?y));\n}\n', 'default', ''); }); </script> <p>The Agda equivalent of this looks very similar to the rules themselves. I use <code>⇒ᵉ</code> instead of \(\Downarrow\), and there&rsquo;s a little bit of tedium with wrapping integers into a new <code>Value</code> type. I also used a (partial) relation <code>(x, v) ∈ ρ</code> instead of explicitly defining accessing an environment, since it is conceivable for a user to attempt accessing a variable that has not been assigned to. Aside from these notational changes, the structure of each of the constructors of the evaluation data type matches the inference rules I showed above.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Semantics.agda" data-first-line="27" data-last-line="35" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Semantics.agda#L27-L35">Semantics.agda</a>, lines 27 through 35</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="kr">data</span><span class="w"> </span>_,_⇒ᵉ_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Value<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⇒ᵉ-ℕ</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>ρ<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="o">)</span><span class="w"> </span><span class="o">(</span>n<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>ρ<span class="w"> </span>,<span class="w"> </span><span class="o">(</span>#<span class="w"> </span>n<span class="o">)</span><span class="w"> </span>⇒ᵉ<span class="w"> </span><span class="o">(</span>↑ᶻ<span class="w"> </span><span class="o">(</span>+<span class="w"> </span>n<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⇒ᵉ-Var</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>ρ<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="o">)</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span><span class="ow">:</span><span class="w"> </span>String<span class="o">)</span><span class="w"> </span><span class="o">(</span>v<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Value<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span>,<span class="w"> </span>v<span class="o">)</span><span class="w"> </span>∈<span class="w"> </span>ρ<span class="w"> </span><span class="ow">→</span><span class="w"> </span>ρ<span class="w"> </span>,<span class="w"> </span><span class="o">(</span>`<span class="w"> </span>x<span class="o">)</span><span class="w"> </span>⇒ᵉ<span class="w"> </span>v<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⇒ᵉ-+</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>ρ<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="o">)</span><span class="w"> </span><span class="o">(</span>e₁<span class="w"> </span>e₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="o">)</span><span class="w"> </span><span class="o">(</span>z₁<span class="w"> </span>z₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℤ<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>ρ<span class="w"> </span>,<span class="w"> </span>e₁<span class="w"> </span>⇒ᵉ<span class="w"> </span><span class="o">(</span>↑ᶻ<span class="w"> </span>z₁<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>ρ<span class="w"> </span>,<span class="w"> </span>e₂<span class="w"> </span>⇒ᵉ<span class="w"> </span><span class="o">(</span>↑ᶻ<span class="w"> </span>z₂<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>ρ<span class="w"> </span>,<span class="w"> </span><span class="o">(</span>e₁<span class="w"> </span>+<span class="w"> </span>e₂<span class="o">)</span><span class="w"> </span>⇒ᵉ<span class="w"> </span><span class="o">(</span>↑ᶻ<span class="w"> </span><span class="o">(</span>z₁<span class="w"> </span>+ᶻ<span class="w"> </span>z₂<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⇒ᵉ--</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>ρ<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="o">)</span><span class="w"> </span><span class="o">(</span>e₁<span class="w"> </span>e₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="o">)</span><span class="w"> </span><span class="o">(</span>z₁<span class="w"> </span>z₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℤ<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>ρ<span class="w"> </span>,<span class="w"> </span>e₁<span class="w"> </span>⇒ᵉ<span class="w"> </span><span class="o">(</span>↑ᶻ<span class="w"> </span>z₁<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>ρ<span class="w"> </span>,<span class="w"> </span>e₂<span class="w"> </span>⇒ᵉ<span class="w"> </span><span class="o">(</span>↑ᶻ<span class="w"> </span>z₂<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>ρ<span class="w"> </span>,<span class="w"> </span><span class="o">(</span>e₁<span class="w"> </span>-<span class="w"> </span>e₂<span class="o">)</span><span class="w"> </span>⇒ᵉ<span class="w"> </span><span class="o">(</span>↑ᶻ<span class="w"> </span><span class="o">(</span>z₁<span class="w"> </span>-ᶻ<span class="w"> </span>z₂<span class="o">))</span></span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#simple-statements"> <h4 id="simple-statements">Simple Statements</h4> </a> <p>The main difference between formalizing (simple and &ldquo;normal&rdquo;) statements is that they modify the environment. If <code>x</code> has one value, writing <code>x = x + 1</code> will certainly change that value. On the other hand, statements don&rsquo;t produce values. So, we will be writing claims like \(\rho_1 , \textit{bs} \Rightarrow \rho_2\) to say that the basic statement \(\textit{bs}\), when starting in environment \(\rho_1\), will produce environment \(\rho_2\). Here&rsquo;s an example:</p> $$ \{ \texttt{x} \mapsto 42, \texttt{y} \mapsto 17 \}, \ \texttt{x = x - \text{1}} \Rightarrow \{ \texttt{x} \mapsto 41, \texttt{y} \mapsto 17 \} $$ <p>Here, we subtracted one from a variable with value <code>42</code>, leaving it with a new value of <code>41</code>.</p> <p>There are two basic statements, and one of them quite literally does nothing. The inference rule for <code>noop</code> is very simple:</p> $$ \rho,\ \texttt{noop} \Rightarrow \rho $$ <p>For the assignment rule, we need to know how to evaluate the expression on the right side of the equal sign. This is why we needed to define the semantics of expressions first. Given those, the evaluation rule for assignment is as follows, with \(\rho[x \mapsto v]\) meaning &ldquo;the environment \(\rho\) but mapping the variable \(x\) to value \(v\)&rdquo;.</p> $$ \frac {\rho, e \Downarrow v} {\rho, x = e \Rightarrow \rho[x \mapsto v]} $$ <p>Those are actually all the rules we need, and below, I am once again configuring a Bergamot instance, this time with simple statements. Try out <code>noop</code> or some sort of variable assignment, like <code>x = x + 1</code>.</p> <div id="basic-stmt-widget"></div> <script> window.addEventListener('load', function() { window.Bergamot.run(null, 'basic-stmt-widget', { "Basic Statement": { "custom": "Basic Statement" }, } , 'stepbasic(extend(extend(extend(empty, x, 17), y, 42), z, 0), TERM, ?env)', '\nhidden section \u0022\u0022 {\n Eq @ eq(?x, ?x) \u003c-;\n}\nhidden section \u0022\u0022 {\n EvalNum @ eval(?rho, lit(?n), ?n) \u003c- int(?n);\n EvalVar @ eval(?rho, var(?x), ?v) \u003c- inenv(?x, ?v, ?rho);\n}\nhidden section \u0022\u0022 {\n EvalPlus @ eval(?rho, plus(?e_1, ?e_2), ?v) \u003c- eval(?rho, ?e_1, ?v_1), eval(?rho, ?e_2, ?v_2), add(?v_1, ?v_2, ?v);\n EvalMinus @ eval(?rho, minus(?e_1, ?e_2), ?v) \u003c- eval(?rho, ?e_1, ?v_1), eval(?rho, ?e_2, ?v_2), subtract(?v_1, ?v_2, ?v);\n}\nsection \u0022\u0022 {\n StepNoop @ stepbasic(?rho, noop, ?rho) \u003c-;\n StepAssign @ stepbasic(?rho, assign(?x, ?e), extend(?rho, ?x, ?v)) \u003c- eval(?rho, ?e, ?v);\n}\nhidden section \u0022\u0022 {\n EnvTake @ inenv(?x, ?v, extend(?rho, ?x, ?v)) \u003c-;\n EnvSkip @ inenv(?x, ?v_1, extend(?rho, ?y, ?v_2)) \u003c- inenv(?x, ?v_1, ?rho), not(eq(?x, ?y));\n}\n', 'default', ''); }); </script> <p>The Agda implementation is once again just a data type with constructors-for-rules. This time they also look quite similar to the rules I&rsquo;ve shown up until now, though I continue to explicitly quantify over variables like <code>ρ</code>.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Semantics.agda" data-first-line="37" data-last-line="40" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Semantics.agda#L37-L40">Semantics.agda</a>, lines 37 through 40</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="kr">data</span><span class="w"> </span>_,_⇒ᵇ_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="w"> </span><span class="ow">→</span><span class="w"> </span>BasicStmt<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Env<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⇒ᵇ-noop</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>ρ<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>ρ<span class="w"> </span>,<span class="w"> </span>noop<span class="w"> </span>⇒ᵇ<span class="w"> </span>ρ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⇒ᵇ-←</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>ρ<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="o">)</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span><span class="ow">:</span><span class="w"> </span>String<span class="o">)</span><span class="w"> </span><span class="o">(</span>e<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="o">)</span><span class="w"> </span><span class="o">(</span>v<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Value<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>ρ<span class="w"> </span>,<span class="w"> </span>e<span class="w"> </span>⇒ᵉ<span class="w"> </span>v<span class="w"> </span><span class="ow">→</span><span class="w"> </span>ρ<span class="w"> </span>,<span class="w"> </span><span class="o">(</span>x<span class="w"> </span>←<span class="w"> </span>e<span class="o">)</span><span class="w"> </span>⇒ᵇ<span class="w"> </span><span class="o">((</span>x<span class="w"> </span>,<span class="w"> </span>v<span class="o">)</span><span class="w"> </span>List.∷<span class="w"> </span>ρ<span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#statements"> <h4 id="statements">Statements</h4> </a> <p>Let&rsquo;s work on non-simple statements next. The easiest rule to define is probably sequencing. When we use <code>then</code> (or <code>;</code>) to combine two statements, what we actually want is to execute the first statement, which may change variables, and then execute the second statement while keeping the changes from the first. This means there are three environments: \(\rho_1\) for the initial state before either statement is executed, \(\rho_2\) for the state between executing the first and second statement, and \(\rho_3\) for the final state after both are done executing. This leads to the following rule:</p> $$ \frac { \rho_1, s_1 \Rightarrow \rho_2 \quad \rho_2, s_2 \Rightarrow \rho_3 } { \rho_1, s_1; s_2 \Rightarrow \rho_3 } $$ <p>We will actually need two rules to evaluate the conditional statement: one for when the condition evaluates to &ldquo;true&rdquo;, and one for when the condition evaluates to &ldquo;false&rdquo;. Only, I never specified booleans as being part of the language, which means that we will need to come up what &ldquo;false&rdquo; and &ldquo;true&rdquo; are. I will take my cue from C++ and use zero as &ldquo;false&rdquo;, and any other number as &ldquo;true&rdquo;.</p> <p>If the condition of an <code>if</code>-<code>else</code> statement is &ldquo;true&rdquo; (nonzero), then the effect of executing the <code>if</code>-<code>else</code> should be the same as executing the &ldquo;then&rdquo; part of the statement, while completely ignoring the &ldquo;else&rdquo; part.</p> $$ \frac { \rho_1 , e \Downarrow v \quad v \neq 0 \quad \rho_1, s_1 \Rightarrow \rho_2} { \rho_1, \textbf{if}\ e\ \{ s_1 \}\ \textbf{else}\ \{ s_2 \}\ \Rightarrow \rho_2 } $$ <p>Notice that in the above rule, we used the evaluation judgement \(\rho_1, e \Downarrow v\) to evaluate the <em>expression</em> that serves as the condition. We then had an additional premise that requires the truthiness of the resulting value \(v\). The rule for evaluating a conditional with a &ldquo;false&rdquo; branch is very similar.</p> $$ \frac { \rho_1 , e \Downarrow v \quad v = 0 \quad \rho_1, s_2 \Rightarrow \rho_2} { \rho_1, \textbf{if}\ e\ \{ s_1 \}\ \textbf{else}\ \{ s_2 \}\ \Rightarrow \rho_2 } $$ <p>Now that we have rules for conditional statements, it will be surprisingly easy to define the rules for <code>while</code> loops. A <code>while</code> loop will also have two rules, one for when its condition is truthy and one for when it&rsquo;s falsey. However, unlike the &ldquo;false&rdquo; case, a while loop will do nothing, leaving the environment unchanged:</p> $$ \frac { \rho_1 , e \Downarrow v \quad v = 0 } { \rho_1 , \textbf{while}\ e\ \{ s \}\ \Rightarrow \rho_1 } $$ <p>The trickiest rule is for when the condition of a <code>while</code> loop is true. We evaluate the body once, starting in environment \(\rho_1\) and finishing in \(\rho_2\), but then we&rsquo;re not done. In fact, we have to go back to the top, and check the condition again, starting over. As a result, we include another premise, that tells us that evaluating the loop starting at \(\rho_2\), we eventually end in state \(\rho_3\). This encodes the &ldquo;rest of the iterations&rdquo; in addition to the one we just performed. The environment \(\rho_3\) is our final state, so that&rsquo;s what we use in the rule&rsquo;s conclusion.</p> $$ \frac { \rho_1 , e \Downarrow v \quad v \neq 0 \quad \rho_1 , s \Rightarrow \rho_2 \quad \rho_2 , \textbf{while}\ e\ \{ s \}\ \Rightarrow \rho_3 } { \rho_1 , \textbf{while}\ e\ \{ s \}\ \Rightarrow \rho_3 } $$ <p>And that&rsquo;s it! We have now seen every rule that defines the little object language I&rsquo;ve been using for my Agda work. Below is a Bergamot widget that implements these rules. Try the following program, which computes the <code>x</code>th power of two, and stores it in <code>y</code>:</p> <pre tabindex="0"><code>x = 5; y = 1; while (x) { y = y + y; x = x - 1 } </code></pre><div id="stmt-widget"></div> <script> window.addEventListener('load', function() { window.Bergamot.run(null, 'stmt-widget', { "Statement": { "custom": "Statement" }, } , 'step(extend(extend(extend(empty, x, 17), y, 42), z, 0), TERM, ?env)', '\nhidden section \u0022\u0022 {\n Eq @ eq(?x, ?x) \u003c-;\n}\nhidden section \u0022\u0022 {\n EvalNum @ eval(?rho, lit(?n), ?n) \u003c- int(?n);\n EvalVar @ eval(?rho, var(?x), ?v) \u003c- inenv(?x, ?v, ?rho);\n}\nhidden section \u0022\u0022 {\n EvalPlus @ eval(?rho, plus(?e_1, ?e_2), ?v) \u003c- eval(?rho, ?e_1, ?v_1), eval(?rho, ?e_2, ?v_2), add(?v_1, ?v_2, ?v);\n EvalMinus @ eval(?rho, minus(?e_1, ?e_2), ?v) \u003c- eval(?rho, ?e_1, ?v_1), eval(?rho, ?e_2, ?v_2), subtract(?v_1, ?v_2, ?v);\n}\nhidden section \u0022\u0022 {\n StepNoop @ stepbasic(?rho, noop, ?rho) \u003c-;\n StepAssign @ stepbasic(?rho, assign(?x, ?e), extend(?rho, ?x, ?v)) \u003c- eval(?rho, ?e, ?v);\n}\nhidden section \u0022\u0022 {\n StepNoop @ stepbasic(?rho, noop, ?rho) \u003c-;\n StepAssign @ stepbasic(?rho, assign(?x, ?e), extend(?rho, ?x, ?v)) \u003c- eval(?rho, ?e, ?v);\n}\nhidden section \u0022\u0022 {\n StepLiftBasic @ step(?rho_1, ?s, ?rho_2) \u003c- stepbasic(?rho_1, ?s, ?rho_2);\n}\nsection \u0022\u0022 {\n StepIfTrue @ step(?rho_1, if(?e, ?s_1, ?s_2), ?rho_2) \u003c- eval(?rho_1, ?e, ?v), not(eq(?v, 0)), step(?rho_1, ?s_1, ?rho_2);\n StepIfFalse @ step(?rho_1, if(?e, ?s_1, ?s_2), ?rho_2) \u003c- eval(?rho_1, ?e, ?v), eq(?v, 0), step(?rho_1, ?s_2, ?rho_2);\n StepWhileTrue @ step(?rho_1, while(?e, ?s), ?rho_3) \u003c- eval(?rho_1, ?e, ?v), not(eq(?v, 0)), step(?rho_1, ?s, ?rho_2), step(?rho_2, while(?e, ?s), ?rho_3);\n StepWhileFalse @ step(?rho_1, while(?e, ?s), ?rho_1) \u003c- eval(?rho_1, ?e, ?v), eq(?v, 0);\n StepSeq @ step(?rho_1, seq(?s_1, ?s_2), ?rho_3) \u003c- step(?rho_1, ?s_1, ?rho_2), step(?rho_2, ?s_2, ?rho_3);\n}\nhidden section \u0022\u0022 {\n EnvTake @ inenv(?x, ?v, extend(?rho, ?x, ?v)) \u003c-;\n EnvSkip @ inenv(?x, ?v_1, extend(?rho, ?y, ?v_2)) \u003c- inenv(?x, ?v_1, ?rho), not(eq(?x, ?y));\n}\n', 'default', ''); }); </script> <p>As with all the other rules we&rsquo;ve seen, the mathematical notation above can be directly translated into Agda:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Semantics.agda" data-first-line="47" data-last-line="64" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Semantics.agda#L47-L64">Semantics.agda</a>, lines 47 through 64</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="kr">data</span><span class="w"> </span>_,_⇒ˢ_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Stmt<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Env<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⇒ˢ-⟨⟩</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="o">)</span><span class="w"> </span><span class="o">(</span>bs<span class="w"> </span><span class="ow">:</span><span class="w"> </span>BasicStmt<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>ρ₁<span class="w"> </span>,<span class="w"> </span>bs<span class="w"> </span>⇒ᵇ<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>ρ₁<span class="w"> </span>,<span class="w"> </span>⟨<span class="w"> </span>bs<span class="w"> </span>⟩<span class="w"> </span>⇒ˢ<span class="w"> </span>ρ₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⇒ˢ-then</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span>ρ₃<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="o">)</span><span class="w"> </span><span class="o">(</span>s₁<span class="w"> </span>s₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Stmt<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>ρ₁<span class="w"> </span>,<span class="w"> </span>s₁<span class="w"> </span>⇒ˢ<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>ρ₂<span class="w"> </span>,<span class="w"> </span>s₂<span class="w"> </span>⇒ˢ<span class="w"> </span>ρ₃<span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>ρ₁<span class="w"> </span>,<span class="w"> </span><span class="o">(</span>s₁<span class="w"> </span>then<span class="w"> </span>s₂<span class="o">)</span><span class="w"> </span>⇒ˢ<span class="w"> </span>ρ₃<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⇒ˢ-if-true</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="o">)</span><span class="w"> </span><span class="o">(</span>e<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="o">)</span><span class="w"> </span><span class="o">(</span>z<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℤ<span class="o">)</span><span class="w"> </span><span class="o">(</span>s₁<span class="w"> </span>s₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Stmt<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>ρ₁<span class="w"> </span>,<span class="w"> </span>e<span class="w"> </span>⇒ᵉ<span class="w"> </span><span class="o">(</span>↑ᶻ<span class="w"> </span>z<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>¬<span class="w"> </span>z<span class="w"> </span>≡<span class="w"> </span><span class="o">(</span>+<span class="w"> </span><span class="mi">0</span><span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>ρ₁<span class="w"> </span>,<span class="w"> </span>s₁<span class="w"> </span>⇒ˢ<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>ρ₁<span class="w"> </span>,<span class="w"> </span><span class="o">(</span>if<span class="w"> </span>e<span class="w"> </span>then<span class="w"> </span>s₁<span class="w"> </span>else<span class="w"> </span>s₂<span class="o">)</span><span class="w"> </span>⇒ˢ<span class="w"> </span>ρ₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⇒ˢ-if-false</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="o">)</span><span class="w"> </span><span class="o">(</span>e<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="o">)</span><span class="w"> </span><span class="o">(</span>s₁<span class="w"> </span>s₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Stmt<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>ρ₁<span class="w"> </span>,<span class="w"> </span>e<span class="w"> </span>⇒ᵉ<span class="w"> </span><span class="o">(</span>↑ᶻ<span class="w"> </span><span class="o">(</span>+<span class="w"> </span><span class="mi">0</span><span class="o">))</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>ρ₁<span class="w"> </span>,<span class="w"> </span>s₂<span class="w"> </span>⇒ˢ<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>ρ₁<span class="w"> </span>,<span class="w"> </span><span class="o">(</span>if<span class="w"> </span>e<span class="w"> </span>then<span class="w"> </span>s₁<span class="w"> </span>else<span class="w"> </span>s₂<span class="o">)</span><span class="w"> </span>⇒ˢ<span class="w"> </span>ρ₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⇒ˢ-while-true</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>ρ₁<span class="w"> </span>ρ₂<span class="w"> </span>ρ₃<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="o">)</span><span class="w"> </span><span class="o">(</span>e<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="o">)</span><span class="w"> </span><span class="o">(</span>z<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℤ<span class="o">)</span><span class="w"> </span><span class="o">(</span>s<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Stmt<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>ρ₁<span class="w"> </span>,<span class="w"> </span>e<span class="w"> </span>⇒ᵉ<span class="w"> </span><span class="o">(</span>↑ᶻ<span class="w"> </span>z<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>¬<span class="w"> </span>z<span class="w"> </span>≡<span class="w"> </span><span class="o">(</span>+<span class="w"> </span><span class="mi">0</span><span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>ρ₁<span class="w"> </span>,<span class="w"> </span>s<span class="w"> </span>⇒ˢ<span class="w"> </span>ρ₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>ρ₂<span class="w"> </span>,<span class="w"> </span><span class="o">(</span>while<span class="w"> </span>e<span class="w"> </span>repeat<span class="w"> </span>s<span class="o">)</span><span class="w"> </span>⇒ˢ<span class="w"> </span>ρ₃<span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>ρ₁<span class="w"> </span>,<span class="w"> </span><span class="o">(</span>while<span class="w"> </span>e<span class="w"> </span>repeat<span class="w"> </span>s<span class="o">)</span><span class="w"> </span>⇒ˢ<span class="w"> </span>ρ₃<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⇒ˢ-while-false</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>ρ<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Env<span class="o">)</span><span class="w"> </span><span class="o">(</span>e<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="o">)</span><span class="w"> </span><span class="o">(</span>s<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Stmt<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>ρ<span class="w"> </span>,<span class="w"> </span>e<span class="w"> </span>⇒ᵉ<span class="w"> </span><span class="o">(</span>↑ᶻ<span class="w"> </span><span class="o">(</span>+<span class="w"> </span><span class="mi">0</span><span class="o">))</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>ρ<span class="w"> </span>,<span class="w"> </span><span class="o">(</span>while<span class="w"> </span>e<span class="w"> </span>repeat<span class="w"> </span>s<span class="o">)</span><span class="w"> </span>⇒ˢ<span class="w"> </span>ρ</span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#semantics-as-ground-truth"> <h3 id="semantics-as-ground-truth">Semantics as Ground Truth</h3> </a> <p>Prior to this post, we had been talking about using lattices and monotone functions for program analysis. The key problem with using this framework to define analyses is that there are many monotone functions that produce complete nonsese; their output is, at best, unrelated to the program they&rsquo;re supposed to analyze. We don&rsquo;t want to write such functions, since having incorrect information about the programs in question is unhelpful.</p> <p>What does it mean for a function to produce correct information, though? In the context of sign analysis, it would mean that if we say a variable <code>x</code> is <code>+</code>, then evaluating the program will leave us in a state in which <code>x</code> is posive. The semantics we defined in this post give us the &ldquo;evaluating the program piece&rdquo;. They establish what the programs <em>actually</em> do, and we can use this ground truth when checking that our analyses are correct. In subsequent posts, I will prove the exact property I informally stated above: <strong>for the program analyses we define, things they &ldquo;claim&rdquo; about our program will match what actually happens when executing the program using our semantics</strong>.</p> <p>A piece of the puzzle still remains: how are we going to use the monotone functions we&rsquo;ve been talking so much about? We need to figure out what to feed to our analyses before we can prove their correctness.</p> <p>I have an answer to that question: we will be using <em>control flow graphs</em> (CFGs). These are another program representation, one that&rsquo;s more commonly found in compilers. I will show what they look like in the next post. I hope to see you there!</p> Implementing and Verifying "Static Program Analysis" in Agda, Part 4: The Fixed-Point Algorithm https://danilafe.com/blog/04_spa_agda_fixedpoint/ Sun, 03 Nov 2024 17:50:26 -0800 https://danilafe.com/blog/04_spa_agda_fixedpoint/ <p>In the preivous post we looked at lattices of finite height, which are a crucial ingredient to our static analyses. In this post, I will describe the specific algorithm that makes use of these lattices; this algorithm will be at the core of this series.</p> <p>Lattice-based static analyses tend to operate by iteratively combining facts from the program into new ones. For instance, when analyzing <code>y = 1 + 2</code>, we take the (trivial) facts that the numbers one and two are positive, and combine them into the knowledge that <code>y</code> is positive as well. If another line of code reads <code>x = y + 1</code>, we then apply our new knowledge of <code>y</code> to determine the sign of <code>x</code>, too. Combining facs in this manner gives us more information, which we can then continue to apply to learn more about the program.</p> <p>A static program analyzer, however, is a very practical thing. Although in mathemaitics we may allow ourselves to delve into infinite algorithms, we have no such luxury while trying to, say, compile some code. As a result, after a certain point, we need to stop our iterative (re)combination of facts. In an ideal world, that point would be when we know we have found out everything we could about the program. A corollary to that would be that this point must be guaranteed to eventually occur, lest we keep looking for it indenfinitely.</p> <p>The fixed-point algorithm does this for us. If we describe our analysis as a monotonic function over a finite-height lattice, this algorithm gives us a surefire way to find out facts about our program that constitute &ldquo;complete&rdquo; information that can&rsquo;t be re-inspected to find out more. The algorithm is guaranteed to terminate, which means that we will not get stuck in an infinite loop.</p> <a href="#the-algorithm"> <h3 id="the-algorithm">The Algorithm</h3> </a> <p>Take a lattice \(L\) and a monotonic function \(f\). We&rsquo;ve <a href="https://danilafe.com/blog/01_spa_agda_lattices/#define-monotonicity">talked about monotonicity before</a>, but it&rsquo;s easy to re-state. Specifically, a function is monotonic if the following rule holds true:</p> $$ \textbf{if}\ a \le b\ \textbf{then}\ f(a) \le f(b) $$ <p>Recall that the less-than relation on lattices in our case <a href="https://danilafe.com/blog/01_spa_agda_lattices/#specificity">encodes specificity</a>. In particular, if elements of our lattice describe our program, than smaller elements should provide more precise descriptions (where &ldquo;<code>x</code> is potive&rdquo; is more precise than &ldquo;<code>x</code> has any sign&rdquo;, for example). Viewed through this lens, monotonicity means that more specific inputs produce more specific outputs. That seems reasonable.</p> <p id="start-least">Now, let&rsquo;s start with the least element of our lattice, denoted \(\bot\). A lattice of finite height is guaranteed to have such an element. If it didn&rsquo;t, we could always extend chains by tacking on a smaller element to their bottom, and then the lattice wouldn&rsquo;t have a finite height anymore.</p> <p>Now, apply \(f\) to \(\bot\) to get \(f(\bot)\). Since \(\bot\) is the least element, it must be true that \(\bot \le f(\bot)\). Now, if it&rsquo;s &ldquo;less than or equal&rdquo;, is it &ldquo;less than&rdquo;, or is &ldquo;equal&rdquo;)? If it&rsquo;s the latter, we have \(\bot = f(\bot)\). This means we&rsquo;ve found a fixed point: given our input \(\bot\) our analysis \(f\) produced no new information, and we&rsquo;re done. Otherwise, we are not done, but we know that \(\bot < f(\bot)\), which will be helpful shortly.</p> <p>Continuing the &ldquo;less than&rdquo; case, we can apply \(f\) again, this time to \(f(\bot)\). This gives us \(f(f(\bot))\). Since \(f\) is monotonic and \(\bot \le f(\bot)\), we know also that \(f(\bot) \le f(f(\bot))\). Again, ask &ldquo;which is it?&rdquo;, and as before, if \(f(\bot) = f(f(\bot))\), we have found a fixed point. Otherwise, we know that \(f(\bot) < f(f(\bot))\).</p> <p>We can keep doing this. Notice that with each step, we are either done (having found a fixed point) or we have a new inequality in our hands. We can arrange the ones we&rsquo;ve seen so far into a chain:</p> $$ \bot &amp;lt; f(\bot) &amp;lt; f(f(\bot)) &amp;lt; ... $$ <p>Each time we fail to find a fixed point, we add one element to our chain, growing it. But if our lattice \(L\) has a finite height, that means eventually this process will have to stop; the chain can&rsquo;t grow forever. Eventually, we will have to find a value such that \(v = f(v)\). Thus, our algorithm is guaranteed to terminate, and give a fixed point.</p> <p>I implemented the iterative process of applying \(f\) using a recursive function. Agda has a termination checker, to which the logic above &mdash; which proves that iteration will eventually finish &mdash; is not at all obvious. The trick to getting it to work was to use a notion of &ldquo;gas&rdquo;: an always-decreasing value that serves as one of the functions&rsquo; arguments. Since the value is always decreasing in size, the termination checker is satisfied.</p> <p>This works by observing that we already have a rough idea of the maximum number of times our function will recurse; that would be the height of the lattice. After that, we would be building an impossibly long chain. So, we&rsquo;ll give the function a &ldquo;budget&rdquo; of that many iterations, plus one more. Since the chain increases once each time the budget shrinks (indicating recursion), running out of our &ldquo;gas&rdquo; will mean that we built an impossibly long chain &mdash; it will provably never happen.</p> <p>In all, the recursive function is as follows:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Fixedpoint.agda" data-first-line="53" data-last-line="64" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Fixedpoint.agda#L53-L64">Fixedpoint.agda</a>, lines 53 through 64</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">doStep</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>g<span class="w"> </span>hᶜ<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="o">)</span><span class="w"> </span><span class="o">(</span>a₁<span class="w"> </span>a₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="o">(</span>c<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ChainA.Chain<span class="w"> </span>a₁<span class="w"> </span>a₂<span class="w"> </span>hᶜ<span class="o">)</span><span class="w"> </span><span class="o">(</span>g+hᶜ≡h<span class="w"> </span><span class="ow">:</span><span class="w"> </span>g<span class="w"> </span>+<span class="w"> </span>hᶜ<span class="w"> </span>≡<span class="w"> </span>suc<span class="w"> </span>h<span class="o">)</span><span class="w"> </span><span class="o">(</span>a₂≼fa₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>a₂<span class="w"> </span>≼<span class="w"> </span>f<span class="w"> </span>a₂<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Σ<span class="w"> </span>A<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>a<span class="w"> </span><span class="ow">→</span><span class="w"> </span>a<span class="w"> </span>≈<span class="w"> </span>f<span class="w"> </span>a<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>doStep<span class="w"> </span><span class="mi">0</span><span class="w"> </span>hᶜ<span class="w"> </span>a₁<span class="w"> </span>a₂<span class="w"> </span>c<span class="w"> </span>g+hᶜ≡sh<span class="w"> </span>a₂≼fa₂<span class="w"> </span><span class="kr">rewrite</span><span class="w"> </span>g+hᶜ≡sh<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥-elim<span class="w"> </span><span class="o">(</span>ChainA.Bounded-suc-n<span class="w"> </span>boundedᴬ<span class="w"> </span>c<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>doStep<span class="w"> </span><span class="o">(</span>suc<span class="w"> </span>g&#39;<span class="o">)</span><span class="w"> </span>hᶜ<span class="w"> </span>a₁<span class="w"> </span>a₂<span class="w"> </span>c<span class="w"> </span>g+hᶜ≡sh<span class="w"> </span>a₂≼fa₂<span class="w"> </span><span class="kr">rewrite</span><span class="w"> </span>sym<span class="w"> </span><span class="o">(</span>+-suc<span class="w"> </span>g&#39;<span class="w"> </span>hᶜ<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">with</span><span class="w"> </span>≈-dec<span class="w"> </span>a₂<span class="w"> </span><span class="o">(</span>f<span class="w"> </span>a₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>yes<span class="w"> </span>a₂≈fa₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>a₂<span class="w"> </span>,<span class="w"> </span>a₂≈fa₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>no<span class="w"> </span>a₂̷≈fa₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>doStep<span class="w"> </span>g&#39;<span class="w"> </span><span class="o">(</span>suc<span class="w"> </span>hᶜ<span class="o">)</span><span class="w"> </span>a₁<span class="w"> </span><span class="o">(</span>f<span class="w"> </span>a₂<span class="o">)</span><span class="w"> </span>c&#39;<span class="w"> </span>g+hᶜ≡sh<span class="w"> </span><span class="o">(</span>Monotonicᶠ<span class="w"> </span>a₂≼fa₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">a₂≺fa₂</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>a₂<span class="w"> </span>≺<span class="w"> </span>f<span class="w"> </span>a₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>a₂≺fa₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>a₂≼fa₂<span class="w"> </span>,<span class="w"> </span>a₂̷≈fa₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">c&#39;</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>ChainA.Chain<span class="w"> </span>a₁<span class="w"> </span><span class="o">(</span>f<span class="w"> </span>a₂<span class="o">)</span><span class="w"> </span><span class="o">(</span>suc<span class="w"> </span>hᶜ<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>c&#39;<span class="w"> </span><span class="kr">rewrite</span><span class="w"> </span>+-comm<span class="w"> </span><span class="mi">1</span><span class="w"> </span>hᶜ<span class="w"> </span><span class="ow">=</span><span class="w"> </span>ChainA.concat<span class="w"> </span>c<span class="w"> </span><span class="o">(</span>ChainA.step<span class="w"> </span>a₂≺fa₂<span class="w"> </span>≈-refl<span class="w"> </span><span class="o">(</span>ChainA.done<span class="w"> </span><span class="o">(</span>≈-refl<span class="w"> </span><span class="o">{</span>f<span class="w"> </span>a₂<span class="o">})))</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The first case handles running out of gas, arguing by bottom-elimination (contradiction). The second case follows the algorithm I&rsquo;ve described pretty closely; it applies \(f\) to an existing value, checks if the result is equal (equivalent) to the original, and if it isn&rsquo;t, it grows the existing chain of elements and invokes the step function rescurisvely with the grown chain and less gas.</p> <p>The recursive function implements a single &ldquo;step&rdquo; of the process (applying <code>f</code>, comparing for equality, returning the fixed point if one was found). All that&rsquo;s left is to kick off the process using \(\bot\). This is what <code>fix</code> does:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Fixedpoint.agda" data-first-line="66" data-last-line="67" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Fixedpoint.agda#L66-L67">Fixedpoint.agda</a>, lines 66 through 67</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">66 </span><span class="lnt">67 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">fix</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Σ<span class="w"> </span>A<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>a<span class="w"> </span><span class="ow">→</span><span class="w"> </span>a<span class="w"> </span>≈<span class="w"> </span>f<span class="w"> </span>a<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>fix<span class="w"> </span><span class="ow">=</span><span class="w"> </span>doStep<span class="w"> </span><span class="o">(</span>suc<span class="w"> </span>h<span class="o">)</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span>⊥ᴬ<span class="w"> </span>⊥ᴬ<span class="w"> </span><span class="o">(</span>ChainA.done<span class="w"> </span>≈-refl<span class="o">)</span><span class="w"> </span><span class="o">(</span>+-comm<span class="w"> </span><span class="o">(</span>suc<span class="w"> </span>h<span class="o">)</span><span class="w"> </span><span class="mi">0</span><span class="o">)</span><span class="w"> </span><span class="o">(</span>⊥ᴬ≼<span class="w"> </span><span class="o">(</span>f<span class="w"> </span>⊥ᴬ<span class="o">))</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This functions is responsible for providing gas to <code>doStep</code>; as I mentioned above, it provides just a bit more gas than the maximum-length chain, which means that if the gas is exhausted, we&rsquo;ve certainly arrived at a contradiction. It also provides an initial chain onto which <code>doStep</code> will keep tacking on new inequalities as it finds them. Since we haven&rsquo;t found any yet, this is the single-element chain of \(\bot\). The last thing is does is set up the recursion invariant (that the sum of the gas and the chain length is constant), and provides a proof that \(\bot \le f(\bot)\). This function always returns a fixed point.</p> <a href="#least-fixed-point"> <h3 id="least-fixed-point">Least Fixed Point</h3> </a> <p>Functions can have many fixed points. Take the identity function that simply returns its argument unchanged; this function has a fixed point for every element in its domain, since, for example, \(\text{id}(1) = 1\), \(\text{id}(2) = 2\), etc. The fixed point found by our algorithm above is somewhat special among the possible fixed points of \(f\): it is the <em>least fixed point</em> of the function. Call our fixed point \(a\); if there&rsquo;s another point \(b\) such that \(b=f(b)\), then the fixed point we found must be less than or equal to \(b\) (that is, \(a \le b\)). This is important given our interpretation of &ldquo;less than&rdquo; as &ldquo;more specific&rdquo;: the fixedpoint algorithm produces the most specific possible information about our program given the rules of our analysis.</p> <p>The proof is simple; suppose that it took \(k\) iterations of calling \(f\) to arrive at our fixed point. This gives us:</p> $$ a = \underbrace{f(f(...(f(}_{k\ \text{times}}\bot)))) = f^k(\bot) $$ <p>Now, take our other fixed point \(b\). Since \(\bot\) is the least element of the lattice, we have \(\bot \le b\).</p> $$ \begin{array}{ccccccccr} &amp;amp; &amp;amp; \bot &amp;amp; \le &amp;amp; &amp;amp; &amp;amp; b &amp;amp; \quad \implies &amp;amp; \text{(monotonicity of}\ f \text{)}\\ &amp;amp; &amp;amp; f(\bot) &amp;amp; \le &amp;amp; f(b) &amp;amp; = &amp;amp; b &amp;amp; \quad \implies &amp;amp; \text{(} b\ \text{is a fixed point, monotonicity of}\ f \text{)}\\ &amp;amp; &amp;amp; f^2(\bot) &amp;amp; \le &amp;amp; f(b) &amp;amp; = &amp;amp; b &amp;amp; \quad \implies &amp;amp; \text{(} b\ \text{is a fixed point, monotonicity of}\ f \text{)}\\ \\ &amp;amp; &amp;amp; \vdots &amp;amp; &amp;amp; \vdots &amp;amp; &amp;amp; \vdots &amp;amp; &amp;amp; \\ \\ a &amp;amp; = &amp;amp; f^k(\bot) &amp;amp; \le &amp;amp; f(b) &amp;amp; = &amp;amp; b &amp;amp; \end{array} $$ <p>Because of the monotonicity of \(f\), each time we apply it, it preserves the less-than relationship that started with \(\bot \le b\). Doing that \(k\) times, we verify that \(a\) is our least fixed point.</p> <p>To convince Agda of this proof, we once again get in an argument with the termination checker, which ends the same way it did last time: with us using the notion of &lsquo;gas&rsquo; to ensure that the repeated application of \(f\) eventually ends. Since we&rsquo;re interested in verifying that <code>doStep</code> producdes the least fixed point, we formulate the proof in terms of <code>doStep</code> applied to various arguments.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Fixedpoint.agda" data-first-line="76" data-last-line="84" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Fixedpoint.agda#L76-L84">Fixedpoint.agda</a>, lines 76 through 84</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span><span class="lnt">79 </span><span class="lnt">80 </span><span class="lnt">81 </span><span class="lnt">82 </span><span class="lnt">83 </span><span class="lnt">84 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">stepPreservesLess</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>g<span class="w"> </span>hᶜ<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="o">)</span><span class="w"> </span><span class="o">(</span>a₁<span class="w"> </span>a₂<span class="w"> </span>b<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="o">(</span>b≈fb<span class="w"> </span><span class="ow">:</span><span class="w"> </span>b<span class="w"> </span>≈<span class="w"> </span>f<span class="w"> </span>b<span class="o">)</span><span class="w"> </span><span class="o">(</span>a₂≼a<span class="w"> </span><span class="ow">:</span><span class="w"> </span>a₂<span class="w"> </span>≼<span class="w"> </span>b<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>c<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ChainA.Chain<span class="w"> </span>a₁<span class="w"> </span>a₂<span class="w"> </span>hᶜ<span class="o">)</span><span class="w"> </span><span class="o">(</span>g+hᶜ≡h<span class="w"> </span><span class="ow">:</span><span class="w"> </span>g<span class="w"> </span>+<span class="w"> </span>hᶜ<span class="w"> </span>≡<span class="w"> </span>suc<span class="w"> </span>h<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>a₂≼fa₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>a₂<span class="w"> </span>≼<span class="w"> </span>f<span class="w"> </span>a₂<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>proj₁<span class="w"> </span><span class="o">(</span>doStep<span class="w"> </span>g<span class="w"> </span>hᶜ<span class="w"> </span>a₁<span class="w"> </span>a₂<span class="w"> </span>c<span class="w"> </span>g+hᶜ≡h<span class="w"> </span>a₂≼fa₂<span class="o">)</span><span class="w"> </span>≼<span class="w"> </span>b<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>stepPreservesLess<span class="w"> </span><span class="mi">0</span><span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span>c<span class="w"> </span>g+hᶜ≡sh<span class="w"> </span>_<span class="w"> </span><span class="kr">rewrite</span><span class="w"> </span>g+hᶜ≡sh<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥-elim<span class="w"> </span><span class="o">(</span>ChainA.Bounded-suc-n<span class="w"> </span>boundedᴬ<span class="w"> </span>c<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>stepPreservesLess<span class="w"> </span><span class="o">(</span>suc<span class="w"> </span>g&#39;<span class="o">)</span><span class="w"> </span>hᶜ<span class="w"> </span>a₁<span class="w"> </span>a₂<span class="w"> </span>b<span class="w"> </span>b≈fb<span class="w"> </span>a₂≼b<span class="w"> </span>c<span class="w"> </span>g+hᶜ≡sh<span class="w"> </span>a₂≼fa₂<span class="w"> </span><span class="kr">rewrite</span><span class="w"> </span>sym<span class="w"> </span><span class="o">(</span>+-suc<span class="w"> </span>g&#39;<span class="w"> </span>hᶜ<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">with</span><span class="w"> </span>≈-dec<span class="w"> </span>a₂<span class="w"> </span><span class="o">(</span>f<span class="w"> </span>a₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>yes<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>a₂≼b<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>no<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>stepPreservesLess<span class="w"> </span>g&#39;<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span>b<span class="w"> </span>b≈fb<span class="w"> </span><span class="o">(</span>≼-cong<span class="w"> </span>≈-refl<span class="w"> </span><span class="o">(</span>≈-sym<span class="w"> </span>b≈fb<span class="o">)</span><span class="w"> </span><span class="o">(</span>Monotonicᶠ<span class="w"> </span>a₂≼b<span class="o">))</span><span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span>_</span></span></code></pre></td></tr></table> </div> </div> </div> <p>As with <code>doStep</code>, this function takes as arguments the amount of gas <code>g</code> and a partially-built chain <code>c</code>, which gets appended to for each failed equality comparison. In addition, however, this function takes another arbitrary fixed point <code>b</code>, which is greater than the current input to <code>doStep</code> (which is a value \(f^i(\bot\)) for some \(i\)). It then proves that when <code>doStep</code> terminates (which will be with a value in the form \(f^k(\bot)\)), this value will still be smaller than <code>b</code>. Since it is a proof about <code>doStep</code>, <code>stepPreservesLess</code> proceeds by the same case analysis as its subject, and has a very similar (albeit simpler) structure. In short, though, it encodes the relatively informal proof I gave above.</p> <p>Just like with <code>doStep</code>, I define a helper function for <code>stepPreservesLess</code> that kicks off its recursive invocations.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Fixedpoint.agda" data-first-line="86" data-last-line="87" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Fixedpoint.agda#L86-L87">Fixedpoint.agda</a>, lines 86 through 87</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">86 </span><span class="lnt">87 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">aᶠ≼</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>a<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>a<span class="w"> </span>≈<span class="w"> </span>f<span class="w"> </span>a<span class="w"> </span><span class="ow">→</span><span class="w"> </span>aᶠ<span class="w"> </span>≼<span class="w"> </span>a<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>aᶠ≼<span class="w"> </span>a<span class="w"> </span>a≈fa<span class="w"> </span><span class="ow">=</span><span class="w"> </span>stepPreservesLess<span class="w"> </span><span class="o">(</span>suc<span class="w"> </span>h<span class="o">)</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span>⊥ᴬ<span class="w"> </span>⊥ᴬ<span class="w"> </span>a<span class="w"> </span>a≈fa<span class="w"> </span><span class="o">(</span>⊥ᴬ≼<span class="w"> </span>a<span class="o">)</span><span class="w"> </span><span class="o">(</span>ChainA.done<span class="w"> </span>≈-refl<span class="o">)</span><span class="w"> </span><span class="o">(</span>+-comm<span class="w"> </span><span class="o">(</span>suc<span class="w"> </span>h<span class="o">)</span><span class="w"> </span><span class="mi">0</span><span class="o">)</span><span class="w"> </span><span class="o">(</span>⊥ᴬ≼<span class="w"> </span><span class="o">(</span>f<span class="w"> </span>⊥ᴬ<span class="o">))</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Above, <code>aᶠ</code> is the output of <code>fix</code>:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Fixedpoint.agda" data-first-line="69" data-last-line="70" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Fixedpoint.agda#L69-L70">Fixedpoint.agda</a>, lines 69 through 70</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">69 </span><span class="lnt">70 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">aᶠ</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>aᶠ<span class="w"> </span><span class="ow">=</span><span class="w"> </span>proj₁<span class="w"> </span>fix</span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#what-is-a-program"> <h3 id="what-is-a-program">What is a Program?</h3> </a> <p>With the fixed point algorithm in hand, we have all the tools we need to define static program analyses:</p> <ol> <li>We&rsquo;ve created a collection of &ldquo;lattice builders&rdquo;, which allow us to combine various lattice building blocks into more complicated structures; these structures are advanced enough to represent the information about our programs.</li> <li>We&rsquo;ve figured out a way (our fixed point algorithm) to repeatedly apply an inference function to our programs and eventually produce results. This algorithm requires some additional properties from our latttices.</li> <li>We&rsquo;ve proven that our lattice builders create lattices with these properties, making it possible to use them to construct functions fit for our fixed point algorithm.</li> </ol> <p>All that&rsquo;s left is to start defining monotonic functions over lattices! Except, what are we analyzing? We&rsquo;ve focused a fair bit on the theory of lattices, but we haven&rsquo;t yet defined even a tiny piece of the language that our programs will be analyzing. We will start with programs like this:</p> <pre tabindex="0"><code>x = 42 y = 1 if y { x = -1; } else { x = -2; } </code></pre><p>We will need to model these programs in Agda by describing them as trees (<a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree"class="external-link">Abstract Syntax Trees<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, to be precise). We will also need to specify how to evaluate these programs (provide the <a href="https://en.wikipedia.org/wiki/Semantics_%28computer_science%29"class="external-link">semantics<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> of our language). We will use big-step (also known as &ldquo;natural&rdquo;) operational semantics to do so; here&rsquo;s an example rule:</p> $$ \frac{\rho_1, e \Downarrow z \quad \neg (z = 0) \quad \rho_1,s_1 \Downarrow \rho_2} {\rho_1, \textbf{if}\ e\ \textbf{then}\ s_1\ \textbf{else}\ s_2\ \Downarrow\ \rho_2} $$ <p>The above reads:</p> <blockquote> <p>If the condition of an <code>if</code>-<code>else</code> statement evaluates to a nonzero value, then to evaluate the statement, you evaluate its <code>then</code> branch.</p> </blockquote> <p>In the next post, we&rsquo;ll talk more about how these rules work, and define the remainder of them to give our language life. See you then!</p> Implementing and Verifying "Static Program Analysis" in Agda, Part 3: Lattices of Finite Height https://danilafe.com/blog/03_spa_agda_fixed_height/ Thu, 08 Aug 2024 17:29:00 -0700 https://danilafe.com/blog/03_spa_agda_fixed_height/ <p>In the previous post, I introduced the class of finite-height lattices: lattices where chains made from elements and the less-than operator <code>&lt;</code> can only be so long. As a first example, <a href="https://danilafe.com/blog/01_spa_agda_lattices/#natural-numbers">natural numbers form a lattice</a>, but they <strong>are not a finite-height lattice</strong>; the following chain can be made infinitely long:</p> $$ 0 &amp;lt; 1 &amp;lt; 2 &amp;lt; ... $$ <p>There isn&rsquo;t a &ldquo;biggest natural number&rdquo;! On the other hand, we&rsquo;ve seen that our <a href="https://danilafe.com/blog/01_spa_agda_lattices/#sign-lattice">sign lattice</a> has a finite height; the longest chain we can make is three elements long; I showed one such chain (there are many chains of three elements) in <a href="https://danilafe.com/blog/02_spa_agda_combining_lattices/#sign-three-elements">the previous post</a>, but here it is again:</p> $$ \bot &amp;lt; &#43; &amp;lt; \top $$ <p>It&rsquo;s also true that the <a href="https://danilafe.com/blog/02_spa_agda_combining_lattices/#the-cartesian-product-lattice">Cartesian product lattice \(L_1 \times L_2\)</a> has a finite height, as long as \(L_1\) and \(L_2\) are themselves finite-height lattices. In the specific case where both \(L_1\) and \(L_2\) are the sign lattice (\(L_1 = L_2 = \text{Sign} \)) we can observe that the longest chains have five elements. The following is one example:</p> <p id="sign-prod-chain">$$ (\bot, \bot) &amp;lt; (\bot, &#43;) &amp;lt; (\bot, \top) &amp;lt; (&#43;, \top) &amp;lt; (\top, \top) $$ </p> <p id="product-both-finite-height">The fact that \(L_1\) and \(L_2\) are themselves finite-height lattices is important; if either one of them is not, we can easily construct an infinite chain of the products. If we allowed \(L_2\) to be natural numbers, we&rsquo;d end up with infinite chains like this one:</p> $$ (\bot, 0) &amp;lt; (\bot, 1) &amp;lt; (\bot, 2) &amp;lt; ... $$ <p>Another lattice that has a finite height under certain conditions is <a href="https://danilafe.com/blog/02_spa_agda_combining_lattices/#the-map-lattice">the map lattice</a>. The &ldquo;under certain conditions&rdquo; part is important; we can easily construct an infinite chain of map lattice elements in general:</p> $$ \{ a : 1 \} &amp;lt; \{ a : 1, b : 1 \} &amp;lt; \{ a: 1, b: 1, c: 1 \} &amp;lt; ... $$ <p id="fin-keys">As long as we have infinite keys to choose from, we can always keep adding new keys to make bigger and bigger maps. But if we fix the keys in the map &mdash; say, we use only <code>a</code> and <code>b</code> &mdash; then suddenly our heights are once again fixed. In fact, for the two keys I just picked, one longest chain is remarkably similar to the product chain above.</p> $$ \{a: \bot, a: \bot\} &amp;lt; \{a: \bot, b: &#43;\} &amp;lt; \{a: \bot, b: \top\} &amp;lt; \{a: &#43;, b: \top\} &amp;lt; \{a: \top, b: \top\} $$ <p>The class of finite-height lattices is important for static program analysis, because it ensures that out our analyses don&rsquo;t take infinite time. Though there&rsquo;s an intuitive connection (&ldquo;finite lattices mean finite execution&rdquo;), the details of why the former is needed for the latter are nuanced. We&rsquo;ll talk about them in a subsequent post.</p> <p>In the meantime, let&rsquo;s dig deeper into the notion of finite height, and the Agda proofs of the properties I&rsquo;ve introduced thus far.</p> <a href="#formalizing-finite-height"> <h3 id="formalizing-finite-height">Formalizing Finite Height</h3> </a> <p>The formalization I settled on is quite similar to the informal description: a lattice has a finite height of length \(h\) if the longest chain of elements compared by \((<)\) is exactly \(h\). There&rsquo;s only a slight complication: we allow for equivalent-but-not-equal elements in lattices. For instance, for a map lattice, we don&rsquo;t care about the order of the keys: so long as two maps relate the same set of keys to the same respective values, we will consider them equal. This, however, is beyond the notion of Agda&rsquo;s propositional equality (<code>_≡_</code>). Thus, we we need to generalize the definition of a chain to support equivalences. I parameterize the <code>Chain</code> module in my code by an equivalence relation, as well as the comparison relation <code>R</code>, which we will set to <code>&lt;</code> for our chains. The equivalence relation <code>_≈_</code> and the ordering relation <code>R</code>/<code>&lt;</code> are expected to play together nicely (if <code>a &lt; b</code>, and <code>a</code> is equivalent to <code>c</code>, then it should be the case that <code>c &lt; b</code>).</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Chain.agda" data-first-line="3" data-last-line="7" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Chain.agda#L3-L7">Chain.agda</a>, lines 3 through 7</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="kr">module</span><span class="w"> </span><span class="n">Chain</span><span class="w"> </span><span class="o">{</span>a<span class="o">}</span><span class="w"> </span><span class="o">{</span>A<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>_≈_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>≈-equiv<span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsEquivalence<span class="w"> </span>A<span class="w"> </span>_≈_<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>_R_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>R-≈-cong<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>a₁<span class="w"> </span>a₁&#39;<span class="w"> </span>a₂<span class="w"> </span>a₂&#39;<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>a₁<span class="w"> </span>≈<span class="w"> </span>a₁&#39;<span class="w"> </span><span class="ow">→</span><span class="w"> </span>a₂<span class="w"> </span>≈<span class="w"> </span>a₂&#39;<span class="w"> </span><span class="ow">→</span><span class="w"> </span>a₁<span class="w"> </span>R<span class="w"> </span>a₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>a₁&#39;<span class="w"> </span>R<span class="w"> </span>a₂&#39;<span class="o">)</span><span class="w"> </span><span class="kr">where</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>From there, the definition of the <code>Chain</code> data type is much like the definition of <a href="https://agda.github.io/agda-stdlib/v2.0/Data.Vec.Base.html#1111"class="external-link">a vector from <code>Data.Vec</code><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, but indexed by the endpoints, and containing witnesses of <code>R</code>/<code>&lt;</code> between its elements. The indexing allows for representing the type of chains between particular lattice elements, and serves to ensure concatenation and other operations don&rsquo;t merge disparate chains.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Chain.agda" data-first-line="19" data-last-line="21" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Chain.agda#L19-L21">Chain.agda</a>, lines 19 through 21</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="kr">data</span><span class="w"> </span>Chain<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>ℕ<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">done</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>a<span class="w"> </span>a&#39;<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>a<span class="w"> </span>≈<span class="w"> </span>a&#39;<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Chain<span class="w"> </span>a<span class="w"> </span>a&#39;<span class="w"> </span><span class="mi">0</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">step</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>a₁<span class="w"> </span>a₂<span class="w"> </span>a₂&#39;<span class="w"> </span>a₃<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">}</span><span class="w"> </span><span class="o">{</span>n<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>a₁<span class="w"> </span>R<span class="w"> </span>a₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>a₂<span class="w"> </span>≈<span class="w"> </span>a₂&#39;<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Chain<span class="w"> </span>a₂&#39;<span class="w"> </span>a₃<span class="w"> </span>n<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Chain<span class="w"> </span>a₁<span class="w"> </span>a₃<span class="w"> </span><span class="o">(</span>suc<span class="w"> </span>n<span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>In the <code>done</code> case, we create a single-element chain, which has no comparisons. In this case, the chain starts and stops at the same element (where &ldquo;the same&rdquo; is modulo our equivalence). The <code>step</code> case prepends a new comparison <code>a1 &lt; a2</code> to an existing chain; once again, we allow for the existing chain to start with a different-but-equivalent element <code>a2'</code>.</p> <p>With that definition in hand, I define what it means for a type of chains between elements of the lattice <code>A</code> to be bounded by a certain height; simply put, all chains must have length less than or equal to the bound.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Chain.agda" data-first-line="38" data-last-line="39" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Chain.agda#L38-L39">Chain.agda</a>, lines 38 through 39</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">38 </span><span class="lnt">39 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">Bounded</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>Bounded<span class="w"> </span>bound<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>a₁<span class="w"> </span>a₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">}</span><span class="w"> </span><span class="o">{</span>n<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Chain<span class="w"> </span>a₁<span class="w"> </span>a₂<span class="w"> </span>n<span class="w"> </span><span class="ow">→</span><span class="w"> </span>n<span class="w"> </span>≤<span class="w"> </span>bound</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Though <code>Bounded</code> specifies <em>a</em> bound on the length of chains, it doesn&rsquo;t specify <em>the</em> (lowest) bound. Specifically, if the chains can only have length three, they are bounded by both 3, 30, and 300. To claim a lowest bound (which would be the maximum length of the lattice), we need to show that a chain of that length actually exists (otherwise, we could take the previous natural number, and it would be a bound as well). Thus, I define the <code>Height</code> predicate to require that a chain of the desired height exists, and that this height bounds the length of all other chains.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Chain.agda" data-first-line="47" data-last-line="53" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Chain.agda#L47-L53">Chain.agda</a>, lines 47 through 53</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="kr">record</span><span class="w"> </span>Height<span class="w"> </span><span class="o">(</span>height<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="o">)</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">field</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⊥</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⊤</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">longestChain</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Chain<span class="w"> </span>⊥<span class="w"> </span>⊤<span class="w"> </span>height<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">bounded</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Bounded<span class="w"> </span>height</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Finally, for a lattice to have a finite height, the type of chains formed by using its less-than operator needs to have that height (satisfy the <code>Height h</code> predicate). To avoid having to thread through the equivalence relation, congruence proof, and more, I define a specialized predicate for lattices specifically. I do so as a &ldquo;method&rdquo; in my <code>IsLattice</code> record.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice.agda" data-first-line="183" data-last-line="210" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice.agda#L183-L210">Lattice.agda</a>, lines 183 through 210</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">183 </span><span class="lnt">184 </span><span class="lnt">185 </span><span class="lnt">186 </span><span class="lnt">187 </span><span class="lnt">188 </span><span class="lnt">189 </span><span class="lnt">190 </span><span class="lnt">191 </span><span class="lnt">192 </span><span class="lnt">193 </span><span class="lnt">194 </span><span class="lnt">195 </span><span class="lnt">196 </span><span class="lnt">197 </span><span class="lnt">198 </span><span class="lnt">199 </span><span class="lnt">200 </span><span class="lnt">201 </span><span class="lnt">202 </span><span class="lnt">203 </span><span class="lnt">204 </span><span class="lnt">205 </span><span class="lnt">206 </span><span class="lnt">207 </span><span class="lnt">208 </span><span class="hl"><span class="lnt">209 </span></span><span class="hl"><span class="lnt">210 </span></span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="kr">record</span><span class="w"> </span>IsLattice<span class="w"> </span><span class="o">{</span>a<span class="o">}</span><span class="w"> </span><span class="o">(</span>A<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>_≈_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>_⊔_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>_⊓_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">field</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">joinSemilattice</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsSemilattice<span class="w"> </span>A<span class="w"> </span>_≈_<span class="w"> </span>_⊔_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">meetSemilattice</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsSemilattice<span class="w"> </span>A<span class="w"> </span>_≈_<span class="w"> </span>_⊓_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">absorb-⊔-⊓</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span>y<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span>⊔<span class="w"> </span><span class="o">(</span>x<span class="w"> </span>⊓<span class="w"> </span>y<span class="o">))</span><span class="w"> </span>≈<span class="w"> </span>x<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">absorb-⊓-⊔</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span>y<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span>⊓<span class="w"> </span><span class="o">(</span>x<span class="w"> </span>⊔<span class="w"> </span>y<span class="o">))</span><span class="w"> </span>≈<span class="w"> </span>x<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">open</span><span class="w"> </span>IsSemilattice<span class="w"> </span>joinSemilattice<span class="w"> </span>public<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">open</span><span class="w"> </span>IsSemilattice<span class="w"> </span>meetSemilattice<span class="w"> </span>public<span class="w"> </span><span class="kr">using</span><span class="w"> </span><span class="o">()</span><span class="w"> </span><span class="kr">renaming</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span><span class="w"> </span>⊔-assoc<span class="w"> </span>to<span class="w"> </span>⊓-assoc<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>⊔-comm<span class="w"> </span>to<span class="w"> </span>⊓-comm<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>⊔-idemp<span class="w"> </span>to<span class="w"> </span>⊓-idemp<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>⊔-Monotonicˡ<span class="w"> </span>to<span class="w"> </span>⊓-Monotonicˡ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>⊔-Monotonicʳ<span class="w"> </span>to<span class="w"> </span>⊓-Monotonicʳ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>≈-⊔-cong<span class="w"> </span>to<span class="w"> </span>≈-⊓-cong<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>_≼_<span class="w"> </span>to<span class="w"> </span>_≽_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>_≺_<span class="w"> </span>to<span class="w"> </span>_≻_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>≼-refl<span class="w"> </span>to<span class="w"> </span>≽-refl<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>≼-trans<span class="w"> </span>to<span class="w"> </span>≽-trans<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span><span class="nf">FixedHeight</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>h<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span>FixedHeight<span class="w"> </span>h<span class="w"> </span><span class="ow">=</span><span class="w"> </span>Chain.Height<span class="w"> </span><span class="o">(</span>_≈_<span class="o">)</span><span class="w"> </span>≈-equiv<span class="w"> </span>_≺_<span class="w"> </span>≺-cong<span class="w"> </span>h</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Thus, bringing the operators and other definitions of <code>IsLattice</code> into scope will also bring in the <code>FixedHeight</code> predicate.</p> <a href="#fixed-height-of-the-above-below-lattice"> <h3 id="fixed-height-of-the-above-below-lattice">Fixed Height of the &ldquo;Above-Below&rdquo; Lattice</h3> </a> <p>We&rsquo;ve already seen intuitive evidence that the sign lattice &mdash; which is an instance of the <a href="https://danilafe.com/blog/01_spa_agda_lattices/#the-above-below-lattice">&ldquo;above-below&rdquo; lattice</a> &mdash; has a fixed height. The reason is simple: we extended a set of incomparable elements with a single element that&rsquo;s greater, and a single element that&rsquo;s lower. We can&rsquo;t make chains out of incomparable elements (since we can&rsquo;t compare them using <code>&lt;</code>); thus, we can only have one <code>&lt;</code> from the new least element, and one <code>&lt;</code> from the new greatest element.</p> <p>The proof is a bit tedious, but not all that complicated. First, a few auxiliary helpers; feel free to read only the type signatures. They specify, respectively:</p> <ol> <li>That the bottom element \(\bot\) of the above-below lattice is less than any concrete value from the underlying set. For instance, in the sign lattice case, \(\bot < +\).</li> <li>That \(\bot\) is the only element satisfying the first property; that is, any value strictly less than an element of the underlying set must be \(\bot\).</li> <li>That the top element \(\top\) of the above-below lattice is greater than any concrete value of the underlying set. This is the dual of the first property.</li> <li>That, much like the bottom element is the only value strictly less than elements of the underlying set, the top element is the only value strictly greater.</li> </ol> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/AboveBelow.agda" data-first-line="315" data-last-line="335" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/AboveBelow.agda#L315-L335">AboveBelow.agda</a>, lines 315 through 335</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">315 </span><span class="lnt">316 </span><span class="lnt">317 </span><span class="lnt">318 </span><span class="lnt">319 </span><span class="lnt">320 </span><span class="lnt">321 </span><span class="lnt">322 </span><span class="lnt">323 </span><span class="lnt">324 </span><span class="lnt">325 </span><span class="lnt">326 </span><span class="lnt">327 </span><span class="lnt">328 </span><span class="lnt">329 </span><span class="lnt">330 </span><span class="lnt">331 </span><span class="lnt">332 </span><span class="lnt">333 </span><span class="lnt">334 </span><span class="lnt">335 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⊥≺[x]</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>⊥<span class="w"> </span>≺<span class="w"> </span>[<span class="w"> </span>x<span class="w"> </span>]<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⊥≺[x]<span class="w"> </span>x<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>≈-refl<span class="w"> </span>,<span class="w"> </span><span class="ow">λ</span><span class="w"> </span><span class="o">())</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">x≺[y]⇒x≡⊥</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span><span class="ow">:</span><span class="w"> </span>AboveBelow<span class="o">)</span><span class="w"> </span><span class="o">(</span>y<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>x<span class="w"> </span>≺<span class="w"> </span>[<span class="w"> </span>y<span class="w"> </span>]<span class="w"> </span><span class="ow">→</span><span class="w"> </span>x<span class="w"> </span>≡<span class="w"> </span>⊥<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>x≺[y]⇒x≡⊥<span class="w"> </span>x<span class="w"> </span>y<span class="w"> </span><span class="o">((</span>x⊔[y]≈[y]<span class="o">)</span><span class="w"> </span>,<span class="w"> </span>x̷≈[y]<span class="o">)</span><span class="w"> </span><span class="kr">with</span><span class="w"> </span>x<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>⊥<span class="w"> </span><span class="ow">=</span><span class="w"> </span>refl<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>⊤<span class="w"> </span><span class="kr">with</span><span class="w"> </span><span class="o">()</span><span class="w"> </span>←<span class="w"> </span>x⊔[y]≈[y]<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>[<span class="w"> </span>b<span class="w"> </span>]<span class="w"> </span><span class="kr">with</span><span class="w"> </span>≈₁-dec<span class="w"> </span>b<span class="w"> </span>y<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>yes<span class="w"> </span>b≈y<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥-elim<span class="w"> </span><span class="o">(</span>x̷≈[y]<span class="w"> </span><span class="o">(</span>≈-lift<span class="w"> </span>b≈y<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>no<span class="w"> </span>_<span class="w"> </span><span class="kr">with</span><span class="w"> </span><span class="o">()</span><span class="w"> </span>←<span class="w"> </span>x⊔[y]≈[y]<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">[x]≺⊤</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>[<span class="w"> </span>x<span class="w"> </span>]<span class="w"> </span>≺<span class="w"> </span>⊤<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>[x]≺⊤<span class="w"> </span>x<span class="w"> </span><span class="kr">rewrite</span><span class="w"> </span>x⊔⊤≡⊤<span class="w"> </span>[<span class="w"> </span>x<span class="w"> </span>]<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>≈-⊤-⊤<span class="w"> </span>,<span class="w"> </span><span class="ow">λ</span><span class="w"> </span><span class="o">())</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">[x]≺y⇒y≡⊤</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="o">(</span>y<span class="w"> </span><span class="ow">:</span><span class="w"> </span>AboveBelow<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>[<span class="w"> </span>x<span class="w"> </span>]<span class="w"> </span>≺<span class="w"> </span>y<span class="w"> </span><span class="ow">→</span><span class="w"> </span>y<span class="w"> </span>≡<span class="w"> </span>⊤<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>[x]≺y⇒y≡⊤<span class="w"> </span>x<span class="w"> </span>y<span class="w"> </span><span class="o">(</span>[x]⊔y≈y<span class="w"> </span>,<span class="w"> </span>[x]̷≈y<span class="o">)</span><span class="w"> </span><span class="kr">with</span><span class="w"> </span>y<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>⊥<span class="w"> </span><span class="kr">with</span><span class="w"> </span><span class="o">()</span><span class="w"> </span>←<span class="w"> </span>[x]⊔y≈y<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>⊤<span class="w"> </span><span class="ow">=</span><span class="w"> </span>refl<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>[<span class="w"> </span>a<span class="w"> </span>]<span class="w"> </span><span class="kr">with</span><span class="w"> </span>≈₁-dec<span class="w"> </span>x<span class="w"> </span>a<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>yes<span class="w"> </span>x≈a<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥-elim<span class="w"> </span><span class="o">(</span>[x]̷≈y<span class="w"> </span><span class="o">(</span>≈-lift<span class="w"> </span>x≈a<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>no<span class="w"> </span>_<span class="w"> </span><span class="kr">with</span><span class="w"> </span><span class="o">()</span><span class="w"> </span>←<span class="w"> </span>[x]⊔y≈y</span></span></code></pre></td></tr></table> </div> </div> </div> <p>From there, we can construct an instance of the longest chain. Actually, there&rsquo;s a bit of a hang-up: what if the underlying set is empty? Concretely, what if there were no signs? Then we could only construct a chain with one comparison: \(\bot < \top\). Instead of adding logic to conditionally specify the length, I simply require that the set is populated by requiring a witness</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/AboveBelow.agda" data-first-line="85" data-last-line="85" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/AboveBelow.agda#L85-L85">AboveBelow.agda</a>, line 85</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">85 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="kr">module</span><span class="w"> </span><span class="n">Plain</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="kr">where</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>I use this witness to construct the two-<code>&lt;</code> chain.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/AboveBelow.agda" data-first-line="339" data-last-line="340" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/AboveBelow.agda#L339-L340">AboveBelow.agda</a>, lines 339 through 340</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">339 </span><span class="lnt">340 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">longestChain</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Chain<span class="w"> </span>⊥<span class="w"> </span>⊤<span class="w"> </span><span class="mi">2</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>longestChain<span class="w"> </span><span class="ow">=</span><span class="w"> </span>step<span class="w"> </span><span class="o">(</span>⊥≺[x]<span class="w"> </span>x<span class="o">)</span><span class="w"> </span>≈-refl<span class="w"> </span><span class="o">(</span>step<span class="w"> </span><span class="o">(</span>[x]≺⊤<span class="w"> </span>x<span class="o">)</span><span class="w"> </span>≈-⊤-⊤<span class="w"> </span><span class="o">(</span>done<span class="w"> </span>≈-⊤-⊤<span class="o">))</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The proof that the length of two &mdash; in terms of comparisons &mdash; is the bound of all chains of <code>AboveBelow</code> elements requires systematically rejecting all longer chains. Informally, suppose you have a chain of three or more comparisons.</p> <ol> <li>If it starts with \(\top\), you can&rsquo;t add any more elements since that&rsquo;s the greatest element (contradiction).</li> <li>If you start with an element of the underlying set, you could add another element, but it has to be the top element; after that, you can&rsquo;t add any more (contradiction).</li> <li>If you start with \(\bot\), you could arrive at a chain of two comparisons, but you can&rsquo;t go beyond that (in three cases, each leading to contradictions).</li> </ol> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/AboveBelow.agda" data-first-line="342" data-last-line="355" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/AboveBelow.agda#L342-L355">AboveBelow.agda</a>, lines 342 through 355</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">342 </span><span class="lnt">343 </span><span class="lnt">344 </span><span class="lnt">345 </span><span class="lnt">346 </span><span class="lnt">347 </span><span class="lnt">348 </span><span class="hl"><span class="lnt">349 </span></span><span class="hl"><span class="lnt">350 </span></span><span class="hl"><span class="lnt">351 </span></span><span class="hl"><span class="lnt">352 </span></span><span class="hl"><span class="lnt">353 </span></span><span class="hl"><span class="lnt">354 </span></span><span class="hl"><span class="lnt">355 </span></span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">¬-Chain-⊤</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>ab<span class="w"> </span><span class="ow">:</span><span class="w"> </span>AboveBelow<span class="o">}</span><span class="w"> </span><span class="o">{</span>n<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>¬<span class="w"> </span>Chain<span class="w"> </span>⊤<span class="w"> </span>ab<span class="w"> </span><span class="o">(</span>suc<span class="w"> </span>n<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>¬-Chain-⊤<span class="w"> </span><span class="o">{</span>x<span class="o">}</span><span class="w"> </span><span class="o">(</span>step<span class="w"> </span><span class="o">(</span>⊤⊔x≈x<span class="w"> </span>,<span class="w"> </span>⊤̷≈x<span class="o">)</span><span class="w"> </span>_<span class="w"> </span>_<span class="o">)</span><span class="w"> </span><span class="kr">rewrite</span><span class="w"> </span>⊤⊔x≡⊤<span class="w"> </span>x<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥-elim<span class="w"> </span><span class="o">(</span>⊤̷≈x<span class="w"> </span>⊤⊔x≈x<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">isLongest</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>ab₁<span class="w"> </span>ab₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>AboveBelow<span class="o">}</span><span class="w"> </span><span class="o">{</span>n<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Chain<span class="w"> </span>ab₁<span class="w"> </span>ab₂<span class="w"> </span>n<span class="w"> </span><span class="ow">→</span><span class="w"> </span>n<span class="w"> </span>≤<span class="w"> </span><span class="mi">2</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>isLongest<span class="w"> </span><span class="o">(</span>done<span class="w"> </span>_<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>z≤n<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>isLongest<span class="w"> </span><span class="o">(</span>step<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span><span class="o">(</span>done<span class="w"> </span>_<span class="o">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>s≤s<span class="w"> </span>z≤n<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>isLongest<span class="w"> </span><span class="o">(</span>step<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span><span class="o">(</span>step<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span><span class="o">(</span>done<span class="w"> </span>_<span class="o">)))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>s≤s<span class="w"> </span><span class="o">(</span>s≤s<span class="w"> </span>z≤n<span class="o">)</span><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span>isLongest<span class="w"> </span><span class="o">{</span>⊤<span class="o">}</span><span class="w"> </span>c@<span class="o">(</span>step<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span>_<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥-elim<span class="w"> </span><span class="o">(</span>¬-Chain-⊤<span class="w"> </span>c<span class="o">)</span><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span>isLongest<span class="w"> </span><span class="o">{</span>[<span class="w"> </span>x<span class="w"> </span>]<span class="o">}</span><span class="w"> </span><span class="o">(</span>step<span class="w"> </span><span class="o">{</span>_<span class="o">}</span><span class="w"> </span><span class="o">{</span>y<span class="o">}</span><span class="w"> </span>[x]≺y<span class="w"> </span>y≈y&#39;<span class="w"> </span>c@<span class="o">(</span>step<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span>_<span class="o">))</span><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span><span class="kr">rewrite</span><span class="w"> </span>[x]≺y⇒y≡⊤<span class="w"> </span>x<span class="w"> </span>y<span class="w"> </span>[x]≺y<span class="w"> </span><span class="kr">with</span><span class="w"> </span>≈-⊤-⊤<span class="w"> </span>←<span class="w"> </span>y≈y&#39;<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥-elim<span class="w"> </span><span class="o">(</span>¬-Chain-⊤<span class="w"> </span>c<span class="o">)</span><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span>isLongest<span class="w"> </span><span class="o">{</span>⊥<span class="o">}</span><span class="w"> </span><span class="o">(</span>step<span class="w"> </span><span class="o">{</span>_<span class="o">}</span><span class="w"> </span><span class="o">{</span>⊥<span class="o">}</span><span class="w"> </span><span class="o">(</span>_<span class="w"> </span>,<span class="w"> </span>⊥̷≈⊥<span class="o">)</span><span class="w"> </span>_<span class="w"> </span>_<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥-elim<span class="w"> </span><span class="o">(</span>⊥̷≈⊥<span class="w"> </span>≈-⊥-⊥<span class="o">)</span><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span>isLongest<span class="w"> </span><span class="o">{</span>⊥<span class="o">}</span><span class="w"> </span><span class="o">(</span>step<span class="w"> </span><span class="o">{</span>_<span class="o">}</span><span class="w"> </span><span class="o">{</span>⊤<span class="o">}</span><span class="w"> </span>_<span class="w"> </span>≈-⊤-⊤<span class="w"> </span>c@<span class="o">(</span>step<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span>_<span class="o">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥-elim<span class="w"> </span><span class="o">(</span>¬-Chain-⊤<span class="w"> </span>c<span class="o">)</span><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span>isLongest<span class="w"> </span><span class="o">{</span>⊥<span class="o">}</span><span class="w"> </span><span class="o">(</span>step<span class="w"> </span><span class="o">{</span>_<span class="o">}</span><span class="w"> </span><span class="o">{</span>[<span class="w"> </span>x<span class="w"> </span>]<span class="o">}</span><span class="w"> </span>_<span class="w"> </span><span class="o">(</span>≈-lift<span class="w"> </span>_<span class="o">)</span><span class="w"> </span><span class="o">(</span>step<span class="w"> </span>[x]≺y<span class="w"> </span>y≈z<span class="w"> </span>c@<span class="o">(</span>step<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span>_<span class="o">)))</span><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span><span class="kr">rewrite</span><span class="w"> </span>[x]≺y⇒y≡⊤<span class="w"> </span>_<span class="w"> </span>_<span class="w"> </span>[x]≺y<span class="w"> </span><span class="kr">with</span><span class="w"> </span>≈-⊤-⊤<span class="w"> </span>←<span class="w"> </span>y≈z<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥-elim<span class="w"> </span><span class="o">(</span>¬-Chain-⊤<span class="w"> </span>c<span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Thus, the above-below lattice has a length of two comparisons (or alternatively, three elements).</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/AboveBelow.agda" data-first-line="357" data-last-line="363" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/AboveBelow.agda#L357-L363">AboveBelow.agda</a>, lines 357 through 363</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">357 </span><span class="lnt">358 </span><span class="lnt">359 </span><span class="lnt">360 </span><span class="lnt">361 </span><span class="lnt">362 </span><span class="lnt">363 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">fixedHeight</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsLattice.FixedHeight<span class="w"> </span>isLattice<span class="w"> </span><span class="mi">2</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>fixedHeight<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">record</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span><span class="w"> </span>⊥<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>⊤<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊤<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>longestChain<span class="w"> </span><span class="ow">=</span><span class="w"> </span>longestChain<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>bounded<span class="w"> </span><span class="ow">=</span><span class="w"> </span>isLongest<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>And that&rsquo;s it.</p> <a href="#fixed-height-of-the-product-lattice"> <h3 id="fixed-height-of-the-product-lattice">Fixed Height of the Product Lattice</h3> </a> <p>Now, for something less tedious. We saw above that for a product lattice to have a finite height, <a href="#product-both-finite-height"class="same-page-link">its constituent lattices must have a finite height</a>. The proof was by contradiction (by constructing an infinitely long product chain given a single infinite lattice). As a result, we&rsquo;ll focus this section on products of two finite lattices <code>A</code> and <code>B</code>. Additionally, for the proofs in this section, I require element equivalence to be decidable.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Prod.agda" data-first-line="115" data-last-line="117" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Prod.agda#L115-L117">Prod.agda</a>, lines 115 through 117</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">115 </span><span class="lnt">116 </span><span class="lnt">117 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="kr">module</span><span class="w"> </span>_ (≈₁-<span class="n">dec</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsDecidable<span class="w"> </span>_≈₁_<span class="o">)</span><span class="w"> </span><span class="o">(</span>≈₂-dec<span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsDecidable<span class="w"> </span>_≈₂_<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>h₁<span class="w"> </span>h₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>fhA<span class="w"> </span><span class="ow">:</span><span class="w"> </span>FixedHeight₁<span class="w"> </span>h₁<span class="o">)</span><span class="w"> </span><span class="o">(</span>fhB<span class="w"> </span><span class="ow">:</span><span class="w"> </span>FixedHeight₂<span class="w"> </span>h₂<span class="o">)</span><span class="w"> </span><span class="kr">where</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Let&rsquo;s think about how we might go about constructing the longest chain in a product lattice. Let&rsquo;s start with some arbitrary element \(p_1 = (a_1, b_1)\). We need to find another value that isn&rsquo;t equal to \(p_1\), because we&rsquo;re building chains of the less-than operator \((<)\), and not the less-than-or-equal operator \((\leq)\). As a result, we need to change either the first component, the second component, or both. If we&rsquo;re building &ldquo;to the right&rdquo; (adding bigger elements), the new components would need to be bigger. Suppose then that we came up with \(a_2\) and \(b_2\), with \(a_1 < a_2\) and \(b_1 < b_2\). We could then create a length-one chain:</p> $$ (a_1, b_1) &amp;lt; (a_2, b_2) $$ <p>That works, but we can construct an even longer chain by increasing only one element at a time:</p> $$ (a_1, b_1) &amp;lt; (a_1, b_2) &amp;lt; (a_2, b_2) $$ <p>We can apply this logic every time; the conclusion is that when building up a chain, we need to increase one element at a time. Then, how many times can we increase an element? Well, if lattice <code>A</code> has a height of two (comparisons), then we can take its lowest element, and increase it twice. Similarly, if lattice <code>B</code> has a height of three, starting at its lowest element, we can increase it three times. In all, when building a chain of <code>A × B</code>, we can increase an element five times. Generally, the number of <code>&lt;</code> in the product chain is the sum of the numbers of <code>&lt;</code> in the chains of <code>A</code> and <code>B</code>.</p> <p>This gives us a recipe for constructing the longest chain in the product lattice: take the longest chains of <code>A</code> and <code>B</code>, and start with the product of their lowest elements. Then, increase the elements one at a time according to the chains. The simplest way to do that might be to increase by all elements of the <code>A</code> chain, and then by all of the elements of the <code>B</code> chain (or the other way around). That&rsquo;s the strategy I took when <a href="#sign-prod-chain"class="same-page-link">constructing the \(\text{Sign} \times \text{Sign}\) chain above</a>.</p> <p>To formalize this notion, a few lemmas. First, given two chains where one starts with the same element another ends with, we can combine them into one long chain.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Chain.agda" data-first-line="31" data-last-line="33" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Chain.agda#L31-L33">Chain.agda</a>, lines 31 through 33</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">concat</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>a₁<span class="w"> </span>a₂<span class="w"> </span>a₃<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">}</span><span class="w"> </span><span class="o">{</span>n₁<span class="w"> </span>n₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Chain<span class="w"> </span>a₁<span class="w"> </span>a₂<span class="w"> </span>n₁<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Chain<span class="w"> </span>a₂<span class="w"> </span>a₃<span class="w"> </span>n₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Chain<span class="w"> </span>a₁<span class="w"> </span>a₃<span class="w"> </span><span class="o">(</span>n₁<span class="w"> </span>+<span class="w"> </span>n₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>concat<span class="w"> </span><span class="o">(</span>done<span class="w"> </span>a₁≈a₂<span class="o">)</span><span class="w"> </span>a₂a₃<span class="w"> </span><span class="ow">=</span><span class="w"> </span>Chain-≈-cong₁<span class="w"> </span><span class="o">(</span>≈-sym<span class="w"> </span>a₁≈a₂<span class="o">)</span><span class="w"> </span>a₂a₃<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>concat<span class="w"> </span><span class="o">(</span>step<span class="w"> </span>a₁Ra<span class="w"> </span>a≈a&#39;<span class="w"> </span>a&#39;a₂<span class="o">)</span><span class="w"> </span>a₂a₃<span class="w"> </span><span class="ow">=</span><span class="w"> </span>step<span class="w"> </span>a₁Ra<span class="w"> </span>a≈a&#39;<span class="w"> </span><span class="o">(</span>concat<span class="w"> </span>a&#39;a₂<span class="w"> </span>a₂a₃<span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>More interestingly, given a chain of comparisons in one lattice, we are able to lift it into a chain in another lattice by applying a function to each element. This function must be monotonic, because it must not be able to reverse \(a < b\) such that \(f(b) < f(a)\). Moreover, this function should be injective, because if \(f(a) = f(b)\), then a chain \(a < b\) might be collapsed into \(f(a) \not< f(a)\), changing its length. Finally, the function needs to produce equivalent outputs when giving equivalent inputs. The result is the following lemma:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice.agda" data-first-line="226" data-last-line="247" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice.agda#L226-L247">Lattice.agda</a>, lines 226 through 247</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">226 </span><span class="lnt">227 </span><span class="lnt">228 </span><span class="lnt">229 </span><span class="lnt">230 </span><span class="lnt">231 </span><span class="lnt">232 </span><span class="lnt">233 </span><span class="lnt">234 </span><span class="lnt">235 </span><span class="lnt">236 </span><span class="lnt">237 </span><span class="lnt">238 </span><span class="lnt">239 </span><span class="lnt">240 </span><span class="lnt">241 </span><span class="lnt">242 </span><span class="lnt">243 </span><span class="lnt">244 </span><span class="lnt">245 </span><span class="lnt">246 </span><span class="lnt">247 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="kr">module</span><span class="w"> </span><span class="n">ChainMapping</span><span class="w"> </span><span class="o">{</span>a<span class="w"> </span>b<span class="o">}</span><span class="w"> </span><span class="o">{</span>A<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">}</span><span class="w"> </span><span class="o">{</span>B<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>b<span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span>_≈₁_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">}</span><span class="w"> </span><span class="o">{</span>_≈₂_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>b<span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span>_⊔₁_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="o">}</span><span class="w"> </span><span class="o">{</span>_⊔₂_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>slA<span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsSemilattice<span class="w"> </span>A<span class="w"> </span>_≈₁_<span class="w"> </span>_⊔₁_<span class="o">)</span><span class="w"> </span><span class="o">(</span>slB<span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsSemilattice<span class="w"> </span>B<span class="w"> </span>_≈₂_<span class="w"> </span>_⊔₂_<span class="o">)</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">open</span><span class="w"> </span>IsSemilattice<span class="w"> </span>slA<span class="w"> </span><span class="kr">renaming</span><span class="w"> </span><span class="o">(</span>_≼_<span class="w"> </span>to<span class="w"> </span>_≼₁_;<span class="w"> </span>_≺_<span class="w"> </span>to<span class="w"> </span>_≺₁_;<span class="w"> </span>≈-equiv<span class="w"> </span>to<span class="w"> </span>≈₁-equiv;<span class="w"> </span>≺-cong<span class="w"> </span>to<span class="w"> </span>≺₁-cong<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">open</span><span class="w"> </span>IsSemilattice<span class="w"> </span>slB<span class="w"> </span><span class="kr">renaming</span><span class="w"> </span><span class="o">(</span>_≼_<span class="w"> </span>to<span class="w"> </span>_≼₂_;<span class="w"> </span>_≺_<span class="w"> </span>to<span class="w"> </span>_≺₂_;<span class="w"> </span>≈-equiv<span class="w"> </span>to<span class="w"> </span>≈₂-equiv;<span class="w"> </span>≺-cong<span class="w"> </span>to<span class="w"> </span>≺₂-cong<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">open</span><span class="w"> </span>Chain<span class="w"> </span>_≈₁_<span class="w"> </span>≈₁-equiv<span class="w"> </span>_≺₁_<span class="w"> </span>≺₁-cong<span class="w"> </span><span class="kr">using</span><span class="w"> </span><span class="o">()</span><span class="w"> </span><span class="kr">renaming</span><span class="w"> </span><span class="o">(</span>Chain<span class="w"> </span>to<span class="w"> </span>Chain₁;<span class="w"> </span>step<span class="w"> </span>to<span class="w"> </span>step₁;<span class="w"> </span>done<span class="w"> </span>to<span class="w"> </span>done₁<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">open</span><span class="w"> </span>Chain<span class="w"> </span>_≈₂_<span class="w"> </span>≈₂-equiv<span class="w"> </span>_≺₂_<span class="w"> </span>≺₂-cong<span class="w"> </span><span class="kr">using</span><span class="w"> </span><span class="o">()</span><span class="w"> </span><span class="kr">renaming</span><span class="w"> </span><span class="o">(</span>Chain<span class="w"> </span>to<span class="w"> </span>Chain₂;<span class="w"> </span>step<span class="w"> </span>to<span class="w"> </span>step₂;<span class="w"> </span>done<span class="w"> </span>to<span class="w"> </span>done₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">Chain-map</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>f<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>Monotonic<span class="w"> </span>_≼₁_<span class="w"> </span>_≼₂_<span class="w"> </span>f<span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>Injective<span class="w"> </span>_≈₁_<span class="w"> </span>_≈₂_<span class="w"> </span>f<span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>f<span class="w"> </span>Preserves<span class="w"> </span>_≈₁_<span class="w"> </span>⟶<span class="w"> </span>_≈₂_<span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>a₁<span class="w"> </span>a₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">}</span><span class="w"> </span><span class="o">{</span>n<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Chain₁<span class="w"> </span>a₁<span class="w"> </span>a₂<span class="w"> </span>n<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Chain₂<span class="w"> </span><span class="o">(</span>f<span class="w"> </span>a₁<span class="o">)</span><span class="w"> </span><span class="o">(</span>f<span class="w"> </span>a₂<span class="o">)</span><span class="w"> </span>n<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>Chain-map<span class="w"> </span>f<span class="w"> </span>Monotonicᶠ<span class="w"> </span>Injectiveᶠ<span class="w"> </span>Preservesᶠ<span class="w"> </span><span class="o">(</span>done₁<span class="w"> </span>a₁≈a₂<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>done₂<span class="w"> </span><span class="o">(</span>Preservesᶠ<span class="w"> </span>a₁≈a₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>Chain-map<span class="w"> </span>f<span class="w"> </span>Monotonicᶠ<span class="w"> </span>Injectiveᶠ<span class="w"> </span>Preservesᶠ<span class="w"> </span><span class="o">(</span>step₁<span class="w"> </span><span class="o">(</span>a₁≼₁a<span class="w"> </span>,<span class="w"> </span>a₁̷≈₁a<span class="o">)</span><span class="w"> </span>a≈₁a&#39;<span class="w"> </span>a&#39;a₂<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">let</span><span class="w"> </span>fa₁≺₂fa<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>Monotonicᶠ<span class="w"> </span>a₁≼₁a<span class="w"> </span>,<span class="w"> </span><span class="ow">λ</span><span class="w"> </span>fa₁≈₂fa<span class="w"> </span><span class="ow">→</span><span class="w"> </span>a₁̷≈₁a<span class="w"> </span><span class="o">(</span>Injectiveᶠ<span class="w"> </span>fa₁≈₂fa<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>fa≈fa&#39;<span class="w"> </span><span class="ow">=</span><span class="w"> </span>Preservesᶠ<span class="w"> </span>a≈₁a&#39;<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">in</span><span class="w"> </span>step₂<span class="w"> </span>fa₁≺₂fa<span class="w"> </span>fa≈fa&#39;<span class="w"> </span><span class="o">(</span>Chain-map<span class="w"> </span>f<span class="w"> </span>Monotonicᶠ<span class="w"> </span>Injectiveᶠ<span class="w"> </span>Preservesᶠ<span class="w"> </span>a&#39;a₂<span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Given this, and two lattices of finite height, we construct the full product chain by lifting the <code>A</code> chain into the product via \(a \mapsto (a, \bot_2)\), lifting the <code>B</code> chain into the product via \(b \mapsto (\top_1, b)\), and concatenating the results. This works because the first chain ends with \((\top_1, \bot_2)\), and the second starts with it.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Prod.agda" data-first-line="169" data-last-line="171" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Prod.agda#L169-L171">Prod.agda</a>, lines 169 through 171</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">169 </span><span class="lnt">170 </span><span class="lnt">171 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>longestChain<span class="w"> </span><span class="ow">=</span><span class="w"> </span>concat<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>ChainMapping₁.Chain-map<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>a<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>a<span class="w"> </span>,<span class="w"> </span>⊥₂<span class="o">))</span><span class="w"> </span><span class="o">(</span>∙,b-Monotonic<span class="w"> </span>_<span class="o">)</span><span class="w"> </span>proj₁<span class="w"> </span><span class="o">(</span>∙,b-Preserves-≈₁<span class="w"> </span>_<span class="o">)</span><span class="w"> </span>longestChain₁<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>ChainMapping₂.Chain-map<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>b<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>⊤₁<span class="w"> </span>,<span class="w"> </span>b<span class="o">))</span><span class="w"> </span><span class="o">(</span>a,∙-Monotonic<span class="w"> </span>_<span class="o">)</span><span class="w"> </span>proj₂<span class="w"> </span><span class="o">(</span>a,∙-Preserves-≈₂<span class="w"> </span>_<span class="o">)</span><span class="w"> </span>longestChain₂<span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This gets us the longest chain; what remains is to prove that this chain&rsquo;s length is the bound of all other changes. To do so, we need to work in the opposite direction; given a chain in the product lattice, we need to somehow reduce it to chains in lattices <code>A</code> and <code>B</code>, and leverage their finite height to complete the proof.</p> <p>The key idea is that for every two consecutive elements in the product lattice chain, we know that at least one of their components must&rsquo;ve increased. This increase had to come either from elements in lattice <code>A</code> or in lattice <code>B</code>. We can thus stick this increase into an <code>A</code>-chain or a <code>B</code>-chain, increasing its length. Since one of the chains grows with every consecutive pair, the number of consecutive pairs can&rsquo;t exceed the combined lengths of the <code>A</code> and <code>B</code> chains.</p> <p>I implement this idea as an <code>unzip</code> function, which takes a product chain and produces two chains made from its increases. By the logic we&rsquo;ve described, the length two chains has to bound the main one&rsquo;s. I give the signature below, and will put the implementation in a collapsible detail block. One last detail is that the need to decide which chain to grow &mdash; and thus which element has increased &mdash; is what introduces the need for decidable equality.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Prod.agda" data-first-line="149" data-last-line="149" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Prod.agda#L149-L149">Prod.agda</a>, line 149</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">149 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">unzip</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>a₁<span class="w"> </span>a₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">}</span><span class="w"> </span><span class="o">{</span>b₁<span class="w"> </span>b₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="o">}</span><span class="w"> </span><span class="o">{</span>n<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Chain<span class="w"> </span><span class="o">(</span>a₁<span class="w"> </span>,<span class="w"> </span>b₁<span class="o">)</span><span class="w"> </span><span class="o">(</span>a₂<span class="w"> </span>,<span class="w"> </span>b₂<span class="o">)</span><span class="w"> </span>n<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Σ<span class="w"> </span><span class="o">(</span>ℕ<span class="w"> </span>×<span class="w"> </span>ℕ<span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span><span class="o">(</span>n₁<span class="w"> </span>,<span class="w"> </span>n₂<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">((</span>Chain₁<span class="w"> </span>a₁<span class="w"> </span>a₂<span class="w"> </span>n₁<span class="w"> </span>×<span class="w"> </span>Chain₂<span class="w"> </span>b₁<span class="w"> </span>b₂<span class="w"> </span>n₂<span class="o">)</span><span class="w"> </span>×<span class="w"> </span><span class="o">(</span>n<span class="w"> </span>≤<span class="w"> </span>n₁<span class="w"> </span>+<span class="w"> </span>n₂<span class="o">)))</span></span></span></code></pre></td></tr></table> </div> </div> </div> <details><summary><strong>(Click here for the implementation of <code>unzip</code>)</strong></summary><div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Prod.agda" data-first-line="149" data-last-line="163" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Prod.agda#L149-L163">Prod.agda</a>, lines 149 through 163</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">149 </span><span class="lnt">150 </span><span class="lnt">151 </span><span class="lnt">152 </span><span class="lnt">153 </span><span class="lnt">154 </span><span class="lnt">155 </span><span class="lnt">156 </span><span class="lnt">157 </span><span class="lnt">158 </span><span class="lnt">159 </span><span class="lnt">160 </span><span class="lnt">161 </span><span class="lnt">162 </span><span class="lnt">163 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">unzip</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>a₁<span class="w"> </span>a₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">}</span><span class="w"> </span><span class="o">{</span>b₁<span class="w"> </span>b₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="o">}</span><span class="w"> </span><span class="o">{</span>n<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Chain<span class="w"> </span><span class="o">(</span>a₁<span class="w"> </span>,<span class="w"> </span>b₁<span class="o">)</span><span class="w"> </span><span class="o">(</span>a₂<span class="w"> </span>,<span class="w"> </span>b₂<span class="o">)</span><span class="w"> </span>n<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Σ<span class="w"> </span><span class="o">(</span>ℕ<span class="w"> </span>×<span class="w"> </span>ℕ<span class="o">)</span><span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span><span class="o">(</span>n₁<span class="w"> </span>,<span class="w"> </span>n₂<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">((</span>Chain₁<span class="w"> </span>a₁<span class="w"> </span>a₂<span class="w"> </span>n₁<span class="w"> </span>×<span class="w"> </span>Chain₂<span class="w"> </span>b₁<span class="w"> </span>b₂<span class="w"> </span>n₂<span class="o">)</span><span class="w"> </span>×<span class="w"> </span><span class="o">(</span>n<span class="w"> </span>≤<span class="w"> </span>n₁<span class="w"> </span>+<span class="w"> </span>n₂<span class="o">)))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>unzip<span class="w"> </span><span class="o">(</span>done<span class="w"> </span><span class="o">(</span>a₁≈a₂<span class="w"> </span>,<span class="w"> </span>b₁≈b₂<span class="o">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">((</span><span class="mi">0</span><span class="w"> </span>,<span class="w"> </span><span class="mi">0</span><span class="o">)</span><span class="w"> </span>,<span class="w"> </span><span class="o">((</span>done₁<span class="w"> </span>a₁≈a₂<span class="w"> </span>,<span class="w"> </span>done₂<span class="w"> </span>b₁≈b₂<span class="o">)</span><span class="w"> </span>,<span class="w"> </span>≤-refl<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>unzip<span class="w"> </span><span class="o">{</span>a₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>a₂<span class="o">}</span><span class="w"> </span><span class="o">{</span>b₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>b₂<span class="o">}</span><span class="w"> </span><span class="o">{</span>n<span class="o">}</span><span class="w"> </span><span class="o">(</span>step<span class="w"> </span><span class="o">{(</span>a₁<span class="w"> </span>,<span class="w"> </span>b₁<span class="o">)}</span><span class="w"> </span><span class="o">{(</span>a<span class="w"> </span>,<span class="w"> </span>b<span class="o">)}</span><span class="w"> </span><span class="o">((</span>a₁≼a<span class="w"> </span>,<span class="w"> </span>b₁≼b<span class="o">)</span><span class="w"> </span>,<span class="w"> </span>a₁b₁̷≈ab<span class="o">)</span><span class="w"> </span><span class="o">(</span>a≈a&#39;<span class="w"> </span>,<span class="w"> </span>b≈b&#39;<span class="o">)</span><span class="w"> </span>a&#39;b&#39;a₂b₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">with</span><span class="w"> </span>≈₁-dec<span class="w"> </span>a₁<span class="w"> </span>a<span class="w"> </span><span class="ow">|</span><span class="w"> </span>≈₂-dec<span class="w"> </span>b₁<span class="w"> </span>b<span class="w"> </span><span class="ow">|</span><span class="w"> </span>unzip<span class="w"> </span>a&#39;b&#39;a₂b₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>yes<span class="w"> </span>a₁≈a<span class="w"> </span><span class="ow">|</span><span class="w"> </span>yes<span class="w"> </span>b₁≈b<span class="w"> </span><span class="ow">|</span><span class="w"> </span><span class="o">((</span>n₁<span class="w"> </span>,<span class="w"> </span>n₂<span class="o">)</span><span class="w"> </span>,<span class="w"> </span><span class="o">((</span>c₁<span class="w"> </span>,<span class="w"> </span>c₂<span class="o">)</span><span class="w"> </span>,<span class="w"> </span>n≤n₁+n₂<span class="o">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥-elim<span class="w"> </span><span class="o">(</span>a₁b₁̷≈ab<span class="w"> </span><span class="o">(</span>a₁≈a<span class="w"> </span>,<span class="w"> </span>b₁≈b<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>no<span class="w"> </span>a₁̷≈a<span class="w"> </span><span class="ow">|</span><span class="w"> </span>yes<span class="w"> </span>b₁≈b<span class="w"> </span><span class="ow">|</span><span class="w"> </span><span class="o">((</span>n₁<span class="w"> </span>,<span class="w"> </span>n₂<span class="o">)</span><span class="w"> </span>,<span class="w"> </span><span class="o">((</span>c₁<span class="w"> </span>,<span class="w"> </span>c₂<span class="o">)</span><span class="w"> </span>,<span class="w"> </span>n≤n₁+n₂<span class="o">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">((</span>suc<span class="w"> </span>n₁<span class="w"> </span>,<span class="w"> </span>n₂<span class="o">)</span><span class="w"> </span>,<span class="w"> </span><span class="o">((</span>step₁<span class="w"> </span><span class="o">(</span>a₁≼a<span class="w"> </span>,<span class="w"> </span>a₁̷≈a<span class="o">)</span><span class="w"> </span>a≈a&#39;<span class="w"> </span>c₁<span class="w"> </span>,<span class="w"> </span>Chain₂-≈-cong₁<span class="w"> </span><span class="o">(</span>≈₂-sym<span class="w"> </span><span class="o">(</span>≈₂-trans<span class="w"> </span>b₁≈b<span class="w"> </span>b≈b&#39;<span class="o">))</span><span class="w"> </span>c₂<span class="o">)</span>,<span class="w"> </span>+-monoʳ-≤<span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">(</span>n≤n₁+n₂<span class="o">)))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>yes<span class="w"> </span>a₁≈a<span class="w"> </span><span class="ow">|</span><span class="w"> </span>no<span class="w"> </span>b₁̷≈b<span class="w"> </span><span class="ow">|</span><span class="w"> </span><span class="o">((</span>n₁<span class="w"> </span>,<span class="w"> </span>n₂<span class="o">)</span><span class="w"> </span>,<span class="w"> </span><span class="o">((</span>c₁<span class="w"> </span>,<span class="w"> </span>c₂<span class="o">)</span><span class="w"> </span>,<span class="w"> </span>n≤n₁+n₂<span class="o">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">((</span>n₁<span class="w"> </span>,<span class="w"> </span>suc<span class="w"> </span>n₂<span class="o">)</span><span class="w"> </span>,<span class="w"> </span><span class="o">(</span><span class="w"> </span><span class="o">(</span>Chain₁-≈-cong₁<span class="w"> </span><span class="o">(</span>≈₁-sym<span class="w"> </span><span class="o">(</span>≈₁-trans<span class="w"> </span>a₁≈a<span class="w"> </span>a≈a&#39;<span class="o">))</span><span class="w"> </span>c₁<span class="w"> </span>,<span class="w"> </span>step₂<span class="w"> </span><span class="o">(</span>b₁≼b<span class="w"> </span>,<span class="w"> </span>b₁̷≈b<span class="o">)</span><span class="w"> </span>b≈b&#39;<span class="w"> </span>c₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>,<span class="w"> </span>subst<span class="w"> </span><span class="o">(</span>n<span class="w"> </span>≤_<span class="o">)</span><span class="w"> </span><span class="o">(</span>sym<span class="w"> </span><span class="o">(</span>+-suc<span class="w"> </span>n₁<span class="w"> </span>n₂<span class="o">))</span><span class="w"> </span><span class="o">(</span>+-monoʳ-≤<span class="w"> </span><span class="mi">1</span><span class="w"> </span>n≤n₁+n₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>no<span class="w"> </span>a₁̷≈a<span class="w"> </span><span class="ow">|</span><span class="w"> </span>no<span class="w"> </span>b₁̷≈b<span class="w"> </span><span class="ow">|</span><span class="w"> </span><span class="o">((</span>n₁<span class="w"> </span>,<span class="w"> </span>n₂<span class="o">)</span><span class="w"> </span>,<span class="w"> </span><span class="o">((</span>c₁<span class="w"> </span>,<span class="w"> </span>c₂<span class="o">)</span><span class="w"> </span>,<span class="w"> </span>n≤n₁+n₂<span class="o">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">((</span>suc<span class="w"> </span>n₁<span class="w"> </span>,<span class="w"> </span>suc<span class="w"> </span>n₂<span class="o">)</span><span class="w"> </span>,<span class="w"> </span><span class="o">(</span><span class="w"> </span><span class="o">(</span>step₁<span class="w"> </span><span class="o">(</span>a₁≼a<span class="w"> </span>,<span class="w"> </span>a₁̷≈a<span class="o">)</span><span class="w"> </span>a≈a&#39;<span class="w"> </span>c₁<span class="w"> </span>,<span class="w"> </span>step₂<span class="w"> </span><span class="o">(</span>b₁≼b<span class="w"> </span>,<span class="w"> </span>b₁̷≈b<span class="o">)</span><span class="w"> </span>b≈b&#39;<span class="w"> </span>c₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>,<span class="w"> </span>m≤n⇒m≤o+n<span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">(</span>subst<span class="w"> </span><span class="o">(</span>n<span class="w"> </span>≤_<span class="o">)</span><span class="w"> </span><span class="o">(</span>sym<span class="w"> </span><span class="o">(</span>+-suc<span class="w"> </span>n₁<span class="w"> </span>n₂<span class="o">))</span><span class="w"> </span><span class="o">(</span>+-monoʳ-≤<span class="w"> </span><span class="mi">1</span><span class="w"> </span>n≤n₁+n₂<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">))</span></span></span></code></pre></td></tr></table> </div> </div> </div> </details> <p>Having decomposed the product chain into constituent chains, we simply combine the facts that they have to be bounded by the height of the <code>A</code> and <code>B</code> lattices, as well as the fact that they bound the combined chain.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Prod.agda" data-first-line="165" data-last-line="175" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Prod.agda#L165-L175">Prod.agda</a>, lines 165 through 175</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">165 </span><span class="lnt">166 </span><span class="lnt">167 </span><span class="lnt">168 </span><span class="lnt">169 </span><span class="lnt">170 </span><span class="lnt">171 </span><span class="hl"><span class="lnt">172 </span></span><span class="hl"><span class="lnt">173 </span></span><span class="hl"><span class="lnt">174 </span></span><span class="lnt">175 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">fixedHeight</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsLattice.FixedHeight<span class="w"> </span>isLattice<span class="w"> </span><span class="o">(</span>h₁<span class="w"> </span>+<span class="w"> </span>h₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>fixedHeight<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">record</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span><span class="w"> </span>⊥<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>⊥₁<span class="w"> </span>,<span class="w"> </span>⊥₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>⊤<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>⊤₁<span class="w"> </span>,<span class="w"> </span>⊤₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>longestChain<span class="w"> </span><span class="ow">=</span><span class="w"> </span>concat<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>ChainMapping₁.Chain-map<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>a<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>a<span class="w"> </span>,<span class="w"> </span>⊥₂<span class="o">))</span><span class="w"> </span><span class="o">(</span>∙,b-Monotonic<span class="w"> </span>_<span class="o">)</span><span class="w"> </span>proj₁<span class="w"> </span><span class="o">(</span>∙,b-Preserves-≈₁<span class="w"> </span>_<span class="o">)</span><span class="w"> </span>longestChain₁<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>ChainMapping₂.Chain-map<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>b<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>⊤₁<span class="w"> </span>,<span class="w"> </span>b<span class="o">))</span><span class="w"> </span><span class="o">(</span>a,∙-Monotonic<span class="w"> </span>_<span class="o">)</span><span class="w"> </span>proj₂<span class="w"> </span><span class="o">(</span>a,∙-Preserves-≈₂<span class="w"> </span>_<span class="o">)</span><span class="w"> </span>longestChain₂<span class="o">)</span><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span>;<span class="w"> </span>bounded<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ow">λ</span><span class="w"> </span>a₁b₁a₂b₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="o">((</span>n₁<span class="w"> </span>,<span class="w"> </span>n₂<span class="o">)</span><span class="w"> </span>,<span class="w"> </span><span class="o">((</span>a₁a₂<span class="w"> </span>,<span class="w"> </span>b₁b₂<span class="o">)</span><span class="w"> </span>,<span class="w"> </span>n≤n₁+n₂<span class="o">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>unzip<span class="w"> </span>a₁b₁a₂b₂<span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span><span class="kr">in</span><span class="w"> </span>≤-trans<span class="w"> </span>n≤n₁+n₂<span class="w"> </span><span class="o">(</span>+-mono-≤<span class="w"> </span><span class="o">(</span>bounded₁<span class="w"> </span>a₁a₂<span class="o">)</span><span class="w"> </span><span class="o">(</span>bounded₂<span class="w"> </span>b₁b₂<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This completes the proof!</p> <a href="#iterated-products"> <h3 id="iterated-products">Iterated Products</h3> </a> <p>The product lattice allows us to combine finite height lattices into a new finite height lattice. From there, we can use this newly created lattice as a component of yet another product lattice. For instance, if we had \(L_1 \times L_2\), we can take a product of that with \(L_1\) again, and get \(L_1 \times (L_1 \times L_2)\). Since this also creates a finite-height lattice, we can repeat this process, and keep taking a product with \(L_1\), creating:</p> $$ \overbrace{L_1 \times ... \times L_1}^{n\ \text{times}} \times L_2. $$ <p>I call this the <em>iterated product lattice</em>. Its significance will become clear shortly; in the meantime, let&rsquo;s prove that it is indeed a lattice (of finite height). To create an iterated product lattice, we still need two constituent lattices as input.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/IterProd.agda" data-first-line="7" data-last-line="11" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/IterProd.agda#L7-L11">IterProd.agda</a>, lines 7 through 11</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="kr">module</span><span class="w"> </span><span class="n">Lattice.IterProd</span><span class="w"> </span><span class="o">{</span>a<span class="o">}</span><span class="w"> </span><span class="o">{</span>A<span class="w"> </span>B<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>_≈₁_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">)</span><span class="w"> </span><span class="o">(</span>_≈₂_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>_⊔₁_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="o">(</span>_⊔₂_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>_⊓₁_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="o">(</span>_⊓₂_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>lA<span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsLattice<span class="w"> </span>A<span class="w"> </span>_≈₁_<span class="w"> </span>_⊔₁_<span class="w"> </span>_⊓₁_<span class="o">)</span><span class="w"> </span><span class="o">(</span>lB<span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsLattice<span class="w"> </span>B<span class="w"> </span>_≈₂_<span class="w"> </span>_⊔₂_<span class="w"> </span>_⊓₂_<span class="o">)</span><span class="w"> </span><span class="kr">where</span></span></span></code></pre></td></tr></table> </div> </div> </div> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/IterProd.agda" data-first-line="23" data-last-line="24" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/IterProd.agda#L23-L24">IterProd.agda</a>, lines 23 through 24</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">23 </span><span class="lnt">24 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="nf">IterProd</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>IterProd<span class="w"> </span>k<span class="w"> </span><span class="ow">=</span><span class="w"> </span>iterate<span class="w"> </span>k<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>t<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span>×<span class="w"> </span>t<span class="o">)</span><span class="w"> </span>B</span></span></code></pre></td></tr></table> </div> </div> </div> <p>At a high level, the proof goes by induction on the number of applications of the product. There&rsquo;s just one trick. I&rsquo;d like to build up an <code>isLattice</code> instance even if <code>A</code> and <code>B</code> are not finite-height. That&rsquo;s because in that case, the iterated product is still a lattice, just not one with a finite height. On the other hand, the <code>isFiniteHeightLattice</code> proof requires the <code>isLattice</code> proof. Since we&rsquo;re building up by induction, that means that every recursive invocation of the function, we need to get the &ldquo;partial&rdquo; lattice instance and give it to the &ldquo;partial&rdquo; finite height lattice instance. When I implemented the inductive proof for <code>isLattice</code> independently from the (more specific) inductive proof of <code>isFiniteHeightLattice</code>, Agda could not unify the two <code>isLattice</code> instances (the &ldquo;actual&rdquo; one and the one that serves as witness for <code>isFiniteHeightLattice</code>). This led to some trouble and inconvenience, and so, I thought it best to build the two up together.</p> <p>To build up with the lattice instance and &mdash; if possible &mdash; the finite height instance, I needed to allow for the constituent lattices being either finite or infinite. I supported this by defining a helper type:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/IterProd.agda" data-first-line="40" data-last-line="55" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/IterProd.agda#L40-L55">IterProd.agda</a>, lines 40 through 55</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="kr">record</span><span class="w"> </span>RequiredForFixedHeight<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span><span class="o">(</span>lsuc<span class="w"> </span>a<span class="o">)</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">field</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">≈₁-dec</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsDecidable<span class="w"> </span>_≈₁_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">≈₂-dec</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsDecidable<span class="w"> </span>_≈₂_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>h₁<span class="w"> </span>h₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">fhA</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>FixedHeight₁<span class="w"> </span>h₁<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">fhB</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>FixedHeight₂<span class="w"> </span>h₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⊥₁</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⊥₁<span class="w"> </span><span class="ow">=</span><span class="w"> </span>Height.⊥<span class="w"> </span>fhA<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⊥₂</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⊥₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>Height.⊥<span class="w"> </span>fhB<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⊥k</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>k<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>IterProd<span class="w"> </span>k<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⊥k<span class="w"> </span><span class="ow">=</span><span class="w"> </span>build<span class="w"> </span>⊥₁<span class="w"> </span>⊥₂</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Then, I defined the &ldquo;everything at once&rdquo; type, in which, instead of a field for the proof of finite height, has a field that constructs this proof <em>if the necessary additional information is present</em>.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/IterProd.agda" data-first-line="57" data-last-line="76" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/IterProd.agda#L57-L76">IterProd.agda</a>, lines 57 through 76</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span><span class="lnt">65 </span><span class="lnt">66 </span><span class="lnt">67 </span><span class="lnt">68 </span><span class="lnt">69 </span><span class="lnt">70 </span><span class="lnt">71 </span><span class="lnt">72 </span><span class="lnt">73 </span><span class="lnt">74 </span><span class="lnt">75 </span><span class="lnt">76 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="kr">record</span><span class="w"> </span>IsFiniteHeightWithBotAndDecEq<span class="w"> </span><span class="o">{</span>A<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">}</span><span class="w"> </span><span class="o">{</span>_≈_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">}</span><span class="w"> </span><span class="o">{</span>_⊔_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="o">}</span><span class="w"> </span><span class="o">{</span>_⊓_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="o">}</span><span class="w"> </span><span class="o">(</span>isLattice<span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsLattice<span class="w"> </span>A<span class="w"> </span>_≈_<span class="w"> </span>_⊔_<span class="w"> </span>_⊓_<span class="o">)</span><span class="w"> </span><span class="o">(</span>⊥<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span><span class="o">(</span>lsuc<span class="w"> </span>a<span class="o">)</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">field</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">height</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">fixedHeight</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsLattice.FixedHeight<span class="w"> </span>isLattice<span class="w"> </span>height<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">≈-dec</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsDecidable<span class="w"> </span>_≈_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⊥-correct</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Height.⊥<span class="w"> </span>fixedHeight<span class="w"> </span>≡<span class="w"> </span>⊥<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">record</span><span class="w"> </span>Everything<span class="w"> </span><span class="o">(</span>k<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="o">)</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span><span class="o">(</span>lsuc<span class="w"> </span>a<span class="o">)</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>T<span class="w"> </span><span class="ow">=</span><span class="w"> </span>IterProd<span class="w"> </span>k<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">field</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">_≈_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>T<span class="w"> </span><span class="ow">→</span><span class="w"> </span>T<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">_⊔_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>T<span class="w"> </span><span class="ow">→</span><span class="w"> </span>T<span class="w"> </span><span class="ow">→</span><span class="w"> </span>T<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">_⊓_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>T<span class="w"> </span><span class="ow">→</span><span class="w"> </span>T<span class="w"> </span><span class="ow">→</span><span class="w"> </span>T<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">isLattice</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsLattice<span class="w"> </span>T<span class="w"> </span>_≈_<span class="w"> </span>_⊔_<span class="w"> </span>_⊓_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">isFiniteHeightIfSupported</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>req<span class="w"> </span><span class="ow">:</span><span class="w"> </span>RequiredForFixedHeight<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>IsFiniteHeightWithBotAndDecEq<span class="w"> </span>isLattice<span class="w"> </span><span class="o">(</span>RequiredForFixedHeight.⊥k<span class="w"> </span>req<span class="w"> </span>k<span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Finally, the proof by induction. It&rsquo;s actually relatively long, so I&rsquo;ll include it as a collapsible block.</p> <details><summary><strong>(Click here to expand the inductive proof)</strong></summary><div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/IterProd.agda" data-first-line="78" data-last-line="120" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/IterProd.agda#L78-L120">IterProd.agda</a>, lines 78 through 120</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 78 </span><span class="lnt"> 79 </span><span class="lnt"> 80 </span><span class="lnt"> 81 </span><span class="lnt"> 82 </span><span class="lnt"> 83 </span><span class="lnt"> 84 </span><span class="lnt"> 85 </span><span class="lnt"> 86 </span><span class="lnt"> 87 </span><span class="lnt"> 88 </span><span class="lnt"> 89 </span><span class="lnt"> 90 </span><span class="lnt"> 91 </span><span class="lnt"> 92 </span><span class="lnt"> 93 </span><span class="lnt"> 94 </span><span class="lnt"> 95 </span><span class="lnt"> 96 </span><span class="lnt"> 97 </span><span class="lnt"> 98 </span><span class="lnt"> 99 </span><span class="lnt">100 </span><span class="lnt">101 </span><span class="lnt">102 </span><span class="lnt">103 </span><span class="lnt">104 </span><span class="lnt">105 </span><span class="lnt">106 </span><span class="lnt">107 </span><span class="lnt">108 </span><span class="lnt">109 </span><span class="lnt">110 </span><span class="lnt">111 </span><span class="lnt">112 </span><span class="lnt">113 </span><span class="lnt">114 </span><span class="lnt">115 </span><span class="lnt">116 </span><span class="lnt">117 </span><span class="lnt">118 </span><span class="lnt">119 </span><span class="lnt">120 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">everything</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>k<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Everything<span class="w"> </span>k<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>everything<span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">record</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span><span class="w"> </span>_≈_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>_≈₂_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>_⊔_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>_⊔₂_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>_⊓_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>_⊓₂_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>isLattice<span class="w"> </span><span class="ow">=</span><span class="w"> </span>lB<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>isFiniteHeightIfSupported<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ow">λ</span><span class="w"> </span>req<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kr">record</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span><span class="w"> </span>height<span class="w"> </span><span class="ow">=</span><span class="w"> </span>RequiredForFixedHeight.h₂<span class="w"> </span>req<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>fixedHeight<span class="w"> </span><span class="ow">=</span><span class="w"> </span>RequiredForFixedHeight.fhB<span class="w"> </span>req<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>≈-dec<span class="w"> </span><span class="ow">=</span><span class="w"> </span>RequiredForFixedHeight.≈₂-dec<span class="w"> </span>req<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>⊥-correct<span class="w"> </span><span class="ow">=</span><span class="w"> </span>refl<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>everything<span class="w"> </span><span class="o">(</span>suc<span class="w"> </span>k&#39;<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">record</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span><span class="w"> </span>_≈_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>P._≈_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>_⊔_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>P._⊔_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>_⊓_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>P._⊓_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>isLattice<span class="w"> </span><span class="ow">=</span><span class="w"> </span>P.isLattice<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>isFiniteHeightIfSupported<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ow">λ</span><span class="w"> </span>req<span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">let</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>fhlRest<span class="w"> </span><span class="ow">=</span><span class="w"> </span>Everything.isFiniteHeightIfSupported<span class="w"> </span>everythingRest<span class="w"> </span>req<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">in</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">record</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span><span class="w"> </span>height<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>RequiredForFixedHeight.h₁<span class="w"> </span>req<span class="o">)</span><span class="w"> </span>+<span class="w"> </span>IsFiniteHeightWithBotAndDecEq.height<span class="w"> </span>fhlRest<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>fixedHeight<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>P.fixedHeight<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>RequiredForFixedHeight.≈₁-dec<span class="w"> </span>req<span class="o">)</span><span class="w"> </span><span class="o">(</span>IsFiniteHeightWithBotAndDecEq.≈-dec<span class="w"> </span>fhlRest<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>RequiredForFixedHeight.h₁<span class="w"> </span>req<span class="o">)</span><span class="w"> </span><span class="o">(</span>IsFiniteHeightWithBotAndDecEq.height<span class="w"> </span>fhlRest<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>RequiredForFixedHeight.fhA<span class="w"> </span>req<span class="o">)</span><span class="w"> </span><span class="o">(</span>IsFiniteHeightWithBotAndDecEq.fixedHeight<span class="w"> </span>fhlRest<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>≈-dec<span class="w"> </span><span class="ow">=</span><span class="w"> </span>P.≈-dec<span class="w"> </span><span class="o">(</span>RequiredForFixedHeight.≈₁-dec<span class="w"> </span>req<span class="o">)</span><span class="w"> </span><span class="o">(</span>IsFiniteHeightWithBotAndDecEq.≈-dec<span class="w"> </span>fhlRest<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>⊥-correct<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>cong<span class="w"> </span><span class="o">((</span>Height.⊥<span class="w"> </span><span class="o">(</span>RequiredForFixedHeight.fhA<span class="w"> </span>req<span class="o">))</span><span class="w"> </span>,_<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>IsFiniteHeightWithBotAndDecEq.⊥-correct<span class="w"> </span>fhlRest<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>everythingRest<span class="w"> </span><span class="ow">=</span><span class="w"> </span>everything<span class="w"> </span>k&#39;<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">import</span><span class="w"> </span><span class="n">Lattice.Prod</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>_≈₁_<span class="w"> </span><span class="o">(</span>Everything._≈_<span class="w"> </span>everythingRest<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>_⊔₁_<span class="w"> </span><span class="o">(</span>Everything._⊔_<span class="w"> </span>everythingRest<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>_⊓₁_<span class="w"> </span><span class="o">(</span>Everything._⊓_<span class="w"> </span>everythingRest<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>lA<span class="w"> </span><span class="o">(</span>Everything.isLattice<span class="w"> </span>everythingRest<span class="o">)</span><span class="w"> </span>as<span class="w"> </span>P</span></span></code></pre></td></tr></table> </div> </div> </div> </details> <a href="#fixed-height-of-the-map-lattice"> <h3 id="fixed-height-of-the-map-lattice">Fixed Height of the Map Lattice</h3> </a> <p>We saw above that <a href="#finite-keys"class="same-page-link">we can make a map lattice have a finite height if we fix its keys</a>. How does this work? Well, if the keys are always the same, we can think of such a map as just a tuple, with as many element as there are keys.</p> $$ \begin{array}{cccccc} \{ &amp;amp; a: 1, &amp;amp; b: 2, &amp;amp; c: 3, &amp;amp; \} \\ &amp;amp; &amp;amp; \iff &amp;amp; &amp;amp; \\ ( &amp;amp; 1, &amp;amp; 2, &amp;amp; 3 &amp;amp; ) \end{array} $$ <p>This is why I introduced <a href="#iterated-products"class="same-page-link">iterated products<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#arrow-up"/> </svg></a> earlier; we can use them to construct the second lattice in the example above. I&rsquo;ll take one departure from that example, though: I&rsquo;ll &ldquo;pad&rdquo; the tuples with an additional unit element at the end. The unit type (denoted \(\top\)) &mdash; which has only a single element &mdash; forms a finite height lattice trivially; I prove this in <a href="#appendix-the-unit-lattice"class="same-page-link">an appendix below</a>. Using this padding helps reduce the number of special cases; without the adding, the tuple definition might be something like the following:</p> $$ \text{tup}(A, k) = \begin{cases} \top &amp;amp; k = 0 \\ A &amp;amp; k = 1 \\ A \times \text{tup}(A, k - 1) &amp;amp; k &amp;gt; 1 \end{cases} $$ <p>On the other hand, if we were to allow the extra padding, we could drop the definition down to:</p> $$ \text{tup}(A, k) = \text{iterate}(t \mapsto A \times t, k, \bot) = \begin{cases} \top &amp;amp; k = 0 \\ A \times \text{tup}(A, k - 1) &amp;amp; k &amp;gt; 0 \end{cases} $$ <p>And so, we drop from two to three cases, which means less proof work for us. The tough part is to prove that the two representations of maps &mdash; the key-value list and the iterated product &mdash; are equivalent. We will not have much trouble proving that they&rsquo;re both lattices (we did that last time, for both <a href="https://danilafe.com/blog/02_spa_agda_combining_lattices/#the-cartesian-product-lattice">products</a> and <a href="https://danilafe.com/blog/02_spa_agda_combining_lattices/#the-map-lattice">maps</a>). Instead, what we need to do is prove that the height of one lattice is the same as the height of the other. We prove this by providing something like an <a href="https://mathworld.wolfram.com/Isomorphism.html"class="external-link">isomorphism<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>: a pair of functions that convert between the two representations, and preserve the properties and relationships (such as \((\sqcup)\)) of lattice elements. In fact, the list of the conversion functions&rsquo; properties is quite extensive:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Isomorphism.agda" data-first-line="22" data-last-line="33" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Isomorphism.agda#L22-L33">Isomorphism.agda</a>, lines 22 through 33</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="hl"><span class="lnt">29 </span></span><span class="hl"><span class="lnt">30 </span></span><span class="hl"><span class="lnt">31 </span></span><span class="hl"><span class="lnt">32 </span></span><span class="hl"><span class="lnt">33 </span></span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="kr">module</span><span class="w"> </span><span class="n">TransportFiniteHeight</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span>a<span class="w"> </span>b<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Level<span class="o">}</span><span class="w"> </span><span class="o">{</span>A<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">}</span><span class="w"> </span><span class="o">{</span>B<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>b<span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span>_≈₁_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">}</span><span class="w"> </span><span class="o">{</span>_≈₂_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>b<span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span>_⊔₁_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="o">}</span><span class="w"> </span><span class="o">{</span>_⊔₂_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span>_⊓₁_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="o">}</span><span class="w"> </span><span class="o">{</span>_⊓₂_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span>height<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>fhlA<span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsFiniteHeightLattice<span class="w"> </span>A<span class="w"> </span>height<span class="w"> </span>_≈₁_<span class="w"> </span>_⊔₁_<span class="w"> </span>_⊓₁_<span class="o">)</span><span class="w"> </span><span class="o">(</span>lB<span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsLattice<span class="w"> </span>B<span class="w"> </span>_≈₂_<span class="w"> </span>_⊔₂_<span class="w"> </span>_⊓₂_<span class="o">)</span><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span><span class="o">{</span>f<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="o">}</span><span class="w"> </span><span class="o">{</span>g<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="o">}</span><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span><span class="o">(</span>f-preserves-≈₁<span class="w"> </span><span class="ow">:</span><span class="w"> </span>f<span class="w"> </span>Preserves<span class="w"> </span>_≈₁_<span class="w"> </span>⟶<span class="w"> </span>_≈₂_<span class="o">)</span><span class="w"> </span><span class="o">(</span>g-preserves-≈₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>g<span class="w"> </span>Preserves<span class="w"> </span>_≈₂_<span class="w"> </span>⟶<span class="w"> </span>_≈₁_<span class="o">)</span><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span><span class="o">(</span>f-⊔-distr<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>a₁<span class="w"> </span>a₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>f<span class="w"> </span><span class="o">(</span>a₁<span class="w"> </span>⊔₁<span class="w"> </span>a₂<span class="o">)</span><span class="w"> </span>≈₂<span class="w"> </span><span class="o">((</span>f<span class="w"> </span>a₁<span class="o">)</span><span class="w"> </span>⊔₂<span class="w"> </span><span class="o">(</span>f<span class="w"> </span>a₂<span class="o">)))</span><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span><span class="o">(</span>g-⊔-distr<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>b₁<span class="w"> </span>b₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>g<span class="w"> </span><span class="o">(</span>b₁<span class="w"> </span>⊔₂<span class="w"> </span>b₂<span class="o">)</span><span class="w"> </span>≈₁<span class="w"> </span><span class="o">((</span>g<span class="w"> </span>b₁<span class="o">)</span><span class="w"> </span>⊔₁<span class="w"> </span><span class="o">(</span>g<span class="w"> </span>b₂<span class="o">)))</span><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span><span class="o">(</span>inverseˡ<span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsInverseˡ<span class="w"> </span>_≈₁_<span class="w"> </span>_≈₂_<span class="w"> </span>f<span class="w"> </span>g<span class="o">)</span><span class="w"> </span><span class="o">(</span>inverseʳ<span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsInverseʳ<span class="w"> </span>_≈₁_<span class="w"> </span>_≈₂_<span class="w"> </span>f<span class="w"> </span>g<span class="o">)</span><span class="w"> </span><span class="kr">where</span></span></span></code></pre></td></tr></table> </div> </div> </div> <ol> <li> <p>First, the functions must preserve our definition of equivalence. Thus, if we convert two equivalent elements from the list representation to the tuple representation, the resulting tuples should be equivalent as well. The reverse must be true, too.</p> </li> <li> <p>Second, the functions must preserve the binary operations &mdash; see also the definition of a <a href="https://en.wikipedia.org/wiki/Homomorphism#Definition"class="external-link">homomorphism<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. Specifically, if \(f\) is a conversion function, then the following should hold:</p> $$ f(a \sqcup b) \approx f(a) \sqcup f(b) $$ <p>For the purposes of proving that equivalent maps have finite heights, it turns out that this property need only hold for the join operator \((\sqcup)\).</p> </li> <li> <p>Finally, the functions must be inverses of each other. If you convert a list to a tuple, and then the tuple back into a list, the resulting value should be equivalent to what we started with. In fact, they need to be both &ldquo;left&rdquo; and &ldquo;right&rdquo; inverses, so that both \(f(g(x))\approx x\) and \(g(f(x)) \approx x\).</p> </li> </ol> <p>Given this, the high-level proof is in two parts:</p> <ol> <li> <p><strong>Proving that a chain of the same height exists in the second (e.g., tuple) lattice:</strong> To do this, we want to take the longest chain in the first (e.g. key-value list) lattice, and convert it into a chain in the second. The mechanism for this is not too hard to imagine: we just take the original chain, and apply the conversion function to each element.</p> <p>Intuitively, this works because of the structure-preserving properties we required above. For instance (recall the <a href="https://lars.hupel.info/topics/crdt/03-lattices/#there-"class="external-link">definition of \((\leq)\) given by Lars Hupel<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, which in brief is \(a \leq b \triangleq a \sqcup b = b\)):</p> $$ \begin{array}{rcr} a \leq b &amp;amp; \iff &amp;amp; (\text{definition of less than})\\ a \sqcup b \approx b &amp;amp; \implies &amp;amp; (\text{conversions preserve equivalence}) \\ f(a \sqcup b) \approx f(b) &amp;amp; \implies &amp;amp; (\text{conversions distribute over binary operations}) \\ f(a) \sqcup f(b) \approx f(b) &amp;amp; \iff &amp;amp; (\text{definition of less than}) \\ f(a) \leq f(b) \end{array} $$ </li> <li> <p><strong>Proving that longer chains can&rsquo;t exist in the second (e.g., tuple) lattice:</strong> we&rsquo;ve already seen the mechanism to port a chain from one lattice to another lattice, and we can use this same mechanism (but switching directions) to go in reverse. If we do that, we can take a chain of questionable length in the tuple lattice, port it back to the key-value map, and use the (already known) fact that its chains are bounded to conclude the same thing about the tuple chain.</p> </li> </ol> <p>As you can tell, the chain porting mechanism is doing the heavy lifting here. It&rsquo;s relatively easy to implement given the conditions we&rsquo;ve set on conversion functions, in both directions:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Isomorphism.agda" data-first-line="52" data-last-line="64" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Isomorphism.agda#L52-L64">Isomorphism.agda</a>, lines 52 through 64</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">f-preserves-̷≈</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>f<span class="w"> </span>Preserves<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>x<span class="w"> </span>y<span class="w"> </span><span class="ow">→</span><span class="w"> </span>¬<span class="w"> </span>x<span class="w"> </span>≈₁<span class="w"> </span>y<span class="o">)</span><span class="w"> </span>⟶<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>x<span class="w"> </span>y<span class="w"> </span><span class="ow">→</span><span class="w"> </span>¬<span class="w"> </span>x<span class="w"> </span>≈₂<span class="w"> </span>y<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>f-preserves-̷≈<span class="w"> </span>x̷≈y<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ow">λ</span><span class="w"> </span>fx≈fy<span class="w"> </span><span class="ow">→</span><span class="w"> </span>x̷≈y<span class="w"> </span><span class="o">(</span>f-Injective<span class="w"> </span>fx≈fy<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">g-preserves-̷≈</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>g<span class="w"> </span>Preserves<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>x<span class="w"> </span>y<span class="w"> </span><span class="ow">→</span><span class="w"> </span>¬<span class="w"> </span>x<span class="w"> </span>≈₂<span class="w"> </span>y<span class="o">)</span><span class="w"> </span>⟶<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>x<span class="w"> </span>y<span class="w"> </span><span class="ow">→</span><span class="w"> </span>¬<span class="w"> </span>x<span class="w"> </span>≈₁<span class="w"> </span>y<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>g-preserves-̷≈<span class="w"> </span>x̷≈y<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ow">λ</span><span class="w"> </span>gx≈gy<span class="w"> </span><span class="ow">→</span><span class="w"> </span>x̷≈y<span class="w"> </span><span class="o">(</span>g-Injective<span class="w"> </span>gx≈gy<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">portChain₁</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>a₁<span class="w"> </span>a₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">}</span><span class="w"> </span><span class="o">{</span>h<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Chain₁<span class="w"> </span>a₁<span class="w"> </span>a₂<span class="w"> </span>h<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Chain₂<span class="w"> </span><span class="o">(</span>f<span class="w"> </span>a₁<span class="o">)</span><span class="w"> </span><span class="o">(</span>f<span class="w"> </span>a₂<span class="o">)</span><span class="w"> </span>h<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>portChain₁<span class="w"> </span><span class="o">(</span>done₁<span class="w"> </span>a₁≈a₂<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>done₂<span class="w"> </span><span class="o">(</span>f-preserves-≈₁<span class="w"> </span>a₁≈a₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>portChain₁<span class="w"> </span><span class="o">(</span>step₁<span class="w"> </span><span class="o">{</span>a₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>a₂<span class="o">}</span><span class="w"> </span><span class="o">(</span>a₁≼a₂<span class="w"> </span>,<span class="w"> </span>a₁̷≈a₂<span class="o">)</span><span class="w"> </span>a₂≈a₂&#39;<span class="w"> </span>c<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>step₂<span class="w"> </span><span class="o">(</span>≈₂-trans<span class="w"> </span><span class="o">(</span>≈₂-sym<span class="w"> </span><span class="o">(</span>f-⊔-distr<span class="w"> </span>a₁<span class="w"> </span>a₂<span class="o">))</span><span class="w"> </span><span class="o">(</span>f-preserves-≈₁<span class="w"> </span>a₁≼a₂<span class="o">)</span><span class="w"> </span>,<span class="w"> </span>f-preserves-̷≈<span class="w"> </span>a₁̷≈a₂<span class="o">)</span><span class="w"> </span><span class="o">(</span>f-preserves-≈₁<span class="w"> </span>a₂≈a₂&#39;<span class="o">)</span><span class="w"> </span><span class="o">(</span>portChain₁<span class="w"> </span>c<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">portChain₂</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>b₁<span class="w"> </span>b₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="o">}</span><span class="w"> </span><span class="o">{</span>h<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Chain₂<span class="w"> </span>b₁<span class="w"> </span>b₂<span class="w"> </span>h<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Chain₁<span class="w"> </span><span class="o">(</span>g<span class="w"> </span>b₁<span class="o">)</span><span class="w"> </span><span class="o">(</span>g<span class="w"> </span>b₂<span class="o">)</span><span class="w"> </span>h<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>portChain₂<span class="w"> </span><span class="o">(</span>done₂<span class="w"> </span>a₂≈a₁<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>done₁<span class="w"> </span><span class="o">(</span>g-preserves-≈₂<span class="w"> </span>a₂≈a₁<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>portChain₂<span class="w"> </span><span class="o">(</span>step₂<span class="w"> </span><span class="o">{</span>b₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>b₂<span class="o">}</span><span class="w"> </span><span class="o">(</span>b₁≼b₂<span class="w"> </span>,<span class="w"> </span>b₁̷≈b₂<span class="o">)</span><span class="w"> </span>b₂≈b₂&#39;<span class="w"> </span>c<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>step₁<span class="w"> </span><span class="o">(</span>≈₁-trans<span class="w"> </span><span class="o">(</span>≈₁-sym<span class="w"> </span><span class="o">(</span>g-⊔-distr<span class="w"> </span>b₁<span class="w"> </span>b₂<span class="o">))</span><span class="w"> </span><span class="o">(</span>g-preserves-≈₂<span class="w"> </span>b₁≼b₂<span class="o">)</span><span class="w"> </span>,<span class="w"> </span>g-preserves-̷≈<span class="w"> </span>b₁̷≈b₂<span class="o">)</span><span class="w"> </span><span class="o">(</span>g-preserves-≈₂<span class="w"> </span>b₂≈b₂&#39;<span class="o">)</span><span class="w"> </span><span class="o">(</span>portChain₂<span class="w"> </span>c<span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>With that, we can prove the second lattice&rsquo;s finite height:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Isomorphism.agda" data-first-line="66" data-last-line="80" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Isomorphism.agda#L66-L80">Isomorphism.agda</a>, lines 66 through 80</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">66 </span><span class="lnt">67 </span><span class="lnt">68 </span><span class="lnt">69 </span><span class="lnt">70 </span><span class="lnt">71 </span><span class="lnt">72 </span><span class="lnt">73 </span><span class="lnt">74 </span><span class="lnt">75 </span><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span><span class="lnt">79 </span><span class="lnt">80 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">isFiniteHeightLattice</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsFiniteHeightLattice<span class="w"> </span>B<span class="w"> </span>height<span class="w"> </span>_≈₂_<span class="w"> </span>_⊔₂_<span class="w"> </span>_⊓₂_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>isFiniteHeightLattice<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">let</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">open</span><span class="w"> </span>Chain.Height<span class="w"> </span><span class="o">(</span>IsFiniteHeightLattice.fixedHeight<span class="w"> </span>fhlA<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">using</span><span class="w"> </span><span class="o">()</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">renaming</span><span class="w"> </span><span class="o">(</span>⊥<span class="w"> </span>to<span class="w"> </span>⊥₁;<span class="w"> </span>⊤<span class="w"> </span>to<span class="w"> </span>⊤₁;<span class="w"> </span>bounded<span class="w"> </span>to<span class="w"> </span>bounded₁;<span class="w"> </span>longestChain<span class="w"> </span>to<span class="w"> </span>c<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">in</span><span class="w"> </span><span class="kr">record</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span><span class="w"> </span>isLattice<span class="w"> </span><span class="ow">=</span><span class="w"> </span>lB<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>fixedHeight<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">record</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span><span class="w"> </span>⊥<span class="w"> </span><span class="ow">=</span><span class="w"> </span>f<span class="w"> </span>⊥₁<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>⊤<span class="w"> </span><span class="ow">=</span><span class="w"> </span>f<span class="w"> </span>⊤₁<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>longestChain<span class="w"> </span><span class="ow">=</span><span class="w"> </span>portChain₁<span class="w"> </span>c<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>bounded<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ow">λ</span><span class="w"> </span>c&#39;<span class="w"> </span><span class="ow">→</span><span class="w"> </span>bounded₁<span class="w"> </span><span class="o">(</span>portChain₂<span class="w"> </span>c&#39;<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The conversion functions are also not too difficult to define. I give them below, but I refrain from showing proofs of the more involved properties (such as the fact that <code>from</code> and <code>to</code> are inverses, preserve equivalence, and distribute over join) here. You can view them by clicking the link at the top of the code block below.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/FiniteValueMap.agda" data-first-line="68" data-last-line="85" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/FiniteValueMap.agda#L68-L85">FiniteValueMap.agda</a>, lines 68 through 85</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">68 </span><span class="lnt">69 </span><span class="lnt">70 </span><span class="lnt">71 </span><span class="lnt">72 </span><span class="lnt">73 </span><span class="lnt">74 </span><span class="lnt">75 </span><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span><span class="lnt">79 </span><span class="lnt">80 </span><span class="lnt">81 </span><span class="lnt">82 </span><span class="lnt">83 </span><span class="lnt">84 </span><span class="lnt">85 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">from</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>ks<span class="w"> </span><span class="ow">:</span><span class="w"> </span>List<span class="w"> </span>A<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>FiniteMap<span class="w"> </span>ks<span class="w"> </span><span class="ow">→</span><span class="w"> </span>IterProd<span class="w"> </span><span class="o">(</span>length<span class="w"> </span>ks<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>from<span class="w"> </span><span class="o">{</span>[]<span class="o">}</span><span class="w"> </span><span class="o">((</span>[]<span class="w"> </span>,<span class="w"> </span>_<span class="o">)</span><span class="w"> </span>,<span class="w"> </span>_<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>tt<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>from<span class="w"> </span><span class="o">{</span>k<span class="w"> </span>∷<span class="w"> </span>ks&#39;<span class="o">}</span><span class="w"> </span><span class="o">(((</span>k&#39;<span class="w"> </span>,<span class="w"> </span>v<span class="o">)</span><span class="w"> </span>∷<span class="w"> </span>fm&#39;<span class="w"> </span>,<span class="w"> </span>push<span class="w"> </span>_<span class="w"> </span>uks&#39;<span class="o">)</span><span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>v<span class="w"> </span>,<span class="w"> </span>from<span class="w"> </span><span class="o">((</span>fm&#39;<span class="w"> </span>,<span class="w"> </span>uks&#39;<span class="o">)</span>,<span class="w"> </span>refl<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">to</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>ks<span class="w"> </span><span class="ow">:</span><span class="w"> </span>List<span class="w"> </span>A<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Unique<span class="w"> </span>ks<span class="w"> </span><span class="ow">→</span><span class="w"> </span>IterProd<span class="w"> </span><span class="o">(</span>length<span class="w"> </span>ks<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>FiniteMap<span class="w"> </span>ks<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>to<span class="w"> </span><span class="o">{</span>[]<span class="o">}</span><span class="w"> </span>_<span class="w"> </span>⊤<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">((</span>[]<span class="w"> </span>,<span class="w"> </span>empty<span class="o">)</span><span class="w"> </span>,<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>to<span class="w"> </span><span class="o">{</span>k<span class="w"> </span>∷<span class="w"> </span>ks&#39;<span class="o">}</span><span class="w"> </span><span class="o">(</span>push<span class="w"> </span>k≢ks&#39;<span class="w"> </span>uks&#39;<span class="o">)</span><span class="w"> </span><span class="o">(</span>v<span class="w"> </span>,<span class="w"> </span>rest<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">let</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">((</span>fm&#39;<span class="w"> </span>,<span class="w"> </span>ufm&#39;<span class="o">)</span><span class="w"> </span>,<span class="w"> </span>fm&#39;≡ks&#39;<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>to<span class="w"> </span>uks&#39;<span class="w"> </span>rest<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">-- This would be easier if we pattern matched on the equiality proof</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">-- to get refl, but that makes it harder to reason about &#39;to&#39; when</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">-- the arguments are not known to be refl.</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>k≢fm&#39;<span class="w"> </span><span class="ow">=</span><span class="w"> </span>subst<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>ks<span class="w"> </span><span class="ow">→</span><span class="w"> </span>All<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>k&#39;<span class="w"> </span><span class="ow">→</span><span class="w"> </span>¬<span class="w"> </span>k<span class="w"> </span>≡<span class="w"> </span>k&#39;<span class="o">)</span><span class="w"> </span>ks<span class="o">)</span><span class="w"> </span><span class="o">(</span>sym<span class="w"> </span>fm&#39;≡ks&#39;<span class="o">)</span><span class="w"> </span>k≢ks&#39;<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>kvs≡ks<span class="w"> </span><span class="ow">=</span><span class="w"> </span>cong<span class="w"> </span><span class="o">(</span>k<span class="w"> </span>∷_<span class="o">)</span><span class="w"> </span>fm&#39;≡ks&#39;<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">in</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(((</span>k<span class="w"> </span>,<span class="w"> </span>v<span class="o">)</span><span class="w"> </span>∷<span class="w"> </span>fm&#39;<span class="w"> </span>,<span class="w"> </span>push<span class="w"> </span>k≢fm&#39;<span class="w"> </span>ufm&#39;<span class="o">)</span><span class="w"> </span>,<span class="w"> </span>kvs≡ks<span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Above, <code>FiniteValueMap ks</code> is the type of maps whose keys are fixed to <code>ks</code>; defined as follows:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/FiniteMap.agda" data-first-line="58" data-last-line="60" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/FiniteMap.agda#L58-L60">FiniteMap.agda</a>, lines 58 through 60</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="kr">module</span><span class="w"> </span><span class="n">WithKeys</span><span class="w"> </span><span class="o">(</span>ks<span class="w"> </span><span class="ow">:</span><span class="w"> </span>List<span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">FiniteMap</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span><span class="o">(</span>a<span class="w"> </span>⊔ℓ<span class="w"> </span>b<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>FiniteMap<span class="w"> </span><span class="ow">=</span><span class="w"> </span>Σ<span class="w"> </span>Map<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>m<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Map.keys<span class="w"> </span>m<span class="w"> </span>≡<span class="w"> </span>ks<span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Proving the remaining properties (which as I mentioned, I omit from the main body of the post) is sufficient to apply the isomorphism, proving that maps with finite keys are of a finite height.</p> <a href="#using-the-finite-height-property"> <h3 id="using-the-finite-height-property">Using the Finite Height Property</h3> </a> <p>Lattices having a finite height is a crucial property for the sorts of static program analyses I&rsquo;ve been working to implement. We can create functions that traverse &ldquo;up&rdquo; through the lattice, creating larger values each time. If these lattices are of a finite height, then the static analyses functions can only traverse &ldquo;so high&rdquo;. Under certain conditions, this guarantees that our static analysis will eventually terminate with a <a href="https://mathworld.wolfram.com/FixedPoint.html"class="external-link">fixed point<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. Pragmatically, this is a state in which running our analysis does not yield any more information.</p> <p>The way that the fixed point is found is called the <em>fixed point algorithm</em>. We&rsquo;ll talk more about this in the next post.</p> <div class="early-navigation-wrapper"> <nav class="series-navigation"><div class="previous wrapper"> <a href="https://danilafe.com/blog/02_spa_agda_combining_lattices/"> <svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#chevrons-left"/> </svg> <div class="title-subtitle"> Previous in Series <div class="title">Combining Lattices</div> </div> </a> </div> <div class="next wrapper"> <a href="https://danilafe.com/blog/04_spa_agda_fixedpoint/"> <div class="title-subtitle"> Next in Series <div class="title">The Fixed-Point Algorithm</div> </div> <svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#chevrons-right"/> </svg> </a> </div> </nav> </div> <a href="#appendix-the-unit-lattice"> <h3 id="appendix-the-unit-lattice">Appendix: The Unit Lattice</h3> </a> <p>The unit lattice is a relatively boring one. I use the built-in unit type in Agda, which (perhaps a bit confusingly) is represented using the symbol <code>⊤</code>. It only has a single constructor, <code>tt</code>.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Unit.agda" data-first-line="6" data-last-line="7" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Unit.agda#L6-L7">Unit.agda</a>, lines 6 through 7</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">6 </span><span class="lnt">7 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="kr">open</span><span class="w"> </span><span class="kr">import</span><span class="w"> </span><span class="n">Data.Unit</span><span class="w"> </span><span class="kr">using</span><span class="w"> </span><span class="o">(</span>⊤;<span class="w"> </span>tt<span class="o">)</span><span class="w"> </span>public<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kr">open</span><span class="w"> </span><span class="kr">import</span><span class="w"> </span><span class="n">Data.Unit.Properties</span><span class="w"> </span><span class="kr">using</span><span class="w"> </span><span class="o">(</span>_≟_;<span class="w"> </span>≡-setoid<span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The equivalence for the unit type is just propositional equality (we have no need to identify unequal values of <code>⊤</code>, since there is only one value).</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Unit.agda" data-first-line="17" data-last-line="25" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Unit.agda#L17-L25">Unit.agda</a>, lines 17 through 25</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="nf">_≈_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>⊤<span class="w"> </span><span class="ow">→</span><span class="w"> </span>⊤<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>_≈_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>_≡_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nf">≈-equiv</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsEquivalence<span class="w"> </span>⊤<span class="w"> </span>_≈_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>≈-equiv<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">record</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span><span class="w"> </span>≈-refl<span class="w"> </span><span class="ow">=</span><span class="w"> </span>refl<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>≈-sym<span class="w"> </span><span class="ow">=</span><span class="w"> </span>sym<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>≈-trans<span class="w"> </span><span class="ow">=</span><span class="w"> </span>trans<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Both the join \((\sqcup)\) and meet \((\sqcap)\) operations are trivially defined; in both cases, they simply take two <code>tt</code>s and produce a new <code>tt</code>. Mathematically, one might write this as \((\text{tt}, \text{tt}) \mapsto \text{tt}\). In Agda:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Unit.agda" data-first-line="30" data-last-line="34" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Unit.agda#L30-L34">Unit.agda</a>, lines 30 through 34</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="nf">_⊔_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>⊤<span class="w"> </span><span class="ow">→</span><span class="w"> </span>⊤<span class="w"> </span><span class="ow">→</span><span class="w"> </span>⊤<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>tt<span class="w"> </span>⊔<span class="w"> </span>tt<span class="w"> </span><span class="ow">=</span><span class="w"> </span>tt<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nf">_⊓_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>⊤<span class="w"> </span><span class="ow">→</span><span class="w"> </span>⊤<span class="w"> </span><span class="ow">→</span><span class="w"> </span>⊤<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>tt<span class="w"> </span>⊓<span class="w"> </span>tt<span class="w"> </span><span class="ow">=</span><span class="w"> </span>tt</span></span></code></pre></td></tr></table> </div> </div> </div> <p>These operations are trivially associative, commutative, and idempotent.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Unit.agda" data-first-line="39" data-last-line="46" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Unit.agda#L39-L46">Unit.agda</a>, lines 39 through 46</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="nf">⊔-assoc</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span>y<span class="w"> </span>z<span class="w"> </span><span class="ow">:</span><span class="w"> </span>⊤<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">((</span>x<span class="w"> </span>⊔<span class="w"> </span>y<span class="o">)</span><span class="w"> </span>⊔<span class="w"> </span>z<span class="o">)</span><span class="w"> </span>≈<span class="w"> </span><span class="o">(</span>x<span class="w"> </span>⊔<span class="w"> </span><span class="o">(</span>y<span class="w"> </span>⊔<span class="w"> </span>z<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>⊔-assoc<span class="w"> </span>tt<span class="w"> </span>tt<span class="w"> </span>tt<span class="w"> </span><span class="ow">=</span><span class="w"> </span>Eq.refl<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nf">⊔-comm</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span>y<span class="w"> </span><span class="ow">:</span><span class="w"> </span>⊤<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span>⊔<span class="w"> </span>y<span class="o">)</span><span class="w"> </span>≈<span class="w"> </span><span class="o">(</span>y<span class="w"> </span>⊔<span class="w"> </span>x<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>⊔-comm<span class="w"> </span>tt<span class="w"> </span>tt<span class="w"> </span><span class="ow">=</span><span class="w"> </span>Eq.refl<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nf">⊔-idemp</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span><span class="ow">:</span><span class="w"> </span>⊤<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span>⊔<span class="w"> </span>x<span class="o">)</span><span class="w"> </span>≈<span class="w"> </span>x<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>⊔-idemp<span class="w"> </span>tt<span class="w"> </span><span class="ow">=</span><span class="w"> </span>Eq.refl</span></span></code></pre></td></tr></table> </div> </div> </div> <p>That&rsquo;s sufficient for them to be semilattices:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Unit.agda" data-first-line="48" data-last-line="54" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Unit.agda#L48-L54">Unit.agda</a>, lines 48 through 54</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="nf">isJoinSemilattice</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsSemilattice<span class="w"> </span>⊤<span class="w"> </span>_≈_<span class="w"> </span>_⊔_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>isJoinSemilattice<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">record</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span><span class="w"> </span>≈-equiv<span class="w"> </span><span class="ow">=</span><span class="w"> </span>≈-equiv<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>≈-⊔-cong<span class="w"> </span><span class="ow">=</span><span class="w"> </span>≈-⊔-cong<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>⊔-assoc<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊔-assoc<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>⊔-comm<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊔-comm<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>⊔-idemp<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊔-idemp</span></span></code></pre></td></tr></table> </div> </div> </div> <p>The <a href="https://danilafe.com/blog/01_spa_agda_lattices/#absorption-laws">absorption laws</a> are also trivially satisfied, which means that the unit type forms a lattice.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Unit.agda" data-first-line="78" data-last-line="90" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Unit.agda#L78-L90">Unit.agda</a>, lines 78 through 90</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">78 </span><span class="lnt">79 </span><span class="lnt">80 </span><span class="lnt">81 </span><span class="lnt">82 </span><span class="lnt">83 </span><span class="lnt">84 </span><span class="lnt">85 </span><span class="lnt">86 </span><span class="lnt">87 </span><span class="lnt">88 </span><span class="lnt">89 </span><span class="lnt">90 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="nf">absorb-⊔-⊓</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span>y<span class="w"> </span><span class="ow">:</span><span class="w"> </span>⊤<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span>⊔<span class="w"> </span><span class="o">(</span>x<span class="w"> </span>⊓<span class="w"> </span>y<span class="o">))</span><span class="w"> </span>≈<span class="w"> </span>x<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>absorb-⊔-⊓<span class="w"> </span>tt<span class="w"> </span>tt<span class="w"> </span><span class="ow">=</span><span class="w"> </span>Eq.refl<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nf">absorb-⊓-⊔</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span>y<span class="w"> </span><span class="ow">:</span><span class="w"> </span>⊤<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span>⊓<span class="w"> </span><span class="o">(</span>x<span class="w"> </span>⊔<span class="w"> </span>y<span class="o">))</span><span class="w"> </span>≈<span class="w"> </span>x<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>absorb-⊓-⊔<span class="w"> </span>tt<span class="w"> </span>tt<span class="w"> </span><span class="ow">=</span><span class="w"> </span>Eq.refl<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nf">isLattice</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsLattice<span class="w"> </span>⊤<span class="w"> </span>_≈_<span class="w"> </span>_⊔_<span class="w"> </span>_⊓_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>isLattice<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">record</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span><span class="w"> </span>joinSemilattice<span class="w"> </span><span class="ow">=</span><span class="w"> </span>isJoinSemilattice<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>meetSemilattice<span class="w"> </span><span class="ow">=</span><span class="w"> </span>isMeetSemilattice<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>absorb-⊔-⊓<span class="w"> </span><span class="ow">=</span><span class="w"> </span>absorb-⊔-⊓<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>absorb-⊓-⊔<span class="w"> </span><span class="ow">=</span><span class="w"> </span>absorb-⊓-⊔<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Since there&rsquo;s only one element, it&rsquo;s not really possible to have chains that contain any more than one value. As a result, the height (in comparisons) of the unit lattice is zero.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Unit.agda" data-first-line="102" data-last-line="117" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Unit.agda#L102-L117">Unit.agda</a>, lines 102 through 117</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">102 </span><span class="lnt">103 </span><span class="lnt">104 </span><span class="lnt">105 </span><span class="lnt">106 </span><span class="lnt">107 </span><span class="lnt">108 </span><span class="lnt">109 </span><span class="lnt">110 </span><span class="lnt">111 </span><span class="lnt">112 </span><span class="lnt">113 </span><span class="lnt">114 </span><span class="lnt">115 </span><span class="lnt">116 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="kr">private</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">longestChain</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Chain<span class="w"> </span>tt<span class="w"> </span>tt<span class="w"> </span><span class="mi">0</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>longestChain<span class="w"> </span><span class="ow">=</span><span class="w"> </span>done<span class="w"> </span>refl<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">isLongest</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>t₁<span class="w"> </span>t₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>⊤<span class="o">}</span><span class="w"> </span><span class="o">{</span>n<span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Chain<span class="w"> </span>t₁<span class="w"> </span>t₂<span class="w"> </span>n<span class="w"> </span><span class="ow">→</span><span class="w"> </span>n<span class="w"> </span>≤<span class="w"> </span><span class="mi">0</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>isLongest<span class="w"> </span><span class="o">{</span>tt<span class="o">}</span><span class="w"> </span><span class="o">{</span>tt<span class="o">}</span><span class="w"> </span><span class="o">(</span>step<span class="w"> </span><span class="o">(</span>tt⊔tt≈tt<span class="w"> </span>,<span class="w"> </span>tt̷≈tt<span class="o">)</span><span class="w"> </span>_<span class="w"> </span>_<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥-elim<span class="w"> </span><span class="o">(</span>tt̷≈tt<span class="w"> </span>refl<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>isLongest<span class="w"> </span><span class="o">(</span>done<span class="w"> </span>_<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>z≤n<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nf">fixedHeight</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsLattice.FixedHeight<span class="w"> </span>isLattice<span class="w"> </span><span class="mi">0</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>fixedHeight<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">record</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span><span class="w"> </span>⊥<span class="w"> </span><span class="ow">=</span><span class="w"> </span>tt<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>⊤<span class="w"> </span><span class="ow">=</span><span class="w"> </span>tt<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>longestChain<span class="w"> </span><span class="ow">=</span><span class="w"> </span>longestChain<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>bounded<span class="w"> </span><span class="ow">=</span><span class="w"> </span>isLongest<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div> </div> Implementing and Verifying "Static Program Analysis" in Agda, Part 2: Combining Lattices https://danilafe.com/blog/02_spa_agda_combining_lattices/ Thu, 08 Aug 2024 16:40:00 -0700 https://danilafe.com/blog/02_spa_agda_combining_lattices/ <p>In the previous post, I wrote about how lattices arise when tracking, comparing and combining static information about programs. I then showed two simple lattices: the natural numbers, and the (parameterized) &ldquo;above-below&rdquo; lattice, which modified an arbitrary set with &ldquo;bottom&rdquo; and &ldquo;top&rdquo; elements (\(\bot\) and \(\top\) respectively). One instance of the &ldquo;above-below&rdquo; lattice was the sign lattice, which could be used to reason about the signs (positive, negative, or zero) of variables in a program.</p> <p>At the end of that post, I introduced a source of complexity: the &ldquo;full&rdquo; lattices that we want to use for the program analysis aren&rsquo;t signs or numbers, but maps of states and variables to lattice-based descriptions. The full lattice for sign analysis might something in the form:</p> $$ \text{Info} \triangleq \text{ProgramStates} \to (\text{Variables} \to \text{Sign}) $$ <p>Thus, we have to compare and find least upper bounds (e.g.) of not just signs, but maps! Proving the various lattice laws for signs was not too challenging, but for for a two-level map like \(\text{Info}\) above, we&rsquo;d need to do a lot more work. We need tools to build up such complicated lattices.</p> <p>The way to do this, it turns out, is by using simpler lattices as building blocks. To start with, let&rsquo;s take a look at a very simple way of combining lattices into a new one: taking the <a href="https://mathworld.wolfram.com/CartesianProduct.html"class="external-link">Cartesian product<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>.</p> <a href="#the-cartesian-product-lattice"> <h3 id="the-cartesian-product-lattice">The Cartesian Product Lattice</h3> </a> <p>Suppose you have two lattices \(L_1\) and \(L_2\). As I covered in the previous post, each lattice comes equipped with a &ldquo;least upper bound&rdquo; operator \((\sqcup)\) and a &ldquo;greatest lower bound&rdquo; operator \((\sqcap)\). Since we now have two lattices, let&rsquo;s use numerical suffixes to disambiguate between the operators of the first and second lattice: \((\sqcup_1)\) will be the LUB operator of the first lattice \(L_1\), and \((\sqcup_2)\) of the second lattice \(L_2\), and so on.</p> <p>Then, let&rsquo;s take the Cartesian product of the elements of \(L_1\) and \(L_2\); mathematically, we&rsquo;ll write this as \(L_1 \times L_2\), and in Agda, we can just use the standard <a href="https://agda.github.io/agda-stdlib/master/Data.Product.html"class="external-link"><code>Data.Product</code><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> module. Then, I&rsquo;ll define the lattice as another <a href="https://agda.readthedocs.io/en/latest/language/module-system.html#parameterised-modules"class="external-link">parameterized module<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. Since both \(L_1\) and \(L_2\) are lattices, this parameterized module will require <code>IsLattice</code> instances for both types:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Prod.agda" data-first-line="1" data-last-line="7" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Prod.agda#L1-L7">Prod.agda</a>, lines 1 through 7</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="hl"><span class="lnt">7 </span></span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="kr">open</span><span class="w"> </span><span class="kr">import</span><span class="w"> </span><span class="n">Lattice</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kr">module</span><span class="w"> </span><span class="n">Lattice.Prod</span><span class="w"> </span><span class="o">{</span>a<span class="w"> </span>b<span class="o">}</span><span class="w"> </span><span class="o">{</span>A<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">}</span><span class="w"> </span><span class="o">{</span>B<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>b<span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>_≈₁_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">)</span><span class="w"> </span><span class="o">(</span>_≈₂_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>b<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>_⊔₁_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="o">(</span>_⊔₂_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>_⊓₁_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="o">(</span>_⊓₂_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="o">)</span><span class="w"> </span></span></span><span class="line hl"><span class="cl"><span class="w"> </span><span class="o">(</span>lA<span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsLattice<span class="w"> </span>A<span class="w"> </span>_≈₁_<span class="w"> </span>_⊔₁_<span class="w"> </span>_⊓₁_<span class="o">)</span><span class="w"> </span><span class="o">(</span>lB<span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsLattice<span class="w"> </span>B<span class="w"> </span>_≈₂_<span class="w"> </span>_⊔₂_<span class="w"> </span>_⊓₂_<span class="o">)</span><span class="w"> </span><span class="kr">where</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Elements of \(L_1 \times L_2\) are in the form \((l_1, l_2)\), where \(l_1 \in L_1\) and \(l_2 \in L_2\). Knowing that, let&rsquo;s define what it means for two such elements to be equal. Recall that we opted for a <a href="https://danilafe.com/blog/01_spa_agda_lattices/#definitional-equality">custom equivalence relation</a> instead of definitional equality to allow similar elements to be considered equal; we&rsquo;ll have to define a similar relation for our new product lattice. That&rsquo;s easy enough: we have an equality predicate <code>_≈₁_</code> that checks if an element of \(L_1\) is equal to another, and we have <code>_≈₂_</code> that does the same for \(L_2\). It&rsquo;s reasonable to say that <em>pairs</em> of elements are equal if their respective first and second elements are equal:</p> $$ (l_1, l_2) \approx (j_1, j_2) \iff l_1 \approx_1 j_1 \land l_2 \approx_2 j_2 $$ <p>In Agda:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Prod.agda" data-first-line="39" data-last-line="40" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Prod.agda#L39-L40">Prod.agda</a>, lines 39 through 40</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">39 </span><span class="lnt">40 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">_≈_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span>×<span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span>×<span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span><span class="o">(</span>a<span class="w"> </span>⊔ℓ<span class="w"> </span>b<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">(</span>a₁<span class="w"> </span>,<span class="w"> </span>b₁<span class="o">)</span><span class="w"> </span>≈<span class="w"> </span><span class="o">(</span>a₂<span class="w"> </span>,<span class="w"> </span>b₂<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>a₁<span class="w"> </span>≈₁<span class="w"> </span>a₂<span class="o">)</span><span class="w"> </span>×<span class="w"> </span><span class="o">(</span>b₁<span class="w"> </span>≈₂<span class="w"> </span>b₂<span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Verifying that this relation has the properties of an equivalence relation boils down to the fact that <code>_≈₁_</code> and <code>_≈₂_</code> are themselves equivalence relations.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Prod.agda" data-first-line="42" data-last-line="48" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Prod.agda#L42-L48">Prod.agda</a>, lines 42 through 48</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">≈-equiv</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsEquivalence<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">)</span><span class="w"> </span>_≈_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>≈-equiv<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">record</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span><span class="w"> </span>≈-refl<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ow">λ</span><span class="w"> </span><span class="o">{</span>p<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>≈₁-refl<span class="w"> </span>,<span class="w"> </span>≈₂-refl<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>≈-sym<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ow">λ</span><span class="w"> </span><span class="o">{</span>p₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>p₂<span class="o">}</span><span class="w"> </span><span class="o">(</span>a₁≈a₂<span class="w"> </span>,<span class="w"> </span>b₁≈b₂<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>≈₁-sym<span class="w"> </span>a₁≈a₂<span class="w"> </span>,<span class="w"> </span>≈₂-sym<span class="w"> </span>b₁≈b₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>≈-trans<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ow">λ</span><span class="w"> </span><span class="o">{</span>p₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>p₂<span class="o">}</span><span class="w"> </span><span class="o">{</span>p₃<span class="o">}</span><span class="w"> </span><span class="o">(</span>a₁≈a₂<span class="w"> </span>,<span class="w"> </span>b₁≈b₂<span class="o">)</span><span class="w"> </span><span class="o">(</span>a₂≈a₃<span class="w"> </span>,<span class="w"> </span>b₂≈b₃<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span><span class="w"> </span>≈₁-trans<span class="w"> </span>a₁≈a₂<span class="w"> </span>a₂≈a₃<span class="w"> </span>,<span class="w"> </span>≈₂-trans<span class="w"> </span>b₁≈b₂<span class="w"> </span>b₂≈b₃<span class="w"> </span><span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Defining \((\sqcup)\) and \((\sqcap)\) by simply applying the corresponding operators from \(L_1\) and \(L_2\) seems quite natural as well.</p> $$ (l_1, l_2) \sqcup (j_1, j_2) \triangleq (l_1 \sqcup_1 j_1, l_2 \sqcup_2 j_2) \\ (l_1, l_2) \sqcap (j_1, j_2) \triangleq (l_1 \sqcap_1 j_1, l_2 \sqcap_2 j_2) $$ <p>As an example, consider the product lattice \(\text{Sign}\times\text{Sign}\), which is made up of pairs of signs that we talked about in the previous post. Two elements of this lattice are \((+, +)\) and \((+, -)\). Here&rsquo;s how the \((\sqcup)\) operation is evaluated on them:</p> $$ (&#43;, &#43;) \sqcup (&#43;, -) = (&#43; \sqcup &#43; , &#43; \sqcup -) = (&#43; , \top) $$ <p>In Agda, the definition is written very similarly to its mathematical form:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Prod.agda" data-first-line="50" data-last-line="54" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Prod.agda#L50-L54">Prod.agda</a>, lines 50 through 54</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">_⊔_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span>×<span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span>×<span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span>×<span class="w"> </span>B<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">(</span>a₁<span class="w"> </span>,<span class="w"> </span>b₁<span class="o">)</span><span class="w"> </span>⊔<span class="w"> </span><span class="o">(</span>a₂<span class="w"> </span>,<span class="w"> </span>b₂<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>a₁<span class="w"> </span>⊔₁<span class="w"> </span>a₂<span class="w"> </span>,<span class="w"> </span>b₁<span class="w"> </span>⊔₂<span class="w"> </span>b₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nf">_⊓_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span>×<span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span>×<span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span>×<span class="w"> </span>B<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">(</span>a₁<span class="w"> </span>,<span class="w"> </span>b₁<span class="o">)</span><span class="w"> </span>⊓<span class="w"> </span><span class="o">(</span>a₂<span class="w"> </span>,<span class="w"> </span>b₂<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>a₁<span class="w"> </span>⊓₁<span class="w"> </span>a₂<span class="w"> </span>,<span class="w"> </span>b₁<span class="w"> </span>⊓₂<span class="w"> </span>b₂<span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>All that&rsquo;s left is to prove the various (semi)lattice properties. Intuitively, we can see that since the &ldquo;combined&rdquo; operator <code>_⊔_</code> just independently applies the element operators <code>_⊔₁_</code> and <code>_⊔₂_</code>, as long as they are idempotent, commutative, and associative, so is the &ldquo;combined&rdquo; operator itself. Moreover, the proofs that <code>_⊔_</code> and <code>_⊓_</code> form semilattices are identical up to replacing \((\sqcup)\) with \((\sqcap)\). Thus, in Agda, we can write the code once, parameterizing it by the binary operators involved (and proofs that these operators obey the semilattice laws).</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Prod.agda" data-first-line="56" data-last-line="82" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Prod.agda#L56-L82">Prod.agda</a>, lines 56 through 82</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span><span class="lnt">65 </span><span class="lnt">66 </span><span class="lnt">67 </span><span class="lnt">68 </span><span class="lnt">69 </span><span class="lnt">70 </span><span class="lnt">71 </span><span class="lnt">72 </span><span class="lnt">73 </span><span class="lnt">74 </span><span class="lnt">75 </span><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span><span class="lnt">79 </span><span class="lnt">80 </span><span class="lnt">81 </span><span class="lnt">82 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="kr">private</span><span class="w"> </span><span class="kr">module</span><span class="w"> </span><span class="n">ProdIsSemilattice</span><span class="w"> </span><span class="o">(</span>f₁<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="o">(</span>f₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="o">)</span><span class="w"> </span><span class="o">(</span>sA<span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsSemilattice<span class="w"> </span>A<span class="w"> </span>_≈₁_<span class="w"> </span>f₁<span class="o">)</span><span class="w"> </span><span class="o">(</span>sB<span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsSemilattice<span class="w"> </span>B<span class="w"> </span>_≈₂_<span class="w"> </span>f₂<span class="o">)</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">isSemilattice</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsSemilattice<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">)</span><span class="w"> </span>_≈_<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span><span class="o">(</span>a₁<span class="w"> </span>,<span class="w"> </span>b₁<span class="o">)</span><span class="w"> </span><span class="o">(</span>a₂<span class="w"> </span>,<span class="w"> </span>b₂<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>f₁<span class="w"> </span>a₁<span class="w"> </span>a₂<span class="w"> </span>,<span class="w"> </span>f₂<span class="w"> </span>b₁<span class="w"> </span>b₂<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>isSemilattice<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">record</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span><span class="w"> </span>≈-equiv<span class="w"> </span><span class="ow">=</span><span class="w"> </span>≈-equiv<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>≈-⊔-cong<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ow">λ</span><span class="w"> </span><span class="o">(</span>a₁≈a₂<span class="w"> </span>,<span class="w"> </span>b₁≈b₂<span class="o">)</span><span class="w"> </span><span class="o">(</span>a₃≈a₄<span class="w"> </span>,<span class="w"> </span>b₃≈b₄<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span><span class="w"> </span>IsSemilattice.≈-⊔-cong<span class="w"> </span>sA<span class="w"> </span>a₁≈a₂<span class="w"> </span>a₃≈a₄<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>,<span class="w"> </span>IsSemilattice.≈-⊔-cong<span class="w"> </span>sB<span class="w"> </span>b₁≈b₂<span class="w"> </span>b₃≈b₄<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>⊔-assoc<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ow">λ</span><span class="w"> </span><span class="o">(</span>a₁<span class="w"> </span>,<span class="w"> </span>b₁<span class="o">)</span><span class="w"> </span><span class="o">(</span>a₂<span class="w"> </span>,<span class="w"> </span>b₂<span class="o">)</span><span class="w"> </span><span class="o">(</span>a₃<span class="w"> </span>,<span class="w"> </span>b₃<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span><span class="w"> </span>IsSemilattice.⊔-assoc<span class="w"> </span>sA<span class="w"> </span>a₁<span class="w"> </span>a₂<span class="w"> </span>a₃<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>,<span class="w"> </span>IsSemilattice.⊔-assoc<span class="w"> </span>sB<span class="w"> </span>b₁<span class="w"> </span>b₂<span class="w"> </span>b₃<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>⊔-comm<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ow">λ</span><span class="w"> </span><span class="o">(</span>a₁<span class="w"> </span>,<span class="w"> </span>b₁<span class="o">)</span><span class="w"> </span><span class="o">(</span>a₂<span class="w"> </span>,<span class="w"> </span>b₂<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span><span class="w"> </span>IsSemilattice.⊔-comm<span class="w"> </span>sA<span class="w"> </span>a₁<span class="w"> </span>a₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>,<span class="w"> </span>IsSemilattice.⊔-comm<span class="w"> </span>sB<span class="w"> </span>b₁<span class="w"> </span>b₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>⊔-idemp<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ow">λ</span><span class="w"> </span><span class="o">(</span>a<span class="w"> </span>,<span class="w"> </span>b<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span><span class="w"> </span>IsSemilattice.⊔-idemp<span class="w"> </span>sA<span class="w"> </span>a<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>,<span class="w"> </span>IsSemilattice.⊔-idemp<span class="w"> </span>sB<span class="w"> </span>b<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nf">isJoinSemilattice</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsSemilattice<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">)</span><span class="w"> </span>_≈_<span class="w"> </span>_⊔_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>isJoinSemilattice<span class="w"> </span><span class="ow">=</span><span class="w"> </span>ProdIsSemilattice.isSemilattice<span class="w"> </span>_⊔₁_<span class="w"> </span>_⊔₂_<span class="w"> </span>joinSemilattice₁<span class="w"> </span>joinSemilattice₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nf">isMeetSemilattice</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsSemilattice<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">)</span><span class="w"> </span>_≈_<span class="w"> </span>_⊓_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>isMeetSemilattice<span class="w"> </span><span class="ow">=</span><span class="w"> </span>ProdIsSemilattice.isSemilattice<span class="w"> </span>_⊓₁_<span class="w"> </span>_⊓₂_<span class="w"> </span>meetSemilattice₁<span class="w"> </span>meetSemilattice₂</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Above, I used <code>f₁</code> to stand for &ldquo;either <code>_⊔₁_</code> or <code>_⊓₁_</code>&rdquo;, and similarly <code>f₂</code> for &ldquo;either <code>_⊔₂_</code> or <code>_⊓₂_</code>&rdquo;. Much like the semilattice properties, proving lattice properties boils down to applying the lattice properties of \(L_1\) and \(L_2\) to individual components.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Prod.agda" data-first-line="84" data-last-line="96" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Prod.agda#L84-L96">Prod.agda</a>, lines 84 through 96</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">84 </span><span class="lnt">85 </span><span class="lnt">86 </span><span class="lnt">87 </span><span class="lnt">88 </span><span class="lnt">89 </span><span class="lnt">90 </span><span class="lnt">91 </span><span class="lnt">92 </span><span class="lnt">93 </span><span class="lnt">94 </span><span class="lnt">95 </span><span class="lnt">96 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">isLattice</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsLattice<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">)</span><span class="w"> </span>_≈_<span class="w"> </span>_⊔_<span class="w"> </span>_⊓_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>isLattice<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">record</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span><span class="w"> </span>joinSemilattice<span class="w"> </span><span class="ow">=</span><span class="w"> </span>isJoinSemilattice<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>meetSemilattice<span class="w"> </span><span class="ow">=</span><span class="w"> </span>isMeetSemilattice<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>absorb-⊔-⊓<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ow">λ</span><span class="w"> </span><span class="o">(</span>a₁<span class="w"> </span>,<span class="w"> </span>b₁<span class="o">)</span><span class="w"> </span><span class="o">(</span>a₂<span class="w"> </span>,<span class="w"> </span>b₂<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span><span class="w"> </span>IsLattice.absorb-⊔-⊓<span class="w"> </span>lA<span class="w"> </span>a₁<span class="w"> </span>a₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>,<span class="w"> </span>IsLattice.absorb-⊔-⊓<span class="w"> </span>lB<span class="w"> </span>b₁<span class="w"> </span>b₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>absorb-⊓-⊔<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ow">λ</span><span class="w"> </span><span class="o">(</span>a₁<span class="w"> </span>,<span class="w"> </span>b₁<span class="o">)</span><span class="w"> </span><span class="o">(</span>a₂<span class="w"> </span>,<span class="w"> </span>b₂<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span><span class="w"> </span>IsLattice.absorb-⊓-⊔<span class="w"> </span>lA<span class="w"> </span>a₁<span class="w"> </span>a₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>,<span class="w"> </span>IsLattice.absorb-⊓-⊔<span class="w"> </span>lB<span class="w"> </span>b₁<span class="w"> </span>b₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This concludes the definition of the product lattice, which is made up of two other lattices. If we have a type of analysis that can be expressed as <span class="sidenote"> <label class="sidenote-label" for="pair-note">a pair of two signs,</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="pair-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Perhaps the signs are the smallest and largest possible values of a variable. <span class="sidenote-delimiter">]</span> </span> </span> for example, we won&rsquo;t have to do all the work of proving the (semi)lattice properties of those pairs. In fact, we can build up even bigger data structures. By taking a product twice, like \(L_1 \times (L_2 \times L_3)\), we can construct a lattice of 3-tuples. Any of the lattices involved in that product can itself be a product; we can therefore create lattices out of arbitrary bundles of data, so long as the smallest pieces that make up the bundles are themselves lattices.</p> <p>Products will come very handy a bit later in this series. For now though, our goal is to create another type of lattice: the map lattice. We will take the same approach we did with products: assuming the elements of the map are lattices, we&rsquo;ll prove that the map itself is a lattice. Then, just like we could put products inside products when building up lattices, we&rsquo;ll be able to put a map inside a map. This will allow us to represent the \(\text{Info}\) lattice, which is a map of maps.</p> <a href="#the-map-lattice"> <h3 id="the-map-lattice">The Map Lattice</h3> </a> <a href="#the-theory"> <h4 id="the-theory">The Theory</h4> </a> <p>When I say &ldquo;map&rdquo;, what I really means is something that associates keys with values, like <a href="https://docs.python.org/3/tutorial/datastructures.html#dictionaries"class="external-link">dictionaries in Python<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. This data structure need not have a value for every possible key; a very precise author might call such a map a &ldquo;partial map&rdquo;. We might have a map whose value (in Python-ish notation) is <code>{ &quot;x&quot;: +, &quot;y&quot;: - }</code>. Such a map states that the sign of the variable <code>x</code> is <code>+</code>, and the sign of variable <code>y</code> is <code>-</code>. Another possible map is <code>{ &quot;y&quot;: +, &quot;z&quot;: - }</code>; this one states that the sign of <code>y</code> is <code>+</code>, and the sign of another variable <code>z</code> is <code>-</code>.</p> <p>Let&rsquo;s start thinking about what sorts of lattices our maps will be. The thing that <a href="https://danilafe.com/blog/01_spa_agda_lattices/#specificity">motivated our introduction</a> of lattices was comparing them by &ldquo;specificity&rdquo;, so let&rsquo;s try figure out how to compare maps. For that, we can begin small, by looking at singleton maps. If we have <code>{&quot;x&quot;: +}</code> and <code>{&quot;x&quot;: ⊤}</code>, which one of them is smaller? Well, we have previously established that <code>+</code> is more specific (and thus less than) <code>⊤</code>. Thus, it shouldn&rsquo;t be too much of a stretch to say that for singleton maps of the same key, the one with the smaller value is smaller.</p> <p>Now, what about a pair of singleton maps like <code>{&quot;x&quot;: +}</code> and <code>{&quot;y&quot;: ⊤}</code>? Among these two, each contains some information that the other does not. Although the value of <code>y</code> is larger than the value of <code>x</code>, it describes a different key, so it seems wrong to use that to call the <code>y</code>-singleton &ldquo;larger&rdquo;. Let&rsquo;s call these maps incompatible, then. More generally, if we have two maps and each one has a key that the other doesn&rsquo;t, we can&rsquo;t compare them.</p> <p>If only one map has a unique key, though, things are different. Take for instance <code>{&quot;x&quot;: +}</code> and <code>{&quot;x&quot;: +, &quot;y&quot;: +}</code>. Are they really incomparable? The keys that the two maps do share can be compared (<code>+ &lt;= +</code>, because they&rsquo;re equal).</p> <p>All of the above leads to the following conventional definition, which I find easier to further motivate using \((\sqcup)\) and \((\sqcap)\) (and <a href="#union-as-or">do so below</a>).</p> <blockquote> <p>A map <code>m1</code> is less than or equal to another map <code>m2</code> (<code>m1 &lt;= m2</code>) if for every key <code>k</code> that has a value in <code>m1</code>, the key also has a value in <code>m2</code>, and <code>m1[k] &lt;= m2[k]</code>.</p> </blockquote> <p>That definitions matches our intuitions so far. The only key in <code>{&quot;x&quot;: +}</code> is <code>x</code>; this key is also in <code>{&quot;x&quot;: ⊤}</code> (check) and <code>+ &lt; ⊤</code> (check). On the other hand, both <code>{&quot;x&quot;: +}</code> and <code>{&quot;y&quot;: ⊤}</code> have a key that the other doesn&rsquo;t, so the definition above is not satisfied. Finally, for <code>{&quot;x&quot;: +}</code> and <code>{&quot;x&quot;: +, &quot;y&quot;: +}</code>, the only key in the former is also present in the latter, and <code>+ &lt;= +</code>; the definition is satisfied.</p> <p>Next, we need to define the \((\sqcup)\) and \((\sqcap)\) operators that match our definition of &ldquo;less than or equal&rdquo;. Let&rsquo;s start with \((\sqcup)\). For two maps \(m_1\) and \(m_2\), the join of those two maps, \(m_1 \sqcup m_2\) should be greater than or equal to both; in other words, both sub-maps should be less than or equal to the join.</p> <p>Our newly-introduced condition for &ldquo;less than or equal&rdquo; requires that each key in the smaller map be present in the larger one; as a result, \(m_1 \sqcup m_2\) should contain all the keys in \(m_1\) <strong>and</strong> all the keys in \(m_2\). So, we could just take the union of the two maps: copy values from both into the result. Only, what happens if both \(m_1\) and \(m_2\) have a value mapped to a particular key \(k\)? The values in the two maps could be distinct, and they might even be incomparable. This is where the second part of the condition kicks in: the value in the combination of the maps needs to be bigger than the value in either sub-map. We already know how to get a value that&rsquo;s bigger than two other values: we use a join on the values!</p> <p>Thus, define \(m_1 \sqcup m_2\) as a map that has all the keys from \(m_1\) and \(m_2\), where the value at a particular key is given as follows:</p> $$ (m_1 \sqcup m_2)[k] = \begin{cases} m_1[k] \sqcup m_2[k] &amp;amp; k \in m_1, k \in m_2 \\ m_1[k] &amp;amp; k \in m_1, k \notin m_2 \\ m_2[k] &amp;amp; k \notin m_1, k \in m_2 \end{cases} $$ <p id="union-as-or">If you&rsquo;re familiar with set theory, this operation is like <span class="sidenote"> <label class="sidenote-label" for="map-union-note">an extension of the union operator \((\cup)\)</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="map-union-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> There are, of course, other ways to extend the "union" operation to maps. Haskell, for instance, defines it in a "left-biased" way (preferring the elements from the left operand of the operation when duplicates are encountered).<br> <br> However, with a "join" operation \((\sqcup)\) that's defined on the values stored in the map gives us an extra tool to work with. As a result, I would argue that our extension, given such an operator, is the most natural. <span class="sidenote-delimiter">]</span> </span> </span> to maps. In fact, this begins to motivate the choice to use \((\sqcup)\) to denote this operation. A further bit of motivation is this: <a href="https://danilafe.com/blog/01_spa_agda_lattices/#lub-glub-or-and">we&rsquo;ve already seen</a> that the \((\sqcup)\) and \((\sqcap)\) operators correspond to &ldquo;or&rdquo; and &ldquo;and&rdquo;. The elements in the union of two sets are precisely those that are in one set <strong>or</strong> the other. Thus, using union here fits our notion of how the \((\sqcup)\) operator behaves.</p> <p>Now, let&rsquo;s take a look at the \((\sqcap)\) operator. For two maps \(m_1\) and \(m_2\), the meet of those two maps, \(m_1 \sqcap m_2\) should be less than or equal to both. Our definition above requires that each key of the smaller map is present in the larger map; for the combination of two maps to be smaller than both, we must ensure that it only has keys present in both maps. To combine the elements from the two maps, we can use the \((\sqcap)\) operator on values.</p> $$ (m_1 \sqcap m_2)[k] = m_1[k] \sqcap m_2[k] $$ <p>Turning once again to set theory, we can think of this operation like the extension of the intersection operator \((\cap)\) to maps. This can be motivated in the same way as the union operation above; the \((\sqcap)\) operator combines lattice elements in such away that the result represents both of them, and intersections of sets contain elements that are in <strong>both</strong> sets.</p> <p>Now we have the the two binary operators and the comparison function in hand. There&rsquo;s just one detail we&rsquo;re missing: what it means for two maps to be equivalent. Here, once again we take our cue from set theory: two sets are said to be equal when each one is a subset of the other. Mathematically, we can write this as follows:</p> $$ m_1 \approx m_2 \triangleq m_1 \subseteq m_2 \land m_1 \supseteq m_2 $$ <p>I might as well show you the Agda definition of this, since it&rsquo;s a word-for-word transliteration:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Map.agda" data-first-line="530" data-last-line="531" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Map.agda#L530-L531">Map.agda</a>, lines 530 through 531</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">530 </span><span class="lnt">531 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">_≈_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Map<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Map<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span><span class="o">(</span>a<span class="w"> </span>⊔ℓ<span class="w"> </span>b<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>_≈_<span class="w"> </span>m₁<span class="w"> </span>m₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>m₁<span class="w"> </span>⊆<span class="w"> </span>m₂<span class="w"> </span>×<span class="w"> </span>m₂<span class="w"> </span>⊆<span class="w"> </span>m₁</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Defining equivalence more abstractly this way helps avoid concerns about the precise implementation of our maps.</p> <p>Okay, but we haven&rsquo;t actually defined what it means for one map to be a subset of another. My definition is as follows: if \(m_1 \subseteq m_2\), that is, if \(m_1\) is a subset of \(m_2\), then every key in \(m_1\) is also present in \(m_2\), and they are mapped to the same value. My first stab at a mathematical definition of this is the following:</p> $$ m_1 \subseteq m_2 \triangleq \forall k, v.\ (k, v) \in m_1 \Rightarrow (k, v) \in m_2 $$ <p>Only there&rsquo;s a slight complication; remember that our values themselves come from a lattice, and that this lattice might use its own equivalence operator \((\approx)\) to group similar elements. One example where this is important is our now-familiar &ldquo;map of maps&rdquo; scenario: the values store in the &ldquo;outer&rdquo; map are themselves maps, and we don&rsquo;t want the order of the keys or other menial details of the inner maps to influence whether the outer maps are equal. Thus, we settle for a more robust definition of \(m_1 \subseteq m_2\) that allows \(m_1\) to have different-but-equivalent values from those in \(m_2\).</p> $$ m_1 \subseteq m_2 \triangleq \forall k, v.\ (k, v) \in m_1 \Rightarrow \exists v&amp;#39;.\ v \approx v&amp;#39; \land (k, v&amp;#39;) \in m_2 $$ <p>In Agda, the core of my definition is once again very close:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Map.agda" data-first-line="98" data-last-line="99" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Map.agda#L98-L99">Map.agda</a>, lines 98 through 99</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">98 </span><span class="lnt">99 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span>subset<span class="w"> </span>m₁<span class="w"> </span>m₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>k<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="o">(</span>v<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>k<span class="w"> </span>,<span class="w"> </span>v<span class="o">)</span><span class="w"> </span>∈<span class="w"> </span>m₁<span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>Σ<span class="w"> </span>B<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>v&#39;<span class="w"> </span><span class="ow">→</span><span class="w"> </span>v<span class="w"> </span>≈₂<span class="w"> </span>v&#39;<span class="w"> </span>×<span class="w"> </span><span class="o">((</span>k<span class="w"> </span>,<span class="w"> </span>v&#39;<span class="o">)</span><span class="w"> </span>∈<span class="w"> </span>m₂<span class="o">))</span></span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#the-implementation"> <h4 id="the-implementation">The Implementation</h4> </a> <p>Now it&rsquo;s time to show you how I implemented the Map lattice. I chose represent maps using a list of key-value pairs, along with a condition that the keys are unique (non-repeating). I chose this definition because it was simple to implement, and because it makes it possible to iterate over the keys of a map. That last property is useful if we use the maps to later represent sets (which I did). Moreover, lists of key-value pairs are easy to serialize and write to disk. This isn&rsquo;t hugely important for my immediate static program analysis needs, but it might be nice in the future. The requirement that the keys are unique prevents the map from being a multi-map (which might have several values associated with a particular key).</p> <p>My <code>Map</code> module is parameterized by the key and value types (<code>A</code> and <code>B</code> respectively), and additionally requires some additional properties to be satisfied by these types.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Map.agda" data-first-line="6" data-last-line="10" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Map.agda#L6-L10">Map.agda</a>, lines 6 through 10</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="kr">module</span><span class="w"> </span><span class="n">Lattice.Map</span><span class="w"> </span><span class="o">{</span>a<span class="w"> </span>b<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Level<span class="o">}</span><span class="w"> </span><span class="o">{</span>A<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">}</span><span class="w"> </span><span class="o">{</span>B<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>b<span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span>_≈₂_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>b<span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span>_⊔₂_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="o">}</span><span class="w"> </span><span class="o">{</span>_⊓₂_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>≡-dec-A<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Decidable<span class="w"> </span><span class="o">(</span>_≡_<span class="w"> </span><span class="o">{</span>a<span class="o">}</span><span class="w"> </span><span class="o">{</span>A<span class="o">}))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>lB<span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsLattice<span class="w"> </span>B<span class="w"> </span>_≈₂_<span class="w"> </span>_⊔₂_<span class="w"> </span>_⊓₂_<span class="o">)</span><span class="w"> </span><span class="kr">where</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>For <code>A</code>, the key property is the <a href="https://en.wikipedia.org/wiki/Decidability_%28logic%29"class="external-link">decidability<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> of equality: there should be a way to compare keys for equality. This is important for all sorts of map operations. For example, when inserting a new value into a map, we need to decide if the value is already present (so that we know to override it), but if we can&rsquo;t check if two values are equal, we can&rsquo;t see if it&rsquo;s already there.</p> <p>The values of the map (represented by <code>B</code>) we expected to be lattices, so we require them to provide the lattice operations \((\sqcup)\) and \((\sqcap)\), as well as the equivalence relation \((\approx)\) and the proof of the lattice properties in <code>isLattice</code>. To distinguish the lattice operations on <code>B</code> from the ones we&rsquo;ll be defining on the map itself &ndash; you might&rsquo;ve noticed that there&rsquo;s a bit of overleading going on in this post &ndash; I&rsquo;ve suffixed them with the subscript <code>2</code>. My convention is to use the subscript corresponding to the number of the type parameter. Here, <code>A</code> is &ldquo;first&rdquo; and <code>B</code> is &ldquo;second&rdquo;, so the operators on <code>B</code> get <code>2</code>.</p> <p>From there, I define the map as a pair; the first component is the list of key-value pairs, and the second is the proof that all the keys in the list occur only once.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Map.agda" data-first-line="480" data-last-line="481" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Map.agda#L480-L481">Map.agda</a>, lines 480 through 481</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">480 </span><span class="lnt">481 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">Map</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span><span class="o">(</span>a<span class="w"> </span>⊔ℓ<span class="w"> </span>b<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>Map<span class="w"> </span><span class="ow">=</span><span class="w"> </span>Σ<span class="w"> </span><span class="o">(</span>List<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">))</span><span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>l<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Unique<span class="w"> </span><span class="o">(</span>ImplKeys.keys<span class="w"> </span>l<span class="o">))</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Now, to implement union and intersection; for the most part, the proofs deal just with the first component of the map &ndash; the key-value pairs. For union, the key operation is &ldquo;insert-or-combine&rdquo;. We can think of merging two maps as inserting all the keys from one map (arbitrary, the &ldquo;left&rdquo;) into the other. If a key is not in the &ldquo;left&rdquo; map, insertion won&rsquo;t do anything to its prior value in the right map; similarly, if a key is not in the &ldquo;right&rdquo; map, then it should appear unchanged in the final result after insertion. Finally, if a key is inserted into the &ldquo;right&rdquo; map, but already has a value there, then the two values need to be combined using <code>_⊔₂_</code>. This leads to the following definition of <code>insert</code> on key-value pair lists:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Map.agda" data-first-line="114" data-last-line="118" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Map.agda#L114-L118">Map.agda</a>, lines 114 through 118</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">114 </span><span class="lnt">115 </span><span class="lnt">116 </span><span class="lnt">117 </span><span class="lnt">118 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">insert</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>insert<span class="w"> </span>k<span class="w"> </span>v<span class="w"> </span>[]<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>k<span class="w"> </span>,<span class="w"> </span>v<span class="o">)</span><span class="w"> </span>∷<span class="w"> </span>[]<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>insert<span class="w"> </span>k<span class="w"> </span>v<span class="w"> </span><span class="o">(</span>x@<span class="o">(</span>k&#39;<span class="w"> </span>,<span class="w"> </span>v&#39;<span class="o">)</span><span class="w"> </span>∷<span class="w"> </span>xs<span class="o">)</span><span class="w"> </span><span class="kr">with</span><span class="w"> </span>≡-dec-A<span class="w"> </span>k<span class="w"> </span>k&#39;<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>yes<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>k&#39;<span class="w"> </span>,<span class="w"> </span>f<span class="w"> </span>v<span class="w"> </span>v&#39;<span class="o">)</span><span class="w"> </span>∷<span class="w"> </span>xs<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>no<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>x<span class="w"> </span>∷<span class="w"> </span>insert<span class="w"> </span>k<span class="w"> </span>v<span class="w"> </span>xs</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Above, <code>f</code> is just a stand-in for <code>_⊓₂_</code> (making the definition a tiny bit more general). For each element in the &ldquo;right&rdquo; key-value list, we check if its key matches the one we&rsquo;re inserting; if it does, we have to combine the values, and there&rsquo;s no need to recurse into the rest of the list. If on the other hand the key doesn&rsquo;t match, we move on to the next element of the list. If we run out of elements, we know that the key we&rsquo;re inserting wasn&rsquo;t in the &ldquo;right&rdquo; map, so we insert it as-is.</p> <p>The union operation is just about inserting every pair from one map into another.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Map.agda" data-first-line="120" data-last-line="121" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Map.agda#L120-L121">Map.agda</a>, lines 120 through 121</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">120 </span><span class="lnt">121 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">union</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>union<span class="w"> </span>m₁<span class="w"> </span>m₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>foldr<span class="w"> </span>insert<span class="w"> </span>m₂<span class="w"> </span>m₁</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Here, I defined my own version of <code>foldr</code> which unpacks the pairs, for convenience:</p> <details><summary><strong>(Click here to see the definition of my <code>foldr</code>)</strong></summary><div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Map.agda" data-first-line="110" data-last-line="112" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Map.agda#L110-L112">Map.agda</a>, lines 110 through 112</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">110 </span><span class="lnt">111 </span><span class="lnt">112 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">foldr</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>c<span class="o">}</span><span class="w"> </span><span class="o">{</span>C<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>c<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>C<span class="w"> </span><span class="ow">→</span><span class="w"> </span>C<span class="o">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span>C<span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span>C<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>foldr<span class="w"> </span>f<span class="w"> </span>b<span class="w"> </span>[]<span class="w"> </span><span class="ow">=</span><span class="w"> </span>b<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>foldr<span class="w"> </span>f<span class="w"> </span>b<span class="w"> </span><span class="o">((</span>k<span class="w"> </span>,<span class="w"> </span>v<span class="o">)</span><span class="w"> </span>∷<span class="w"> </span>xs<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>f<span class="w"> </span>k<span class="w"> </span>v<span class="w"> </span><span class="o">(</span>foldr<span class="w"> </span>f<span class="w"> </span>b<span class="w"> </span>xs<span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> </details> <p>For intersection, we do something similar; however, since only elements in <em>both</em> maps should be in the final output, if our &ldquo;insertion&rdquo; doesn&rsquo;t find an existing key, it should just fall through; this can be achieved by defining a version of <code>insert</code> whose base case simply throws away the input. Of course, this function should also use <code>_⊓₂_</code> instead of <code>_⊔₂_</code>; below, though, I again use a general function <code>f</code> to provide a more general definition. I called this version of the function <code>update</code>.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Map.agda" data-first-line="295" data-last-line="299" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Map.agda#L295-L299">Map.agda</a>, lines 295 through 299</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">295 </span><span class="lnt">296 </span><span class="lnt">297 </span><span class="lnt">298 </span><span class="lnt">299 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">update</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>update<span class="w"> </span>k<span class="w"> </span>v<span class="w"> </span>[]<span class="w"> </span><span class="ow">=</span><span class="w"> </span>[]<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>update<span class="w"> </span>k<span class="w"> </span>v<span class="w"> </span><span class="o">((</span>k&#39;<span class="w"> </span>,<span class="w"> </span>v&#39;<span class="o">)</span><span class="w"> </span>∷<span class="w"> </span>xs<span class="o">)</span><span class="w"> </span><span class="kr">with</span><span class="w"> </span>≡-dec-A<span class="w"> </span>k<span class="w"> </span>k&#39;<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>yes<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>k&#39;<span class="w"> </span>,<span class="w"> </span>f<span class="w"> </span>v<span class="w"> </span>v&#39;<span class="o">)</span><span class="w"> </span>∷<span class="w"> </span>xs<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>no<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>k&#39;<span class="w"> </span>,<span class="w"> </span>v&#39;<span class="o">)</span><span class="w"> </span>∷<span class="w"> </span>update<span class="w"> </span>k<span class="w"> </span>v<span class="w"> </span>xs</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Just changing <code>insert</code> to <code>update</code> is not enough. It&rsquo;s true that calling <code>update</code> with all keys from <code>m1</code> on <code>m2</code> would forget all keys unique to <code>m1</code>, it would still leave behind the only-in-<code>m2</code> keys. To get rid of these, I defined another function, <code>restrict</code>, that drops all keys in its second argument that aren&rsquo;t present in its first argument.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Map.agda" data-first-line="304" data-last-line="308" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Map.agda#L304-L308">Map.agda</a>, lines 304 through 308</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">304 </span><span class="lnt">305 </span><span class="lnt">306 </span><span class="lnt">307 </span><span class="lnt">308 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">restrict</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>restrict<span class="w"> </span>l<span class="w"> </span>[]<span class="w"> </span><span class="ow">=</span><span class="w"> </span>[]<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>restrict<span class="w"> </span>l<span class="w"> </span><span class="o">((</span>k&#39;<span class="w"> </span>,<span class="w"> </span>v&#39;<span class="o">)</span><span class="w"> </span>∷<span class="w"> </span>xs<span class="o">)</span><span class="w"> </span><span class="kr">with</span><span class="w"> </span>∈k-dec<span class="w"> </span>k&#39;<span class="w"> </span>l<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>yes<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>k&#39;<span class="w"> </span>,<span class="w"> </span>v&#39;<span class="o">)</span><span class="w"> </span>∷<span class="w"> </span>restrict<span class="w"> </span>l<span class="w"> </span>xs<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>no<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>restrict<span class="w"> </span>l<span class="w"> </span>xs</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Altogether, intesection is defined as follows, where <code>updates</code> just calls <code>update</code> for every key-value pair in its first argument.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Map.agda" data-first-line="310" data-last-line="311" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Map.agda#L310-L311">Map.agda</a>, lines 310 through 311</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">310 </span><span class="lnt">311 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">intersect</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>intersect<span class="w"> </span>l₁<span class="w"> </span>l₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>restrict<span class="w"> </span>l₁<span class="w"> </span><span class="o">(</span>updates<span class="w"> </span>l₁<span class="w"> </span>l₂<span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The next hurdle is all the proofs about these implementations. I will leave the details of the proofs either as appendices or as links to other posts on this site.</p> <p>The first key property is that the insertion, union, update, and intersection operations all preserve uniqueness of keys; the <a href="#appendix-proof-of-uniqueness-of-keys"class="same-page-link">proofs for this are here</a>. The set of properties are the lattice laws for union and intersection. The proofs of those proceed by cases; to prove that \((\sqcup)\) is commutative, we reason that if \((k , v) \in m_1 \sqcup m_2\), then it must be either in \(m_1\), in \(m_2\), or in both; for each of these three possible cases, we can show that \((k , v)\) must be the same in \(m_2 \sqcup m_1\). Things get even more tedious for proofs of associativity, since there are 7 cases to consider; I describe the strategy I used for such proofs in my <a href="https://danilafe.com/blog/agda_expr_pattern/">article about the &ldquo;Expression&rdquo; pattern</a> in Agda.</p> <a href="#additional-properties-of-lattices"> <h3 id="additional-properties-of-lattices">Additional Properties of Lattices</h3> </a> <p>The product and map lattices are the two pulling the most weight in my implementation of program analyses. However, there&rsquo;s an additional property that they have: if the lattices they are made of have a <em>finite height</em>, then so do products and map lattices themselves. A lattice having a finite height means that we can only line up so many elements using the less-than operator <code>&lt;</code>. For instance, the natural numbers are <em>not</em> a finite-height lattice; we can create the infinite chain:</p> $$ 0 &amp;lt; 1 &amp;lt; 2 &amp;lt; ... $$ <p id="sign-three-elements">On the other hand, our sign lattice <em>is</em> of finite height; the longest chains we can make have three elements and two <code>&lt;</code> signs. Here&rsquo;s one:</p> $$ \bot &amp;lt; &#43; &amp;lt; \top $$ <p>As a result of this, <em>pairs</em> of signs also have a finite height; the longest chains we can make have five elements and four <code>&lt;</code> signs. <span class="sidenote"> <label class="sidenote-label" for="example-note">An example:</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="example-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Notice that the elements in the example progress the same way as the ones in the single-sign chain. This is no accident; the longest chains in the pair lattice can be constructed from longest chains of its element lattices. The length of the product lattice chain, counted by the number of "less than" signs, is the sum of the lengths of the element chains. <span class="sidenote-delimiter">]</span> </span> </span> </p> $$ (\bot, \bot) &amp;lt; (\bot, &#43;) &amp;lt; (\bot, \top) &amp;lt; (&#43;, \top) &amp;lt; (\top, \top) $$ <p>The same is true for maps, under certain conditions.</p> <p>The finite-height property is crucial to lattice-based static program analysis; we&rsquo;ll talk about it in more detail in the next post of this series.</p> <div class="early-navigation-wrapper"> <nav class="series-navigation"><div class="previous wrapper"> <a href="https://danilafe.com/blog/01_spa_agda_lattices/"> <svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#chevrons-left"/> </svg> <div class="title-subtitle"> Previous in Series <div class="title">Lattices</div> </div> </a> </div> <div class="next wrapper"> <a href="https://danilafe.com/blog/03_spa_agda_fixed_height/"> <div class="title-subtitle"> Next in Series <div class="title">Lattices of Finite Height</div> </div> <svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#chevrons-right"/> </svg> </a> </div> </nav> </div> <a href="#appendix-proof-of-uniqueness-of-keys"> <h3 id="appendix-proof-of-uniqueness-of-keys">Appendix: Proof of Uniqueness of Keys</h3> </a> <p>I will provide sketches of the proofs here, and omit the implementations of my lemmas. Click on the link in the code block headers to jump to their implementation on my Git server.</p> <p>First, note that if we&rsquo;re inserting a key that&rsquo;s already in a list, then the keys of that list are unchanged.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Map.agda" data-first-line="123" data-last-line="124" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Map.agda#L123-L124">Map.agda</a>, lines 123 through 124</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">123 </span><span class="lnt">124 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">insert-keys-∈</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>k<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">}</span><span class="w"> </span><span class="o">{</span>v<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="o">}</span><span class="w"> </span><span class="o">{</span>l<span class="w"> </span><span class="ow">:</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">)}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>k<span class="w"> </span>∈k<span class="w"> </span>l<span class="w"> </span><span class="ow">→</span><span class="w"> </span>keys<span class="w"> </span>l<span class="w"> </span>≡<span class="w"> </span>keys<span class="w"> </span><span class="o">(</span>insert<span class="w"> </span>k<span class="w"> </span>v<span class="w"> </span>l<span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>On the other hand, if we&rsquo;re inserting a new key, it ends up at the end, and the rest of the keys are unchanged.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Map.agda" data-first-line="134" data-last-line="135" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Map.agda#L134-L135">Map.agda</a>, lines 134 through 135</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">134 </span><span class="lnt">135 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">insert-keys-∉</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>k<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">}</span><span class="w"> </span><span class="o">{</span>v<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="o">}</span><span class="w"> </span><span class="o">{</span>l<span class="w"> </span><span class="ow">:</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">)}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>¬<span class="w"> </span><span class="o">(</span>k<span class="w"> </span>∈k<span class="w"> </span>l<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>keys<span class="w"> </span>l<span class="w"> </span>++<span class="w"> </span><span class="o">(</span>k<span class="w"> </span>∷<span class="w"> </span>[]<span class="o">))</span><span class="w"> </span>≡<span class="w"> </span>keys<span class="w"> </span><span class="o">(</span>insert<span class="w"> </span>k<span class="w"> </span>v<span class="w"> </span>l<span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Then, for any given key-value pair, the key either is or isn&rsquo;t in the list we&rsquo;re inserting it into. If it is, then the list ends up unchanged, and remains unique if it was already unique. On the other hand, if it&rsquo;s not in the list, then it ends up at the end; adding a new element to the end of a unique list produces another unique list. Thus, in either case, the final keys are unique.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Map.agda" data-first-line="143" data-last-line="148" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Map.agda#L143-L148">Map.agda</a>, lines 143 through 148</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">143 </span><span class="lnt">144 </span><span class="lnt">145 </span><span class="lnt">146 </span><span class="lnt">147 </span><span class="lnt">148 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">insert-preserves-Unique</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>k<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">}</span><span class="w"> </span><span class="o">{</span>v<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="o">}</span><span class="w"> </span><span class="o">{</span>l<span class="w"> </span><span class="ow">:</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">)}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Unique<span class="w"> </span><span class="o">(</span>keys<span class="w"> </span>l<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Unique<span class="w"> </span><span class="o">(</span>keys<span class="w"> </span><span class="o">(</span>insert<span class="w"> </span>k<span class="w"> </span>v<span class="w"> </span>l<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>insert-preserves-Unique<span class="w"> </span><span class="o">{</span>k<span class="o">}</span><span class="w"> </span><span class="o">{</span>v<span class="o">}</span><span class="w"> </span><span class="o">{</span>l<span class="o">}</span><span class="w"> </span>u<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">with</span><span class="w"> </span><span class="o">(</span>∈k-dec<span class="w"> </span>k<span class="w"> </span>l<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>yes<span class="w"> </span>k∈kl<span class="w"> </span><span class="kr">rewrite</span><span class="w"> </span>insert-keys-∈<span class="w"> </span><span class="o">{</span>v<span class="w"> </span><span class="ow">=</span><span class="w"> </span>v<span class="o">}</span><span class="w"> </span>k∈kl<span class="w"> </span><span class="ow">=</span><span class="w"> </span>u<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>no<span class="w"> </span>k∉kl<span class="w"> </span><span class="kr">rewrite</span><span class="w"> </span>sym<span class="w"> </span><span class="o">(</span>insert-keys-∉<span class="w"> </span><span class="o">{</span>v<span class="w"> </span><span class="ow">=</span><span class="w"> </span>v<span class="o">}</span><span class="w"> </span>k∉kl<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>Unique-append<span class="w"> </span>k∉kl<span class="w"> </span>u</span></span></code></pre></td></tr></table> </div> </div> </div> <p>By induction, we can then prove that calling <code>insert</code> many times as we do in <code>union</code> preserves uniqueness too. Here, <code>insert-preserves-Unique</code> serves as the inductive step.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Map.agda" data-first-line="164" data-last-line="168" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Map.agda#L164-L168">Map.agda</a>, lines 164 through 168</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">164 </span><span class="lnt">165 </span><span class="lnt">166 </span><span class="lnt">167 </span><span class="lnt">168 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">union-preserves-Unique</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>l₁<span class="w"> </span>l₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">))</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>Unique<span class="w"> </span><span class="o">(</span>keys<span class="w"> </span>l₂<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Unique<span class="w"> </span><span class="o">(</span>keys<span class="w"> </span><span class="o">(</span>union<span class="w"> </span>l₁<span class="w"> </span>l₂<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>union-preserves-Unique<span class="w"> </span>[]<span class="w"> </span>l₂<span class="w"> </span>u₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>u₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>union-preserves-Unique<span class="w"> </span><span class="o">((</span>k₁<span class="w"> </span>,<span class="w"> </span>v₁<span class="o">)</span><span class="w"> </span>∷<span class="w"> </span>xs₁<span class="o">)</span><span class="w"> </span>l₂<span class="w"> </span>u₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>insert-preserves-Unique<span class="w"> </span><span class="o">(</span>union-preserves-Unique<span class="w"> </span>xs₁<span class="w"> </span>l₂<span class="w"> </span>u₂<span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>For <code>update</code>, things are simple; it doesn&rsquo;t change the keys of the argument list at all, since it only modifies, and doesn&rsquo;t add new pairs. This is captured by the <code>update-keys</code> property:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Map.agda" data-first-line="313" data-last-line="314" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Map.agda#L313-L314">Map.agda</a>, lines 313 through 314</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">313 </span><span class="lnt">314 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">update-keys</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>k<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">}</span><span class="w"> </span><span class="o">{</span>v<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="o">}</span><span class="w"> </span><span class="o">{</span>l<span class="w"> </span><span class="ow">:</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">)}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>keys<span class="w"> </span>l<span class="w"> </span>≡<span class="w"> </span>keys<span class="w"> </span><span class="o">(</span>update<span class="w"> </span>k<span class="w"> </span>v<span class="w"> </span>l<span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>If the keys don&rsquo;t change, they obviously remain unique.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Map.agda" data-first-line="328" data-last-line="330" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Map.agda#L328-L330">Map.agda</a>, lines 328 through 330</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">328 </span><span class="lnt">329 </span><span class="lnt">330 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">update-preserves-Unique</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>k<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">}</span><span class="w"> </span><span class="o">{</span>v<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="o">}</span><span class="w"> </span><span class="o">{</span>l<span class="w"> </span><span class="ow">:</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">)}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>Unique<span class="w"> </span><span class="o">(</span>keys<span class="w"> </span>l<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Unique<span class="w"> </span><span class="o">(</span>keys<span class="w"> </span><span class="o">(</span>update<span class="w"> </span>k<span class="w"> </span>v<span class="w"> </span>l<span class="w"> </span><span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>update-preserves-Unique<span class="w"> </span><span class="o">{</span>k<span class="o">}</span><span class="w"> </span><span class="o">{</span>v<span class="o">}</span><span class="w"> </span><span class="o">{</span>l<span class="o">}</span><span class="w"> </span>u<span class="w"> </span><span class="kr">rewrite</span><span class="w"> </span>update-keys<span class="w"> </span><span class="o">{</span>k<span class="o">}</span><span class="w"> </span><span class="o">{</span>v<span class="o">}</span><span class="w"> </span><span class="o">{</span>l<span class="o">}</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>u</span></span></code></pre></td></tr></table> </div> </div> </div> <p>For <code>restrict</code>, we note that it only ever removes keys; as a result, if a key was not in the input to <code>restrict</code>, then it won&rsquo;t be in its output, either.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Map.agda" data-first-line="337" data-last-line="338" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Map.agda#L337-L338">Map.agda</a>, lines 337 through 338</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">337 </span><span class="lnt">338 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">restrict-preserves-k≢</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>k<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">}</span><span class="w"> </span><span class="o">{</span>l₁<span class="w"> </span>l₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">)}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>All<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>k&#39;<span class="w"> </span><span class="ow">→</span><span class="w"> </span>¬<span class="w"> </span>k<span class="w"> </span>≡<span class="w"> </span>k&#39;<span class="o">)</span><span class="w"> </span><span class="o">(</span>keys<span class="w"> </span>l₂<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>All<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span>k&#39;<span class="w"> </span><span class="ow">→</span><span class="w"> </span>¬<span class="w"> </span>k<span class="w"> </span>≡<span class="w"> </span>k&#39;<span class="o">)</span><span class="w"> </span><span class="o">(</span>keys<span class="w"> </span><span class="o">(</span>restrict<span class="w"> </span>l₁<span class="w"> </span>l₂<span class="o">))</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>As a result, for each key of the list being restricted, we either drop it (which does not damage uniqueness) or we keep it; since we only remove keys, and since the keys were originally unique, the key we kept won&rsquo;t conflict with any of the other final keys.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Map.agda" data-first-line="345" data-last-line="351" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Map.agda#L345-L351">Map.agda</a>, lines 345 through 351</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">345 </span><span class="lnt">346 </span><span class="lnt">347 </span><span class="lnt">348 </span><span class="lnt">349 </span><span class="lnt">350 </span><span class="lnt">351 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">restrict-preserves-Unique</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>l₁<span class="w"> </span>l₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">)}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>Unique<span class="w"> </span><span class="o">(</span>keys<span class="w"> </span>l₂<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Unique<span class="w"> </span><span class="o">(</span>keys<span class="w"> </span><span class="o">(</span>restrict<span class="w"> </span>l₁<span class="w"> </span>l₂<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>restrict-preserves-Unique<span class="w"> </span><span class="o">{</span>l₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>[]<span class="o">}</span><span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>Utils.empty<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>restrict-preserves-Unique<span class="w"> </span><span class="o">{</span>l₁<span class="o">}</span><span class="w"> </span><span class="o">{(</span>k<span class="w"> </span>,<span class="w"> </span>v<span class="o">)</span><span class="w"> </span>∷<span class="w"> </span>xs<span class="o">}</span><span class="w"> </span><span class="o">(</span>push<span class="w"> </span>k≢xs<span class="w"> </span>uxs<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">with</span><span class="w"> </span>∈k-dec<span class="w"> </span>k<span class="w"> </span>l₁<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>yes<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>push<span class="w"> </span><span class="o">(</span>restrict-preserves-k≢<span class="w"> </span>k≢xs<span class="o">)</span><span class="w"> </span><span class="o">(</span>restrict-preserves-Unique<span class="w"> </span>uxs<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>no<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>restrict-preserves-Unique<span class="w"> </span>uxs</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Since both <code>update</code> and <code>restrict</code> preserve uniqueness, then so does <code>intersect</code>:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Map.agda" data-first-line="353" data-last-line="355" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Map.agda#L353-L355">Map.agda</a>, lines 353 through 355</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">353 </span><span class="lnt">354 </span><span class="lnt">355 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">intersect-preserves-Unique</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>l₁<span class="w"> </span>l₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>List<span class="w"> </span><span class="o">(</span>A<span class="w"> </span>×<span class="w"> </span>B<span class="o">)}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>Unique<span class="w"> </span><span class="o">(</span>keys<span class="w"> </span>l₂<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Unique<span class="w"> </span><span class="o">(</span>keys<span class="w"> </span><span class="o">(</span>intersect<span class="w"> </span>l₁<span class="w"> </span>l₂<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>intersect-preserves-Unique<span class="w"> </span><span class="o">{</span>l₁<span class="o">}</span><span class="w"> </span>u<span class="w"> </span><span class="ow">=</span><span class="w"> </span>restrict-preserves-Unique<span class="w"> </span><span class="o">(</span>updates-preserve-Unique<span class="w"> </span><span class="o">{</span>l₁<span class="o">}</span><span class="w"> </span>u<span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> Untitled Short Story https://danilafe.com/writing/thevoid/ Thu, 01 Aug 2024 20:31:18 -0700 https://danilafe.com/writing/thevoid/ <blockquote> <p>I&rsquo;m losing my edge to the art-school Brooklynites in little jackets and<br> <em>borrowed nostalgia for the unremembered Eighties</em></p> </blockquote> <p>The <span class="glitch" data-text="Everpresent Void"><span>Everpresent Void</span></span> was first discovered at a children&rsquo;s birthday party.</p> <p>Among the laughter and alluring warbling of an arcade, a party was preparing to take their seats at a worn table. The food was french fries, mediocre cheese pizza, and hamburgers; the sort of diet that would be frowned upon at home, and as a result was now highly coveted. The main event, however, turned out to be the self-serve soda machine. It provided unlimited amounts of sugary beverages at the slightest provocation, which was evidenced by the sticky layer of dried drinks that covered the table.</p> <p>It was an unusual sight: such machines were still somewhat rare in those days. Soon, the children were drunk on Coca-Cola and power. Cups were filled, emptied, spilled, dropped on the floor, and used as musical instruments all while the group crowded around the soda dispenser. The birthday girl soon found a new dimension along which the machine could be abused. One cup needed not contain a single drink.</p> <p>This new discovery reignited the drinking frenzy. Sensible combinations soon gave way to outrageous mixes. Drinks were paired up, tripled, quadrupled. Soon, everyone was rushing to mix every flavor together, telling stories of a chemical reaction that would occur when they were combined with precise proportions. No such reaction came.</p> <p>The children were not satisfied with this conclusion. They continued their search for the missing ingredient. Having exhausted the products of the soda machine, they had to broaden their horizons. Ketchup and mustard were the first additions to their repertoire. The boys made shows of tasting and being revolted by their mustard-root-cola, while the girls squealed with disapproval and laughter. Having still failed to perform their act of alchemy, the kids braved yet further frontiers, dropping leftover pieces of cheese and torn fragments of napkins into their cup-cauldrons.</p> <p>Then, it worked.</p> <p>When one of the children looked back at his cup, having been distracted by another&rsquo;s exaggerated gagging, he found it to contain a uniformily black fluid. This intrigued the boy; he went to prod it with his fork, but it never reached the side of the cup. Startled, he dropped the utensil, and watched it sink out of sight. This too was intriguing: the fork was noticeably longer than the container.</p> <p>The others soon crowded around him to examine what was later understood to be the first instance of the <span class="glitch" data-text="Everpresent Void"><span>Everpresent Void</span></span>. They dropped straws, arcade tickets, cheap toys (purchased with arcade tickets), and coins into the cup, all of which disappeared without a sound. The boy found himself at the center of attention, and took great pleasure in recounting his latest recipe. Soon, the <span class="glitch" data-text="Void"><span>Void</span></span> was replicated in the cups of everyone at the party.</p> <hr> <p>During the first week after that incident, teachers and janitors had a particularly difficult time. Various quantities of the <span class="glitch" data-text="Void"><span>Void</span></span> were smuggled into schools. When the staff caught on to peculiarly black water bottles, smugglers switched to more creative techniques involving Ziploc bags and photographic film tubes. Like crystals around an impurity, crowded islands formed at lunch tables with <span class="glitch" data-text="Void"><span>Void</span></span> at their centers. The featureless and endless substance drew all attention away from civil wars, derivatives, and Steinbeck novels.</p> <p>Only, the <span class="glitch" data-text="Void"><span>Void</span></span> was not entirely featureless and endless. As kids spent entire lunch breaks gazing into the darkness of the fluid, some thought they saw something. As more took on the arduous task of sitting perfectly still and staring into space, it became clear that this was no mere trick of the mind.</p> <p>With time, a light show emerged from the emptiness of the <span class="glitch" data-text="Void"><span>Void</span></span>. It was not unlike pressing down on one’s eyes: colorful particles swirled in the darkness forming spirals and fractals. These gradually changed colors, appearing at times red-and-beige, at times blue-and-green, and everything in-between.</p> <p>The display was polarizing. Swaths of children, though initially enthralled by the mysterious properties of the <span class="glitch" data-text="Void"><span>Void</span></span>, were not sufficiently re-captured by some flashing colors. In the later parts of the week, they would leave lunch halls early to study, practice, or socialize. There were, they thought, better, more normal things to do. A minority, however, only grew more enamored with their philosopher&rsquo;s stones.</p> <hr> <p>Like alchemists of the past, many of the remaining experimenters had a tendency to obsess. Even as the world &mdash; with its track meets, birthday parties, and dances &mdash; went on around them, they continued their close observation of the mysterious substance. The <span class="glitch" data-text="Void"><span>Void</span></span> proved worthy of this sustained attention. The patterns that swirled in its depths were not entirely random: they responded, reluctantly and sluggishly, to the observer&rsquo;s mind. Anger and frustration tended to produce redder hues; sadness manifested as a snotty shade of green. Focusing on a particular color made it more likely to appear, as well. Following its own peculiar kind of intuition, the <span class="glitch" data-text="Void"><span>Void</span></span> responded faster when more individuals were present.</p> <p>Other promising avenues of research also grew in popularity over the following days and weeks. The precise recipe for the <span class="glitch" data-text="Void"><span>Void</span></span> was not, it turned out, very strict. Though soda and fast food remained a constant fixture, the precise ingredients could be substituted for alternates. Pieces of swiss cheese worked just as well as cheddar. A fragment of a turkey patty replaced the traditional 100% Angus beef in a pinch. The resulting substance was as opaque and inscrutable as ever.</p> <p>Following much trial error, adolescent adventurers mapped the frontiers of <span class="glitch" data-text="Void"><span>Void</span></span> synthesis. Though the full specification is not particularly relevant, of note was the requirement for the base to be made of a mixture of sodas, and another for the final concoction to contain at least two sandwich ingredients. Orange juice, though sweet and liquid, did not catalyze the reaction, but Orange Fanta did, even if it was completely flat.</p> <hr> <p>If all properties hereto described were the only notable aspects of the <span class="glitch" data-text="Void"><span>Void</span></span>, it would have been a mere curiosity. Late-night History Channel shows might have shown it along theories of ancient aliens or telepathy, filling inattentive viewers&rsquo; dimly lit homes with comfortable background noise. The substance, however, had one final, crucial aspect. The discovery was made &ndash; as is often the case &ndash; in the midst of conflict.</p> <p>The two parties could not be more different. One group consistent of the boys from Mr. Thompson&rsquo;s physics class. They were skinny, bespectacled, and dressed in graphic t-shirts and jeans. The other group was made of the boys from Mrs. Leonard&rsquo;s biology class; they were skinny, bespectacled, and dressed in graphic t-shirts and jeans. Naturally, the two factions were sworn enemies.</p> <p>One rainy West Coast day, the two groups were engaging in customary lunch-break <span class="glitch" data-text="Void"><span>Void</span></span>-viewing. By then, participants in the activity were relegated to the floors of a hallway in the back of the school, their sustained interest in staring-into-space taking on toll on their social standing. They were making use of advanced techniques; by then, experts were able to influence the <span class="glitch" data-text="Void"><span>Void</span></span> not only to change color, but to form into shimmering images. The Thompsonians were constructing an image of a Christmas tree; the Leonardese were working on The Funniest Image Image to Ever Exist (something phallic).</p> <p>Neither group was having much luck. The tree&rsquo;s trunk was consistently much too short, and its bottom too curvy. The phallus, conversely, was quite sharp and somehow sickly looking. Each side mocked the other relentlessly. It took the insult-hurlers from the two camps several back-and-forth trips to realize the images they were ridiculing and the images their friends were conjuring were one and the same.</p> <hr> <p>The <span class="glitch" data-text="Void"><span>Void</span></span> was interconnected. By repeating a specific and precise recipe, one could reliably come back over and over to the same &ldquo;place&rdquo; in the infinite blackness. Painstakingly painted pictures persisted into the next day, and anyone with the same recipe could tune in to see. Enthusiasts rushed to claim recipes most affordable on modest allowances. Registries of locations and ingredients were posted on bulletin boards, written in bathroom stalls, tossed around as crumpled paper balls in class. Even those who had previously shunned the alchemists were drawn back into the fold by the promise of conjuring images of their own for others to see.</p> <hr> <p>It was not until weeks later that the first glimpses of a post-<span class="glitch" data-text="Void"><span>Void</span></span> future revealed themselves, to no one&rsquo;s attention or particular interest. A groggy, not-yet-caffeinated Mrs. Leonard walked into class one day to find a sea of red fabric. Nearly every girl in the morning section showed up to class that day waring a red dress. Not a single one of the students could provide a concrete method by which they chose the day&rsquo;s wardrobe; feelings, whims, and even coin-flipping were cited as reasons for wearing the outfit. What&rsquo;s more, the same happened in Mr. Thompson&rsquo;s class, and in a number of scattered schools throughout the country.</p> <p>Being a scientist at heart, and rejecting wholeheartedly the possibility of coincidence of paranormal involvement, Mrs. Leonard spent the rest of the day distractedly overcorrecting for her earlier lack of coffee. A satisfying answer eluded her, and she came home jittery and defeated. Walking past her son&rsquo;s room she noted that he was indulging in his habitual <span class="glitch" data-text="Void"><span>Void</span></span>-gazing. In the depths of the pitch-black bowl, she glimpsed swirls of that very same shade of red.</p> <hr> <p>The mechanism that precipitated the red-dress curiosity was not all that sinister. The <span class="glitch" data-text="Void"><span>Void</span></span> was not unlike an ocean, absorbing and releasing heat, mitigating changes in its environment. After a day in which red was prevalent in the collective thoughts of <span class="glitch" data-text="Void"><span>Void</span></span>-viewers, the color dissipated like heat through the dark realm, was stirred somehow by the convection of its hidden currents, and re-entered the minds of practitioners in its altered form. They averted their gazes and went to put on red clothes.</p> <p>There was another way in which the <span class="glitch" data-text="Void"><span>Void</span></span> resembled an ocean. Locations within it drifted through the darkness like rafts. Each day, some recipes would move closer or further apart. Whenever others occupied a nearby place in that ocean, their thoughts echoed in the silences between one&rsquo;s own. <span class="glitch" data-text="Void"><span>Void</span></span>-voyagers, their eyes directed at customary, comforting blackness, would encounter each other there, often without knowing. With each encounter, they left unnoticeable imprimnts upon one another.</p> <p>If the Thompsonians or Leonardese were chemists rather than physicists and biologists, and if they were more inclined towards introspection or calm, deliberate thought, they might have observed this gradual exchange, and seen in it the physical process of diffusion, with its particles and collisions. They might have thought of drops of dye in water, swirling in beautiful patterns until finally there were no recognizable shapes, nothing to see at all except a gentle haze of red in an erlenmeyer flask. The final stage of diffusion was uniformity.</p> <hr> <p>The viewers of the <span class="glitch" data-text="Void"><span>Void</span></span> were as much connected to each other as they were disconnected from the rest of the world. They were distant, sitting in exile at lunch, looking for hours on end with mild expressions into their bowls of inky soup, talking about their latest journeys with each other on the phone. They spoke in references to landmarks in <span class="glitch" data-text="The Ocean"><span>The Ocean</span></span>, to happenings in their shared dimension. At the same time, they knew little of parties and dances, and they seldom &mdash; if ever &mdash; went to cheer for their peers in athletic events. It was hard for others to hold a conversation with them.</p> <p>It was not only children that sought to explore the <span class="glitch" data-text="Everpresent Void"><span>Everpresent Void</span></span>; adult interest in the substance was growing. Grownups heard about it from their children, their students, or news reports. They could understand the desire to be seen. More than that, though, they say potential in the <span class="glitch" data-text="Void"><span>Void</span></span>.</p> <p>The substance was unprecedented. It was one thing to call someone on the phone, or come to their door; it was something else altogether to create a scene, in three crisp (with practice) dimensions, and to set it adrift in the populous sea of black. The <span class="glitch" data-text="Void"><span>Void</span></span> was an invaluable tool for promotional material, advertising, and even storefronts. Adults adept at <span class="glitch" data-text="Void"><span>Void</span></span> manipulation found themselves employed with various large companies, and sometimes even created their own. Though preconceptions about them &mdash; largely negative, and often substantiated &mdash; successfully made the jump across the age gap, these men and women became sought after and well-rewarded for their talents.</p> <p>With adult influence, of course, came adult concerns. Though recipes for their windows into the other world remained permanent reminders of that first day at the arcade, older practitioners were too used to thinking about elections, taxes, and mortgages. What&rsquo;s worse, the presidential candidates, tax collectors, and banks did not stop thinking about <em>them</em>. As months went by, it became more and more common to see blue donkeys and red elephants as motifs in the <span class="glitch" data-text="Void"><span>Void</span></span>. These drops of color swirled with all others, splashing from raft to raft on their predetermined path to joining the haze.</p> <hr> <p>Among some practitioners, there was a growing sense that the <span class="glitch" data-text="Everpresent Void"><span>Everpresent Void</span></span> was alive. It didn&rsquo;t speak, or think, or breathe. Sometimes, though, its movements and currents were too deliberate to be mere chance. The connections that it made, the glimpses of nearby islands that viewers saw in the corners of their eyes, must&rsquo;ve been chosen on purpose; chosen to entice. <span class="glitch" data-text="The Void wanted to be seen"><span>The Void wanted to be seen</span></span>. It spoke to its sailors in echoes of others&rsquo; words, it showed them films whose frames were others&rsquo; images. Encountering another voyager with his nearby recipe, it was insufficient to simply extricate his thoughts from one&rsquo;s own; it was also necessary to determine why he was sent as the <span class="glitch" data-text="Void"><span>Void</span></span>&rsquo;s emissary.</p> <p>The echoes or films were not sent to convey a message. The <span class="glitch" data-text="Void"><span>Void</span></span> was not aware of human logic or values, or even of the physical reality outside of its own darkness. It was indifferent to such things, and continued to behave according to some incomprehensible laws. Nevertheless, somewhere near the core of these laws was the desire to command human attention.</p> <p>Nature did not bestow upon humanity the mechanisms to defend against something as otherworldly as the <span class="glitch" data-text="Void"><span>Void</span></span>. The stories they learned each day were spoken by a chorus of voices, so loud and numerous that it seemed the whole world was speaking to them. In truth, however, each human argument sounded within that ocean&rsquo;s surf &mdash; as did its refutation. Each voyager heard fragments they were used to hearing, stories they wanted to learn. Though the <span class="glitch" data-text="Void"><span>Void</span></span> reflected no light, staring at it was looking into an endless mirror.</p> <hr> <p>Through this process, the modern-day alchemists&rsquo; demeanor began to resemble their ancient counterparts&rsquo; mercury-induced insanity. They spoke in baffling absolutisms. Their language, already rich with <span class="glitch" data-text="Void"><span>Void</span></span>-specific jargon, grew further removed from the words spoken still in coffee shops and bars. Anger and anxiety attracted attention, and so they were angry and anxious, exploding at times at seemingly innocuous occurrences. Sometimes, as with the red-dress incident, hundreds of alchemists were compelled to eat a certain food, or dress in a certain outfit. They swayed like kelp with the invisible waves of the <span class="glitch" data-text="Void"><span>Void</span></span>.</p> <p>Concurrently, the <span class="glitch" data-text="Void"><span>Void</span></span>&rsquo;s influence grew, its versatility and power proving impossible to surrender. More and more learned to create viewports into the blackness. As they did, the prevalence of madness increased. Soon everyone knew somebody affected by the lunacy. The victims remained entirely human; they kept their fond memories, their endearing mannerisms, their connections. The <span class="glitch" data-text="Void"><span>Void</span></span> reflected their light like a carnival mirror and amplified thoughts, but it could not cultivate that which was not already there. There was nothing to do but to stand by them and hope that with time, their features would recede back into their former shape.</p> <hr> <p>Years later, on a chill November night, a weary Mrs. Leonard re-entered her home, one lit house in an entire city of houses that were dark. Her son was away at college now, and her husband out with friends, leaving her to collapse onto the couch and revisit the events of the day. Her students had done well on their exams, and were rewarded with a day off; in class, they watched a nature documentary. The subject was the Amazon rainforest, and among its inhabitants were the leaf-cutter ants.</p> <p>Mrs. Leonard thought about the ants. Day after day they scoured the rainforest, collecting leaves to feed to a fungal garden in their colony. Day after day, the fungus emitted chemicals that diffused from the garden, swirling in the air currents that permeated the rest of the colony. Sensing changes, the ants altered their routes to look for different sources of food.</p> <p>There was, she thought, a first day to all of this, even a first moment. Before that moment, they were <em>just</em> ants, going about their day as all their relatives do to this day. Then, perhaps, a worker returned accompanied by a spore, and changed the course of the colony&rsquo;s history.</p> <p>Outside, it was storming. In the dark, roads were discernible only through streaking reflections of stoplights in puddles. Rain drummed with increasing urgency against the house&rsquo;s windows; larger drops left craters in the waterscape forming on the glass. The resulting texture was not unlike mycelium.</p> <p>Mrs. Leonard wondered whether the first of the leaf-cutter ants were wary of the transformation occurring around them, whether the incursion of hyphae into their familiar tunnels concerned them. Something new was beginning to live alongside them, something decidedly un-ant-like. It thought nothing of workers, queens, or larvae, and it was unconcerned with conquest, hunting, or foraging. The fungus only swelled more with each day, entangling itself deeper into their lives. Were the ants really not afraid of this <span class="glitch" data-text="thing"><span>thing</span></span>? It certainly scared her.</p> <p>It was over 45 million years ago, the narrator of the documentary had said, that the colonies likely began their new mutualistic way of life. That November night, the ants were still there, tending to their gardens. The fungus nourished their larvae in exchange for their protection. Perhaps, in some way neither she nor the ants could understand, that symbiosis warded off dangers that their competitors succumbed to.</p> <p>Upon returning home, Mr. Leonard found his wife still on the couch, embers of a dying fireplace casting playful shadows across her face. He had no way of knowing, but she was dreaming of gathering leaves in a warm, humid jungle.</p> Implementing and Verifying "Static Program Analysis" in Agda, Part 1: Lattices https://danilafe.com/blog/01_spa_agda_lattices/ Sat, 06 Jul 2024 17:37:43 -0700 https://danilafe.com/blog/01_spa_agda_lattices/ <p>This is the first post in a series on <a href="https://danilafe.com/series/static-program-analysis-in-agda/">static program analysis in Agda</a>. See the <a href="https://danilafe.com/blog/00_spa_agda_intro/">introduction</a> for a little bit more context.</p> <p>The goal of this post is to motivate the algebraic structure called a <a href="https://en.wikipedia.org/wiki/Lattice_%28order%29"class="external-link">lattice<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. Lattices have <span class="sidenote"> <label class="sidenote-label" for="crdt-note">broad applications</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="crdt-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> See, for instance, Lars Hupel's excellent <a href="https://lars.hupel.info/topics/crdt/01-intro/">introduction to CRDTs</a> which uses lattices for Conflict-Free Replicated Data Types. CRDTs can be used to implement peer-to-peer distributed systems. <span class="sidenote-delimiter">]</span> </span> </span> beyond static program analysis, so the work in this post is interesting in its own right. However, for the purposes of this series, I&rsquo;m most interested in lattices as an encoding of program information when performing analysis. To start motivating lattices in that context, I&rsquo;ll need to start with <em>monotone frameworks</em>.</p> <a href="#monotone-frameworks"> <h3 id="monotone-frameworks">Monotone Frameworks</h3> </a> <p>The key notion for monotone frameworks is the &ldquo;specificity&rdquo; of information. Take, for instance, an analyzer that tries to figure out if a variable is positive, negative, or equal to zero (this is called a <em>sign analysis</em>, and we&rsquo;ll be using this example a lot). Of course, the variable could be &ldquo;none of the above&rdquo; &ndash; perhaps if it was initialized from user input, which would allow both positive and negative numbers. Such an analyzer might return <code>+</code>, <code>-</code>, <code>0</code>, or <code>unknown</code> for any given variable. These outputs are not created equal: if a variable has sign <code>+</code>, we know more about it than if the sign is <code>unknown</code>: we&rsquo;ve ruled out negative numbers as possible values!</p> <p id="specificity">Specificity is important to us because we want our analyses to be as precise as possible. It would be valid for a program analysis to just return <code>unknown</code> for everything, but it wouldn&rsquo;t be very useful. Thus, we want to rank possible outputs, and try pick the most specific one. The <span class="sidenote"> <label class="sidenote-label" for="convention-note">convention</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="convention-note"></input><span class="sidenote-content sidenote-right" style="margin-top:-12rem"><span class="sidenote-delimiter">[note:</span> I say convention, because it doesn't actually matter if we represent more specific values as "larger" or "smaller". Given a lattice with a particular order written as <code>&lt;</code>, we can flip the sign in all relations (turning <code>a &lt; b</code> into <code>a &gt; b</code>), and get back another lattice. This lattice will have the same properties (more precisely, the properties will be <a href="https://en.wikipedia.org/wiki/Duality_(mathematics)">dual</a>). So we shouldn't fret about picking a direction for "what's less than what". <span class="sidenote-delimiter">]</span> </span> </span> seems to be to make <span class="sidenote"> <label class="sidenote-label" for="order-note">more specific things &ldquo;smaller&rdquo;</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="order-note"></input><span class="sidenote-content sidenote-right" style="margin-top:1rem"><span class="sidenote-delimiter">[note:</span> Admittedly, it's a little bit odd to say that something which is "more" than something else is actually smaller. The intuition that I favor is that something that's more specific describes fewer objects: there are less white horses than horses, so "white horse" is more specific than "horse". The direction of <code>&lt</code> can be thought of as comparing the number of objects.<br> <br> Note that this is only an intuition; there are equally many positive and negative numbers, but we will <em>not</em> group them together in our order. <span class="sidenote-delimiter">]</span> </span> </span> , and less specific things &ldquo;larger&rdquo;. Coming back to our previous example, we&rsquo;d write <code>+ &lt; unknown</code>, since <code>+</code> is more specific. Of course, the exact things we&rsquo;re trying to rank depend on the sort of analysis we&rsquo;re trying to perform. Since I introduced sign analysis, we&rsquo;re ranking signs like <code>+</code> and <code>-</code>. For other analyses, the elements will be different. The <em>comparison</em>, however, will be a permanent fixture.</p> <p id="define-monotonicity">Suppose now that we have some program analysis, and we&rsquo;re feeding it some input information. Perhaps we&rsquo;re giving it the signs of variables <code>x</code> and <code>y</code>, and hoping for it to give us the sign of a third variable <code>z</code>. It would be very unfortunate if, when given more specific information, the analysis would return a less specific output! The more you know going in, the more you should know coming out. Similarly, when given less specific / vaguer information, the analysis shouldn&rsquo;t produce a more specific answer &ndash; how could it do that? This leads us to come up with the following rule:</p> $$ \textbf{if}\ \text{input}_1 \le \text{input}_2, \textbf{then}\ \text{analyze}(\text{input}_1) \le \text{analyze}(\text{input}_2) $$ <p>In mathematics, such a property is called <em>monotonicity</em>. We say that &ldquo;analyze&rdquo; is a <a href="https://en.wikipedia.org/wiki/Monotonic_function"class="external-link">monotonic function<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. This property gives its name to monotone frameworks. For our purposes, this property means that being more specific &ldquo;pays off&rdquo;: better information in means better information out. In Agda, we can encode monotonicity as follows:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice.agda" data-first-line="17" data-last-line="21" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice.agda#L17-L21">Lattice.agda</a>, lines 17 through 21</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="kr">module</span><span class="w"> </span>_ {<span class="n">a</span><span class="w"> </span>b<span class="o">}</span><span class="w"> </span><span class="o">{</span>A<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">}</span><span class="w"> </span><span class="o">{</span>B<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>b<span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>_≼₁_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">)</span><span class="w"> </span><span class="o">(</span>_≼₂_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>b<span class="o">)</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">Monotonic</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="o">(</span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>B<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span><span class="o">(</span>a<span class="w"> </span>⊔ℓ<span class="w"> </span>b<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>Monotonic<span class="w"> </span>f<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>a₁<span class="w"> </span>a₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>a₁<span class="w"> </span>≼₁<span class="w"> </span>a₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>f<span class="w"> </span>a₁<span class="w"> </span>≼₂<span class="w"> </span>f<span class="w"> </span>a₂</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Note that above, I defined <code>Monotonic</code> on an arbitrary function, whose outputs might be of a different type than its inputs. This will come in handy later.</p> <p>The order <code>&lt;</code> of our elements and the monotonicity of our analysis are useful to us for another reason: they help gauge and limit, in a roundabout way, how much work might be left for our analysis to do. This matters because we don&rsquo;t want to allow analyses that can take forever to finish &ndash; that&rsquo;s a little too long for a pragmatic tool used by people.</p> <p>The key observation &ndash; which I will describe in detail in a later post &ndash; is that a monotonic analysis, in a way, &ldquo;climbs upwards&rdquo; through an order. As we continue using this analysis to refine information over and over, its results get <span class="sidenote"> <label class="sidenote-label" for="less-specific-note">less and less specific.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="less-specific-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> It is not a bad thing for our results to get less specific over time, because our initial information is probably incomplete. If you've only seen German shepherds in your life, that might be your picture of what a dog is like. If you then come across a chihuahua, your initial definition of "dog" would certainly not accommodate it. To allow for both German shepherds and chihuahuas, you'd have to loosen the definition of "dog". This new definition would be less specific, but it would be more accurate. <span class="sidenote-delimiter">]</span> </span> </span> If we add an additional ingredient, and say that the order has a <em>fixed height</em>, we can deduce that the analysis will eventually stop producing additional information: either it will keep &ldquo;climbing&rdquo;, and reach the top (thus having to stop), or it will stop on its own before reaching the top. This is the essence of the fixed-point algorithm, which in Agda-like pseudocode can be stated as follows:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="kr">module</span><span class="w"> </span>_ (<span class="n">IsFiniteHeight</span><span class="w"> </span>A<span class="w"> </span>≺<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>f<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>Monotonicᶠ<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Monotonic<span class="w"> </span>_≼_<span class="w"> </span>_≼_<span class="w"> </span>f<span class="o">)</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">-- There exists a point...</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">aᶠ</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">-- Such that applying the monotonic function doesn&#39;t change the result.</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">aᶠ≈faᶠ</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>aᶠ<span class="w"> </span>≈<span class="w"> </span>f<span class="w"> </span>aᶠ<span class="w"> </span></span></span></code></pre></div><p>Moreover, the value we&rsquo;ll get out of the fixed point algorithm will be the <em>least fixed point</em>. For us, this means that the result will be &ldquo;the most specific result possible&rdquo;.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Fixedpoint.agda" data-first-line="86" data-last-line="86" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Fixedpoint.agda#L86-L86">Fixedpoint.agda</a>, line 86</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">86 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">aᶠ≼</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>a<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>a<span class="w"> </span>≈<span class="w"> </span>f<span class="w"> </span>a<span class="w"> </span><span class="ow">→</span><span class="w"> </span>aᶠ<span class="w"> </span>≼<span class="w"> </span>a</span></span></code></pre></td></tr></table> </div> </div> </div> <p>The above explanation omits a lot of details, but it&rsquo;s a start. To get more precise, we must drill down into several aspects of what I&rsquo;ve said so far. The first of them is, <strong>how can we compare program information using an order?</strong></p> <a href="#lattices"> <h3 id="lattices">Lattices</h3> </a> <p>Let&rsquo;s start with a question: when it comes to our specificity-based order, is <code>-</code> less than, greater than, or equal to <code>+</code>? Surely it&rsquo;s not less specific; knowing that a number is negative doesn&rsquo;t give you less information than knowing if that number is positive. Similarly, it&rsquo;s not any more specific, for the same reason. You could consider it equally specific, but that doesn&rsquo;t seem quite right either; the information is different, so comparing specificity feels apples-to-oranges. On the other hand, both <code>+</code> and <code>-</code> are clearly more specific than <code>unknown</code>.</p> <p>The solution to this conundrum is to simply refuse to compare certain elements: <code>+</code> is neither less than, greater than, nor equal to <code>-</code>, but <code>+ &lt; unknown</code> and <code>- &lt; unknown</code>. Such an ordering is called a <a href="https://en.wikipedia.org/wiki/Partially_ordered_set"class="external-link">partial order<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>.</p> <p>Next, another question. Suppose that the user writes code like this:</p> <pre tabindex="0"><code>if someCondition { x = exprA; } else { x = exprB; } y = x; </code></pre><p>If <code>exprA</code> has sign <code>s1</code>, and <code>exprB</code> has sign <code>s2</code>, what&rsquo;s the sign of <code>y</code>? It&rsquo;s not necessarily <code>s1</code> nor <code>s2</code>, since they might not match: <code>s1</code> could be <code>+</code>, and <code>s2</code> could be <code>-</code>, and using either <code>+</code> or <code>-</code> for <code>y</code> would be incorrect. We&rsquo;re looking for something that can encompass <em>both</em> <code>s1</code> and <code>s2</code>. Necessarily, it would be either equally specific or less specific than either <code>s1</code> or <code>s2</code>: there isn&rsquo;t any new information coming in about <code>x</code>, and since we don&rsquo;t know which branch is taken, we stand to lose a little bit of info. However, our goal is always to maximize specificity, since more specific signs give us more information about our program.</p> <p>This gives us the following constraints. Since the combined sign <code>s</code> has to be equally or less specific than either <code>s1</code> and <code>s2</code>, we have <code>s1 &lt;= s</code> and <code>s2 &lt;= s</code>. However, we want to pick <code>s</code> such that it&rsquo;s more specific than any other &ldquo;combined sign&rdquo; candidate. Thus, if there&rsquo;s another sign <code>t</code>, with <code>s1 &lt;= t</code> and <code>s2 &lt;= t</code>, then it must be less specific than <code>s</code>: <code>s &lt;= t</code>.</p> <p>At first, the above constraints might seem quite complicated. We can interpret them in more familiar territory by looking at numbers instead of signs. If we have two numbers <code>n1</code> and <code>n2</code>, what number is the smallest number that&rsquo;s bigger than either <code>n1</code> or <code>n2</code>? Why, the maximum of the two, of course!</p> <p id="least-upper-bound">There is a reason why I used the constraints above instead of just saying &ldquo;maximum&rdquo;. For numbers, <code>max(a,b)</code> is either <code>a</code> or <code>b</code>. However, we saw earlier that neither <code>+</code> nor <code>-</code> works as the sign for <code>y</code> in our program. Moreover, we agreed above that our order is <em>partial</em>: how can we pick &ldquo;the bigger of two elements&rdquo; if neither is bigger than the other? <code>max</code> itself doesn&rsquo;t quite work, but what we&rsquo;re looking for is something similar. Instead, we simply require a similar function for our signs. We call this function &ldquo;<a href="https://en.wikipedia.org/wiki/Least-upper-bound_property"class="external-link">least upper bound<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>&rdquo;, since it is the &ldquo;least (most specific) element that&rsquo;s greater (less specific) than either <code>s1</code> or <code>s2</code>&rdquo;. Conventionally, this function is written as \(a \sqcup b\) (or in our case, \(s_1 \sqcup s_2\)). The \((\sqcup)\) symbol is also called the <em>join</em> of \(a\) and \(b\). We can define it for our signs so far using the following <a href="https://en.wikipedia.org/wiki/Cayley_table"class="external-link">Cayley table<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>.</p> $$ \begin{array}{c|cccc} \sqcup &amp;amp; - &amp;amp; 0 &amp;amp; &#43; &amp;amp; ? \\ \hline - &amp;amp; - &amp;amp; ? &amp;amp; ? &amp;amp; ? \\ 0 &amp;amp; ? &amp;amp; 0 &amp;amp; ? &amp;amp; ? \\ &#43; &amp;amp; ? &amp;amp; ? &amp;amp; &#43; &amp;amp; ? \\ ? &amp;amp; ? &amp;amp; ? &amp;amp; ? &amp;amp; ? \\ \end{array} $$ <p>By using the above table, we can see that \((+\ \sqcup\ -)\ =\ ?\) (aka <code>unknown</code>). This is correct; given the four signs we&rsquo;re working with, that&rsquo;s the most we can say. Let&rsquo;s explore the analogy to the <code>max</code> function a little bit more, by observing that this function has certain properties:</p> <ul> <li><code>max(a, a) = a</code>. The maximum of one number is just that number. Mathematically, this property is called <em>idempotence</em>. Note that by inspecting the diagonal of the above table, we can confirm that our \((\sqcup)\) function is idempotent.</li> <li><code>max(a, b) = max(b, a)</code>. If you&rsquo;re taking the maximum of two numbers, it doesn&rsquo;t matter which one you consider first. This property is called <em>commutativity</em>. Note that if you mirror the table along the diagonal, it doesn&rsquo;t change; this shows that our \((\sqcup)\) function is commutative.</li> <li><code>max(a, max(b, c)) = max(max(a, b), c)</code>. When you have three numbers, and you&rsquo;re determining the maximum value, it doesn&rsquo;t matter which pair of numbers you compare first. This property is called <em>associativity</em>. You can use the table above to verify the \((\sqcup)\) is associative, too.</li> </ul> <p>A set that has a binary operation (like <code>max</code> or \((\sqcup)\)) that satisfies the above properties is called a <a href="https://en.wikipedia.org/wiki/Semilattice"class="external-link">semilattice<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. In Agda, we can write this definition roughly as follows:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="kr">record</span><span class="w"> </span>IsSemilattice<span class="w"> </span><span class="o">{</span>a<span class="o">}</span><span class="w"> </span><span class="o">(</span>A<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">)</span><span class="w"> </span><span class="o">(</span>_⊔_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">field</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⊔-assoc</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span>y<span class="w"> </span>z<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">((</span>x<span class="w"> </span>⊔<span class="w"> </span>y<span class="o">)</span><span class="w"> </span>⊔<span class="w"> </span>z<span class="o">)</span><span class="w"> </span>≡<span class="w"> </span><span class="o">(</span>x<span class="w"> </span>⊔<span class="w"> </span><span class="o">(</span>y<span class="w"> </span>⊔<span class="w"> </span>z<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⊔-comm</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span>y<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span>⊔<span class="w"> </span>y<span class="o">)</span><span class="w"> </span>≡<span class="w"> </span><span class="o">(</span>y<span class="w"> </span>⊔<span class="w"> </span>x<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⊔-idemp</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span>⊔<span class="w"> </span>x<span class="o">)</span><span class="w"> </span>≡<span class="w"> </span>x<span class="w"> </span></span></span></code></pre></div><p id="definitional-equality">Note that this is an example of the <a href="https://danilafe.com/blog/agda_is_pattern/">&ldquo;Is Something&rdquo; pattern</a>. It turns out to be convenient, however, to not require definitional equality (<code>≡</code>). For instance, we might model sets as lists. Definitional equality would force us to consider lists with the same elements but a different order to be unequal. Instead, we parameterize our definition of <code>IsSemilattice</code> by a binary relation <code>_≈_</code>, which we ask to be an <a href="https://en.wikipedia.org/wiki/Equivalence_relation"class="external-link">equivalence relation<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice.agda" data-first-line="23" data-last-line="39" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice.agda#L23-L39">Lattice.agda</a>, lines 23 through 39</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="kr">record</span><span class="w"> </span>IsSemilattice<span class="w"> </span><span class="o">{</span>a<span class="o">}</span><span class="w"> </span><span class="o">(</span>A<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>_≈_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>_⊔_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">_≼_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>a<span class="w"> </span>≼<span class="w"> </span>b<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>a<span class="w"> </span>⊔<span class="w"> </span>b<span class="o">)</span><span class="w"> </span>≈<span class="w"> </span>b<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">_≺_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>a<span class="w"> </span>≺<span class="w"> </span>b<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">(</span>a<span class="w"> </span>≼<span class="w"> </span>b<span class="o">)</span><span class="w"> </span>×<span class="w"> </span><span class="o">(</span>¬<span class="w"> </span>a<span class="w"> </span>≈<span class="w"> </span>b<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">field</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">≈-equiv</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsEquivalence<span class="w"> </span>A<span class="w"> </span>_≈_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">≈-⊔-cong</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>a₁<span class="w"> </span>a₂<span class="w"> </span>a₃<span class="w"> </span>a₄<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>a₁<span class="w"> </span>≈<span class="w"> </span>a₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>a₃<span class="w"> </span>≈<span class="w"> </span>a₄<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>a₁<span class="w"> </span>⊔<span class="w"> </span>a₃<span class="o">)</span><span class="w"> </span>≈<span class="w"> </span><span class="o">(</span>a₂<span class="w"> </span>⊔<span class="w"> </span>a₄<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⊔-assoc</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span>y<span class="w"> </span>z<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">((</span>x<span class="w"> </span>⊔<span class="w"> </span>y<span class="o">)</span><span class="w"> </span>⊔<span class="w"> </span>z<span class="o">)</span><span class="w"> </span>≈<span class="w"> </span><span class="o">(</span>x<span class="w"> </span>⊔<span class="w"> </span><span class="o">(</span>y<span class="w"> </span>⊔<span class="w"> </span>z<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⊔-comm</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span>y<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span>⊔<span class="w"> </span>y<span class="o">)</span><span class="w"> </span>≈<span class="w"> </span><span class="o">(</span>y<span class="w"> </span>⊔<span class="w"> </span>x<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⊔-idemp</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span>⊔<span class="w"> </span>x<span class="o">)</span><span class="w"> </span>≈<span class="w"> </span>x</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Notice that the above code also provides &ndash; but doesn&rsquo;t require &ndash; <code>_≼_</code> and <code>_≺_</code>. That&rsquo;s because a least-upper-bound operation encodes an order: intuitively, if <code>max(a, b) = b</code>, then <code>b</code> must be larger than <code>a</code>. Lars Hupel&rsquo;s CRDT series includes <a href="https://lars.hupel.info/topics/crdt/03-lattices/#there-"class="external-link">an explanation<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> of how the ordering operator and the &ldquo;least upper bound&rdquo; function can be constructed from one another.</p> <p id="lub-glub-or-and">As it turns out, the <code>min</code> function has very similar properties to <code>max</code>: it&rsquo;s idempotent, commutative, and associative. For a partial order like ours, the analog to <code>min</code> is &ldquo;greatest lower bound&rdquo;, or &ldquo;the largest value that&rsquo;s smaller than both inputs&rdquo;. Such a function is denoted as \(a\sqcap b\), and often called the &ldquo;meet&rdquo; of \(a\) and \(b\). As for what it means, where \(s_1 \sqcup s_2\) means &ldquo;combine two signs where you don&rsquo;t know which one will be used&rdquo; (like in an <code>if</code>/<code>else</code>), \(s_1 \sqcap s_2\) means &ldquo;combine two signs where you know <span class="sidenote"> <label class="sidenote-label" for="or-join-note">both of them to be true</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="or-join-note"></input><span class="sidenote-content sidenote-right" style="margin-top:-7rem"><span class="sidenote-delimiter">[note:</span> If you're familiar with <a href="https://en.wikipedia.org/wiki/Boolean_algebra"> Boolean algebra</a>, this might look a little bit familiar to you. In fact, the symbol for "and" on booleans is \(\land\). Similarly, the symbol for "or" is \(\lor\). So, \(s_1 \sqcup s_2\) means "the sign is \(s_1\) or \(s_2\)", or "(the sign is \(s_1\)) \(\lor\) (the sign is \(s_2\))". Similarly, \(s_1 \sqcap s_2\) means "(the sign is \(s_1\)) \(\land\) (the sign is \(s_2\))". Don't these symbols look similar?<br> <br> In fact, booleans with \((\lor)\) and \((\land)\) satisfy the semilattice laws we've been discussing, and together form a lattice (to which I'm building to in the main body of the text). The same is true for the set union and intersection operations, \((\cup)\) and \((\cap)\). <span class="sidenote-delimiter">]</span> </span> </span> &rdquo;. For example, \((+\ \sqcap\ ?)\ =\ +\), because a variable that&rsquo;s both &ldquo;any sign&rdquo; and &ldquo;positive&rdquo; must be positive.</p> <p>There&rsquo;s just one hiccup: what&rsquo;s the greatest lower bound of <code>+</code> and <code>-</code>? it needs to be a value that&rsquo;s less than both of them, but so far, we don&rsquo;t have such a value. Intuitively, this value should be called something like <code>impossible</code>, because a number that&rsquo;s both positive and negative doesn&rsquo;t exist. So, let&rsquo;s extend our analyzer to have a new <code>impossible</code> value. In fact, it turns out that this &ldquo;impossible&rdquo; value is the least element of our set (we added it to be the lower bound of <code>+</code> and co., which in turn are less than <code>unknown</code>). Similarly, <code>unknown</code> is the largest element of our set, since it&rsquo;s greater than <code>+</code> and co, and transitively greater than <code>impossible</code>. In mathematics, it&rsquo;s not uncommon to define the least element as \(\bot\) (read &ldquo;bottom&rdquo;), and the greatest element as \(\top\) (read &ldquo;top&rdquo;). With that in mind, the following are the updated Cayley tables for our operations.</p> <p id="sign-lattice">$$ \begin{array}{c|ccccc} \sqcup &amp;amp; - &amp;amp; 0 &amp;amp; &#43; &amp;amp; \top &amp;amp; \bot \\ \hline - &amp;amp; - &amp;amp; \top &amp;amp; \top &amp;amp; \top &amp;amp; - \\ 0 &amp;amp; \top &amp;amp; 0 &amp;amp; \top &amp;amp; \top &amp;amp; 0 \\ &#43; &amp;amp; \top &amp;amp; \top &amp;amp; &#43; &amp;amp; \top &amp;amp; &#43; \\ \top &amp;amp; \top &amp;amp; \top &amp;amp; \top &amp;amp; \top &amp;amp; \top \\ \bot &amp;amp; - &amp;amp; 0 &amp;amp; &#43; &amp;amp; \top &amp;amp; \bot \\ \end{array} \qquad \begin{array}{c|ccccc} \sqcap &amp;amp; - &amp;amp; 0 &amp;amp; &#43; &amp;amp; \top &amp;amp; \bot \\ \hline - &amp;amp; - &amp;amp; \bot &amp;amp; \bot &amp;amp; - &amp;amp; \bot \\ 0 &amp;amp; \bot &amp;amp; 0 &amp;amp; \bot &amp;amp; 0 &amp;amp; \bot \\ &#43; &amp;amp; \bot &amp;amp; \bot &amp;amp; &#43; &amp;amp; &#43; &amp;amp; \bot \\ \top &amp;amp; - &amp;amp; 0 &amp;amp; &#43; &amp;amp; \top &amp;amp; \bot \\ \bot &amp;amp; \bot &amp;amp; \bot &amp;amp; \bot &amp;amp; \bot &amp;amp; \bot \\ \end{array} $$ </p> <p>So, it turns out that our set of possible signs is a semilattice in two ways. And if &ldquo;semi&rdquo; means &ldquo;half&rdquo;, does two &ldquo;semi&quot;s make a whole? Indeed it does!</p> <p id="absorption-laws">A lattice is made up of two semilattices. The operations of these two lattices, however, must satisfy some additional properties. Let&rsquo;s examine the properties in the context of <code>min</code> and <code>max</code> as we have before. They are usually called the <em>absorption laws</em>:</p> <ul> <li><code>max(a, min(a, b)) = a</code>. <code>a</code> is either less than or bigger than <code>b</code>; so if you try to find the maximum <strong>and</strong> the minimum of <code>a</code> and <code>b</code>, one of the operations will return <code>a</code>.</li> <li><code>min(a, max(a, b)) = a</code>. The reason for this one is the same as the reason above.</li> </ul> <p>In Agda, we can therefore write a lattice as follows:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice.agda" data-first-line="183" data-last-line="193" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice.agda#L183-L193">Lattice.agda</a>, lines 183 through 193</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">183 </span><span class="lnt">184 </span><span class="lnt">185 </span><span class="lnt">186 </span><span class="lnt">187 </span><span class="lnt">188 </span><span class="lnt">189 </span><span class="lnt">190 </span><span class="lnt">191 </span><span class="lnt">192 </span><span class="lnt">193 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="kr">record</span><span class="w"> </span>IsLattice<span class="w"> </span><span class="o">{</span>a<span class="o">}</span><span class="w"> </span><span class="o">(</span>A<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>_≈_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>_⊔_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>_⊓_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">field</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">joinSemilattice</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsSemilattice<span class="w"> </span>A<span class="w"> </span>_≈_<span class="w"> </span>_⊔_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">meetSemilattice</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsSemilattice<span class="w"> </span>A<span class="w"> </span>_≈_<span class="w"> </span>_⊓_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">absorb-⊔-⊓</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span>y<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span>⊔<span class="w"> </span><span class="o">(</span>x<span class="w"> </span>⊓<span class="w"> </span>y<span class="o">))</span><span class="w"> </span>≈<span class="w"> </span>x<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">absorb-⊓-⊔</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span>y<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span>⊓<span class="w"> </span><span class="o">(</span>x<span class="w"> </span>⊔<span class="w"> </span>y<span class="o">))</span><span class="w"> </span>≈<span class="w"> </span>x</span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#concrete-examples"> <h3 id="concrete-examples">Concrete Examples</h3> </a> <a href="#natural-numbers"> <h4 id="natural-numbers">Natural Numbers</h4> </a> <p>Since we&rsquo;ve been talking about <code>min</code> and <code>max</code> as motivators for properties of \((\sqcap)\) and \((\sqcup)\), it might not be all that surprising that natural numbers form a lattice with <code>min</code> and <code>max</code> as the two binary operations. In fact, the Agda standard library writes <code>min</code> as <code>_⊓_</code> and <code>max</code> as <code>_⊔_</code>! We can make use of the already-proven properties of these operators to easily define <code>IsLattice</code> for natural numbers. Notice that since we&rsquo;re not doing anything clever, like considering lists up to reordering, there&rsquo;s no reason not to use definitional equality <code>≡</code> for our equivalence relation.</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Nat.agda" data-first-line="1" data-last-line="45" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Nat.agda#L1-L45">Nat.agda</a>, lines 1 through 45</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="kr">module</span><span class="w"> </span><span class="n">Lattice.Nat</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kr">open</span><span class="w"> </span><span class="kr">import</span><span class="w"> </span><span class="n">Equivalence</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kr">open</span><span class="w"> </span><span class="kr">import</span><span class="w"> </span><span class="n">Lattice</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kr">open</span><span class="w"> </span><span class="kr">import</span><span class="w"> </span><span class="n">Relation.Binary.PropositionalEquality</span><span class="w"> </span><span class="kr">using</span><span class="w"> </span><span class="o">(</span>_≡_;<span class="w"> </span>refl;<span class="w"> </span>sym;<span class="w"> </span>trans<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kr">open</span><span class="w"> </span><span class="kr">import</span><span class="w"> </span><span class="n">Data.Nat</span><span class="w"> </span><span class="kr">using</span><span class="w"> </span><span class="o">(</span>ℕ;<span class="w"> </span>_⊔_;<span class="w"> </span>_⊓_;<span class="w"> </span>_≤_<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kr">open</span><span class="w"> </span><span class="kr">import</span><span class="w"> </span><span class="n">Data.Nat.Properties</span><span class="w"> </span><span class="kr">using</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span><span class="w"> </span>⊔-assoc;<span class="w"> </span>⊔-comm;<span class="w"> </span>⊔-idem<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>⊓-assoc;<span class="w"> </span>⊓-comm;<span class="w"> </span>⊓-idem<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>⊓-mono-≤;<span class="w"> </span>⊔-mono-≤<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>m≤n⇒m≤o⊔n;<span class="w"> </span>m≤n⇒m⊓o≤n;<span class="w"> </span>≤-refl;<span class="w"> </span>≤-antisym<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kr">private</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">≡-⊔-cong</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>a₁<span class="w"> </span>a₂<span class="w"> </span>a₃<span class="w"> </span>a₄<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>a₁<span class="w"> </span>≡<span class="w"> </span>a₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>a₃<span class="w"> </span>≡<span class="w"> </span>a₄<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>a₁<span class="w"> </span>⊔<span class="w"> </span>a₃<span class="o">)</span><span class="w"> </span>≡<span class="w"> </span><span class="o">(</span>a₂<span class="w"> </span>⊔<span class="w"> </span>a₄<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>≡-⊔-cong<span class="w"> </span>a₁≡a₂<span class="w"> </span>a₃≡a₄<span class="w"> </span><span class="kr">rewrite</span><span class="w"> </span>a₁≡a₂<span class="w"> </span><span class="kr">rewrite</span><span class="w"> </span>a₃≡a₄<span class="w"> </span><span class="ow">=</span><span class="w"> </span>refl<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">≡-⊓-cong</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>a₁<span class="w"> </span>a₂<span class="w"> </span>a₃<span class="w"> </span>a₄<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>a₁<span class="w"> </span>≡<span class="w"> </span>a₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>a₃<span class="w"> </span>≡<span class="w"> </span>a₄<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>a₁<span class="w"> </span>⊓<span class="w"> </span>a₃<span class="o">)</span><span class="w"> </span>≡<span class="w"> </span><span class="o">(</span>a₂<span class="w"> </span>⊓<span class="w"> </span>a₄<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>≡-⊓-cong<span class="w"> </span>a₁≡a₂<span class="w"> </span>a₃≡a₄<span class="w"> </span><span class="kr">rewrite</span><span class="w"> </span>a₁≡a₂<span class="w"> </span><span class="kr">rewrite</span><span class="w"> </span>a₃≡a₄<span class="w"> </span><span class="ow">=</span><span class="w"> </span>refl<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nf">isMaxSemilattice</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsSemilattice<span class="w"> </span>ℕ<span class="w"> </span>_≡_<span class="w"> </span>_⊔_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>isMaxSemilattice<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">record</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span><span class="w"> </span>≈-equiv<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">record</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span><span class="w"> </span>≈-refl<span class="w"> </span><span class="ow">=</span><span class="w"> </span>refl<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>≈-sym<span class="w"> </span><span class="ow">=</span><span class="w"> </span>sym<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>≈-trans<span class="w"> </span><span class="ow">=</span><span class="w"> </span>trans<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>≈-⊔-cong<span class="w"> </span><span class="ow">=</span><span class="w"> </span>≡-⊔-cong<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>⊔-assoc<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊔-assoc<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>⊔-comm<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊔-comm<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>⊔-idemp<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊔-idem<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nf">isMinSemilattice</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsSemilattice<span class="w"> </span>ℕ<span class="w"> </span>_≡_<span class="w"> </span>_⊓_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>isMinSemilattice<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">record</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span><span class="w"> </span>≈-equiv<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">record</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">{</span><span class="w"> </span>≈-refl<span class="w"> </span><span class="ow">=</span><span class="w"> </span>refl<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>≈-sym<span class="w"> </span><span class="ow">=</span><span class="w"> </span>sym<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>≈-trans<span class="w"> </span><span class="ow">=</span><span class="w"> </span>trans<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>≈-⊔-cong<span class="w"> </span><span class="ow">=</span><span class="w"> </span>≡-⊓-cong<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>⊔-assoc<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊓-assoc<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>⊔-comm<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊓-comm<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>;<span class="w"> </span>⊔-idemp<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊓-idem<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The definition for the lattice instance itself is pretty similar; I&rsquo;ll omit it here to avoid taking up a lot of vertical space, but you can find it on lines 47 through 83 of <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Nat.agda">my <code>Lattice.Nat</code> module</a>.</p> <a href="#the-above-below-lattice"> <h4 id="the-above-below-lattice">The &ldquo;Above-Below&rdquo; Lattice</h4> </a> <p>It&rsquo;s not too hard to implement our sign lattice in Agda. However, we can do it in a somewhat general way. As it turns out, extending an existing set, such as \(\{+, -, 0\}\), with a &ldquo;bottom&rdquo; and &ldquo;top&rdquo; element (to be used when taking the least upper bound and greatest lower bound) is quite common and useful. For instance, if we were to do constant propagation (simplifying <code>7+4</code> to <code>11</code>), we would probably do something similar, using the set of integers \(\mathbb{Z}\) instead of the plus-zero-minus set.</p> <p>The general definition is as follows. Take some original set \(S\) (like our 3-element set of signs), and extend it with new &ldquo;top&rdquo; and &ldquo;bottom&rdquo; elements (\(\top\) and \(\bot\)). Then, define \((\sqcup)\) as follows:</p> $$ x_1 \sqcup x_2 = \begin{cases} \top &amp;amp; x_1 = \top\ \text{or}\ x_2 = \top \\ \top &amp;amp; x_1, x_2 \in S, x_1 \neq x_2 \\ x_1 = x_2 &amp;amp; x_1, x_2 \in S, x_1 = x_2 \\ x_1 &amp;amp; x_2 = \bot \\ x_2 &amp;amp; x_1 = \bot \end{cases} $$ <p>In other words, \(\top\) overrules anything that it&rsquo;s combined with. In math terms, it&rsquo;s the <strong>absorbing element</strong> of the lattice. On the other hand, \(\bot\) gets overruled by anything it&rsquo;s combined with. In math terms, that&rsquo;s an <strong>identity element</strong>. Finally, when combining two elements that <em>aren&rsquo;t</em> \(\top\) or \(\bot\) (which would otherwise be covered by the prior sentences), combining an element with itself leaves it unchanged (upholding idempotence), while combining two unequal element results in \(\top\). That last part matches the way we defined &ldquo;least upper bound&rdquo; earlier.</p> <p>The intuition is as follows: the \((\sqcup)\) operator is like an &ldquo;or&rdquo;. Then, &ldquo;anything or positive&rdquo; means &ldquo;anything&rdquo;; same with &ldquo;anything or negative&rdquo;, etc. On the other hand, &ldquo;impossible or positive&rdquo; means positive, since one of those cases will never happen. Finally, in the absense of additional elements, the most we can say about &ldquo;positive or negative&rdquo; is &ldquo;any sign&rdquo;; of course, &ldquo;positive or positive&rdquo; is the same as &ldquo;positive&rdquo;.</p> <p>The &ldquo;greatest lower bound&rdquo; operator is defined by effectively swapping top and bottom.</p> $$ x_1 \sqcup x_2 = \begin{cases} \bot &amp;amp; x_1 = \bot\ \text{or}\ x_2 = \bot \\ \bot &amp;amp; x_1, x_2 \in S, x_1 \neq x_2 \\ x_1 = x_2 &amp;amp; x_1, x_2 \in S, x_1 = x_2 \\ x_1 &amp;amp; x_2 = \top \\ x_2 &amp;amp; x_1 = \top \end{cases} $$ <p>For this operator, \(\bot\) is the absorbing element, and \(\top\) is the identity element. The intuition here is not too different: if \((\sqcap)\) is like an &ldquo;and&rdquo;, then &ldquo;impossible and positive&rdquo; can&rsquo;t happen; same with &ldquo;impossible and negative&rdquo;, and so on. On the other hand, &ldquo;anything and positive&rdquo; clearly means positive. Finally, &ldquo;negative and positive&rdquo; can&rsquo;t happen (again, there is no number that&rsquo;s both positive and negative), and &ldquo;positive and positive&rdquo; is just &ldquo;positive&rdquo;.</p> <p>What properties of the underlying set did we use to get this to work? The only thing we needed is to be able to check and see if two elements are equal or not; this is called <em>decidable equality</em>. Since that&rsquo;s the only thing we used, this means that we can define an &ldquo;above/below&rdquo; lattice like this for any type for which we can check if two elements are equal. In Agda, I encoded this using a <a href="https://agda.readthedocs.io/en/latest/language/module-system.html#parameterised-modules"class="external-link">parameterized module<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/AboveBelow.agda" data-first-line="5" data-last-line="8" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/AboveBelow.agda#L5-L8">AboveBelow.agda</a>, lines 5 through 8</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="kr">module</span><span class="w"> </span><span class="n">Lattice.AboveBelow</span><span class="w"> </span><span class="o">{</span>a<span class="o">}</span><span class="w"> </span><span class="o">(</span>A<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>_≈₁_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>≈₁-equiv<span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsEquivalence<span class="w"> </span>A<span class="w"> </span>_≈₁_<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>≈₁-dec<span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsDecidable<span class="w"> </span>_≈₁_<span class="o">)</span><span class="w"> </span><span class="kr">where</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>From there, I defined the actual data type as follows:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/AboveBelow.agda" data-first-line="23" data-last-line="26" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/AboveBelow.agda#L23-L26">AboveBelow.agda</a>, lines 23 through 26</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="kr">data</span><span class="w"> </span>AboveBelow<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⊥</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>AboveBelow<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⊤</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>AboveBelow<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">[_]</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>AboveBelow</span></span></code></pre></td></tr></table> </div> </div> </div> <p>From there, I defined the \((\sqcup)\) and \((\sqcap)\) operations almost exactly to the mathematical equation above (the cases were re-ordered to improve Agda&rsquo;s reduction behavior). Here&rsquo;s the former:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/AboveBelow.agda" data-first-line="86" data-last-line="93" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/AboveBelow.agda#L86-L93">AboveBelow.agda</a>, lines 86 through 93</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">86 </span><span class="lnt">87 </span><span class="lnt">88 </span><span class="lnt">89 </span><span class="lnt">90 </span><span class="lnt">91 </span><span class="lnt">92 </span><span class="lnt">93 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">_⊔_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>AboveBelow<span class="w"> </span><span class="ow">→</span><span class="w"> </span>AboveBelow<span class="w"> </span><span class="ow">→</span><span class="w"> </span>AboveBelow<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⊥<span class="w"> </span>⊔<span class="w"> </span>x<span class="w"> </span><span class="ow">=</span><span class="w"> </span>x<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⊤<span class="w"> </span>⊔<span class="w"> </span>x<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊤<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>[<span class="w"> </span>x<span class="w"> </span>]<span class="w"> </span>⊔<span class="w"> </span>[<span class="w"> </span>y<span class="w"> </span>]<span class="w"> </span><span class="kr">with</span><span class="w"> </span>≈₁-dec<span class="w"> </span>x<span class="w"> </span>y<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>yes<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>[<span class="w"> </span>x<span class="w"> </span>]<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>no<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊤<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>x<span class="w"> </span>⊔<span class="w"> </span>⊥<span class="w"> </span><span class="ow">=</span><span class="w"> </span>x<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>x<span class="w"> </span>⊔<span class="w"> </span>⊤<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊤</span></span></code></pre></td></tr></table> </div> </div> </div> <p>And here&rsquo;s the latter:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/AboveBelow.agda" data-first-line="181" data-last-line="188" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/AboveBelow.agda#L181-L188">AboveBelow.agda</a>, lines 181 through 188</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">181 </span><span class="lnt">182 </span><span class="lnt">183 </span><span class="lnt">184 </span><span class="lnt">185 </span><span class="lnt">186 </span><span class="lnt">187 </span><span class="lnt">188 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">_⊓_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>AboveBelow<span class="w"> </span><span class="ow">→</span><span class="w"> </span>AboveBelow<span class="w"> </span><span class="ow">→</span><span class="w"> </span>AboveBelow<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⊥<span class="w"> </span>⊓<span class="w"> </span>x<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⊤<span class="w"> </span>⊓<span class="w"> </span>x<span class="w"> </span><span class="ow">=</span><span class="w"> </span>x<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>[<span class="w"> </span>x<span class="w"> </span>]<span class="w"> </span>⊓<span class="w"> </span>[<span class="w"> </span>y<span class="w"> </span>]<span class="w"> </span><span class="kr">with</span><span class="w"> </span>≈₁-dec<span class="w"> </span>x<span class="w"> </span>y<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>yes<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>[<span class="w"> </span>x<span class="w"> </span>]<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>no<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>x<span class="w"> </span>⊓<span class="w"> </span>⊥<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⊥<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>x<span class="w"> </span>⊓<span class="w"> </span>⊤<span class="w"> </span><span class="ow">=</span><span class="w"> </span>x</span></span></code></pre></td></tr></table> </div> </div> </div> <p>The proofs of the lattice properties are straightforward and proceed by simple case analysis. Unfortunately, Agda doesn&rsquo;t quite seem to evaluate the binary operator in every context that I would expect it to, which has led me to define some helper lemmas such as the following:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/AboveBelow.agda" data-first-line="95" data-last-line="96" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/AboveBelow.agda#L95-L96">AboveBelow.agda</a>, lines 95 through 96</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">95 </span><span class="lnt">96 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⊤⊔x≡⊤</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>x<span class="w"> </span><span class="ow">:</span><span class="w"> </span>AboveBelow<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>⊤<span class="w"> </span>⊔<span class="w"> </span>x<span class="w"> </span>≡<span class="w"> </span>⊤<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⊤⊔x≡⊤<span class="w"> </span>_<span class="w"> </span><span class="ow">=</span><span class="w"> </span>refl</span></span></code></pre></td></tr></table> </div> </div> </div> <p>As a sample, here&rsquo;s a proof of commutativity of \((\sqcup)\):</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/AboveBelow.agda" data-first-line="158" data-last-line="165" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/AboveBelow.agda#L158-L165">AboveBelow.agda</a>, lines 158 through 165</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">158 </span><span class="lnt">159 </span><span class="lnt">160 </span><span class="lnt">161 </span><span class="lnt">162 </span><span class="lnt">163 </span><span class="lnt">164 </span><span class="lnt">165 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⊔-comm</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>ab₁<span class="w"> </span>ab₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>AboveBelow<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>ab₁<span class="w"> </span>⊔<span class="w"> </span>ab₂<span class="o">)</span><span class="w"> </span>≈<span class="w"> </span><span class="o">(</span>ab₂<span class="w"> </span>⊔<span class="w"> </span>ab₁<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⊔-comm<span class="w"> </span>⊤<span class="w"> </span>x<span class="w"> </span><span class="kr">rewrite</span><span class="w"> </span>x⊔⊤≡⊤<span class="w"> </span>x<span class="w"> </span><span class="ow">=</span><span class="w"> </span>≈-refl<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⊔-comm<span class="w"> </span>⊥<span class="w"> </span>x<span class="w"> </span><span class="kr">rewrite</span><span class="w"> </span>x⊔⊥≡x<span class="w"> </span>x<span class="w"> </span><span class="ow">=</span><span class="w"> </span>≈-refl<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⊔-comm<span class="w"> </span>x<span class="w"> </span>⊤<span class="w"> </span><span class="kr">rewrite</span><span class="w"> </span>x⊔⊤≡⊤<span class="w"> </span>x<span class="w"> </span><span class="ow">=</span><span class="w"> </span>≈-refl<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⊔-comm<span class="w"> </span>x<span class="w"> </span>⊥<span class="w"> </span><span class="kr">rewrite</span><span class="w"> </span>x⊔⊥≡x<span class="w"> </span>x<span class="w"> </span><span class="ow">=</span><span class="w"> </span>≈-refl<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⊔-comm<span class="w"> </span>[<span class="w"> </span>x₁<span class="w"> </span>]<span class="w"> </span>[<span class="w"> </span>x₂<span class="w"> </span>]<span class="w"> </span><span class="kr">with</span><span class="w"> </span>≈₁-dec<span class="w"> </span>x₁<span class="w"> </span>x₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>yes<span class="w"> </span>x₁≈x₂<span class="w"> </span><span class="kr">rewrite</span><span class="w"> </span>x≈y⇒[x]⊔[y]≡[x]<span class="w"> </span><span class="o">(</span>≈₁-sym<span class="w"> </span>x₁≈x₂<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>≈-lift<span class="w"> </span>x₁≈x₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>no<span class="w"> </span>x₁̷≈x₂<span class="w"> </span><span class="kr">rewrite</span><span class="w"> </span>x̷≈y⇒[x]⊔[y]≡⊤<span class="w"> </span><span class="o">(</span>x₁̷≈x₂<span class="w"> </span>∘<span class="w"> </span>≈₁-sym<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>≈-⊤-⊤</span></span></code></pre></td></tr></table> </div> </div> </div> <p>The details of the rest of the proofs can be found in the <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/AboveBelow.agda"><code>AboveBelow.agda</code> file</a>.</p> <p>To recover the sign lattice we&rsquo;ve been talking about all along, it&rsquo;s sufficient to define a sign data type:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Sign.agda" data-first-line="19" data-last-line="22" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Sign.agda#L19-L22">Sign.agda</a>, lines 19 through 22</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="kr">data</span><span class="w"> </span>Sign<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">+</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Sign<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">-</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Sign<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">0ˢ</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Sign</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Then, prove decidable equality on it (effecitly defining a comparison function), and instantiate the <code>AboveBelow</code> module:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Analysis/Sign.agda" data-first-line="34" data-last-line="47" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Analysis/Sign.agda#L34-L47">Sign.agda</a>, lines 34 through 47</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="c1">-- g for siGn; s is used for strings and i is not very descriptive.</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nf">_≟ᵍ_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsDecidable<span class="w"> </span><span class="o">(</span>_≡_<span class="w"> </span><span class="o">{</span>_<span class="o">}</span><span class="w"> </span><span class="o">{</span>Sign<span class="o">})</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>_≟ᵍ_<span class="w"> </span>+<span class="w"> </span>+<span class="w"> </span><span class="ow">=</span><span class="w"> </span>yes<span class="w"> </span>refl<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>_≟ᵍ_<span class="w"> </span>+<span class="w"> </span>-<span class="w"> </span><span class="ow">=</span><span class="w"> </span>no<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span><span class="o">())</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>_≟ᵍ_<span class="w"> </span>+<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span><span class="ow">=</span><span class="w"> </span>no<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span><span class="o">())</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>_≟ᵍ_<span class="w"> </span>-<span class="w"> </span>+<span class="w"> </span><span class="ow">=</span><span class="w"> </span>no<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span><span class="o">())</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>_≟ᵍ_<span class="w"> </span>-<span class="w"> </span>-<span class="w"> </span><span class="ow">=</span><span class="w"> </span>yes<span class="w"> </span>refl<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>_≟ᵍ_<span class="w"> </span>-<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span><span class="ow">=</span><span class="w"> </span>no<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span><span class="o">())</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>_≟ᵍ_<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span>+<span class="w"> </span><span class="ow">=</span><span class="w"> </span>no<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span><span class="o">())</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>_≟ᵍ_<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span>-<span class="w"> </span><span class="ow">=</span><span class="w"> </span>no<span class="w"> </span><span class="o">(</span><span class="ow">λ</span><span class="w"> </span><span class="o">())</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>_≟ᵍ_<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span><span class="mi">0</span>ˢ<span class="w"> </span><span class="ow">=</span><span class="w"> </span>yes<span class="w"> </span>refl<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">-- embelish &#39;sign&#39; with a top and bottom element.</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kr">open</span><span class="w"> </span><span class="kr">import</span><span class="w"> </span><span class="n">Lattice.AboveBelow</span><span class="w"> </span>Sign<span class="w"> </span>_≡_<span class="w"> </span><span class="o">(</span><span class="kr">record</span><span class="w"> </span><span class="o">{</span><span class="w"> </span>≈-refl<span class="w"> </span><span class="ow">=</span><span class="w"> </span>refl;<span class="w"> </span>≈-sym<span class="w"> </span><span class="ow">=</span><span class="w"> </span>sym;<span class="w"> </span>≈-trans<span class="w"> </span><span class="ow">=</span><span class="w"> </span>trans<span class="w"> </span><span class="o">})</span><span class="w"> </span>_≟ᵍ_<span class="w"> </span>as<span class="w"> </span>AB</span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#from-simple-lattices-to-complex-ones"> <h3 id="from-simple-lattices-to-complex-ones">From Simple Lattices to Complex Ones</h3> </a> <p>Natural numbers and signs alone are cool enough, but they will not be sufficient to write program analyzers. That&rsquo;s because when we&rsquo;re writing an analyzer, we don&rsquo;t just care about one variable: we care about all of them! An initial guess might be to say that when analyzing a program, we really need <em>several</em> signs: one for each variable. This might be reminiscent of a <a href="https://en.wikipedia.org/wiki/Associative_array"class="external-link">map<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. So, when we compare specificity, we&rsquo;ll really be comparing the specificity of maps. Even that, though, is not enough. The reason is that variables might have different signs at different points in the program! A single map would not be able to capture that sort of nuance, so what we really need is a map associating states with another map, which in turn associates variables with their signs.</p> <p>Mathematically, we might write this as:</p> $$ \text{Info} \triangleq \text{ProgramStates} \to (\text{Variables} \to \text{Sign}) $$ <p>That&rsquo;s a big step up in complexity. We now have a doubly-nested map structure instead of just a sign. and we need to compare such maps in order to gaugage their specificity and advance our analyses. But where do we even start with maps, and how do we define the \((\sqcup)\) and \((\sqcap)\) operations?</p> <p>The solution turns out to be to define ways in which simpler lattices (like our sign) can be combined and transformed to define more complex lattices. We&rsquo;ll move on to that in the next post of this series.</p> Implementing and Verifying "Static Program Analysis" in Agda, Part 0: Intro https://danilafe.com/blog/00_spa_agda_intro/ Sat, 06 Jul 2024 17:37:42 -0700 https://danilafe.com/blog/00_spa_agda_intro/ <p>Some years ago, when the Programming Languages research group at Oregon State University was discussing what to read, the <a href="https://cs.au.dk/~amoeller/spa/"class="external-link"><em>Static Program Analysis</em><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> lecture notes came up. The group didn&rsquo;t end up reading the lecture notes, but I did. As I was going through them, I noticed that they were quite rigorous: the first several chapters cover a little bit of <a href="https://en.wikipedia.org/wiki/Lattice_%28order%29"class="external-link">lattice theory<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, and the subsequent analyses &ndash; and the descriptions thereof &ndash; are quite precise. When I went to implement the algorithms in the textbook, I realized that just writing them down would not be enough. After all, the textbook also proves several properties of the lattice-based analyses, which would be lost in translation if I were to just write C++ or Haskell.</p> <p>At the same time, I noticed that lots of recent papers in programming language theory were formalizing their results in <a href="https://agda.readthedocs.io/en/latest/getting-started/what-is-agda.html"class="external-link">Agda<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. Having <a href="https://danilafe.com/series/meaningfully-typechecking-a-language-in-idris/">played</a> <a href="https://danilafe.com/series/advent-of-code-in-coq/">with</a> <a href="https://danilafe.com/blog/coq_dawn_eval/">dependent</a> <a href="https://danilafe.com/blog/coq_palindrome/">types</a> before, I was excited to try it out. Thus began my journey to formalize (the first few chapters of) <em>Static Program Analysis</em> in Agda.</p> <p>In all, I built a framework for static analyses, based on a tool called <em>motone functions</em>. This framework can be used to implement and reason about many different analyses (currently only a certain class called <em>forward analyses</em>, but that&rsquo;s not hard limitation). Recently, I&rsquo;ve proven the <em>correctness</em> of the algorithms my framework produces. Having reached this milestone, I&rsquo;d like to pause and talk about what I&rsquo;ve done.</p> <p>In subsequent posts in this series, will describe what I have so far. It&rsquo;s not perfect, and some work is yet to be done; however, getting to this point was no joke, and I think it&rsquo;s worth discussing. In all, I&rsquo;d like to cover the following major topics, spending a couple of posts on each:</p> <ul> <li> <p><strong>Lattices</strong>: the analyses I&rsquo;m reasoning about use an algebraic structure called a <em>lattice</em>. This structure has certain properties that make it amenable to describing degrees of &ldquo;knowledge&rdquo; about a program. In lattice-based static program analysis, the various elements of the lattice represent different facts or properties that we know about the program in question; operations on the lattice help us combine these facts and reason about them. I write about this in <a href="https://danilafe.com/blog/01_spa_agda_lattices/">Part 1: Lattices</a>.</p> <p>Interestingly, lattices can be made by combining other lattices in certain ways. We can therefore use simpler lattices as building blocks to create more complex ones, all while preserving the algebraic structure that we need for program analysis. I write about this in <a href="https://danilafe.com/blog/02_spa_agda_combining_lattices/">Part 2: Combining Lattices</a>.</p> </li> <li> <p><strong>The Fixed-Point Algorithm</strong>: to analyze a program, we use information that we already know to compute additional information. For instance, we might use the fact that <code>1</code> is positive to compute the fact that <code>1+1</code> is positive as well. Using that information, we can determine the sign of <code>(1+1)+1</code>, and so on. In practice, this is often done by calling some kind of &ldquo;analyze&rdquo; function over and over, each time getting closer to an accurate characterization of the program&rsquo;s behavior. When the output of &ldquo;analyze&rdquo; stops changing, we know we&rsquo;ve found as much as we can find, and stop.</p> <p>What does it mean for the output to stop changing? Roughly, that&rsquo;s when the following equation holds: <code>knownInfo = analyze(knownInfo)</code>. In mathematics, this is known as a <a href="https://mathworld.wolfram.com/FixedPoint.html"class="external-link">fixed point<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. To enable computing fixed points, we focus on a specific kind of lattices: those with a <em>fixed height</em>. I talk about what this means in <a href="https://danilafe.com/blog/03_spa_agda_fixed_height/">Part 3: Lattices of Finite Height</a>.</p> <p>Even if we restrict our attention to lattices of fixed height, not all functions have fixed points; however, certain types of functions on lattices always do. The <em>fixed-point algorithm</em> is a way to compute these points, and we will use this to drive our analyses. I talk about this in <a href="https://danilafe.com/blog/04_spa_agda_fixedpoint/">Part 4: The Fixed-Point Algorithm</a>.</p> </li> <li> <p><strong>Correctness</strong>: putting together the work on lattices and the fixed-point algorithm, we can implement a static program analyzer in Agda. However, it&rsquo;s not hard to write an &ldquo;analyze&rdquo; function that has a fixed point but produces an incorrect result. Thus, the next step is to prove that the results of our analyzer accurately describe the program in question.</p> <p>The interesting aspect of this step is that our program analyzer works on <a href="https://en.wikipedia.org/wiki/Control-flow_graph"class="external-link">control-flow graphs<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> (CFGs), which are a relatively compiler-centric representation of programs. On the other hand, what the language <em>actually does</em> is defined by its <a href="https://en.wikipedia.org/wiki/Semantics_%28computer_science%29"class="external-link">semantics<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, which is not at all compiler-centric. We need to connect these two, showing that the CFGs we produce &ldquo;make sense&rdquo; for our language, and that given CFGs that make sense, our analysis produces results that match the language&rsquo;s execution. To do so, I write about the language and its semantics in <a href="https://danilafe.com/blog/05_spa_agda_semantics/">Part 5: Our Programming Language</a>, then about building control flow graphs for the language in <a href="https://danilafe.com/blog/06_spa_agda_cfg/">Part 6: Control Flow Graphs</a>. I then write about combining these two representations in <a href="https://danilafe.com/blog/07_spa_agda_semantics_and_cfg/">Part 7: Connecting Semantics and Control Flow Graphs</a>.</p> </li> </ul> <a href="#navigation"> <h3 id="navigation">Navigation</h3> </a> <p>Here are the posts that I’ve written so far for this series:</p> <ul> <li><a href="https://danilafe.com/blog/01_spa_agda_lattices/">Lattices</a></li> <li><a href="https://danilafe.com/blog/02_spa_agda_combining_lattices/">Combining Lattices</a></li> <li><a href="https://danilafe.com/blog/03_spa_agda_fixed_height/">Lattices of Finite Height</a></li> <li><a href="https://danilafe.com/blog/04_spa_agda_fixedpoint/">The Fixed-Point Algorithm</a></li> <li><a href="https://danilafe.com/blog/05_spa_agda_semantics/">Our Programming Language</a></li> <li><a href="https://danilafe.com/blog/06_spa_agda_cfg/">Control Flow Graphs</a></li> <li><a href="https://danilafe.com/blog/07_spa_agda_semantics_and_cfg/">Connecting Semantics and Control Flow Graphs</a></li> <li><a href="https://danilafe.com/blog/08_spa_agda_forward/">Forward Analysis</a></li> <li><a href="https://danilafe.com/blog/09_spa_agda_verified_forward/">Verifying the Forward Analysis</a></li> </ul> Microfeatures I Love in Blogs and Personal Websites https://danilafe.com/blog/blog_microfeatures/ Sun, 23 Jun 2024 11:03:10 -0700 https://danilafe.com/blog/blog_microfeatures/ <p>Some time ago, Hillel Wayne published an article titled <a href="https://buttondown.email/hillelwayne/archive/microfeatures-id-like-to-see-in-more-languages/"class="external-link"><em>Microfeatures I&rsquo;d like to see in more languages</em><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. In this article, he described three kinds of features in <em>programming languages</em>: fundamental features, deeply engrained features, and nice-to-have convenience features. Hillel&rsquo;s premise was that language designers tend to focus on the first two; however, because the convenience features are relatively low-overhead, it&rsquo;s easier for them to jump between projects, and they provide a quality-of-life increase.</p> <p>I&rsquo;ve been running a blog for a while &mdash; some of the oldest posts I&rsquo;ve found (which are no longer reflected on this site due to their low quality) were from 2015. In this time, I&rsquo;ve been on the lookout for ways to improve the site, and I&rsquo;ve seen quite a few little things that are nice to use, but relatively easy to implement. They don&rsquo;t really make or break a website; the absence of such features might be noticed, but will not cause any disruption for the reader. On the other hand, their presence serves as a QoL enhancement. I find these to be analogous to Hillel&rsquo;s notion of &ldquo;microfeatures&rdquo;. If you&rsquo;re interested in adding something to your site, consider browsing this menu to see if anything resonates!</p> <p>One last thing is that this post is not necessarily about microfeatures I&rsquo;d like <em>every</em> blog or personal website to have. Some ideas I present here are only well-suited to certain types of content and certain written voices. They need not be applied indiscriminately.</p> <p>With that, let&rsquo;s get started!</p> <a href="#sidenotes"> <h3 id="sidenotes">Sidenotes</h3> </a> <p><a href="https://gwern.net/me"class="external-link">Gwern<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> is, in my view, the king of sidenotes. Gwern&rsquo;s writing makes very heavy use of them (at least based on the articles that I&rsquo;ve read). This is where I originally got inspiration for <a href="https://danilafe.com/blog/sidenotes/">my own implementation in Hugo</a>. Check out the page on <a href="https://gwern.net/hydrocephalus"class="external-link">hydrocephalus<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> for an example; Here&rsquo;s what a piece of that page looks like on my end at the time of writing:</p> <figure class="fullwide"><img src="https://danilafe.com/blog/blog_microfeatures/gwern-sidenotes.png" alt="A screenshot of Gwern&#39;s page on hydrocephalus. The main article text is accompanied by notes in both the left and right margin."><figcaption> <p>A screenshot of Gwern&rsquo;s page on hydrocephalus</p> </figcaption> </figure> <p>Sidenotes are nice because they allow for diversions without interrupting the main article&rsquo;s flow. You can provide additional details for the curious reader, or &mdash; <a href="https://gwern.net/hydrocephalus#sn4"class="external-link">as Gwern does<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> &mdash; use the sidenotes for citing studies or sources. In either case, the reading experience is significantly more pleasant that footnotes, for which you typically have to go to the bottom of the page, and then return to the top.</p> <p>Another reason I called Gwern the &ldquo;king of sidenotes&rdquo; is <a href="https://gwern.net/sidenote"class="external-link">this page on sidenotes<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. There, Gwern documents numerous approaches to this feature, mostly inspired by <a href="https://edwardtufte.github.io/tufte-css/"class="external-link">Tufte CSS<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. The page is very thorough &mdash; it even includes a link to my own work, as unknown as it may be! I would recommend checking it out if you are interested in enhancing your site with sidenotes.</p> <a href="#tables-of-contents"> <h3 id="tables-of-contents">Tables of Contents</h3> </a> <p>Not all personal sites include tables of contents (TOCs), but they are nice. They serve two purposes:</p> <ol> <li>Seeing at a glance what the post will be about, in the form of headings.</li> <li>Being able to navigate to an interesting part of the page without having to scroll.</li> </ol> <p>Static site generators (I myself use <a href="https://gohugo.io/"class="external-link">Hugo<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>) are typically able to generate TOCs automatically, since they are already generating the HTML and know what headings they are inserting into the page. For instance, Hugo has <a href="https://gohugo.io/methods/page/tableofcontents/"class="external-link"><code>TableOfContents</code><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. I suspect the same is true for other existing website technologies.</p> <p>Despite this, I actually had to look relatively long to find sites I frequent that have TOCs to show off as examples here. The first one I came across &mdash; after Gwern&rsquo;s, whose site will be mentioned plenty in this post anyway &mdash; is <a href="https://fasterthanli.me"class="external-link">Faster than Lime<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. Take this post on <a href="https://fasterthanli.me/articles/understanding-rust-futures-by-going-way-too-deep"class="external-link">Rust&rsquo;s Futures<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>; this is what the top of it looks like at the time of writing:</p> <figure class="small"><img src="https://danilafe.com/blog/blog_microfeatures/fasterthanlime-toc.png" alt="A screenshot of the table of contents on Faster than Lime. A box with the word &#34;Contents&#34; contains links to several sections on the page bellow (off screen)"><figcaption> <p>A screenshot of the table of contents on Faster than Lime</p> </figcaption> </figure> <p>The quality and value of TOCs certainly depends on the sections within the page itself &mdash; and whether or not the page has sections at all! &mdash; but in my opinion, the benefits to navigation become apparent even for relatively simple pages.</p> <p>As an honorable mention, I&rsquo;d like to show <a href="https://lars.hupel.info/"class="external-link">Lars Hupel&rsquo;s site<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. The pages on the site don&rsquo;t &mdash; as far as I can tell &mdash; have <em>internal</em> tables of contents. However, pages that are part of a series &mdash; such as the <a href="https://lars.hupel.info/topics/crdt/01-intro/"class="external-link">introduction to CRDTs<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> &mdash; have tables of contents that span the entire series.</p> <figure class="small"><img src="https://danilafe.com/blog/blog_microfeatures/lars-toc.png" alt="A screenshot of the table of contents on Lars Hupel&#39;s site. A box with the words &#34;Series Navigation&#34; contains links to several other pages in the series."><figcaption> <p>A screenshot of the table of contents on Lars Hupel&rsquo;s site</p> </figcaption> </figure> <p>I also find this very nice, though it does miss out on headings within a page.</p> <a href="#bonus-showing-page-progress"> <h4 id="bonus-showing-page-progress">Bonus: Showing Page Progress</h4> </a> <p>I&rsquo;ve mentioned that tables of contents can communicate the structure of the page. However, they do so from the outset, before you&rsquo;ve started reading. In their &ldquo;base form&rdquo;, the reader stops benefiting from tables of contents <span class="sidenote"> <label class="sidenote-label" for="jump-top-note">once they&rsquo;ve started reading.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="jump-top-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> That is, of course, unless they jump back to the top of the post and find the table of contents again. <span class="sidenote-delimiter">]</span> </span> </span> </p> <p>If you want to show progress while the reader is somewhere in the middle of a page, you could use a page progress bar. I&rsquo;ve noticed one while reading <a href="https://www.quantamagazine.org"class="external-link">Quanta Magazine<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>; it looks like this (recording my scrolling through the <a href="https://www.quantamagazine.org/how-the-square-root-of-2-became-a-number-20240621/"class="external-link">most recent article at the time of writing<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>).</p> <figure class="fullwide"><img src="https://danilafe.com/blog/blog_microfeatures/quanta-scroll.gif" alt="The progress bar on a Quanta Magazine article. As the page scrolls, an orange bar at the top gradually fills up from left to right."><figcaption> <p>The progress bar on a Quanta Magazine article</p> </figcaption> </figure> <p>One immediate thought is that this is completely superseded by the regular browser scroll bar that&rsquo;s ever-present at the side of the page. However, the scroll bar could be deceiving. If your page has a comments section, the comments could make the page look dauntingly long. Similarly, references to other pages and general &ldquo;footer material&rdquo; count towards the scroll bar, but would not count towards the progress bar.</p> <p>Combining the two, you could imagine an always-visible table of contents that highlights the current section you&rsquo;re in. With such a feature, you can always see where you are (including a rough estimate of how far into the page you&rsquo;ve scrolled), and at the same time see how the current section integrates into the broader structure. I&rsquo;ve seen this done before, but could not find a site off the top of my head that implements the feature; as a fallback, here&rsquo;s the <a href="https://css-tricks.com/sticky-table-of-contents-with-scrolling-active-states/"class="external-link">CSS tricks tutorial<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> that shows how to implement a dynamic table of contents, and a recording of me scrolling through it:</p> <figure class="fullwide"><img src="https://danilafe.com/blog/blog_microfeatures/csstricks-toc.gif" alt="The table of contents from a CSS Tricks demo. As the page scrolls, the current section in the table of contents becomes bold."><figcaption> <p>The table of contents from a CSS Tricks demo</p> </figcaption> </figure> <a href="#easily-linkable-headings"> <h3 id="easily-linkable-headings">Easily Linkable Headings</h3> </a> <p>How can you link a particular section of a page to your friend? There&rsquo;s a well-defined mechanism to do this in HTML: you can use the ID of a particular HTML element, and add it as <code>#some-id</code> to the end of a link to the page. The link then takes the user to that particular HTML element. I can do this, for instance, to link to the <a href="#sidenotes"class="same-page-link">sidenotes section above<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#arrow-up"/> </svg></a>.</p> <p>How does one discover the ID of the part of the page that they want to link to? The ID is not a &ldquo;visual&rdquo; property; it&rsquo;s not displayed to the user, and is rather a detail of HTML itself. Thus, on any given page, even if every element has a unique, linkable ID, I can&rsquo;t make use of it without going into <strong>Inspect Element</strong> and trying to find the ID in the HTML tree.</p> <p id="linked-paragraph">The simple solution is to make the elements that you want to be easily &ldquo;linkable&rdquo; into links to themselves! Then, the user can right-click the element in question (probably the heading) and click <strong>Copy Link</strong>. Much easier! To demonstrate a similar idea, <a href="#linked-paragraph"class="same-page-link">here is a link to this paragraph itself</a>. You can now use the context menu to <strong>Copy Link</strong>, put it in your browser, and voilà &mdash; you&rsquo;re right back here!</p> <p>As with <a href="#tables-of-contents"class="same-page-link">tables of contents<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#arrow-up"/> </svg></a>, many website technologies provide most of the tooling to add support for this feature. Relatively often I come across pages that have unique IDs for each header, but no clickable links! I end up having to use inspect element to find the anchor points.</p> <p>A variation on this idea &mdash; if you don&rsquo;t want to make the entire heading or title a link &mdash; is to include alongside it (before or after) a clickable element that is a link to that title. You can click that element to retrieve link information, instead (and the icon additionally tells you that this is possible). Hugo&rsquo;s documentation does this: here&rsquo;s a screenshot of <a href="https://gohugo.io/content-management/markdown-attributes/#overview"class="external-link">an arbitrary page<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>.</p> <figure class="small"><img src="https://danilafe.com/blog/blog_microfeatures/hugo-titlelink.png" alt="A title and paragraph from the Hugo documentation. Next to the title there is a blue link symbol."><figcaption> <p>A title and paragraph from the Hugo documentation</p> </figcaption> </figure> <a href="#grouping-series-of-posts"> <h3 id="grouping-series-of-posts">Grouping Series of Posts</h3> </a> <p>Some authors like to write at length on a particular topic; to get the content out to readers faster (and to make the resulting pages less daunting), it makes sense to break a single topic up into a series. The easiest way to do this is to just&hellip; publish several articles, possibly with related names, and link them to each other. Done!</p> <p>With a little more effort, though, the series-reading and series-writing experience could be nicer. Instead of manually inserting links, you could configure your website to automatically add a &ldquo;next&rdquo; and &ldquo;previous&rdquo; button to pages in a given series. You could also give an overview of a particular series and create a &ldquo;navigation hub&rdquo; for it.</p> <p>As an example, the <a href="https://chapel-lang.org/blog/"class="external-link">Chapel language blog<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> has navigation buttons. Here&rsquo;s a screenshot from <a href="https://chapel-lang.org/blog/posts/aoc2022-day09-elvish-string-theory/"class="external-link">a post in the Advent of Code series<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>:</p> <figure class="fullwide"><img src="https://danilafe.com/blog/blog_microfeatures/chapel-seriesnav.png" alt="Series navigation buttons on a Chapel blog post. There are two buttons; one links to a previous page in the series, another links to the next."><figcaption> <p>Series navigation buttons on a Chapel blog post</p> </figcaption> </figure> <p>I&rsquo;ve mentioned this in the section on <a href="#tables-of-contents"class="same-page-link">tables of contents<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#arrow-up"/> </svg></a>, but <a href="https://lars.hupel.info/"class="external-link">Lars Hupel&rsquo;s site<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> has tables of contents that link between series. I&rsquo;m not sure if it&rsquo;s automatically generated or hand-written, but it&rsquo;s definitely nice.</p> <figure class="small"><img src="https://danilafe.com/blog/blog_microfeatures/lars-toc.png" alt="A screenshot of the table of contents on Lars Hupel&#39;s site. A box with the words &#34;Series Navigation&#34; contains links to several other pages in the series."><figcaption> <p>A screenshot of the table of contents on Lars Hupel&rsquo;s site</p> </figcaption> </figure> <a href="#dialogues"> <h3 id="dialogues">Dialogues</h3> </a> <p>I first came across dialogues on <a href="https://xeiaso.net/"class="external-link">Xe Iaso&rsquo;s site<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, but I think I see them used most often in posts on <a href="https://fasterthanli.me/"class="external-link">Faster than Lime<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. As an example, here&rsquo;s a little dialogue on <a href="https://fasterthanli.me/articles/understanding-rust-futures-by-going-way-too-deep#it-s-waiting-for-the-first-one-to-finish"class="external-link">a post about Rust&rsquo;s futures<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. At the time of writing, it looks like this:</p> <figure class="medium"><img src="https://danilafe.com/blog/blog_microfeatures/fasterthanlime-dialogue.png" alt="A dialogue with &#34;cool bear&#34; on Faster than Lime. The page contains chat bubbles that alternate between a bear character and the author."><figcaption> <p>A dialogue with &ldquo;cool bear&rdquo; on Faster than Lime</p> </figcaption> </figure> <p>Using dialogues &mdash; even for technical writing &mdash; is not a particularly novel idea. I know I&rsquo;ve seen it in a textbook before; probably this part of <a href="https://pages.cs.wisc.edu/~remzi/OSTEP/dialogue-virtualization.pdf"class="external-link">Operating Systems: Three Easy Pieces<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. It can help ask questions from a less-experienced point of view, and therefore possibly voice concerns that a reader might themselves be having. And of course &mdash; as with &ldquo;cool bear&rdquo; and Xe Iaso&rsquo;s <a href="https://xeiaso.net/characters"class="external-link">many characters<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> &mdash; it can change the tone and make the page a bit more fun.</p> <a href="#code-blocks-with-origin"> <h3 id="code-blocks-with-origin">Code Blocks with Origin</h3> </a> <p>This one was recommended to me by a reader, and so I&rsquo;ll be talking about my page specifically!</p> <p>When I was <a href="https://danilafe.com/series/compiling-a-functional-language-using-c&#43;&#43;/">writing about making a compiler</a>, a reader emailed me and pointed out that they were getting lost in the various code blocks. My page displayed the code that I was writing about, but the project had grown beyond a single file. As a result, I&rsquo;d be making changes midway through one file at one moment, and another file the next. This prompted me to add decorators to my code blocks that look something like this:</p> <div class="highlight-group" data-base-path="" data-file-path="patterns/patterns.rb" data-first-line="3" data-last-line="8"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/patterns/patterns.rb#L3-L8">patterns.rb</a>, lines 3 through 8</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Ruby" data-lang="Ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">sum_digits</span><span class="p">(</span><span class="n">n</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="n">n</span> <span class="o">&gt;</span> <span class="mi">9</span> </span></span><span class="line"><span class="cl"> <span class="n">n</span> <span class="o">=</span> <span class="n">n</span><span class="o">.</span><span class="n">to_s</span><span class="o">.</span><span class="n">chars</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:to_i</span><span class="p">)</span><span class="o">.</span><span class="n">sum</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"> <span class="n">n</span> </span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The decorator says what file the code is from, as well as what lines are being presented. If you click the file name, the decorator links to my Gitea instance, allowing you to read the code in context.</p> <p>Though it&rsquo;s not quite the same (in particular, it&rsquo;s unfortunately missing links), the Crafting Interpreters online book does something similar. It describes changes to the code in words next to the changed code itself, like &ldquo;added after <code>MyStruct</code>&rdquo;. Here&rsquo;s a screenshot of the page on <a href="https://craftinginterpreters.com/local-variables.html"class="external-link">local variables<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> at the time of writing.</p> <figure class="fullwide"><img src="https://danilafe.com/blog/blog_microfeatures/craftinginterpreters-codenotes.png" alt="Location notes on code in Crafting Interpreters. On the right of code blocks, a margin note describes the file and nature of the change."><figcaption> <p>Location notes on code in Crafting Interpreters</p> </figcaption> </figure> <p>I think it looks quite elegant, and in some ways &mdash; specifically in the verbal descriptions of what each change does &mdash; might be superior to my approach.</p> <p>It&rsquo;s not quite the same thing, but <a href="https://gist.github.com/"class="external-link">GitHub Gists<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> can help approximate this feature. A Gist could contain multiple files, and each file can be individually embedded into your page. Hugo in particular has <a href="https://gohugo.io/content-management/shortcodes/#gist"class="external-link">built-in support<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> for Gists (and I&rsquo;ve snagged that link using the docs&rsquo; <a href="#easily-linkable-headings"class="same-page-link">easily linkable headings<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#arrow-up"/> </svg></a>); I suspect that other website engines have some form of support as well. At the time of writing, an embedded Gist looks something like this:</p> <figure class="small"><img src="https://danilafe.com/blog/blog_microfeatures/hugo-gist.png" alt="Code embedded in Hugo documentation using a GitHub Gist."><figcaption> <p>Code embedded in Hugo documentation using a GitHub Gist</p> </figcaption> </figure> <p>Clicking <code>list.html</code> takes you to the source code of the file.</p> <a href="#bonus-code-blocks-with-clickable-links"> <h4 id="bonus-code-blocks-with-clickable-links">Bonus: Code Blocks with Clickable Links</h4> </a> <p>If we&rsquo;re going for fancy code blocks, another fancy feature is provided by the <a href="https://agda.readthedocs.io/en/latest/getting-started/what-is-agda.html"class="external-link">Agda programming language<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. Agda can generate HTML code blocks in which every symbol (like a variable, record name, function name) are linked to where they are defined. So if you&rsquo;re reading the code, and wonder &ldquo;what the heck is <code>x</code>?&rdquo;, you can just click it to see how it&rsquo;s defined.</p> <p>It&rsquo;s not simple to integrate Agda&rsquo;s plain HTML output into an existing webpage, but some projects do that. I took a stab at it in my <a href="https://danilafe.com/blog/agda_hugo/">post about integrating it with Hugo</a>. I wager this would be even harder for other languages. However, it leads to nice results; my go-to is <a href="https://plfa.github.io/"class="external-link">Programming Languages Foundations in Agda<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. The online book introduces various concepts from Programming Language Theory, and each code block that it shows is fully linked. This makes it possible to jump around the page like so:</p> <figure class="fullwide"><img src="https://danilafe.com/blog/blog_microfeatures/plfa-goto.gif" alt="Navigating code blocks on a page from PLFA. I hover over then click a plus sign to see how addition is defined. I then do the same to see how natural numbers are defined."><figcaption> <p>Navigating code blocks on a page from PLFA</p> </figcaption> </figure> <a href="#markers-for-external-links"> <h3 id="markers-for-external-links">Markers for External Links</h3> </a> <p>Some sites I&rsquo;ve seen mark links that go to a different domain with a little icon. If you&rsquo;ve read this far, you&rsquo;ve likely noticed that my site does the same. Another good example of this &mdash; even though the CSS is little rough at the time of writing &mdash; is <a href="https://jamesg.blog/"class="external-link">James&rsquo; Coffee Blog ☕<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. I&rsquo;ve taken the (small) liberty to adjust the color of the icon, which I suspect is buggy in my browser.</p> <figure class="fullwide"><img src="https://danilafe.com/blog/blog_microfeatures/jamesg-external.png" alt="An external link on James&#39; blog. The link is displayed as normal, and an additional diagonal arrow aiming up and to the right and surrounded by a square is displayed to the right of the link text."><figcaption> <p>An external link on James&rsquo; blog</p> </figcaption> </figure> <p>Some websites (<del>this one included</del>) also make such links open in a new tab automatically. That way, you tend to not lose the original article by clicking through one of its references.</p> <a href="#bonus-different-markers-for-different-destinations"> <h4 id="bonus-different-markers-for-different-destinations">Bonus: Different Markers for Different Destinations</h4> </a> <p><a href="https://gwern.net"class="external-link">Gwern&rsquo;s website<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> takes this idea further, by changing the icon for external links depending on the destination. For instance, links to Wikipedia articles are stylized with a little &ldquo;W&rdquo;, links to Haskell.org are stylized using a lambda (\(\lambda\)), and links to <code>.zip</code> files have a little archive icon. There are more; <del>I&rsquo;ve found the <a href="https://github.com/gwern/gwern.net/blob/959ba9c50d327a960e07241b2c7f13630bf8b80c/js/old/links.js"class="external-link">link processing code on GitHub<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, and even the <a href="https://github.com/gwern/gwern.net/blob/959ba9c50d327a960e07241b2c7f13630bf8b80c/js/old/links.js#L380-L387"class="external-link">list of websites that get their own icons<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>.</del> I could not find a verbal description, though.</p> <p><strong>Edit:</strong> Gwern has pointed out that the links I provided go to obsolete code. The link processing functionality is <a href="https://github.com/gwern/gwern.net/blob/959ba9c50d327a960e07241b2c7f13630bf8b80c/build/LinkIcon.hs#L15"class="external-link">documented in comments here<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> and the <a href="https://github.com/gwern/gwern.net/blob/959ba9c50d327a960e07241b2c7f13630bf8b80c/build/Config/LinkIcon.hs#L83"class="external-link">link icon rules are here<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. A <a href="https://gwern.net/lorem-link#link-icons"class="external-link">non-code list of icons<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> exists too.</p> <p>Now for some pictures. Here are a ton of links from the <a href="https://gwern.net/about"class="external-link">&ldquo;About&rdquo;<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> page!</p> <p><figure class="fullwide"><img src="https://danilafe.com/blog/blog_microfeatures/gwern-linkicons-wiki.png" alt="Links to Wikipedia on Gwern&#39;s blog. Each link is followed by a superscript &#34;W&#34;."><figcaption> <p>Links to Wikipedia on Gwern&rsquo;s site</p> </figcaption> </figure> <figure class="fullwide"><img src="https://danilafe.com/blog/blog_microfeatures/gwern-linkicons-haskell.png" alt="A link to Haskell.org on Gwern&#39;s blog. The link is followed by a superscript lambda."><figcaption> <p>A link to Haskell.org on Gwern&rsquo;s site</p> </figcaption> </figure> <figure class="fullwide"><img src="https://danilafe.com/blog/blog_microfeatures/gwern-linkicons-zip.png" alt="Links zip files on Gwern&#39;s site. Each link is followed by an archive icon."><figcaption> <p>Links zip files on Gwern&rsquo;s site</p> </figcaption> </figure> </p> <a href="#bonus-link-preview"> <h4 id="bonus-link-preview">Bonus: Link Preview</h4> </a> <p><a href="https://gwern.net"class="external-link">Gwern&rsquo;s website<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> has no shortage of cool ideas. Among them showing link previews on hover. When hovering over a link, the site displays a popup window that contains a view into that page. I suspect that this view is also archived somehow, so that it retains a view into the page that matches it at the time of writing.</p> <p>To be perfectly honest, I found this feature a little jarring at first. As I would try to click links, I would get surprised by an additional overlay. However, as I spent more time browsing the site, I grew quite accustomed to the previews. I would hover over a link to see the first paragraph and thus get a short synopsis. This worked really well in tandem with <a href="#bonus-different-markers-for-different-destinations"class="same-page-link">per-destination marker icons<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#arrow-up"/> </svg></a>; I could tell at a glance whether a link was worth hovering over.</p> <p>Here&rsquo;s what it looks like:</p> <figure class="medium"><img src="https://danilafe.com/blog/blog_microfeatures/gwern-hover.gif" alt="Hovering over a link on Gwern&#39;s site. After the link is hovered over, a rectangular popup displays a section of the Wikipedia page the link goes to. I scroll through the section to the table of contents."><figcaption> <p>Hovering over a link on Gwern&rsquo;s site</p> </figcaption> </figure> <a href="#rss-feeds"> <h3 id="rss-feeds">RSS Feeds</h3> </a> <p>RSS is a feed standard that allows sites to publish updates. Blogs in particular can make use of RSS to notify readers of updates. RSS feeds are processed by a feed reader, which is a program that polls a website&rsquo;s <code>index.xml</code> file (or other similar files) and reads it to detect new content. If you opt in to full-text RSS feeds, users can read the entire post entirely from their reader.</p> <p>RSS makes it easier to keep up with your site. Rather than having to check in on every author whose content I enjoy on the internet, I can add their feed URL to my list, and have my feed reader automatically aggregate all updates for me to read. It&rsquo;s kind of like a social media or news feed, except that I control what&rsquo;s shown to me, and authors of the blogs I follow don&rsquo;t need to create accounts and explicitly share their work on social media!</p> <p>I don&rsquo;t have any particular website to show off in this section; instead I&rsquo;ll show you a list of websites that I&rsquo;m following in my feed reader of choice. You might notice that a lot of these websites are listed here as inspiration for other microfeatures.</p> <figure class="small"><img src="https://danilafe.com/blog/blog_microfeatures/feedbin.png" alt="A screenshot of my Feedbin list. Some sites include Hillel Wayne&#39;s, Faster than Lime, Drew DeVault, and the Chapel Language Blog"><figcaption> <p>A screenshot of my Feedbin list</p> </figcaption> </figure> <a href="#links-to-other-sites"> <h3 id="links-to-other-sites">Links to Other Sites</h3> </a> <p>This feature I first noticed on Drew DeVault&rsquo;s blog. Every page on Drew&rsquo;s blog, at the bottom, has a section titled &ldquo;Articles from blogs I read&rdquo;. For instance, on <a href="https://drewdevault.com/2024/05/24/2024-05-24-Bunnix.html"class="external-link">a sample post<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, at the time of writing, I&rsquo;m seeing the following footer:</p> <figure class="fullwide"><img src="https://danilafe.com/blog/blog_microfeatures/drew-openring.png" alt="Links to other blogs from Drew DeVault&#39;s blog. The links consist of three side-by-side boxes, each with a title and brief excerpt."><figcaption> <p>Links to other blogs from Drew DeVault&rsquo;s blog</p> </figcaption> </figure> <p>As indicated in the image, Drew&rsquo;s site in particular uses a program called <a href="https://git.sr.ht/~sircmpwn/openring"class="external-link"><code>openring</code><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, which is based on RSS feeds (another <a href="#rss-feeds"class="same-page-link">microfeature I love<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#arrow-up"/> </svg></a>). However, <em>how</em> the site finds such articles (statically like <code>openring</code>, or on page load using some JavaScript) isn&rsquo;t hugely important to me. What&rsquo;s important is that you&rsquo;re promoting other content creators whose work you enjoy, which is the ethos of my favorite slice of the internet.</p> <a href="#conclusion--anything-else"> <h3 id="conclusion--anything-else">Conclusion + Anything Else?</h3> </a> <p>Those are all the microfeatures that I could think of in a single sitting. I hope that you have been inspired to integrate features like these into your own site, or at the very least that you think doing so would be a good idea.</p> <p>This list isn&rsquo;t exhaustive. I&rsquo;ve probably missed some good microfeatures! If you can think of such a feature, let me know; my email address is linked in the footer of this article.</p> <p>Thank you for reading, and cheers!</p> Integrating Agda's HTML Output with Hugo https://danilafe.com/blog/agda_hugo/ Thu, 30 May 2024 00:29:26 -0700 https://danilafe.com/blog/agda_hugo/ <p>One of my favorite things about Agda are its clickable HTML pages. If you don&rsquo;t know what they are, that&rsquo;s pages like <a href="https://agda.github.io/agda-stdlib/master/Data.List.Properties.html"class="external-link"><code>Data.List.Properties</code><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>; they just give the code from a particular Agda file, but make every identifier clickable. Then, if you see some variable or function that you don&rsquo;t know, you can just click it and jump right to it! It makes exploring the documentation a lot smoother. I&rsquo;ve found that these HTML pages provide all the information I need for writing proofs.</p> <p>Recently, I&rsquo;ve been writing a fair bit about Agda; mostly about the patterns that I&rsquo;ve learned about, such as the <a href="https://danilafe.com/blog/agda_is_pattern/">&ldquo;is something&rdquo; pattern</a> and the <a href="https://danilafe.com/blog/agda_expr_pattern/">&ldquo;deeply embedded expression&rdquo; trick</a>. I&rsquo;ve found myself wanting to click on definitions in my own code blocks; recently, I got this working, and I wanted to share how I did it, in case someone else wants to integrate Agda into their own static website. Though my stack is based on Hugo, the general idea should work with any other static site generator.</p> <a href="#tldr-and-demo"> <h3 id="tldr-and-demo">TL;DR and Demo</h3> </a> <p>I wrote a script to transfer links from an Agda HTML file into Hugo&rsquo;s HTML output, making it possible to embellish &ldquo;plain&rdquo; Hugo output with Agda&rsquo;s &lsquo;go-to-definition links&rsquo;. It looks like this. Here&rsquo;s an Agda code block defining an &rsquo;expression&rsquo; data type, from a project of mine:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Map.agda" data-first-line="543" data-last-line="546" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Map.agda#L543-L546">Map.agda</a>, lines 543 through 546</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">543 </span><span class="lnt">544 </span><span class="lnt">545 </span><span class="lnt">546 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="kr">data</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span><span class="o">(</span>a<span class="w"> </span>⊔ℓ<span class="w"> </span>b<span class="o">)</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">`_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Map<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Expr<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">_∪_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Expr<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">_∩_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Expr</span></span></code></pre></td></tr></table> </div> </div> </div> <p>And here&rsquo;s the denotational semantics for that expression:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Map.agda" data-first-line="586" data-last-line="589" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Map.agda#L586-L589">Map.agda</a>, lines 586 through 589</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">586 </span><span class="lnt">587 </span><span class="lnt">588 </span><span class="lnt">589 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">⟦_⟧</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span>Map<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>⟦<span class="w"> </span>`<span class="w"> </span>m<span class="w"> </span>⟧<span class="w"> </span><span class="ow">=</span><span class="w"> </span>m<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>⟦<span class="w"> </span>e₁<span class="w"> </span>∪<span class="w"> </span>e₂<span class="w"> </span>⟧<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⟦<span class="w"> </span>e₁<span class="w"> </span>⟧<span class="w"> </span>⊔<span class="w"> </span>⟦<span class="w"> </span>e₂<span class="w"> </span>⟧<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>⟦<span class="w"> </span>e₁<span class="w"> </span>∩<span class="w"> </span>e₂<span class="w"> </span>⟧<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⟦<span class="w"> </span>e₁<span class="w"> </span>⟧<span class="w"> </span>⊓<span class="w"> </span>⟦<span class="w"> </span>e₂<span class="w"> </span>⟧</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Notice that you can click <code>Expr</code>, <code>_∪_</code>, <code>⟦</code>, etc.! All of this integrates with my existing Hugo site, and only required a little bit of additional metadata to make it work. The conversion is implemented as <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/commit/04f12b545d5692a78b1a2f13ef968417c749e295/agda.rb"class="external-link">a Ruby script<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>; this script transfers the link structure from an Agda-generated documentation HTML file onto lightly-annotated Hugo code blocks.</p> <p>To use the script, your Hugo theme (or your Markdown content) must annotate the code blocks with several properties:</p> <ul> <li><code>data-agda-block</code>, which marks code that needs to be processed.</li> <li><code>data-file-path</code>, which tells the script what Agda file provided the code in the block, and therefore what Agda HTML file should be searched for links.</li> <li><code>data-first-line</code> and <code>data-last-line</code>, which tell the script what section of the Agda HTML file should be searched for said links.</li> </ul> <p>Given this &ndash; and a couple of other assumptions, such as that all Agda projects are in a <code>code/&lt;project&gt;</code> folder, the script post-processes the HTML files automatically. Right now, the solution is pretty tailored to my site and workflow, but the core of the script &ndash; the piece that transfers links from an Agda HTML file into a syntax-highlighted Hugo HTML block &ndash; should be fairly reusable.</p> <p>Now, the details.</p> <a href="#the-constraints"> <h3 id="the-constraints">The Constraints</h3> </a> <p>The goal was simple: to allow the code blocks on my Hugo-generated site to have links that take the user to the definition of a given symbol. Specifically, if the symbol occurs somewhere on the same blog page, the link should take the user there (and not to a regular <code>Module.html</code> file). That way, the reader can not only get to the code that they want to see, but also have a chance to read the surrounding prose in properly-rendered Markdown.</p> <p>Next, unlike standard &ldquo;literate Agda&rdquo; files, my blog posts are not single <code>.agda</code> files with Markdown in comments. Rather, I use regular Hugo Markdown, and present portions of an existing project, weaving together many files, and showing the fragments out of order. So, my tool needs to support links that come from distinct modules, in any order.</p> <p>Additionally, I&rsquo;ve recently been writing a whole series about an Agda project of mine; in this series, I gradually build up to the final product, explaining one or two modules at a time. I would expect that links on pages in this series could jump to other pages in the same series: if I cover module <code>A</code> in part 1, then write <code>A.f</code> in part 2, clicking on <code>A</code> &ndash; and maybe <code>f</code> &ndash; should take the reader back to the first part&rsquo;s page; once again, this would help provide them with the surrounding explanation.</p> <p>Finally, I wanted the Agda code to appear exactly the same as any other code on my site, including the Hugo-provided syntax highlighting and theme. This ruled out just copy-pasting pieces of the Agda-generated HTML in place of code blocks on my page (and redirecting the links). Thought it was not a hard requirement, I also hoped to include Agda code in the same manner that I include all other code: <a href="https://danilafe.com/blog/codelines/">my <code>codelines</code> shortcode</a>. In brief, the <code>codelines</code> shortcode creates a syntax-highlighted code block, as well as a surrounding &ldquo;context&rdquo; that says what file the code is from, which lines are listed, and where to find the full code (e.g., on my Git server). It looks something like this:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Language/Base.agda" data-first-line="12" data-last-line="20" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Language/Base.agda#L12-L20">Base.agda</a>, lines 12 through 20</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="kr">data</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">_+_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Expr<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">_-_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Expr<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">`_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>String<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Expr<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">#_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>ℕ<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Expr<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kr">data</span><span class="w"> </span>BasicStmt<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">_←_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>String<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">→</span><span class="w"> </span>BasicStmt<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">noop</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>BasicStmt</span></span></code></pre></td></tr></table> </div> </div> </div> <p>In summary:</p> <ol> <li>I want to create cross-links between symbols in Agda blocks in a blog post.</li> <li>These code blocks could include code from disjoint files, and be out of order.</li> <li>Code blocks among a whole series of posts should be cross-linked too.</li> <li>The code blocks should be syntax highlighted the same way as the rest of the code on the site.</li> <li>Ideally, I should be able to use my regular method for referencing code.</li> </ol> <p>I&rsquo;ve hit all of these requirements; now it&rsquo;s time to dig into how I got there.</p> <a href="#implementation"> <h3 id="implementation">Implementation</h3> </a> <a href="#processing-agdas-html-output"> <h4 id="processing-agdas-html-output">Processing Agda&rsquo;s HTML Output</h4> </a> <p>It&rsquo;s pretty much a no-go to try to resolve Agda from Hugo, or perform some sort of &ldquo;heuristic&rdquo; to detect cross-links. Agda is a very complex programming language, and Hugo&rsquo;s templating engine, though powerful, is just not up to this task. Fortunately, Agda has support for <a href="https://agda.readthedocs.io/en/v2.6.4.3-r1/tools/generating-html.html"class="external-link">HTML output using the <code>--html</code> flag<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. As a build step, I can invoke Agda on files that are referenced by my blog, and generate HTML. This would decidedly slow down the site build process, but it would guarantee accurate link information.</p> <p>On the other hand, to satisfy the 4th constraint, I need to somehow mimic &ndash; or keep &ndash; the format of Hugo&rsquo;s existing HTML output. The easiest way to do this without worrying about breaking changes and version incompatibility is to actually use the existing syntax-highlighted HTML, and annotate it with links as I discover them. Effectively, what I need to do is a &ldquo;link transfer&rdquo;: I need to identify regions of code that are highlighted in Agda&rsquo;s HTML, find those regions in Hugo&rsquo;s HTML output, and mark them with links. In addition, I&rsquo;ll need to fix up the links themselves: the HTML output assumes that each Agda file is its own HTML page, but this is ruled out by the second constraint of mine.</p> <p>As a little visualization, the overall problems looks something like this:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="c1">-- Agda&#39;s HTML output (blocks of &#39;t&#39; are links):</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">-- |tttttt| |tttt| |t| |t| |ttttt|</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">module</span><span class="w"> </span><span class="n">ModX</span><span class="w"> </span><span class="o">(</span><span class="w"> </span>x<span class="w"> </span><span class="ow">:</span><span class="w"> </span>T<span class="w"> </span><span class="o">)</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">-- |tttttt| |tt|t| |t| |t| |ttttt|</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">-- Hugo&#39;s HTML output (blocks of &#39;t&#39; are syntax highlighting spans)</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><p>Both Agda and Hugo output a preformatted code block, decorated with various inline HTMl that indicates information (token color for Hugo; symbol IDs and links in Agda). However, Agda and Hugo do not use the same process to create this decorated output; it&rsquo;s entirely possible &ndash; and not uncommon &ndash; for Hugo and Agda to produce misaligned HTML nodes. In my diagram above, this is reflected as <code>ModX</code> being considered a single token by Agda, but two tokens (<code>Mod</code> and <code>X</code>) by the syntax highlighter. As a result, it&rsquo;s difficult to naively iterate the two HTML formats in parallel.</p> <p id="plain-text">What I ended up doing is translating Agda&rsquo;s HTML output into offsets and data about the code block&rsquo;s <em>plain text</em> &ndash; the source code being decorated. Both the Agda and Hugo HTML describe the same code; thus, the plain text is the common denominator between the two.</p> <p>I wrote a Ruby script to extract the decorations from the Agda output; here it is in slightly abridged form. You can find the <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/commit/04f12b545d5692a78b1a2f13ef968417c749e295/agda.rb"class="external-link">original <code>agda.rb</code> file here<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Ruby" data-lang="Ruby"><span class="line"><span class="cl"><span class="c1"># Traverse the preformatted Agda block in the given Agda HTML file</span> </span></span><span class="line"><span class="cl"><span class="c1"># and find which textual ranges have IDs and links to other ranges.</span> </span></span><span class="line"><span class="cl"><span class="c1"># Store this information in a hash, line =&gt; links[]</span> </span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">process_agda_html_file</span><span class="p">(</span><span class="n">file</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">document</span> <span class="o">=</span> <span class="no">Nokogiri</span><span class="o">::</span><span class="no">HTML</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="no">File</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="n">file</span><span class="p">))</span> </span></span><span class="line"><span class="cl"> <span class="n">pre_code</span> <span class="o">=</span> <span class="n">document</span><span class="o">.</span><span class="n">css</span><span class="p">(</span><span class="s2">&#34;pre.Agda&#34;</span><span class="p">)</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1"># The traversal is postorder; we always visit children before their</span> </span></span><span class="line"><span class="cl"> <span class="c1"># parents, and we visit leaves in sequence.</span> </span></span><span class="line"><span class="cl"> <span class="n">line_infos</span> <span class="o">=</span> <span class="o">[]</span> </span></span><span class="line"><span class="cl"> <span class="n">offset</span> <span class="o">=</span> <span class="mi">0</span> <span class="c1"># Column index within the current Agda source code line</span> </span></span><span class="line"><span class="cl"> <span class="n">line</span> <span class="o">=</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="n">pre_code</span><span class="o">.</span><span class="n">traverse</span> <span class="k">do</span> <span class="o">|</span><span class="n">at</span><span class="o">|</span> </span></span><span class="line"><span class="cl"> <span class="c1"># Text nodes are always leaves; visiting a new leaf means we&#39;ve advanced</span> </span></span><span class="line"><span class="cl"> <span class="c1"># in the text by the length of that text. However, if there are newlines</span> </span></span><span class="line"><span class="cl"> <span class="c1"># -- since this is a preformatted block -- we also advanced by a line.</span> </span></span><span class="line"><span class="cl"> <span class="c1"># At this time, do not support links that span multiple lines, but</span> </span></span><span class="line"><span class="cl"> <span class="c1"># Agda doesn&#39;t produce those either.</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">at</span><span class="o">.</span><span class="n">text?</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">at</span><span class="o">.</span><span class="n">content</span><span class="o">.</span><span class="n">include?</span> <span class="s2">&#34;</span><span class="se">\n</span><span class="s2">&#34;</span> </span></span><span class="line"><span class="cl"> <span class="k">raise</span> <span class="s2">&#34;no support for links with newlines inside&#34;</span> <span class="k">if</span> <span class="n">at</span><span class="o">.</span><span class="n">parent</span><span class="o">.</span><span class="n">name</span> <span class="o">!=</span> <span class="s2">&#34;pre&#34;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1"># Increase the line and track the final offset. Written as a loop</span> </span></span><span class="line"><span class="cl"> <span class="c1"># in case we eventually want to add some handling for the pieces</span> </span></span><span class="line"><span class="cl"> <span class="c1"># sandwiched between newlines.</span> </span></span><span class="line"><span class="cl"> <span class="n">at</span><span class="o">.</span><span class="n">content</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34;</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span><span class="o">.</span><span class="n">each_with_index</span> <span class="k">do</span> <span class="o">|</span><span class="n">bit</span><span class="p">,</span> <span class="n">idx</span><span class="o">|</span> </span></span><span class="line"><span class="cl"> <span class="n">line</span> <span class="o">+=</span> <span class="mi">1</span> <span class="k">unless</span> <span class="n">idx</span> <span class="o">==</span> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="n">offset</span> <span class="o">=</span> <span class="n">bit</span><span class="o">.</span><span class="n">length</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span> </span></span><span class="line"><span class="cl"> <span class="c1"># It&#39;s not a newline node. Just adjust the offset within the plain text.</span> </span></span><span class="line"><span class="cl"> <span class="n">offset</span> <span class="o">+=</span> <span class="n">at</span><span class="o">.</span><span class="n">content</span><span class="o">.</span><span class="n">length</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"> <span class="k">elsif</span> <span class="n">at</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="s2">&#34;a&#34;</span> </span></span><span class="line"><span class="cl"> <span class="c1"># Agda emits both links and things-to-link-to as &#39;a&#39; nodes.</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">line_info</span> <span class="o">=</span> <span class="n">line_infos</span><span class="o">.</span><span class="n">fetch</span><span class="p">(</span><span class="n">line</span><span class="p">)</span> <span class="p">{</span> <span class="n">line_infos</span><span class="o">[</span><span class="n">line</span><span class="o">]</span> <span class="o">=</span> <span class="o">[]</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">href</span> <span class="o">=</span> <span class="n">at</span><span class="o">.</span><span class="n">attribute</span><span class="p">(</span><span class="s2">&#34;href&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="nb">id</span> <span class="o">=</span> <span class="n">at</span><span class="o">.</span><span class="n">attribute</span><span class="p">(</span><span class="s2">&#34;id&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">href</span> <span class="ow">or</span> <span class="nb">id</span> </span></span><span class="line"><span class="cl"> <span class="n">new_node</span> <span class="o">=</span> <span class="p">{</span> <span class="ss">:from</span> <span class="o">=&gt;</span> <span class="n">offset</span><span class="o">-</span><span class="n">at</span><span class="o">.</span><span class="n">content</span><span class="o">.</span><span class="n">length</span><span class="p">,</span> <span class="ss">:to</span> <span class="o">=&gt;</span> <span class="n">offset</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">new_node</span><span class="o">[</span><span class="ss">:href</span><span class="o">]</span> <span class="o">=</span> <span class="n">href</span> <span class="k">if</span> <span class="n">href</span> </span></span><span class="line"><span class="cl"> <span class="n">new_node</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span> <span class="o">=</span> <span class="nb">id</span> <span class="k">if</span> <span class="nb">id</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">line_info</span> <span class="o">&lt;&lt;</span> <span class="n">new_node</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">line_infos</span> </span></span><span class="line"><span class="cl"><span class="k">end</span> </span></span></code></pre></div><p>This script takes an Agda HTML file and returns a map in which each line of the Agda source code is associated with a list of ranges; the ranges indicate links or places that can be linked to. For example, for the <code>ModX</code> example above, the script might produce:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Ruby" data-lang="Ruby"><span class="line"><span class="cl"><span class="mi">3</span> <span class="o">=&gt;</span> <span class="o">[</span> </span></span><span class="line"><span class="cl"> <span class="p">{</span> <span class="ss">:from</span> <span class="o">=&gt;</span> <span class="mi">3</span><span class="p">,</span> <span class="ss">:to</span> <span class="o">=&gt;</span> <span class="mi">9</span><span class="p">,</span> <span class="nb">id</span> <span class="o">=&gt;</span> <span class="s2">&#34;...&#34;</span> <span class="p">},</span> <span class="c1"># Agda creates &lt;a&gt; nodes even for keywords.</span> </span></span><span class="line"><span class="cl"> <span class="p">{</span> <span class="ss">:from</span> <span class="o">=&gt;</span> <span class="mi">12</span><span class="p">,</span> <span class="ss">:to</span> <span class="o">=&gt;</span> <span class="mi">16</span><span class="p">,</span> <span class="nb">id</span> <span class="o">=&gt;</span> <span class="s2">&#34;ModX-id&#34;</span> <span class="p">},</span> <span class="c1"># The IDs Agda generates aren&#39;t usually this nice.</span> </span></span><span class="line"><span class="cl"> <span class="p">{</span> <span class="ss">:from</span> <span class="o">=&gt;</span> <span class="mi">20</span><span class="p">,</span> <span class="ss">:to</span> <span class="o">=&gt;</span> <span class="mi">21</span><span class="p">,</span> <span class="nb">id</span> <span class="o">=&gt;</span> <span class="s2">&#34;x-id&#34;</span> <span class="p">},</span> </span></span><span class="line"><span class="cl"><span class="o">]</span> </span></span></code></pre></div><a href="#modifying-hugos-html"> <h4 id="modifying-hugos-html">Modifying Hugo&rsquo;s HTML</h4> </a> <p>Given such line information, the next step is to transfer it onto existing Hugo HTML files. Within a file, I&rsquo;ve made my <code>codelines</code> shortcode emit custom attributes that can be used to find syntax-highlighted Agda code. The chief such attribute is <code>data-agda-block</code>; my script traverses all elements with this attribute.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Ruby" data-lang="Ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">process_source_file</span><span class="p">(</span><span class="n">file</span><span class="p">,</span> <span class="n">document</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="c1"># Process each highlight group that&#39;s been marked as an Agda file.</span> </span></span><span class="line"><span class="cl"> <span class="n">document</span><span class="o">.</span><span class="n">css</span><span class="p">(</span><span class="s1">&#39;div[data-agda-block]&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">t</span><span class="o">|</span> </span></span><span class="line"><span class="cl"> <span class="c1"># ...</span> </span></span></code></pre></div><p>To figure out which Agda HTML file to use, and which lines to search for links, the script also expects some additional attributes.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Ruby" data-lang="Ruby"><span class="line"><span class="cl"> <span class="c1"># ...</span> </span></span><span class="line"><span class="cl"> <span class="n">first_line</span><span class="p">,</span> <span class="n">last_line</span> <span class="o">=</span> <span class="kp">nil</span><span class="p">,</span> <span class="kp">nil</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">first_line_attr</span> <span class="o">=</span> <span class="n">t</span><span class="o">.</span><span class="n">attribute</span><span class="p">(</span><span class="s2">&#34;data-first-line&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">first_line</span> <span class="o">=</span> <span class="n">first_line_attr</span><span class="o">.</span><span class="n">to_s</span><span class="o">.</span><span class="n">to_i</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">last_line_attr</span> <span class="o">=</span> <span class="n">t</span><span class="o">.</span><span class="n">attribute</span><span class="p">(</span><span class="s2">&#34;data-last-line&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">last_line</span> <span class="o">=</span> <span class="n">last_line_attr</span><span class="o">.</span><span class="n">to_s</span><span class="o">.</span><span class="n">to_i</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">first_line</span> <span class="ow">and</span> <span class="n">last_line</span> </span></span><span class="line"><span class="cl"> <span class="n">line_range</span> <span class="o">=</span> <span class="n">first_line</span><span class="o">..</span><span class="n">last_line</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span> </span></span><span class="line"><span class="cl"> <span class="c1"># no line number attributes = the code block contains the whole file</span> </span></span><span class="line"><span class="cl"> <span class="n">line_range</span> <span class="o">=</span> <span class="mi">1</span><span class="o">..</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">full_path</span> <span class="o">=</span> <span class="n">t</span><span class="o">.</span><span class="n">attribute</span><span class="p">(</span><span class="s2">&#34;data-file-path&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">to_s</span> </span></span><span class="line"><span class="cl"> <span class="c1"># ...</span> </span></span></code></pre></div><p>At this point, the Agda file could be in some nested directory, like <code>A/B/C/File.agda</code>. However, the project root &ndash; the place where Agda modules are compiled from &ndash; could be any one of the folders <code>A</code>, <code>B</code>, or <code>C</code>. Thus, the fully qualified module name for <code>File.agda</code> could be <code>File</code>, <code>C.File</code>, <code>B.C.File</code>, or <code>A.B.C.File</code>. Since Agda&rsquo;s HTML output produces files named after the fully qualified module name, the script needs to guess what the module file is. This is where some conventions come in play: I keep my code in folders directly nested within a top-level <code>code</code> directory; thus, I&rsquo;ll have folders <code>project1</code> or <code>project2</code> inside <code>code</code>, and those will always be project roots. As a result, I guess that the first directory relative to <code>code</code> should be discarded, while the rest should be included in the path. The only exception to this is Git submodules: if an Agda file is included using a submodule, the root directory of the submodule is considered the Agda project root. My Hugo theme indicates the submodule using an additional <code>data-base-path</code> attribute; in all, that leads to the following logic:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Ruby" data-lang="Ruby"><span class="line"><span class="cl"> <span class="c1"># ...</span> </span></span><span class="line"><span class="cl"> <span class="n">full_path_dirs</span> <span class="o">=</span> <span class="no">Pathname</span><span class="p">(</span><span class="n">full_path</span><span class="p">)</span><span class="o">.</span><span class="n">each_filename</span><span class="o">.</span><span class="n">to_a</span> </span></span><span class="line"><span class="cl"> <span class="n">base_path</span> <span class="o">=</span> <span class="n">t</span><span class="o">.</span><span class="n">attribute</span><span class="p">(</span><span class="s2">&#34;data-base-path&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">to_s</span> </span></span><span class="line"><span class="cl"> <span class="n">base_dir_depth</span> <span class="o">=</span> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">base_path</span><span class="o">.</span><span class="n">empty?</span> </span></span><span class="line"><span class="cl"> <span class="c1"># No submodules were used. Assume code/&lt;X&gt; is the root.</span> </span></span><span class="line"><span class="cl"> <span class="c1"># The path of the file is given relative to `code`, so need</span> </span></span><span class="line"><span class="cl"> <span class="c1"># to strip only the one outermost directory.</span> </span></span><span class="line"><span class="cl"> <span class="n">base_dir_depth</span> <span class="o">=</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="n">base_path</span> <span class="o">=</span> <span class="n">full_path_dirs</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span> </span></span><span class="line"><span class="cl"> <span class="c1"># The code is in a submodule. Assume that the base path / submodule</span> </span></span><span class="line"><span class="cl"> <span class="c1"># root is the Agda module root, ignore all folders before that.</span> </span></span><span class="line"><span class="cl"> <span class="n">base_path_dirs</span> <span class="o">=</span> <span class="no">Pathname</span><span class="p">(</span><span class="n">base_path</span><span class="p">)</span><span class="o">.</span><span class="n">each_filename</span><span class="o">.</span><span class="n">to_a</span> </span></span><span class="line"><span class="cl"> <span class="n">base_dir_depth</span> <span class="o">=</span> <span class="n">base_path_dirs</span><span class="o">.</span><span class="n">length</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"> <span class="c1"># ...</span> </span></span></code></pre></div><p>With that, the script determines the actual HTML file path &mdash; by assuming that there&rsquo;s an <code>html</code> folder in the same place as the Agda project root &mdash; and runs the above <code>process_agda_html_file</code>:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Ruby" data-lang="Ruby"><span class="line"><span class="cl"> <span class="c1"># ...</span> </span></span><span class="line"><span class="cl"> <span class="n">dirs_in_base</span> <span class="o">=</span> <span class="n">full_path_dirs</span><span class="o">[</span><span class="n">base_dir_depth</span><span class="o">..-</span><span class="mi">1</span><span class="o">]</span> </span></span><span class="line"><span class="cl"> <span class="n">html_file</span> <span class="o">=</span> <span class="n">dirs_in_base</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&#34;.&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">gsub</span><span class="p">(</span><span class="sr">/\.agda$/</span><span class="p">,</span> <span class="s2">&#34;.html&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">html_path</span> <span class="o">=</span> <span class="no">File</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="o">[</span><span class="s2">&#34;code&#34;</span><span class="p">,</span> <span class="n">base_path</span><span class="p">,</span> <span class="s2">&#34;html&#34;</span><span class="p">,</span> <span class="n">html_file</span><span class="o">]</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">agda_info</span> <span class="o">=</span> <span class="n">process_agda_html_file</span><span class="p">(</span><span class="n">html_path</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="c1"># ...</span> </span></span></code></pre></div><p>The next step is specific to the output of Hugo&rsquo;s syntax highlighter, <a href="https://github.com/alecthomas/chroma"class="external-link">Chroma<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. When line numbers are enabled &ndash; and they are on my site &ndash; Chroma generates a table that, at some point, contains a bunch of <code>span</code> HTML nodes, each with the <code>line</code> class. Each such <code>span</code> corresponds to a single line of output; naturally, the first one contains the code from <code>first_line</code>, the second from <code>first_line + 1</code>, and so on until <code>last_line</code>. This is quite convenient, because it saves the headache of counting newlines the way that the Agda processing code above has to.</p> <p>For each line of syntax-highlighted code, the script retrieves the corresponding list of links that were collected from the Agda HTML file.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Ruby" data-lang="Ruby"><span class="line"><span class="cl"> <span class="c1"># ...</span> </span></span><span class="line"><span class="cl"> <span class="n">lines</span> <span class="o">=</span> <span class="n">t</span><span class="o">.</span><span class="n">css</span><span class="p">(</span><span class="s2">&#34;pre.chroma code[data-lang] .line&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">lines</span><span class="o">.</span><span class="n">zip</span><span class="p">(</span><span class="n">line_range</span><span class="p">)</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">line</span><span class="p">,</span> <span class="n">line_no</span><span class="o">|</span> </span></span><span class="line"><span class="cl"> <span class="n">line_info</span> <span class="o">=</span> <span class="n">agda_info</span><span class="o">[</span><span class="n">line_no</span><span class="o">]</span> </span></span><span class="line"><span class="cl"> <span class="k">next</span> <span class="k">unless</span> <span class="n">line_info</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1"># ...</span> </span></span></code></pre></div><p>The subsequent traversal &ndash; which picks out the plain text of the Agda file as <a href="#plain-text"class="same-page-link">reasoned above</a> &ndash; is very similar to the previous one. Here too there&rsquo;s an <code>offset</code> variable, which gets incremented with the length of a new plain text pieces. Since we know the lines match up to <code>span</code>s, there&rsquo;s no need to count newlines.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Ruby" data-lang="Ruby"><span class="line"><span class="cl"> <span class="c1"># ...</span> </span></span><span class="line"><span class="cl"> <span class="n">offset</span> <span class="o">=</span> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="n">line</span><span class="o">.</span><span class="n">traverse</span> <span class="k">do</span> <span class="o">|</span><span class="n">lt</span><span class="o">|</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">lt</span><span class="o">.</span><span class="n">text?</span> </span></span><span class="line"><span class="cl"> <span class="n">content</span> <span class="o">=</span> <span class="n">lt</span><span class="o">.</span><span class="n">content</span> </span></span><span class="line"><span class="cl"> <span class="n">new_offset</span> <span class="o">=</span> <span class="n">offset</span> <span class="o">+</span> <span class="n">content</span><span class="o">.</span><span class="n">length</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1"># ...</span> </span></span></code></pre></div><p>At this point, we have a line number, and an offset within that line number that describes the portion of the text under consideration. We can traverse all the links for the line, and find ones that mark a piece of text somewhere in this range. For the time being &ndash; since inserting overlapping spans is quite complicated &ndash; I require the links to lie entirely within a particular plain text region. As a result, if Chroma splits a single Agda identifier into several tokens, it will not be linked. For now, this seems like the most conservative and safe approach.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Ruby" data-lang="Ruby"><span class="line"><span class="cl"> <span class="c1"># ...</span> </span></span><span class="line"><span class="cl"> <span class="n">matching_links</span> <span class="o">=</span> <span class="n">line_info</span><span class="o">.</span><span class="n">links</span><span class="o">.</span><span class="n">filter</span> <span class="k">do</span> <span class="o">|</span><span class="n">link</span><span class="o">|</span> </span></span><span class="line"><span class="cl"> <span class="n">link</span><span class="o">[</span><span class="ss">:from</span><span class="o">]</span> <span class="o">&gt;=</span> <span class="n">offset</span> <span class="ow">and</span> <span class="n">link</span><span class="o">[</span><span class="ss">:to</span><span class="o">]</span> <span class="o">&lt;=</span> <span class="n">new_offset</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"> <span class="c1"># ...</span> </span></span></code></pre></div><p>All that&rsquo;s left is to slice up the plain text fragment into a bunch of HTML pieces: the substrings that are links will turn into <code>a</code> HTML nodes, while the substrings that are &ldquo;in between&rdquo; the links will be left over as plain text nodes. The code to do so is relatively verbose, but not all that complicated.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Ruby" data-lang="Ruby"><span class="line"><span class="cl"> <span class="n">replace_with</span> <span class="o">=</span> <span class="o">[]</span> </span></span><span class="line"><span class="cl"> <span class="n">replace_offset</span> <span class="o">=</span> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="n">matching_links</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">match</span><span class="o">|</span> </span></span><span class="line"><span class="cl"> <span class="c1"># The link&#39;s range is an offset from the beginning of the line,</span> </span></span><span class="line"><span class="cl"> <span class="c1"># but the text piece we&#39;re splitting up might be partway into</span> </span></span><span class="line"><span class="cl"> <span class="c1"># the line. Convert the link coordinates to piece-relative ones.</span> </span></span><span class="line"><span class="cl"> <span class="n">relative_from</span> <span class="o">=</span> <span class="n">match</span><span class="o">[</span><span class="ss">:from</span><span class="o">]</span> <span class="o">-</span> <span class="n">offset</span> </span></span><span class="line"><span class="cl"> <span class="n">relative_to</span> <span class="o">=</span> <span class="n">match</span><span class="o">[</span><span class="ss">:to</span><span class="o">]</span> <span class="o">-</span> <span class="n">offset</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1"># If the previous link ended some time before the new link</span> </span></span><span class="line"><span class="cl"> <span class="c1"># began (or if the current link is the first one, and is not</span> </span></span><span class="line"><span class="cl"> <span class="c1"># at the beginning), ensure that the plain text &#34;in between&#34;</span> </span></span><span class="line"><span class="cl"> <span class="c1"># is kept.</span> </span></span><span class="line"><span class="cl"> <span class="n">replace_with</span> <span class="o">&lt;&lt;</span> <span class="n">content</span><span class="o">[</span><span class="n">replace_offset</span><span class="o">...</span><span class="n">relative_from</span><span class="o">]</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">tag</span> <span class="o">=</span> <span class="p">(</span><span class="n">match</span><span class="o">.</span><span class="n">include?</span> <span class="ss">:href</span><span class="p">)</span> <span class="p">?</span> <span class="s1">&#39;a&#39;</span> <span class="p">:</span> <span class="s1">&#39;span&#39;</span> </span></span><span class="line"><span class="cl"> <span class="n">new_node</span> <span class="o">=</span> <span class="no">Nokogiri</span><span class="o">::</span><span class="no">XML</span><span class="o">::</span><span class="no">Node</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">tag</span><span class="p">,</span> <span class="n">document</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">match</span><span class="o">.</span><span class="n">include?</span> <span class="ss">:href</span> </span></span><span class="line"><span class="cl"> <span class="c1"># For nodes with links, note what they&#39;re referring to, so</span> </span></span><span class="line"><span class="cl"> <span class="c1"># we can adjust their hrefs when we assign global IDs.</span> </span></span><span class="line"><span class="cl"> <span class="n">href</span> <span class="o">=</span> <span class="n">match</span><span class="o">[</span><span class="ss">:href</span><span class="o">].</span><span class="n">to_s</span> </span></span><span class="line"><span class="cl"> <span class="n">new_node</span><span class="o">[</span><span class="s1">&#39;href&#39;</span><span class="o">]</span> <span class="o">=</span> <span class="n">note_used_href</span> <span class="n">file</span><span class="p">,</span> <span class="n">new_node</span><span class="p">,</span> <span class="n">href</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">match</span><span class="o">.</span><span class="n">include?</span> <span class="ss">:id</span> </span></span><span class="line"><span class="cl"> <span class="c1"># For nodes with IDs visible in the current Hugo file, we&#39;ll</span> </span></span><span class="line"><span class="cl"> <span class="c1"># want to redirect links that previously go to other Agda</span> </span></span><span class="line"><span class="cl"> <span class="c1"># module HTML files. So, note the ID that we want to redirect,</span> </span></span><span class="line"><span class="cl"> <span class="c1"># and pick a new unique ID to replace it with.</span> </span></span><span class="line"><span class="cl"> <span class="nb">id</span> <span class="o">=</span> <span class="n">match</span><span class="o">[</span><span class="ss">:id</span><span class="o">].</span><span class="n">to_s</span> </span></span><span class="line"><span class="cl"> <span class="n">new_node</span><span class="o">[</span><span class="s1">&#39;id&#39;</span><span class="o">]</span> <span class="o">=</span> <span class="n">note_defined_href</span> <span class="n">file</span><span class="p">,</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">html_file</span><span class="si">}</span><span class="s2">#</span><span class="si">#{</span><span class="nb">id</span><span class="si">}</span><span class="s2">&#34;</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"> <span class="n">new_node</span><span class="o">.</span><span class="n">content</span> <span class="o">=</span> <span class="n">content</span><span class="o">[</span><span class="n">relative_from</span><span class="o">...</span><span class="n">relative_to</span><span class="o">]</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">replace_with</span> <span class="o">&lt;&lt;</span> <span class="n">new_node</span> </span></span><span class="line"><span class="cl"> <span class="n">replace_offset</span> <span class="o">=</span> <span class="n">relative_to</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"> <span class="n">replace_with</span> <span class="o">&lt;&lt;</span> <span class="n">content</span><span class="o">[</span><span class="n">replace_offset</span><span class="o">..-</span><span class="mi">1</span><span class="o">]</span> </span></span></code></pre></div><p>There&rsquo;s a little bit of a subtlety in the above code: specifically, I use the <code>note_used_href</code> and <code>note_defined_href</code> methods. These are important for rewriting links. Like I mentioned earlier, Agda&rsquo;s HTML output assumes that each source file should produce a single HTML file &ndash; named after its qualified module &ndash; and creates links accordingly. However, my blog posts interweave multiple source files. Some links that would&rsquo;ve jumped to a different file must now point to an internal identifier within the page. Another important aspect of the transformation is that, since I&rsquo;m pulling HTML files from distinct files, it&rsquo;s not guaranteed that each of them will have a unique <code>id</code> attribute. After all, Agda just assigns sequential numbers to each node that it generates; it would only take, e.g., including the first line from two distinct modules to end up with two nodes with <code>id=&quot;1&quot;</code>.</p> <p>The solution is then twofold:</p> <ol> <li>Track all the nodes referencing a particular <code>href</code> (made up of an HTML file and a numerical identifier, like <code>File.html#123</code>). When we pick new IDs &ndash; thus guaranteeing their uniqueness &ndash; we&rsquo;ll visit all the nodes that refer to the old ID and HTML file, and update their <code>href</code>.</li> <li>Track all existing Agda HTML IDs that we&rsquo;re inserting. If we transfer an <code>&lt;a id=&quot;1234&quot;&gt;</code> onto the Hugo content, we know we&rsquo;ll need to pick a new ID for it (since <code>1234</code> need not be unique), and that we&rsquo;ll need to redirect the other links to that new ID as the previous bullet describes.</li> </ol> <p>Here&rsquo;s how these two methods work:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Ruby" data-lang="Ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">note_defined_href</span><span class="p">(</span><span class="n">file</span><span class="p">,</span> <span class="n">href</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">file_hrefs</span> <span class="o">=</span> <span class="vi">@local_seen_hrefs</span><span class="o">.</span><span class="n">fetch</span><span class="p">(</span><span class="n">file</span><span class="p">)</span> <span class="k">do</span> </span></span><span class="line"><span class="cl"> <span class="vi">@local_seen_hrefs</span><span class="o">[</span><span class="n">file</span><span class="o">]</span> <span class="o">=</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">uniq_id</span> <span class="o">=</span> <span class="n">file_hrefs</span><span class="o">.</span><span class="n">fetch</span><span class="p">(</span><span class="n">href</span><span class="p">)</span> <span class="k">do</span> </span></span><span class="line"><span class="cl"> <span class="n">new_id</span> <span class="o">=</span> <span class="s2">&#34;agda-unique-ident-</span><span class="si">#{</span><span class="vi">@id_counter</span><span class="si">}</span><span class="s2">&#34;</span> </span></span><span class="line"><span class="cl"> <span class="vi">@id_counter</span> <span class="o">+=</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="n">file_hrefs</span><span class="o">[</span><span class="n">href</span><span class="o">]</span> <span class="o">=</span> <span class="n">new_id</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">unless</span> <span class="vi">@global_seen_hrefs</span><span class="o">.</span><span class="n">include?</span> <span class="n">href</span> </span></span><span class="line"><span class="cl"> <span class="vi">@global_seen_hrefs</span><span class="o">[</span><span class="n">href</span><span class="o">]</span> <span class="o">=</span> <span class="p">{</span> <span class="ss">:file</span> <span class="o">=&gt;</span> <span class="n">file</span><span class="p">,</span> <span class="ss">:id</span> <span class="o">=&gt;</span> <span class="n">uniq_id</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">uniq_id</span> </span></span><span class="line"><span class="cl"><span class="k">end</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">note_used_href</span><span class="p">(</span><span class="n">file</span><span class="p">,</span> <span class="n">node</span><span class="p">,</span> <span class="n">href</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">ref_list</span> <span class="o">=</span> <span class="vi">@nodes_referencing_href</span><span class="o">.</span><span class="n">fetch</span><span class="p">(</span><span class="n">href</span><span class="p">)</span> <span class="p">{</span> <span class="vi">@nodes_referencing_href</span><span class="o">[</span><span class="n">href</span><span class="o">]</span> <span class="o">=</span> <span class="o">[]</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">ref_list</span> <span class="o">&lt;&lt;</span> <span class="p">{</span> <span class="ss">:file</span> <span class="o">=&gt;</span> <span class="n">file</span><span class="p">,</span> <span class="ss">:node</span> <span class="o">=&gt;</span> <span class="n">node</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">href</span> </span></span><span class="line"><span class="cl"><span class="k">end</span> </span></span></code></pre></div><p>Note that they use class variables: these are methods on a <code>FileGroup</code> class. I&rsquo;ve omitted the various classes I&rsquo;ve declared from the above code for brevity, but here it makes sense to show them. Like I mentioned earlier, you can view the <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/commit/6a168f2fe144850ed3a81b796e07266cbf80f382/agda.rb"class="external-link">complete code here<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>.</p> <p>Interestingly, <code>note_defined_href</code> makes use of <em>two</em> global maps: <code>@local_seen_hrefs</code> and <code>@global_seen_hrefs</code>. This helps satisfy the third constraint above, which is linking between code defined in the same series. The logic is as follows: when rewriting a link to a new HTML file and ID, if the code we&rsquo;re trying to link to exists on the current page, we should link to that. Otherwise, if the code we&rsquo;re trying to link to was presented in a different part of the series, then we should link to that other part. So, we consult the &ldquo;local&rdquo; map for <code>href</code>s that will be rewritten to HTML nodes in the current file, and as a fallback, consult the &ldquo;global&rdquo; map for <code>hrefs</code> that were introduced in other parts. The <code>note_defined_href</code> populates both maps, and is &ldquo;biased&rdquo; towards the first occurrence of a piece of code: if posts A and B define a function <code>f</code>, and post C only references <code>f</code>, then that link will go to post A&rsquo;s definition, which came earlier.</p> <p>The other method, <code>note_used_href</code>, is simpler. It just appends to a list of Nokogiri HTML nodes that reference a given <code>href</code>. We keep track of the file in which the reference occurred so we can be sure to consult the right sub-map of <code>@local_seen_hrefs</code> when checking for in-page rewrites.</p> <p>After running <code>process_source_file</code> on all Hugo HTML files within a particular series, the following holds true:</p> <ul> <li>We have inserted <code>span</code> or <code>a</code> nodes wherever Agda&rsquo;s original output had nodes with <code>id</code> or <code>href</code> elements. This is with the exception of the case where Hugo&rsquo;s inline HTML doesn&rsquo;t &ldquo;line up&rdquo; with Agda&rsquo;s inline HTML, which I&rsquo;ve only found to happen when the leading character of an identifier is a digit.</li> <li>We have picked new IDs for each HTML node we inserted that had an ID, noting them both globally and for the current file. We noted their original <code>href</code> value (in the form <code>File.html#123</code>) and that it should be transformed into our globally-unique identifiers, in the form <code>agda-unique-ident-1234</code>.</li> <li>For each HTML node we inserted that links to another, we noted the <code>href</code> of the reference (also in the form <code>File.html#123</code>).</li> </ul> <p>Now, all that&rsquo;s left is to redirect the <code>href</code>s of the nodes we inserted from their old values to the new ones. I do this by iterating over <code>@nodes_referencing_href</code>, which contains every link we inserted.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Ruby" data-lang="Ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">cross_link_files</span> </span></span><span class="line"><span class="cl"> <span class="vi">@nodes_referencing_href</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">href</span><span class="p">,</span> <span class="n">references</span><span class="o">|</span> </span></span><span class="line"><span class="cl"> <span class="n">references</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">reference</span><span class="o">|</span> </span></span><span class="line"><span class="cl"> <span class="n">file</span> <span class="o">=</span> <span class="n">reference</span><span class="o">[</span><span class="ss">:file</span><span class="o">]</span> </span></span><span class="line"><span class="cl"> <span class="n">node</span> <span class="o">=</span> <span class="n">reference</span><span class="o">[</span><span class="ss">:node</span><span class="o">]</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">local_targets</span> <span class="o">=</span> <span class="vi">@local_seen_hrefs</span><span class="o">[</span><span class="n">file</span><span class="o">]</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">local_targets</span><span class="o">.</span><span class="n">include?</span> <span class="n">href</span> </span></span><span class="line"><span class="cl"> <span class="c1"># A code block in this file provides this href, create a local link.</span> </span></span><span class="line"><span class="cl"> <span class="n">node</span><span class="o">[</span><span class="s1">&#39;href&#39;</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;#</span><span class="si">#{</span><span class="n">local_targets</span><span class="o">[</span><span class="n">href</span><span class="o">]</span><span class="si">}</span><span class="s2">&#34;</span> </span></span><span class="line"><span class="cl"> <span class="k">elsif</span> <span class="vi">@global_seen_hrefs</span><span class="o">.</span><span class="n">include?</span> <span class="n">href</span> </span></span><span class="line"><span class="cl"> <span class="c1"># A code block in this series, but not in this file, defines</span> </span></span><span class="line"><span class="cl"> <span class="c1"># this href. Create a cross-file link.</span> </span></span><span class="line"><span class="cl"> <span class="n">target</span> <span class="o">=</span> <span class="vi">@global_seen_hrefs</span><span class="o">[</span><span class="n">href</span><span class="o">]</span> </span></span><span class="line"><span class="cl"> <span class="n">other_file</span> <span class="o">=</span> <span class="n">target</span><span class="o">[</span><span class="ss">:file</span><span class="o">]</span> </span></span><span class="line"><span class="cl"> <span class="nb">id</span> <span class="o">=</span> <span class="n">target</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">relpath</span> <span class="o">=</span> <span class="no">Pathname</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">other_file</span><span class="p">)</span><span class="o">.</span><span class="n">dirname</span><span class="o">.</span><span class="n">relative_path_from</span><span class="p">(</span><span class="no">Pathname</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">file</span><span class="p">)</span><span class="o">.</span><span class="n">dirname</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">node</span><span class="o">[</span><span class="s1">&#39;href&#39;</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">relpath</span><span class="si">}</span><span class="s2">#</span><span class="si">#{</span><span class="nb">id</span><span class="si">}</span><span class="s2">&#34;</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span> </span></span><span class="line"><span class="cl"> <span class="c1"># No definitions in any blog page. For now, just delete the anchor.</span> </span></span><span class="line"><span class="cl"> <span class="n">node</span><span class="o">.</span><span class="n">replace</span> <span class="n">node</span><span class="o">.</span><span class="n">content</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"><span class="k">end</span> </span></span></code></pre></div><p>Notice that for the time being, I simply remove links to Agda definitions that didn&rsquo;t occur in the Hugo post. Ideally, this would link to the plain, non-blog documentation page generated by Agda; however, this requires either hosting those documentation pages, or expecting the Agda standard library HTML pages to remain stable and hosted at a fixed URL. Neither was simple enough to do, so I opted for the conservative &ldquo;just don&rsquo;t insert links&rdquo; approach.</p> <p>And that&rsquo;s all of the approach that I wanted to show off today! There are other details, like finding posts in the same series (I achieve this with a <code>meta</code> element) and invoking <code>agda --html</code> on the necessary source files (my <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/build-agda-html.rb"class="external-link"><code>build-agda-html.rb</code><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> script is how I personally do this), but I don&rsquo;t think it&rsquo;s all that valuable to describe them here.</p> <p>Unfortunately, the additional metadata I had my theme insert makes it harder for others to use this approach out of the box. However, I hope that by sharing my experience, others who write Agda and post about it might be able to get a similar solution working. And of course, it&rsquo;s always fun to write about a recent project or endeavor.</p> <p>Happy (dependently typed) programming and blogging!</p> The "Deeply Embedded Expression" Trick in Agda https://danilafe.com/blog/agda_expr_pattern/ Mon, 11 Mar 2024 14:25:52 -0700 https://danilafe.com/blog/agda_expr_pattern/ <p>I&rsquo;ve been working on a relatively large Agda project for a few months now, and I&rsquo;d like to think that I&rsquo;ve become quite proficient. Recently, I came up with a little trick to help simplify some of my proofs, and it seems like this trick might have broader applications.</p> <p>In my head, I call this trick &lsquo;Deeply Embedded Expressions&rsquo;. Before I introduce it, let me explain the part of my work that motivated developing the trick.</p> <a href="#proofs-about-map-operations"> <h3 id="proofs-about-map-operations">Proofs about Map Operations</h3> </a> <p>A part of my Agda project is the formalization of simple key-value maps. I model key-value maps as lists of key-value pairs. On top of this, I implement two operations: <code>join</code> and <code>meet</code>, which in my code are denoted using <code>⊔</code> and <code>⊓</code>. When &ldquo;joining&rdquo; two maps, you create a new map that has the keys from both input ones. If a key is only present in one of the input maps, then the new &ldquo;joined&rdquo; map has the same value for that key as the original. On the other hand, if the key is present in both maps, then its value in the new map is the result of &ldquo;joining&rdquo; the original values. The &ldquo;meet&rdquo; operation is similar, except instead of taking keys from either map, the result only has keys that were present in both maps, &ldquo;meeting&rdquo; their values. In a way, &ldquo;join&rdquo; and &ldquo;meet&rdquo; are similar to set union and intersection &mdash; but they also operate on the values in the map.</p> <p>Given these operations, I need to prove certain properties of these operation. The most inconvenient to prove is probably associativity:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Map.agda" data-first-line="752" data-last-line="752" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Map.agda#L752-L752">Map.agda</a>, line 752</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">752 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="nf">⊔-assoc</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>m₁<span class="w"> </span>m₂<span class="w"> </span>m₃<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Map<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">((</span>m₁<span class="w"> </span>⊔<span class="w"> </span>m₂<span class="o">)</span><span class="w"> </span>⊔<span class="w"> </span>m₃<span class="o">)</span><span class="w"> </span>≈<span class="w"> </span><span class="o">(</span>m₁<span class="w"> </span>⊔<span class="w"> </span><span class="o">(</span>m₂<span class="w"> </span>⊔<span class="w"> </span>m₃<span class="o">))</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This property is, in turn, proven using two &lsquo;subset&rsquo; relations on maps, defined in the usual way.</p> <p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Map.agda" data-first-line="755" data-last-line="755" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Map.agda#L755-L755">Map.agda</a>, line 755</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">755 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⊔-assoc₁</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="o">((</span>m₁<span class="w"> </span>⊔<span class="w"> </span>m₂<span class="o">)</span><span class="w"> </span>⊔<span class="w"> </span>m₃<span class="o">)</span><span class="w"> </span>⊆<span class="w"> </span><span class="o">(</span>m₁<span class="w"> </span>⊔<span class="w"> </span><span class="o">(</span>m₂<span class="w"> </span>⊔<span class="w"> </span>m₃<span class="o">))</span></span></span></code></pre></td></tr></table> </div> </div> </div> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Map.agda" data-first-line="774" data-last-line="774" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Map.agda#L774-L774">Map.agda</a>, line 774</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">774 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⊔-assoc₂</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="o">(</span>m₁<span class="w"> </span>⊔<span class="w"> </span><span class="o">(</span>m₂<span class="w"> </span>⊔<span class="w"> </span>m₃<span class="o">))</span><span class="w"> </span>⊆<span class="w"> </span><span class="o">((</span>m₁<span class="w"> </span>⊔<span class="w"> </span>m₂<span class="o">)</span><span class="w"> </span>⊔<span class="w"> </span>m₃<span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>The reason this property is so inconvenient to prove is that there are a lot of cases to consider. That&rsquo;s because your claim, in words, is something like:</p> <blockquote> <p>Suppose a key-value pair <code>k , v</code> is present in <code>(m₁ ⊔ m₂) ⊔ m₃</code>. Show that <code>k , v</code> is also in <code>m₁ ⊔ (m₂ ⊔ m₃)</code>.</p> </blockquote> <p>The only thing you can really do with <code>k , v</code> is figure out how it got into the three-way union map: did it come from <code>m₁</code>, <code>m₂</code>, or <code>m₃</code>, or perhaps several of them? The essence of the proof boils down to repeated uses of the fact that for a key to be in the union, it must be in at least one of the two maps. You end up with witnesses, repeated application of the same lemmas, lots of <code>let</code>-expressions or <code>where</code> clauses. It&rsquo;s relatively tedious and, what&rsquo;s more frustrating, <strong>driven entirely by the structure of the map operations</strong>. It seems like one shouldn&rsquo;t have to mimic that structure using boilerplate lemmas. So I started looking at other ways.</p> <a href="#case-analysis-using-gadts"> <h3 id="case-analysis-using-gadts">Case Analysis using GADTs</h3> </a> <p>A &ldquo;proof by cases&rdquo; in a dependently typed language like Agda usually brings to mind pattern matching. So, here&rsquo;s an idea: what if for each expression involving <code>⊔</code> and <code>⊓</code>, we had some kind of data type, and that data type had exactly as many inhabitants as there are cases to analyze? A data type corresponding to <code>m₁ ⊔ m₂</code> might have three cases, and the one for <code>(m₁ ⊔ m₂) ⊔ m₃</code> might have seven. Each case would contain the information necessary to perform the proof.</p> <p>A data type whose &ldquo;shape&rdquo; depends on an expression in the way I described above is said to be <em>indexed by</em> that expression. In Agda, GADTs are used to create indexed types. My initial attempt was something like this:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="kr">data</span><span class="w"> </span>Provenance<span class="w"> </span><span class="o">(</span>k<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Map<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span><span class="o">(</span>a<span class="w"> </span>⊔ℓ<span class="w"> </span>b<span class="o">)</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">single</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>v<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="o">}</span><span class="w"> </span><span class="o">{</span>m<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Map<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>k<span class="w"> </span>,<span class="w"> </span>v<span class="o">)</span><span class="w"> </span>∈<span class="w"> </span>m<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Provenance<span class="w"> </span>k<span class="w"> </span>v<span class="w"> </span>m<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">in₁</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>v<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="o">}</span><span class="w"> </span><span class="o">{</span>m₁<span class="w"> </span>m₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Provenance<span class="w"> </span>k<span class="w"> </span>v<span class="w"> </span>e₁<span class="w"> </span><span class="ow">→</span><span class="w"> </span>¬<span class="w"> </span>k<span class="w"> </span>∈k<span class="w"> </span>m₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Provenance<span class="w"> </span>k<span class="w"> </span>v<span class="w"> </span><span class="o">(</span>e₁<span class="w"> </span>⊔<span class="w"> </span>e₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">in₂</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>v<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="o">}</span><span class="w"> </span><span class="o">{</span>m₁<span class="w"> </span>m₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>¬<span class="w"> </span>k<span class="w"> </span>∈k<span class="w"> </span>m₁<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Provenance<span class="w"> </span>k<span class="w"> </span>v<span class="w"> </span>m₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Provenance<span class="w"> </span>k<span class="w"> </span>v<span class="w"> </span><span class="o">(</span>e₁<span class="w"> </span>⊔<span class="w"> </span>e₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">bothᵘ</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>v₁<span class="w"> </span>v₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="o">}</span><span class="w"> </span><span class="o">{</span>m₁<span class="w"> </span>m₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Provenance<span class="w"> </span>k<span class="w"> </span>v₁<span class="w"> </span>m₁<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Provenance<span class="w"> </span>k<span class="w"> </span>v₂<span class="w"> </span>m₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Provenance<span class="w"> </span>k<span class="w"> </span><span class="o">(</span>v₁<span class="w"> </span>⊔<span class="w"> </span>v₂<span class="o">)</span><span class="w"> </span><span class="o">(</span>e₁<span class="w"> </span>⊔<span class="w"> </span>e₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">bothⁱ</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>v₁<span class="w"> </span>v₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="o">}</span><span class="w"> </span><span class="o">{</span>m₁<span class="w"> </span>m₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Provenance<span class="w"> </span>k<span class="w"> </span>v₁<span class="w"> </span>m₁<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Provenance<span class="w"> </span>k<span class="w"> </span>v₂<span class="w"> </span>m₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Provenance<span class="w"> </span>k<span class="w"> </span><span class="o">(</span>v₁<span class="w"> </span>⊓<span class="w"> </span>v₂<span class="o">)</span><span class="w"> </span><span class="o">(</span>e₁<span class="w"> </span>⊓<span class="w"> </span>e₂<span class="o">)</span><span class="w"> </span></span></span></code></pre></div><p>I was planning on a proof of associativity (in one direction) that looked something like the following &mdash; pattern matching on cases from the new <code>Provenance</code> type.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="nf">⊔-assoc₁</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="o">((</span>m₁<span class="w"> </span>⊔<span class="w"> </span>m₂<span class="o">)</span><span class="w"> </span>⊔<span class="w"> </span>m₃<span class="o">)</span><span class="w"> </span>⊆<span class="w"> </span><span class="o">(</span>m₁<span class="w"> </span>⊔<span class="w"> </span><span class="o">(</span>m₂<span class="w"> </span>⊔<span class="w"> </span>m₃<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>⊔-assoc₁<span class="w"> </span>k<span class="w"> </span>v<span class="w"> </span>k,v∈m₁₂m₃<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">with</span><span class="w"> </span>get-Provenance<span class="w"> </span>k,v∈m₁₂m₃<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span><span class="kr">in</span>₂<span class="w"> </span>k∉km₁₂<span class="w"> </span><span class="o">(</span>single<span class="w"> </span>v∈m₃<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ow">...</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span><span class="kr">in</span>₁<span class="w"> </span><span class="o">(</span><span class="kr">in</span>₂<span class="w"> </span>k∉km₁<span class="w"> </span><span class="o">(</span>single<span class="w"> </span>v∈m₂<span class="o">))</span><span class="w"> </span>k∉km₃<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ow">...</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>bothᵘ<span class="w"> </span><span class="o">(</span><span class="kr">in</span>₂<span class="w"> </span>k∉km₁<span class="w"> </span><span class="o">(</span>single<span class="w"> </span><span class="o">{</span>v₂<span class="o">}</span><span class="w"> </span>v₂∈m₂<span class="o">))</span><span class="w"> </span><span class="o">(</span>single<span class="w"> </span><span class="o">{</span>v₃<span class="o">}</span><span class="w"> </span>v₃∈m₃<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ow">...</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span><span class="kr">in</span>₁<span class="w"> </span><span class="o">(</span><span class="kr">in</span>₁<span class="w"> </span><span class="o">(</span>single<span class="w"> </span>v₁∈m₁<span class="o">)</span><span class="w"> </span>k∉km₂<span class="o">)</span><span class="w"> </span>k∉km₃<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ow">...</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>bothᵘ<span class="w"> </span><span class="o">(</span><span class="kr">in</span>₁<span class="w"> </span><span class="o">(</span>single<span class="w"> </span><span class="o">{</span>v₁<span class="o">}</span><span class="w"> </span>v₁∈m₁<span class="o">)</span><span class="w"> </span>k∉km₂<span class="o">)</span><span class="w"> </span><span class="o">(</span>single<span class="w"> </span><span class="o">{</span>v₃<span class="o">}</span><span class="w"> </span>v₃∈m₃<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ow">...</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span><span class="kr">in</span>₁<span class="w"> </span><span class="o">(</span>bothᵘ<span class="w"> </span><span class="o">(</span>single<span class="w"> </span><span class="o">{</span>v₁<span class="o">}</span><span class="w"> </span>v₁∈m₁<span class="o">)</span><span class="w"> </span><span class="o">(</span>single<span class="w"> </span><span class="o">{</span>v₂<span class="o">}</span><span class="w"> </span>v₂∈m₂<span class="o">))</span><span class="w"> </span>k∉ke₃<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ow">...</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>bothᵘ<span class="w"> </span><span class="o">(</span>bothᵘ<span class="w"> </span><span class="o">(</span>single<span class="w"> </span><span class="o">{</span>v₁<span class="o">}</span><span class="w"> </span>v₁∈m₁<span class="o">)</span><span class="w"> </span><span class="o">(</span>single<span class="w"> </span><span class="o">{</span>v₂<span class="o">}</span><span class="w"> </span>v₂∈m₂<span class="o">))</span><span class="w"> </span><span class="o">(</span>single<span class="w"> </span><span class="o">{</span>v₃<span class="o">}</span><span class="w"> </span>v₃∈m₃<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ow">...</span><span class="w"> </span></span></span></code></pre></div><p>However, this doesn&rsquo;t work. Agda has trouble figuring out which cases of the <code>Provenance</code> GADT are allowed, in which aren&rsquo;t. Is <code>m₁ ⊔ m</code> a single map, fit for the <code>single</code> case, or should it be broken up into more cases like <code>in₁</code> and <code>in₂</code>? In general, is some expression of type <code>Map</code> the &ldquo;bottom&rdquo; of our recursion, or should it be analyzed further?</p> <p>The above hints at what&rsquo;s wrong. The mistake here is requiring Agda to infer the shape of our &ldquo;join&rdquo; and &ldquo;meet&rdquo; expressions from arbitrary terms. The set of expressions that we want to reason about is much more restricted &ndash; each expression will always be of three components: &ldquo;meet&rdquo;, &ldquo;join&rdquo;, and base-case maps being combined using these operations.</p> <a href="#defining-an-expression-data-type"> <h3 id="defining-an-expression-data-type">Defining an Expression Data Type</h3> </a> <p>If you&rsquo;re like me, and have spent years of your life around programming language theory and domain specific languages (DSLs), the last sentence of the previous section may be ringing a bell. In fact, it&rsquo;s eerily similar to how we describe recursive grammars:</p> <blockquote> <p>An expression of interest is either,</p> <ul> <li>A map</li> <li>The &ldquo;join&rdquo; of two expressions</li> <li>The &ldquo;meet&rdquo; of two expressions</li> </ul> </blockquote> <p>Mathematically, we might write this as follows:</p> $$ \begin{array}{rcll} e &amp;amp; ::= &amp;amp; m &amp;amp; \text{(maps)} \\ &amp;amp; | &amp;amp; e \sqcup e &amp;amp; \text{(join)} \\ &amp;amp; | &amp;amp; e \sqcap e &amp;amp; \text{(meet)} \end{array} $$ <p>And in Agda,</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Map.agda" data-first-line="543" data-last-line="546" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Map.agda#L543-L546">Map.agda</a>, lines 543 through 546</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">543 </span><span class="lnt">544 </span><span class="lnt">545 </span><span class="lnt">546 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="kr">data</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span><span class="o">(</span>a<span class="w"> </span>⊔ℓ<span class="w"> </span>b<span class="o">)</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">`_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Map<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Expr<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">_∪_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Expr<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">_∩_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Expr</span></span></code></pre></td></tr></table> </div> </div> </div> <p>In the code, I used the set union and intersection operators to avoid overloading the <code>⊔</code> and <code>⊓</code> more than they already are.</p> <p>We have just defined a very small expression language. In computer science, a language is called <em>deeply embedded</em> if a data type (or class hierarchy, or other &rsquo;explicit&rsquo; representation) is defined for its syntax in the <em>host</em> language (Agda, in our case). This is in contrast to a <em>shallow embedding</em>, in which expressions in the (new) language are just expressions in the host language.</p> <p>In this sense, our <code>Expr</code> is deeply embedded &mdash; we defined new container for it, and <code>_∪_</code> is a distinct entity from <code>_⊔_</code>. Our first attempt was a shallow embedding. That fell through because the Agda language is much broader than our expression language, which makes case analysis very difficult.</p> <p>An obvious thing to do with an expression is to evaluate it. This will be important for our proofs, because it will establish a connection between expressions (created via <code>Expr</code>) and actual Agda objects that we need to reason about at the end of the day. The notation \(\llbracket e \rrbracket\) is commonly used in PL circles for evaluation (it comes from <a href="https://en.wikipedia.org/wiki/Denotational_semantics"class="external-link">Denotational Semantics<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>). Thus, my Agda evaluation function is written as follows:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Map.agda" data-first-line="586" data-last-line="589" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Map.agda#L586-L589">Map.agda</a>, lines 586 through 589</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">586 </span><span class="lnt">587 </span><span class="lnt">588 </span><span class="lnt">589 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="nf">⟦_⟧</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span>Map<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>⟦<span class="w"> </span>`<span class="w"> </span>m<span class="w"> </span>⟧<span class="w"> </span><span class="ow">=</span><span class="w"> </span>m<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>⟦<span class="w"> </span>e₁<span class="w"> </span>∪<span class="w"> </span>e₂<span class="w"> </span>⟧<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⟦<span class="w"> </span>e₁<span class="w"> </span>⟧<span class="w"> </span>⊔<span class="w"> </span>⟦<span class="w"> </span>e₂<span class="w"> </span>⟧<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span>⟦<span class="w"> </span>e₁<span class="w"> </span>∩<span class="w"> </span>e₂<span class="w"> </span>⟧<span class="w"> </span><span class="ow">=</span><span class="w"> </span>⟦<span class="w"> </span>e₁<span class="w"> </span>⟧<span class="w"> </span>⊓<span class="w"> </span>⟦<span class="w"> </span>e₂<span class="w"> </span>⟧</span></span></code></pre></td></tr></table> </div> </div> </div> <p>On top of this, here is my actual implementation of the <code>Provenance</code> data type. This time, it&rsquo;s indexed by expressions in <code>Expr</code>, which makes it much easier to pattern match on instances:</p> <div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Map.agda" data-first-line="591" data-last-line="596" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Map.agda#L591-L596">Map.agda</a>, lines 591 through 596</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">591 </span><span class="lnt">592 </span><span class="lnt">593 </span><span class="lnt">594 </span><span class="lnt">595 </span><span class="lnt">596 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="kr">data</span><span class="w"> </span>Provenance<span class="w"> </span><span class="o">(</span>k<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Expr<span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span><span class="o">(</span>a<span class="w"> </span>⊔ℓ<span class="w"> </span>b<span class="o">)</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">single</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>v<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="o">}</span><span class="w"> </span><span class="o">{</span>m<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Map<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span><span class="o">(</span>k<span class="w"> </span>,<span class="w"> </span>v<span class="o">)</span><span class="w"> </span>∈<span class="w"> </span>m<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Provenance<span class="w"> </span>k<span class="w"> </span>v<span class="w"> </span><span class="o">(</span>`<span class="w"> </span>m<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">in₁</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>v<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="o">}</span><span class="w"> </span><span class="o">{</span>e₁<span class="w"> </span>e₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Provenance<span class="w"> </span>k<span class="w"> </span>v<span class="w"> </span>e₁<span class="w"> </span><span class="ow">→</span><span class="w"> </span>¬<span class="w"> </span>k<span class="w"> </span>∈k<span class="w"> </span>⟦<span class="w"> </span>e₂<span class="w"> </span>⟧<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Provenance<span class="w"> </span>k<span class="w"> </span>v<span class="w"> </span><span class="o">(</span>e₁<span class="w"> </span>∪<span class="w"> </span>e₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">in₂</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>v<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="o">}</span><span class="w"> </span><span class="o">{</span>e₁<span class="w"> </span>e₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>¬<span class="w"> </span>k<span class="w"> </span>∈k<span class="w"> </span>⟦<span class="w"> </span>e₁<span class="w"> </span>⟧<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Provenance<span class="w"> </span>k<span class="w"> </span>v<span class="w"> </span>e₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Provenance<span class="w"> </span>k<span class="w"> </span>v<span class="w"> </span><span class="o">(</span>e₁<span class="w"> </span>∪<span class="w"> </span>e₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">bothᵘ</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>v₁<span class="w"> </span>v₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="o">}</span><span class="w"> </span><span class="o">{</span>e₁<span class="w"> </span>e₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Provenance<span class="w"> </span>k<span class="w"> </span>v₁<span class="w"> </span>e₁<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Provenance<span class="w"> </span>k<span class="w"> </span>v₂<span class="w"> </span>e₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Provenance<span class="w"> </span>k<span class="w"> </span><span class="o">(</span>v₁<span class="w"> </span>⊔₂<span class="w"> </span>v₂<span class="o">)</span><span class="w"> </span><span class="o">(</span>e₁<span class="w"> </span>∪<span class="w"> </span>e₂<span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">bothⁱ</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">{</span>v₁<span class="w"> </span>v₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>B<span class="o">}</span><span class="w"> </span><span class="o">{</span>e₁<span class="w"> </span>e₂<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Expr<span class="o">}</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>Provenance<span class="w"> </span>k<span class="w"> </span>v₁<span class="w"> </span>e₁<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Provenance<span class="w"> </span>k<span class="w"> </span>v₂<span class="w"> </span>e₂<span class="w"> </span><span class="ow">→</span><span class="w"> </span>Provenance<span class="w"> </span>k<span class="w"> </span><span class="o">(</span>v₁<span class="w"> </span>⊓₂<span class="w"> </span>v₂<span class="o">)</span><span class="w"> </span><span class="o">(</span>e₁<span class="w"> </span>∩<span class="w"> </span>e₂<span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Note that we have to use the evaluation function to be able to use operators such as <code>∈</code>. That&rsquo;s because these are still defined on maps, and not expressions.</p> <p>With this, I was able to write my proof in the way that I had hoped. It has the exact form of my previous sketch-of-proof.</p> <details><summary><strong>(click here to see the full example, including each case&rsquo;s implementation)</strong></summary><div class="highlight-group" data-base-path="agda-spa" data-file-path="agda-spa/Lattice/Map.agda" data-first-line="755" data-last-line="773" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/agda-spa/src/commit/828b652d3b9266e27ef7cf5a8a7fb82e3fd3133f/Lattice/Map.agda#L755-L773">Map.agda</a>, lines 755 through 773</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">755 </span><span class="lnt">756 </span><span class="lnt">757 </span><span class="lnt">758 </span><span class="lnt">759 </span><span class="lnt">760 </span><span class="lnt">761 </span><span class="lnt">762 </span><span class="lnt">763 </span><span class="lnt">764 </span><span class="lnt">765 </span><span class="lnt">766 </span><span class="lnt">767 </span><span class="lnt">768 </span><span class="lnt">769 </span><span class="lnt">770 </span><span class="lnt">771 </span><span class="lnt">772 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-agda" data-lang="agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">⊔-assoc₁</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="o">((</span>m₁<span class="w"> </span>⊔<span class="w"> </span>m₂<span class="o">)</span><span class="w"> </span>⊔<span class="w"> </span>m₃<span class="o">)</span><span class="w"> </span>⊆<span class="w"> </span><span class="o">(</span>m₁<span class="w"> </span>⊔<span class="w"> </span><span class="o">(</span>m₂<span class="w"> </span>⊔<span class="w"> </span>m₃<span class="o">))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>⊔-assoc₁<span class="w"> </span>k<span class="w"> </span>v<span class="w"> </span>k,v∈m₁₂m₃<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">with</span><span class="w"> </span>Expr-Provenance-≡<span class="w"> </span><span class="o">(((</span>`<span class="w"> </span>m₁<span class="o">)</span><span class="w"> </span>∪<span class="w"> </span><span class="o">(</span>`<span class="w"> </span>m₂<span class="o">))</span><span class="w"> </span>∪<span class="w"> </span><span class="o">(</span>`<span class="w"> </span>m₃<span class="o">))</span><span class="w"> </span>k,v∈m₁₂m₃<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span><span class="kr">in</span>₂<span class="w"> </span>k∉ke₁₂<span class="w"> </span><span class="o">(</span>single<span class="w"> </span><span class="o">{</span>v₃<span class="o">}</span><span class="w"> </span>v₃∈e₃<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="o">(</span>k∉ke₁<span class="w"> </span>,<span class="w"> </span>k∉ke₂<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span>I⊔.∉-union-∉-either<span class="w"> </span><span class="o">{</span>l₁<span class="w"> </span><span class="ow">=</span><span class="w"> </span>l₁<span class="o">}</span><span class="w"> </span><span class="o">{</span>l₂<span class="w"> </span><span class="ow">=</span><span class="w"> </span>l₂<span class="o">}</span><span class="w"> </span>k∉ke₁₂<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">in</span><span class="w"> </span><span class="o">(</span>v₃<span class="w"> </span>,<span class="w"> </span><span class="o">(</span>≈₂-refl<span class="w"> </span>,<span class="w"> </span>I⊔.union-preserves-∈₂<span class="w"> </span>k∉ke₁<span class="w"> </span><span class="o">(</span>I⊔.union-preserves-∈₂<span class="w"> </span>k∉ke₂<span class="w"> </span>v₃∈e₃<span class="o">)))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span><span class="kr">in</span>₁<span class="w"> </span><span class="o">(</span><span class="kr">in</span>₂<span class="w"> </span>k∉ke₁<span class="w"> </span><span class="o">(</span>single<span class="w"> </span><span class="o">{</span>v₂<span class="o">}</span><span class="w"> </span>v₂∈e₂<span class="o">))</span><span class="w"> </span>k∉ke₃<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>v₂<span class="w"> </span>,<span class="w"> </span><span class="o">(</span>≈₂-refl<span class="w"> </span>,<span class="w"> </span>I⊔.union-preserves-∈₂<span class="w"> </span>k∉ke₁<span class="w"> </span><span class="o">(</span>I⊔.union-preserves-∈₁<span class="w"> </span>u₂<span class="w"> </span>v₂∈e₂<span class="w"> </span>k∉ke₃<span class="o">)))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>bothᵘ<span class="w"> </span><span class="o">(</span><span class="kr">in</span>₂<span class="w"> </span>k∉ke₁<span class="w"> </span><span class="o">(</span>single<span class="w"> </span><span class="o">{</span>v₂<span class="o">}</span><span class="w"> </span>v₂∈e₂<span class="o">))</span><span class="w"> </span><span class="o">(</span>single<span class="w"> </span><span class="o">{</span>v₃<span class="o">}</span><span class="w"> </span>v₃∈e₃<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>v₂<span class="w"> </span>⊔₂<span class="w"> </span>v₃<span class="w"> </span>,<span class="w"> </span><span class="o">(</span>≈₂-refl<span class="w"> </span>,<span class="w"> </span>I⊔.union-preserves-∈₂<span class="w"> </span>k∉ke₁<span class="w"> </span><span class="o">(</span>I⊔.union-combines<span class="w"> </span>u₂<span class="w"> </span>u₃<span class="w"> </span>v₂∈e₂<span class="w"> </span>v₃∈e₃<span class="o">)))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span><span class="kr">in</span>₁<span class="w"> </span><span class="o">(</span><span class="kr">in</span>₁<span class="w"> </span><span class="o">(</span>single<span class="w"> </span><span class="o">{</span>v₁<span class="o">}</span><span class="w"> </span>v₁∈e₁<span class="o">)</span><span class="w"> </span>k∉ke₂<span class="o">)</span><span class="w"> </span>k∉ke₃<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>v₁<span class="w"> </span>,<span class="w"> </span><span class="o">(</span>≈₂-refl<span class="w"> </span>,<span class="w"> </span>I⊔.union-preserves-∈₁<span class="w"> </span>u₁<span class="w"> </span>v₁∈e₁<span class="w"> </span><span class="o">(</span>I⊔.union-preserves-∉<span class="w"> </span>k∉ke₂<span class="w"> </span>k∉ke₃<span class="o">)))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>bothᵘ<span class="w"> </span><span class="o">(</span><span class="kr">in</span>₁<span class="w"> </span><span class="o">(</span>single<span class="w"> </span><span class="o">{</span>v₁<span class="o">}</span><span class="w"> </span>v₁∈e₁<span class="o">)</span><span class="w"> </span>k∉ke₂<span class="o">)</span><span class="w"> </span><span class="o">(</span>single<span class="w"> </span><span class="o">{</span>v₃<span class="o">}</span><span class="w"> </span>v₃∈e₃<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>v₁<span class="w"> </span>⊔₂<span class="w"> </span>v₃<span class="w"> </span>,<span class="w"> </span><span class="o">(</span>≈₂-refl<span class="w"> </span>,<span class="w"> </span>I⊔.union-combines<span class="w"> </span>u₁<span class="w"> </span><span class="o">(</span>I⊔.union-preserves-Unique<span class="w"> </span>l₂<span class="w"> </span>l₃<span class="w"> </span>u₃<span class="o">)</span><span class="w"> </span>v₁∈e₁<span class="w"> </span><span class="o">(</span>I⊔.union-preserves-∈₂<span class="w"> </span>k∉ke₂<span class="w"> </span>v₃∈e₃<span class="o">)))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span><span class="kr">in</span>₁<span class="w"> </span><span class="o">(</span>bothᵘ<span class="w"> </span><span class="o">(</span>single<span class="w"> </span><span class="o">{</span>v₁<span class="o">}</span><span class="w"> </span>v₁∈e₁<span class="o">)</span><span class="w"> </span><span class="o">(</span>single<span class="w"> </span><span class="o">{</span>v₂<span class="o">}</span><span class="w"> </span>v₂∈e₂<span class="o">))</span><span class="w"> </span>k∉ke₃<span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>v₁<span class="w"> </span>⊔₂<span class="w"> </span>v₂<span class="w"> </span>,<span class="w"> </span><span class="o">(</span>≈₂-refl<span class="w"> </span>,<span class="w"> </span>I⊔.union-combines<span class="w"> </span>u₁<span class="w"> </span><span class="o">(</span>I⊔.union-preserves-Unique<span class="w"> </span>l₂<span class="w"> </span>l₃<span class="w"> </span>u₃<span class="o">)</span><span class="w"> </span>v₁∈e₁<span class="w"> </span><span class="o">(</span>I⊔.union-preserves-∈₁<span class="w"> </span>u₂<span class="w"> </span>v₂∈e₂<span class="w"> </span>k∉ke₃<span class="o">)))</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="ow">...</span><span class="w"> </span><span class="ow">|</span><span class="w"> </span>bothᵘ<span class="w"> </span><span class="o">(</span>bothᵘ<span class="w"> </span><span class="o">(</span>single<span class="w"> </span><span class="o">{</span>v₁<span class="o">}</span><span class="w"> </span>v₁∈e₁<span class="o">)</span><span class="w"> </span><span class="o">(</span>single<span class="w"> </span><span class="o">{</span>v₂<span class="o">}</span><span class="w"> </span>v₂∈e₂<span class="o">))</span><span class="w"> </span><span class="o">(</span>single<span class="w"> </span><span class="o">{</span>v₃<span class="o">}</span><span class="w"> </span>v₃∈e₃<span class="o">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span>v₁<span class="w"> </span>⊔₂<span class="w"> </span><span class="o">(</span>v₂<span class="w"> </span>⊔₂<span class="w"> </span>v₃<span class="o">)</span><span class="w"> </span>,<span class="w"> </span><span class="o">(</span>⊔₂-assoc<span class="w"> </span>v₁<span class="w"> </span>v₂<span class="w"> </span>v₃<span class="w"> </span>,<span class="w"> </span>I⊔.union-combines<span class="w"> </span>u₁<span class="w"> </span><span class="o">(</span>I⊔.union-preserves-Unique<span class="w"> </span>l₂<span class="w"> </span>l₃<span class="w"> </span>u₃<span class="o">)</span><span class="w"> </span>v₁∈e₁<span class="w"> </span><span class="o">(</span>I⊔.union-combines<span class="w"> </span>u₂<span class="w"> </span>u₃<span class="w"> </span>v₂∈e₂<span class="w"> </span>v₃∈e₃<span class="o">)))</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div> </div> </details> <a href="#the-general-trick"> <h3 id="the-general-trick">The General Trick</h3> </a> <p>So far, I&rsquo;ve presented a problem I faced in my Agda proof and a solution for that problem. However, it may not be clear how useful the trick is beyond this narrow case that I&rsquo;ve encountered. The way I see it, the &ldquo;deeply embedded expression&rdquo; trick is applicable whenever you have data that is constructed from some fixed set of cases, and when proofs about that data need to follow the structure of these cases. Thus, examples include:</p> <ul> <li><strong>Proofs about the origin of keys in a map (this one):</strong> the &ldquo;data&rdquo; is the key-value map that is being analyzed. The enumeration of cases for this map is driven by the structure of the &ldquo;join&rdquo; and &ldquo;meet&rdquo; operations used to build the map.</li> <li><strong>Automatic derivation of function properties:</strong> suppose you&rsquo;re interested in working with continuous functions. You also know that the addition, subtraction, and multiplication of two functions preserves continuity. Of course, the constant function \(x \mapsto c\) and the identity function \(x \mapsto x\) are continuous too. You may define an expression data type that has cases for these operations. Then, your evaluation function could transform the expression into a plain function, and a proof on the structure of the expression can be used to verify the resulting function&rsquo;s continuity.</li> <li><strong>Proof search for algebraic expressions:</strong> suppose that you wanted to automatically find solutions for certain algebraic (in)equalities. Instead of using some sort of reflection mechanism to inspect terms and determine how constraints should be solved, you might represent the set of operations in you equation system as cases in a data type. You can then use regular Agda code to manipulate terms; an evaluation function can then be used to recover the equations in Agda, together with witnesses justifying the solution.</li> </ul> <p>There are some pretty clear commonalities about examples above, which are the ingredients to this trick:</p> <ul> <li><strong>The expression:</strong> you create a new expression data type that encodes all the operations (and bases cases) on your data. In my example, this is the <code>Expr</code> data type.</li> <li><strong>The evaluation function</strong>: you provide a way to lower the expression you&rsquo;ve defined back into a regular Agda term. This connects your (abstract) operations to their interpretation in Agda. In my example, this is the <code>⟦_⟧</code> function.</li> <li><strong>The proofs</strong>: you write proofs that consider only the fixed set of cases encoded by the data type (<code>Expr</code>), but state properties about the <em>evaluated</em> expression. In my example, this is <code>Provenance</code> and the <code>Expr-Provenance</code> function. Specifically, the <code>Provenance</code> data type connects expressions and the terms they evaluate to, because it is indexed by expressions, but contains data in the form <code>k ∈k ⟦ e₂ ⟧</code>.</li> </ul> <a href="#conclusion"> <h3 id="conclusion">Conclusion</h3> </a> <p>I&rsquo;ll be the first to admit that this trick is quite situational, and may not be as far-reaching as the <a href="https://danilafe.com/blog/agda_is_pattern/">&ldquo;Is Something&rdquo; pattern</a> I wrote about before, which seems to occur far more in the wild. However, there have now been two times when I personally reached for this trick, which seems to suggest that it may be useful to someone else.</p> <p>I hope you&rsquo;ve found this useful. Happy (dependently typed) programming!</p> Bergamot: Exploring Programming Language Inference Rules https://danilafe.com/blog/bergamot/ Fri, 22 Dec 2023 18:16:44 -0800 https://danilafe.com/blog/bergamot/ <a href="#inference-rules-and-the-study-of-programming-languages"> <h3 id="inference-rules-and-the-study-of-programming-languages">Inference Rules and the Study of Programming Languages</h3> </a> <p>In this post, I will talk about <em>inference rules</em>, particularly in the field of programming language theory. The first question to get out of the way is &ldquo;what on earth is an inference rule?&rdquo;. The answer is simple: an inference rule is just a way of writing &ldquo;if &hellip; then &hellip;&rdquo;. When writing an inference rule, we write the &ldquo;if&rdquo; stuff above a line, and the &ldquo;then&rdquo; stuff below the line. Really, that&rsquo;s all there is to it. I&rsquo;ll steal an example from another one of my posts on the blog &ndash; here&rsquo;s an inference rule:</p> $$ \frac {\text{I&amp;#39;m allergic to cats} \quad \text{My friend has a cat}} {\text{I will not visit my friend very much}} $$ <p>We can read this as &ldquo;<strong>if</strong> I&rsquo;m allergic to cats, and my friend has a cat, <strong>then</strong> I will not visit my friend very much&rdquo;.</p> <p>In the field of programming languages, inference rules are everywhere. Practically any paper I read has a table that looks something like this:</p> <figure class="fullwide"><img src="https://danilafe.com/blog/bergamot/rules.png" alt="Inference rules from Logarithm and program testing by Kuen-Bang Hou (Favonia) and Zhuyang Wang"><figcaption> <p>Inference rules from <a href="https://dl.acm.org/doi/abs/10.1145/3498726"class="external-link">Logarithm and program testing<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> by Kuen-Bang Hou (Favonia) and Zhuyang Wang</p> </figcaption> </figure> <p>And I, for one, love it! They&rsquo;re a precise and concise way to describe static and dynamic behavior of programs. I might&rsquo;ve written this elsewhere on the blog, but whenever I read a paper, my eyes search for the rules first and foremost.</p> <p>But to those just starting their PL journey, inference rules can be quite cryptic &ndash; I know they were to me! The first level of difficulty are the symbols: we have lots of Greek (\(\Gamma\) and \(\Delta\) for environments, \(\tau\) and perhaps \(\sigma\) for types), and the occasional mathematical symbol (the &ldquo;entails&rdquo; symbol \(\vdash\) is the most common, but for operational semantics we can have \(\leadsto\) and \(\Downarrow\)). If you don&rsquo;t know what they mean, or if you&rsquo;re still getting used to them, symbols in judgements are difficult enough to parse.</p> <p>The second level of difficulty is making sense of the individual rules: although they tend to not be too bad, for some languages even making sense of one rule can be challenging. The following rule from the Calculus of Inductive Constructions is a doozy, for instance.</p> <figure class="fullwide"><img src="https://danilafe.com/blog/bergamot/CIC.png" alt="The match inference rule from Introduction to the Calculus of Inductive Constructions by Christine Paulin-Mohring"><figcaption> <p>The <code>match</code> inference rule from <a href="https://inria.hal.science/hal-01094195/document"class="external-link">Introduction to the Calculus of Inductive Constructions<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> by Christine Paulin-Mohring</p> </figcaption> </figure> <p>Just look at the metavariables! We have \(\textit{pars}\), \(t_1\) through \(t_p\), \(x_1\) through \(x_n\), plain \(x\), and at least two other sets of variables. Not only this, but the rule requires at least some familiarity with <a href="https://en.wikipedia.org/wiki/Generalized_algebraic_data_type"class="external-link">GADTs<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> to understand completely.</p> <p>The third level is making sense of how the rules work, <em>together</em>. In my programming languages class in college, a familiar question was:</p> <blockquote> <p>the <a href="https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system"class="external-link">Hindley-Milner type system<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> supports let-polymorphism only. What is it about the rules that implies let-polymorphism, and not any other kind of polymorphism?</p> </blockquote> <p>If you don&rsquo;t know the answer, or the question doesn&rsquo;t make sense do you, don&rsquo;t worry about it &ndash; suffice to say that whole systems of inference rules exhibit certain behaviors, and it takes familiarity with several rules to spot these behaviors.</p> <a href="#seeing-what-works-and-what-doesnt"> <h3 id="seeing-what-works-and-what-doesnt">Seeing What Works and What Doesn&rsquo;t</h3> </a> <p>Maybe I&rsquo;m just a tinker-y sort of person, but for me, teaching inference rules just by showing them is not really enough. For instance, let me show you two ways of writing the following (informal) rule:</p> <blockquote> <p>When adding two operands, if both operands are strings, then the result of adding them is also a string.</p> </blockquote> <p>There&rsquo;s a right way to write this inference rule, and there is a wrong way. Let me show you both, and try to explain the two. First, here&rsquo;s the wrong way:</p> $$ \cfrac {x : \text{string} \in \Gamma \quad y : \text{string} \in \Gamma} {\Gamma \vdash x &#43; y : \text{string}} $$ <p>This says that the type of adding two <em>variables</em> of type <code>string</code> is still <code>string</code>. Here, \(\Gamma\) is a <em>context</em>, which keeps track of which variable has what type. Writing \(x : \text{string} \in \Gamma\) is the same as saying &ldquo;we know the variable <code>x</code> has type <code>string</code>&rdquo;. The whole rule reads,</p> <blockquote> <p>If the variables <code>x</code> and <code>y</code> both have type <code>string</code>, then the result of adding these two variables, <code>x+y</code>, also has type <code>string</code>.</p> </blockquote> <p>The trouble with this rule is that it only works when adding two variables. But <code>x+x</code> is not itself a variable, so the rule wouldn&rsquo;t work for an expression like <code>(x+x)+(y+y)</code>. The proper way of writing the rule is, then, something like this:</p> $$ \cfrac {\Gamma \vdash e_1 : \text{string} \quad \Gamma \vdash e_2 : \text{string}} {\Gamma \vdash e_1 &#43; e_2 : \text{string}} $$ <p>This rule says:</p> <blockquote> <p>If the two subexpressions <code>e1</code> and <code>e2</code> both have type <code>string</code>, then the result of adding these two subexpressions, <code>e1+e2</code>, also has type <code>string</code>.</p> </blockquote> <p>Much better! We can apply this rule recursively: to get the type of <code>(x+x)+(y+y)</code>, we consider <code>(x+x)</code> and <code>(y+y)</code> as two subexpressions, and go on to compute their types first. We can then break <code>(x+x)</code> into two subexpressions (<code>x</code> and <code>x</code>), and determine their type separately. Supposing that the variables <code>x</code> and <code>y</code> indeed have the type <code>string</code>, this tells us that <code>(x+x)</code> and <code>(y+y)</code> are both string, and therefore that the whole of <code>(x+x)+(y+y)</code> is a string.</p> <p>What I&rsquo;d really like to do is type the program in question and have the computer figure out whether my rules accept or reject this program. With my new rules, perhaps I&rsquo;d get something like this:</p> <figure class="fullwide"><img src="https://danilafe.com/blog/bergamot/goodrule.png" alt="Verifying the (x&#43;x)&#43;(y&#43;y) expression using the good rule"><figcaption> <p>Verifying the <code>(x+x)+(y+y)</code> expression using the good rule</p> </figcaption> </figure> <p>To fully understand how the rule works to check a big expression like the above sum, I&rsquo;d need to see the recursion we applied a couple of paragraphs ago. My ideal tool would display this too. For simplicity, I&rsquo;ll just show the output for <code>(1+1)+(1+1)</code>, sidestepping variables and using numbers instead. This just saves a little bit of space and visual noise.</p> <figure class="fullwide"><img src="https://danilafe.com/blog/bergamot/goodrulerec.png" alt="Verifying the (1&#43;1)&#43;(1&#43;1) expression using the good rule"><figcaption> <p>Verifying the <code>(1+1)+(1+1)</code> expression using the good rule</p> </figcaption> </figure> <p>On the other hand, since the sum of two <code>x</code>s and two <code>y</code>s doesn&rsquo;t work with my old rules, maybe i wouldn&rsquo;t get a valid type at all:</p> <figure class="fullwide"><img src="https://danilafe.com/blog/bergamot/badrule.png" alt="Verifying (unsuccessfully) the (x&#43;x)&#43;(y&#43;y) expression using the old rule"><figcaption> <p>Verifying (unsuccessfully) the <code>(x+x)+(y+y)</code> expression using the old rule</p> </figcaption> </figure> <p>More generally, I want to be able to write down some inference rules, and apply them to some programs. This way, I can see what works and what doesn&rsquo;t, and when it works, which rules were used for what purposes. I also want to be able to try tweaking, removing, or adding inference rules, to see what breaks.</p> <p>This brings me to the project that I&rsquo;m trying to show off in this post: Bergamot!</p> <a href="#introducing-bergamot"> <h3 id="introducing-bergamot">Introducing Bergamot</h3> </a> <p>A certain class of programming languages lends itself particularly well to writing inference rules and applying them to programs: logic programming. The most famous example of a logic programming language is <a href="https://www.swi-prolog.org/"class="external-link">Prolog<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. In logic programming languages like Prolog, we can write rules describing when certain statements should hold. The simplest rule I could write is a fact. Perhaps I&rsquo;d like to say that the number 42 is a &ldquo;good&rdquo; number:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Prolog" data-lang="Prolog"><span class="line"><span class="cl"><span class="nf">good</span><span class="p">(</span><span class="mi">42</span><span class="p">).</span> </span></span></code></pre></div><p>Perhaps I&rsquo;d then like to say that adding two good numbers together creates another good number.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Prolog" data-lang="Prolog"><span class="line"><span class="cl"><span class="nf">good</span><span class="p">(</span><span class="nv">N</span><span class="p">)</span> <span class="p">:-</span> <span class="nf">good</span><span class="p">(</span><span class="nv">A</span><span class="p">),</span> <span class="nf">good</span><span class="p">(</span><span class="nv">B</span><span class="p">),</span> <span class="nv">N</span> <span class="o">is</span> <span class="nv">A</span><span class="o">+</span><span class="nv">B</span><span class="p">.</span> </span></span></code></pre></div><p>The above can be read as:</p> <blockquote> <p>the number <code>N</code> is good if the numbers <code>A</code> and <code>B</code> are good, and <code>N</code> is the sum of <code>A</code> and <code>B</code>.</p> </blockquote> <p>I can then ask Prolog to give me some good numbers:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Prolog" data-lang="Prolog"><span class="line"><span class="cl"><span class="s">?-</span> <span class="nf">good</span><span class="p">(</span><span class="nv">X</span><span class="p">)</span> </span></span></code></pre></div><p>Prompting Prolog a few times, I get:</p> <pre tabindex="0"><code>X = 42 X = 84 X = 126 X = 168 </code></pre><p>It&rsquo;s not a huge leap from this to programming language type rules. Perhaps instead of something being &ldquo;good&rdquo;, we can say that it has type <code>string</code>. Of course, adding two strings together, as we&rsquo;ve established, creates another string. In Prolog:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Prolog" data-lang="Prolog"><span class="line"><span class="cl"><span class="cm">/* A string literal like &#34;hello&#34; has type string */</span> </span></span><span class="line"><span class="cl"><span class="nf">type</span><span class="p">(</span><span class="k">_</span><span class="p">,</span> <span class="nf">strlit</span><span class="p">(</span><span class="k">_</span><span class="p">),</span> <span class="s">string</span><span class="p">).</span> </span></span><span class="line"><span class="cl"><span class="cm">/* Adding two string expressions has type string */</span> </span></span><span class="line"><span class="cl"><span class="nf">type</span><span class="p">(</span><span class="nv">Env</span><span class="p">,</span> <span class="nf">plus</span><span class="p">(</span><span class="nv">X</span><span class="p">,</span> <span class="nv">Y</span><span class="p">),</span> <span class="s">string</span><span class="p">)</span> <span class="p">:-</span> <span class="nf">type</span><span class="p">(</span><span class="nv">Env</span><span class="p">,</span> <span class="nv">X</span><span class="p">,</span> <span class="s">string</span><span class="p">),</span> <span class="nf">type</span><span class="p">(</span><span class="nv">Env</span><span class="p">,</span> <span class="nv">Y</span><span class="p">,</span> <span class="s">string</span><span class="p">).</span> </span></span></code></pre></div><p>That&rsquo;s almost identical to our inference rules above, except that it&rsquo;s written using code instead of mathematical notation! If we could just take these Prolog rules and display them as inference rules, we&rsquo;d be able to &ldquo;have our cake&rdquo; (draw pretty inference rules like in the papers) and &ldquo;eat it too&rdquo; (run our rules on the computer against various programs).</p> <p>This is where it gets a teensy bit hairy. It&rsquo;s not that easy to embed a Prolog engine into the browser; alternatives that I&rsquo;ve surveyed are either poorly documented, hard to extend, or both. Furthermore, for studying <em>what each rule was used for</em>, it&rsquo;s nice to be able to see a <em>proof tree</em>: a tree made up from the rules that we used to arrive at a particular answer. Prolog engines are excellent at applying rules and finding answers, but they don&rsquo;t usually provide a way to get all the rules that were used, making it harder to get proof trees.</p> <p>Thus, <a href="https://dev.danilafe.com/Everything-I-Know-About-Types/bergamot-elm"class="external-link">Bergamot<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> is a new, tiny programming language that I threw together in a couple of days. It comes as JavaScript-based widget, and can be embedded into web pages like this one to provide an interactive way to write and explore proof trees. Here&rsquo;s a screenshot of what all of that looks like:</p> <figure class="fullwide"><img src="https://danilafe.com/blog/bergamot/bergamot.png" alt="A screenshot of a Bergamot widget with some type rules"><figcaption> <p>A screenshot of a Bergamot widget with some type rules</p> </figcaption> </figure> <p>The components of Bergamot are:</p> <ul> <li>The programming language, as stated above. This language is a very simple, unification-based logic programming language.</li> <li>A rule rendering system, which takes Prolog-like rules written in Bergamot and converts them into pretty LaTeX inference rules.</li> <li>An Elm-based widget that you can embed into your web page, which accepts Bergamot rule and an input expression, and applies the rules to produce a result (or a whole proof tree!).</li> </ul> <p>Much like in Prolog, we can write Bergamot rules that describe when certain things are true. Unlike Prolog, Bergamot requires each rule to have a name. This is common practice in programming languages literature (when we talk about rules in papers, we like to be able to refer to them by name). Below are some sample Bergamot rules, corresponding to the first few inference rules in the above screenshot.</p> <pre tabindex="0"><code>TNumber @ type(?Gamma, intlit(?n), number) &lt;-; TString @ type(?Gamma, strlit(?s), string) &lt;-; TVar @ type(?Gamma, var(?x), ?tau) &lt;- inenv(?x, ?tau, ?Gamma); TPlusI @ type(?Gamma, plus(?e_1, ?e_2), number) &lt;- type(?Gamma, ?e_1, number), type(?Gamma, ?e_2, number); TPlusS @ type(?Gamma, plus(?e_1, ?e_2), string) &lt;- type(?Gamma, ?e_1, string), type(?Gamma, ?e_2, string); </code></pre><p>Unlike Prolog, where &ldquo;variables&rdquo; are anything that starts with a capital letter, in Bergamot, variables are things that start with the special <code>?</code> symbol. Also, Prolog&rsquo;s <code>:-</code> has been replaced with an arrow symbol <code>&lt;-</code>, for reverse implication. These are both purely syntactic differences.</p> <a href="#demo"> <h4 id="demo">Demo</h4> </a> <p>If you want to play around with it, here&rsquo;s an embedded Bergamot widget with <span class="sidenote"> <label class="sidenote-label" for="wrong-rule-note">some rules pre-programmed in.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="wrong-rule-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Actually, one of the rules is incorrect to my knowledge. Can you spot it? Hint: is <code>\x : number. \x: string. x+1</code> well-typed? What does Bergamot report? Can you see why? <span class="sidenote-delimiter">]</span> </span> </span> It has two modes:</p> <ol> <li><strong>Language Term</strong>: accepts a rather simple programming language to typecheck. Try <code>1+1</code>, <code>fst((1,2))</code>, or maybe <code>(\x : number. x) 42</code>.</li> <li><strong>Query</strong>: it accepts Bergamot expressions to query, similarly to Prolog; try <code>type(empty, ?e, tpair(number, string))</code> to search for expressions that have the type &ldquo;a pair of a number and a string&rdquo;.</li> </ol> <div id="widget"></div> <script> window.addEventListener('load', function() { window.Bergamot.run(null, 'widget', { "Languge Term": { "custom": "Bergamot Object Language" }, "Query": "query", } , 'type(empty, TERM, ?t)', '\nsection \u0022\u0022 {\n TNumber @ type(?Gamma, lit(?n), number) \u003c- num(?n);\n TString @ type(?Gamma, lit(?s), string) \u003c- str(?s);\n TVar @ type(?Gamma, var(?x), ?tau) \u003c- inenv(?x, ?tau, ?Gamma);\n TPlusI @ type(?Gamma, plus(?e_1, ?e_2), number) \u003c-\n type(?Gamma, ?e_1, number), type(?Gamma, ?e_2, number);\n TPlusS @ type(?Gamma, plus(?e_1, ?e_2), string) \u003c-\n type(?Gamma, ?e_1, string), type(?Gamma, ?e_2, string);\n}\nsection \u0022\u0022 {\n TPair @ type(?Gamma, pair(?e_1, ?e_2), tpair(?tau_1, ?tau_2)) \u003c-\n type(?Gamma, ?e_1, ?tau_1), type(?Gamma, ?e_2, ?tau_2);\n TFst @ type(?Gamma, fst(?e), ?tau_1) \u003c-\n type(?Gamma, ?e, tpair(?tau_1, ?tau_2));\n TSnd @ type(?Gamma, snd(?e), ?tau_2) \u003c-\n type(?Gamma, ?e, tpair(?tau_1, ?tau_2));\n}\nsection \u0022\u0022 {\n TAbs @ type(?Gamma, abs(?x, ?tau_1, ?e), tarr(?tau_1, ?tau_2)) \u003c-\n type(extend(?Gamma, ?x, ?tau_1), ?e, ?tau_2);\n TApp @ type(?Gamma, app(?e_1, ?e_2), ?tau_2) \u003c-\n type(?Gamma, ?e_1, tarr(?tau_1, ?tau_2)), type(?Gamma, ?e_2, ?tau_1);\n}\n\nsection \u0022\u0022 {\n GammaTake @ inenv(?x, ?tau_1, extend(?Gamma, ?x, ?tau_1)) \u003c-;\n GammaSkip @ inenv(?x, ?tau_1, extend(?Gamma, ?y, ?tau_2)) \u003c- inenv(?x, ?tau_1, ?Gamma);\n}\n', 'default', ''); }); </script> <a href="#rendering-bergamot-with-bergamot"> <h4 id="rendering-bergamot-with-bergamot">Rendering Bergamot with Bergamot</h4> </a> <p>There&rsquo;s something to be said about the conversion between Bergamot&rsquo;s rules, encoded as plain text, and pretty LaTeX-based inference rules that the users see. Crucially, <strong>we don&rsquo;t want to hardcode how any particular Bergamot expression is rendered</strong>. For one, this is a losing battle: we can&rsquo;t possibly keep up with all the notation that people use in PL literature, and even if we focused ourselves on only &ldquo;beginner&rdquo; notation, there wouldn&rsquo;t be one way to do it! Different PL papers and texts use slightly different variations of notation. For instance, I render my pairs as \((a, b)\), but the very first screenshot in this post demonstrates a PL paper that writes pairs as \(\langle a, b \rangle\). Neither way (as far as I know!) is right or wrong. But if we hardcode one, we lose the ability to draw the other.</p> <p>More broadly, one aspect about writing PL rules is that <em>we control the notation</em>. We are free to define shorthands, symbols, and anything else that would make reading our rules easier for others. As an example, a paper from POPL22 about programming language semantics with garbage collection used a literal trash symbol in their rules:</p> <figure><img src="https://danilafe.com/blog/bergamot/trashrule.png" alt="A rule that uses a trashcan icon as notation, from A separation logic for heap space under garbage collection by Jean-Marie Madiot and François Pottier"><figcaption> <p>A rule that uses a trashcan icon as notation, from <a href="https://dl.acm.org/doi/10.1145/3498672"class="external-link">A separation logic for heap space under garbage collection<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> by Jean-Marie Madiot and François Pottier</p> </figcaption> </figure> <p>Thus, what I want to do is <strong>encourage the (responsible) introduction of new notation</strong>. This can only be done if Bergamot itself supports custom notation.</p> <p>When thinking about how I&rsquo;d like to implement this custom notation, I was imagining some sort of templated rule engine, that would define how each term in a Bergamot program can be converted to its LaTeX variant. But then I realized: Bergamot is already a rule engine! Instead of inventing yet another language or format for defining LaTeX pretty printing, I could just use Bergamot. This turned out to work quite nicely &ndash; the &ldquo;Presentation Rules&rdquo; tab in the demo above should open a text editor with Bergamot rules that handle the conversion of Bergamot notation into LaTex. Here are some example rules:</p> <pre tabindex="0"><code>LatexPlus @ latex(plus(?e_1, ?e_2), ?l) &lt;- latex(?e_1, ?l_1), latex(?e_2, ?l_2), join([?l_1, &#34; + &#34;, ?l_2], ?l); LatexPair @ latex(pair(?e_1, ?e_2), ?l) &lt;- latex(?e_1, ?l_1), latex(?e_2, ?l_2), join([&#34;(&#34;, ?l_1, &#34;, &#34;, ?l_2, &#34;)&#34;], ?l); </code></pre><p>If we change the <code>LatexPair</code> to the following, we can make all pairs render using angle brackets:</p> <pre tabindex="0"><code>LatexPair @ latex(pair(?e_1, ?e_2), ?l) &lt;- latex(?e_1, ?l_1), latex(?e_2, ?l_2), join([&#34;\\langle&#34;, ?l_1, &#34;, &#34;, ?l_2, &#34;\\rangle&#34;], ?l); </code></pre><figure><img src="https://danilafe.com/blog/bergamot/rangle.png" alt="The LaTeX output when angle brackets are used in the rule instead of parentheses."><figcaption> <p>The LaTeX output when angle brackets are used in the rule instead of parentheses.</p> </figcaption> </figure> <p>You can write rules about arbitrary Bergamot terms for rendering; thus, you can invent completely new notation for absolutely anything.</p> <a href="#next-steps"> <h3 id="next-steps">Next Steps</h3> </a> <p>I hope to use Bergamot to write a series of articles about type systems. By providing an interactive widget, I hope to make it possible for users to do exercises: writing variations of inference rules, or even tweaking the notation, and checking them against sets of programs to make sure that they work. Of course, I also hope that Bergamot can be used to explore <em>why</em> an existing set of inference rules (such as Hindley-Milner) works. Stay tuned for those!</p> My Favorite C++ Pattern: X Macros https://danilafe.com/blog/chapel_x_macros/ Sat, 14 Oct 2023 15:38:17 -0700 https://danilafe.com/blog/chapel_x_macros/ <p>When I first joined the <a href="https://github.com/chapel-lang/chapel/"class="external-link">Chapel<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> team, one pattern used in its C++-based compiler made a strong impression on me. Since then, I&rsquo;ve used the pattern many more times, and have been very satisfied with how it turned out. However, it feels like the pattern is relatively unknown, so I thought I&rsquo;d show it off, and some of its applications in the <a href="https://github.com/chapel-lang/chapel/"class="external-link">Chapel compiler<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. I&rsquo;ve slightly tweaked a lot of the snippets I directly show in this article for the sake of simpler presentation; I&rsquo;ve included links to the original code (available on GitHub) if you want to see the unabridged version.</p> <p>Broadly speaking, the &ldquo;X Macros&rdquo; pattern is about generating code. If you have a <em>lot</em> of repetitive code to write (declaring many variables or classes, performing many very similar actions, etc.), this pattern can save a lot of time, lead to much more maintainable code, and reduce the effort required to add <em>more</em> code.</p> <p>I will introduce the pattern in its simplest form with my first example: <a href="https://en.wikipedia.org/wiki/String_interning"class="external-link">interning strings<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>.</p> <a href="#application-1-string-interning"> <h3 id="application-1-string-interning">Application 1: String Interning</h3> </a> <p>The Chapel compiler interns a lot of its strings. This way, it can reduce the memory footprint of keeping identifiers in memory (every string <code>&quot;x&quot;</code> is actually the <em>same</em> string) and make for much faster equality comparisons (you can just perform a pointer comparison!). Generally, a <code>Context</code> class is used to manage interning state. A new interned string can be constructed using the context object in the following manner:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">UniqueString</span><span class="o">::</span><span class="n">get</span><span class="p">(</span><span class="n">ctxPtr</span><span class="p">,</span> <span class="s">&#34;the string&#34;</span><span class="p">);</span> </span></span></code></pre></div><p>Effectively, this performs a search of the currently existing unique strings. If one with the content (<code>&quot;the string&quot;</code> in this case) doesn&rsquo;t exist, it&rsquo;s created and registered with the <code>Context</code>. Otherwise, the existing string is returned. Some strings, however, occur a lot in the compiler, to the point that it would be inefficient to perform the whole &ldquo;find-or-create&rdquo; operation every time. One example is the <code>&quot;this&quot;</code> string, which is an identifier with a lot of special behavior in the language (much like <code>this</code> in languages such as Java). To support such frequent flier strings, the compiler initializes them once, and creates a variable per-string that can be accessed to get that string&rsquo;s value.</p> <p>There&rsquo;s that repetitive code. Defining a brand new variable for each string, of which there are around 100 at the time of writing, is a lot of boilerplate. There are also at least two places where code needs to be added: <span class="sidenote"> <label class="sidenote-label" for="template-note">once in the declaration of the variables, once in the code that initializes them.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="template-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> A third use in the compiler is actually a variadic template defined over character arrays. The template is defined and specialized in such a way that you can refer to a variable by its string contents (i.e., you can write <code>USTR("the string")</code> instead of <code>theStringVariable</code>). <span class="sidenote-delimiter">]</span> </span> </span> It would be very easy to accidentally modify the former but not the latter, especially for developers not familiar with how these &ldquo;common strings&rdquo; are implemented.</p> <p>This is where the X Macros come in. If you look around the compiler source code, there&rsquo;s a header file that looks something like the following:</p> <div class="highlight-group" data-base-path="%!s(<nil>)" data-file-path="frontend/include/chpl/framework/all-global-strings.h"> <div class="highlight-label">From <a href="https://github.com/chapel-lang/chapel/blob/cd108338d321d0b3edf6258e0b2a58459d88a348/frontend/include/chpl/framework/all-global-strings.h#L31">all-global-strings.h</a>, around line 31</div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">X</span><span class="p">(</span><span class="n">align</span> <span class="p">,</span> <span class="s">&#34;align&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"><span class="n">X</span><span class="p">(</span><span class="n">atomic</span> <span class="p">,</span> <span class="s">&#34;atomic&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"><span class="n">X</span><span class="p">(</span><span class="n">bool_</span> <span class="p">,</span> <span class="s">&#34;bool&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"><span class="n">X</span><span class="p">(</span><span class="n">borrow</span> <span class="p">,</span> <span class="s">&#34;borrow&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"><span class="n">X</span><span class="p">(</span><span class="n">borrowed</span> <span class="p">,</span> <span class="s">&#34;borrowed&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"><span class="n">X</span><span class="p">(</span><span class="n">by</span> <span class="p">,</span> <span class="s">&#34;by&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"><span class="n">X</span><span class="p">(</span><span class="n">bytes</span> <span class="p">,</span> <span class="s">&#34;bytes&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"><span class="c1">// A lot more of these... </span></span></span></code></pre></div> </div> <p>What&rsquo;s this <code>X</code> thing? That right there is the essence of the pattern: the macro <code>X</code> <em>isn&rsquo;t defined in the header!</em> Effectively, <code>all-global-strings.h</code> is just a list, and we can &ldquo;iterate&rdquo; over this list to generate some code for each one of its elements, in as many places as we want. What I mean by this is that we can then write code like the following:</p> <div class="highlight-group" data-base-path="%!s(<nil>)" data-file-path="frontend/include/chpl/framework/global-strings.h"> <div class="highlight-label">From <a href="https://github.com/chapel-lang/chapel/blob/cd108338d321d0b3edf6258e0b2a58459d88a348/frontend/include/chpl/framework/global-strings.h#L76">global-strings.h</a>, around line 76</div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="k">struct</span> <span class="nc">GlobalStrings</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"><span class="cp">#define X(field, str) UniqueString field; </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;all-global-strings.h&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#undef X </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="p">};</span></span></span></code></pre></div> </div> <p>In this case, we define the macro <code>X</code> to ignore the value of the string (we&rsquo;re just declaring it here), and create a new <code>UniqueString</code> variable declaration. Since the declaration is inside the <code>GlobalStrings</code> struct, this ends up creating a field. Just like that, we&rsquo;ve declared a class with over 100 fields. Initialization is equally simple:</p> <div class="highlight-group" data-base-path="%!s(<nil>)" data-file-path="frontend/lib/framework/Context.cpp"> <div class="highlight-label">From <a href="https://github.com/chapel-lang/chapel/blob/cd108338d321d0b3edf6258e0b2a58459d88a348/frontend/lib/framework/Context.cpp#L49">Context.cpp</a>, around line 49</div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="n">GlobalStrings</span> <span class="n">globalStrings</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">Context</span> <span class="n">rootContext</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">static</span> <span class="kt">void</span> <span class="nf">initGlobalStrings</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"><span class="cp">#define X(field, str) globalStrings.field = UniqueString::get(&amp;rootContext, str); </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;chpl/framework/all-global-strings.h&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#undef X </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="p">}</span></span></span></code></pre></div> </div> <p>With this, we&rsquo;ve completely automated the code for for both declaring and initializing all 100 of our unique strings. Adding a new string doesn&rsquo;t require a developer to know all of the places where this is implemented: just by modifying the <code>all-global-strings.h</code> header with a new call to <code>X</code>, they can add both a new variable and code to initialize it. Pretty robust!</p> <a href="#application-2-ast-class-hierarchy"> <h3 id="application-2-ast-class-hierarchy">Application 2: AST Class Hierarchy</h3> </a> <p>Altough the interned strings are an excellent first example, it wasn&rsquo;t the first usage of X Macros that I encountered in the Chapel compiler. Beyond strings, the compiler uses X Macros to represent the whole class hierarchy of <a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree"class="external-link">abstract syntax tree (AST)<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> nodes that it uses. Here, the code is actually a bit more complicated; the class hierarchy isn&rsquo;t a <em>list</em> like the strings were; it is itself a tree. To represent such a structure, we need more than a single <code>X</code> macro; the compiler went with <code>AST_NODE</code>, <code>AST_BEGIN_SUBCLASSES</code>, and <code>AST_END_SUBCLASSES</code>. Here&rsquo;s what that looks like:</p> <div class="highlight-group" data-base-path="%!s(<nil>)" data-file-path="frontend/include/chpl/uast/uast-classes-list.h"> <div class="highlight-label">From <a href="https://github.com/chapel-lang/chapel/blob/cd108338d321d0b3edf6258e0b2a58459d88a348/frontend/include/chpl/uast/uast-classes-list.h#L96">uast-classes-list.h</a>, around line 96</div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="c1">// Other AST nodes above... </span></span></span><span class="line"><span class="cl"><span class="c1"></span> </span></span><span class="line"><span class="cl"> <span class="n">AST_BEGIN_SUBCLASSES</span><span class="p">(</span><span class="n">Loop</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">AST_NODE</span><span class="p">(</span><span class="n">DoWhile</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">AST_NODE</span><span class="p">(</span><span class="n">While</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">AST_BEGIN_SUBCLASSES</span><span class="p">(</span><span class="n">IndexableLoop</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">AST_NODE</span><span class="p">(</span><span class="n">BracketLoop</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">AST_NODE</span><span class="p">(</span><span class="n">Coforall</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">AST_NODE</span><span class="p">(</span><span class="n">For</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">AST_NODE</span><span class="p">(</span><span class="n">Forall</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">AST_NODE</span><span class="p">(</span><span class="n">Foreach</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">AST_END_SUBCLASSES</span><span class="p">(</span><span class="n">IndexableLoop</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">AST_END_SUBCLASSES</span><span class="p">(</span><span class="n">Loop</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1">// Other AST nodes below... </span></span></span></code></pre></div> </div> <p>The class hierarchy defined in this header, called <code>uast-classes-list.h</code>, is used for a lot of things, both in the compiler itself and in some libraries that <em>use</em> the compiler. I&rsquo;ll go through the use cases in turn.</p> <a href="#tags-and-dynamic-casting"> <h4 id="tags-and-dynamic-casting">Tags and Dynamic Casting</h4> </a> <p>First, to deal with a general absence of <a href="https://en.wikipedia.org/wiki/Run-time_type_information"class="external-link">RTTI<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, the hierarchy header is used to declare a &ldquo;tag&rdquo; enum. Each AST node has a tag matching its class; this allows us inspect the AST and perform safe casts similar to <code>dynamic_cast</code>. Note that for parent classes (defined via <code>BEGIN_SUBCLASSES</code>), we actually end up creating <em>two</em> tags: one <code>START_...</code> and one <code>END_...</code>. The reason for this will become clear in a moment.</p> <div class="highlight-group" data-base-path="%!s(<nil>)" data-file-path="frontend/include/chpl/uast/AstTag.h"> <div class="highlight-label">From <a href="https://github.com/chapel-lang/chapel/blob/cd108338d321d0b3edf6258e0b2a58459d88a348/frontend/include/chpl/uast/AstTag.h#L36">AstTag.h</a>, around line 36</div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">enum</span> <span class="nc">AstTag</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"><span class="cp">#define AST_NODE(NAME) NAME , </span></span></span><span class="line"><span class="cl"><span class="cp">#define AST_BEGIN_SUBCLASSES(NAME) START_##NAME , </span></span></span><span class="line"><span class="cl"><span class="cp">#define AST_END_SUBCLASSES(NAME) END_##NAME , </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;chpl/uast/uast-classes-list.h&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#undef AST_NODE </span></span></span><span class="line"><span class="cl"><span class="cp">#undef AST_BEGIN_SUBCLASSES </span></span></span><span class="line"><span class="cl"><span class="cp">#undef AST_END_SUBCLASSES </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="n">NUM_AST_TAGS</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">AST_TAG_UNKNOWN</span> </span></span><span class="line"><span class="cl"><span class="p">};</span></span></span></code></pre></div> </div> <p>The above snippet makes <code>AstTag</code> contain elements such as <code>DoWhile</code>, <code>While</code>, <code>START_Loop</code>, and <code>END_Loop</code>. For convenience, we also add a couple of other elements: <code>NUM_AST_TAGS</code>, which is <span class="sidenote"> <label class="sidenote-label" for="numbering-node">automatically assigned the number of tags we generated,</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="numbering-node"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> This is because C++ assigns integer values to enum elements sequentially, starting at zero. <span class="sidenote-delimiter">]</span> </span> </span> and a generic &ldquo;unknown tag&rdquo; value.</p> <p>Having generated the enum elements in this way, we can write query functions. This way, the API consumer can write <code>isLoop(tag)</code> instead of manually performing a comparison. Code generation here is actually split into two distinct forms of &ldquo;is bla&rdquo; methods: those for concrete AST nodes (<code>DoWhile,</code> <code>While</code>) and those for abstract base classes (<code>Loop</code>). The reason for this is simple: only a <code>AstTag::DoWhile</code> represents a do-while loop, but both <code>DoWhile</code> and <code>While</code> are instances of <code>Loop</code>. So, <code>isLoop</code> should return true for both.</p> <p>This is where the <code>START_...</code> and <code>END_...</code> enum elements come in. Reading the header file top-to-bottom, we first end up generating <code>START_Loop</code>, then <code>DoWhile</code> and <code>While</code>, and then <code>END_Loop</code>. Since C++ assigns integer value to enums sequentially, to check if a tag &ldquo;extends&rdquo; a base class, it&rsquo;s sufficient to check if its value is greater than the <code>START</code> token, and smaller than the <code>END</code> token &ndash; this means it was declared within the matching pair of <code>BEGIN_SUBCLASSES</code> and <code>END_SUBCLASES</code>.</p> <div class="highlight-group" data-base-path="%!s(<nil>)" data-file-path="frontend/include/chpl/uast/AstTag.h"> <div class="highlight-label">From <a href="https://github.com/chapel-lang/chapel/blob/cd108338d321d0b3edf6258e0b2a58459d88a348/frontend/include/chpl/uast/AstTag.h#L59">AstTag.h</a>, around line 59</div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="c1">// define is___ for leaf and regular nodes </span></span></span><span class="line"><span class="cl"><span class="c1">// (not yet for abstract parent classes) </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define AST_NODE(NAME) \ </span></span></span><span class="line"><span class="cl"><span class="cp"> static inline bool is##NAME(AstTag tag) { \ </span></span></span><span class="line"><span class="cl"><span class="cp"> return tag == NAME; \ </span></span></span><span class="line"><span class="cl"><span class="cp"> } </span></span></span><span class="line"><span class="cl"><span class="cp">#define AST_BEGIN_SUBCLASSES(NAME) </span></span></span><span class="line"><span class="cl"><span class="cp">#define AST_END_SUBCLASSES(NAME) </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="c1">// Apply the above macros to uast-classes-list.h </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&#34;chpl/uast/uast-classes-list.h&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="c1">// clear the macros </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#undef AST_NODE </span></span></span><span class="line"><span class="cl"><span class="cp">#undef AST_BEGIN_SUBCLASSES </span></span></span><span class="line"><span class="cl"><span class="cp">#undef AST_END_SUBCLASSES </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="c1">// define is___ for abstract parent classes </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define AST_NODE(NAME) </span></span></span><span class="line"><span class="cl"><span class="cp">#define AST_BEGIN_SUBCLASSES(NAME) \ </span></span></span><span class="line"><span class="cl"><span class="cp"> static inline bool is##NAME(AstTag tag) { \ </span></span></span><span class="line"><span class="cl"><span class="cp"> return START_##NAME &lt; tag &amp;&amp; tag &lt; END_##NAME; \ </span></span></span><span class="line"><span class="cl"><span class="cp"> } </span></span></span><span class="line"><span class="cl"><span class="cp">#define AST_END_SUBCLASSES(NAME) </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="c1">// Apply the above macros to uast-classes-list.h </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&#34;chpl/uast/uast-classes-list.h&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="c1">// clear the macros </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#undef AST_NODE </span></span></span><span class="line"><span class="cl"><span class="cp">#undef AST_BEGIN_SUBCLASSES </span></span></span><span class="line"><span class="cl"><span class="cp">#undef AST_END_SUBCLASSES</span></span></span></code></pre></div> </div> <p>These helpers are quite convenient. Here are a few examples of what we end up with:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">isFor</span><span class="p">(</span><span class="n">AstTag</span><span class="o">::</span><span class="n">For</span><span class="p">)</span> <span class="c1">// Returns true; a &#39;for&#39; loop is indeed a &#39;for&#39; loop. </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">isIndexableLoop</span><span class="p">(</span><span class="n">AstTag</span><span class="o">::</span><span class="n">For</span><span class="p">)</span> <span class="c1">// Returns true; a &#39;for&#39; loop is &#34;indexable&#34; (&#39;for i in ...&#39;) </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">isLoop</span><span class="p">(</span><span class="n">AstTag</span><span class="o">::</span><span class="n">For</span><span class="p">)</span> <span class="c1">// Returns true; a &#39;for&#39; loop is a loop. </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">isFor</span><span class="p">(</span><span class="n">AstTag</span><span class="o">::</span><span class="n">While</span><span class="p">)</span> <span class="c1">// Returns false; a &#39;while&#39; loop is not a &#39;for&#39; loop. </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">isIndexableLoop</span><span class="p">(</span><span class="n">AstTag</span><span class="o">::</span><span class="n">While</span><span class="p">)</span> <span class="c1">// Returns false; a &#39;while&#39; loop uses a boolean condition, not an index </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">isLoop</span><span class="p">(</span><span class="n">AstTag</span><span class="o">::</span><span class="n">While</span><span class="p">)</span> <span class="c1">// Returns true; a &#39;while&#39; loop is a loop. </span></span></span></code></pre></div><p>On the top-level AST node class, we generate <code>isWhateverNode</code> and <code>toWhateverNode</code> for each AST subclass. Thus, user code is able to inspect the AST and perform (checked) casts using plain methods. I omit <code>isWhateverNode</code> here for brevity (its definition is very simple), and include only <code>toWhateverNode</code>.</p> <div class="highlight-group" data-base-path="%!s(<nil>)" data-file-path="frontend/include/chpl/uast/AstNode.h"> <div class="highlight-label">From <a href="https://github.com/chapel-lang/chapel/blob/cd108338d321d0b3edf6258e0b2a58459d88a348/frontend/include/chpl/uast/AstNode.h#L313">AstNode.h</a>, around line 313</div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="cp">#define AST_TO(NAME) \ </span></span></span><span class="line"><span class="cl"><span class="cp"> const NAME * to##NAME() const { \ </span></span></span><span class="line"><span class="cl"><span class="cp"> return this-&gt;is##NAME() ? (const NAME *)this : nullptr; \ </span></span></span><span class="line"><span class="cl"><span class="cp"> } \ </span></span></span><span class="line"><span class="cl"><span class="cp"> NAME * to##NAME() { \ </span></span></span><span class="line"><span class="cl"><span class="cp"> return this-&gt;is##NAME() ? (NAME *)this : nullptr; \ </span></span></span><span class="line"><span class="cl"><span class="cp"> } </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="cp">#define AST_NODE(NAME) AST_TO(NAME) </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="cp">#define AST_LEAF(NAME) AST_TO(NAME) </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="cp">#define AST_BEGIN_SUBCLASSES(NAME) AST_TO(NAME) </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="cp">#define AST_END_SUBCLASSES(NAME) </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="c1">// Apply the above macros to uast-classes-list.h </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="cp">#include</span> <span class="cpf">&#34;chpl/uast/uast-classes-list.h&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="c1">// clear the macros </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="cp">#undef AST_NODE </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="cp">#undef AST_LEAF </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="cp">#undef AST_BEGIN_SUBCLASSES </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="cp">#undef AST_END_SUBCLASSES </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="cp">#undef AST_TO</span></span></span></code></pre></div> </div> <p>These methods are used heavily in the compiler. For example, here&rsquo;s a completely random snippet of code I pulled out:</p> <div class="highlight-group" data-base-path="%!s(<nil>)" data-file-path="frontend/lib/resolution/Resolver.cpp"> <div class="highlight-label">From <a href="https://github.com/chapel-lang/chapel/blob/cd108338d321d0b3edf6258e0b2a58459d88a348/frontend/lib/resolution/Resolver.cpp#L1161">Resolver.cpp</a>, around line 1161</div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="k">auto</span> <span class="n">var</span> <span class="o">=</span> <span class="n">decl</span><span class="o">-&gt;</span><span class="n">toVarLikeDecl</span><span class="p">())</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// Figure out variable type based upon: </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// * the type in the variable declaration </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// * the initialization expression in the variable declaration </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// * the initialization expression from split-init </span></span></span><span class="line"><span class="cl"><span class="c1"></span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">typeExpr</span> <span class="o">=</span> <span class="n">var</span><span class="o">-&gt;</span><span class="n">typeExpression</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">initExpr</span> <span class="o">=</span> <span class="n">var</span><span class="o">-&gt;</span><span class="n">initExpression</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="k">auto</span> <span class="n">var</span> <span class="o">=</span> <span class="n">decl</span><span class="o">-&gt;</span><span class="n">toVariable</span><span class="p">())</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">var</span><span class="o">-&gt;</span><span class="n">isField</span><span class="p">())</span> </span></span><span class="line"><span class="cl"> <span class="n">isField</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span></span></span></code></pre></div> </div> <p>Thus, developers adding new AST nodes are not required to manually implement the <code>isWhatever</code>, <code>toWhatever</code>, and other functions. This and a fair bit of other AST functionality (which I will cover in the next subsection) is automatically generated using X Macros.</p> <p class="dialog"> <span class="message side-question"> <span class="message-sender"><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#moon"/> </svg></span> <span class="message-text"> You haven't actually shown how the AST node classes are declared, only the tags. It seems implausible that they be generated using this same strategy - doesn't each AST node have its own different methods and implementation code? </span> </span> <span class="message side-answer"> <span class="message-sender"><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#sun"/> </svg></span> <span class="message-text"> You're right. The AST node classes are defined "as usual", and their constructors must explicitly set their <code>tag</code> field to the corresponding <code>AstTag</code> value. It's also on the person defining the new class to extend the node that they promise to extend in <code>uast-classes-list.h</code>. </span> </span> <span class="message side-question"> <span class="message-sender"><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#moon"/> </svg></span> <span class="message-text"> This seems like an opportunity for bugs. Nothing is stopping a developer from returning the wrong tag, which would break the auto-casting behavior. </span> </span> <span class="message side-answer"> <span class="message-sender"><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#sun"/> </svg></span> <span class="message-text"> Yes, it's not bulletproof. Just recently, a team meber found <a href="https://github.com/chapel-lang/chapel/pull/23508"> a bug</a> in which a node was listed to inherit from <code>AstNode</code>, but actually inherited from <code>NamedDecl</code>. The <code>toNamedDecl</code> method would not have worked on it, even though it inherited from the class.<br> <br> Still, this pattern provides the Chapel compiler with a lot of value; I will show more use cases in the next subsection, like promised. </span> </span> </p> <a href="#the-visitor-pattern-without-double-dispatch"> <h4 id="the-visitor-pattern-without-double-dispatch">The Visitor Pattern without Double Dispatch</h4> </a> <p>The Visitor Pattern is very important in general, but it&rsquo;s beyond ubiquitous for us compiler developers. It helps avoid bloating AST node classes with methods and state required for the various operations we perform on them. It also often saves us from writing AST traversal code.</p> <p>Essentially, rather than adding each new operation (e.g. convert to string, compute the type, assign IDs) as methods on each AST node class, we extract this code into a per-operation <em>visitor</em>. This visitor is a class that has methods implementing the custom behavior on the AST nodes. A <code>visit(WhileLoop*)</code> method might be used to perform the operation on &lsquo;while&rsquo; loops, and <code>visit(ForLoop*)</code> might do the same for &lsquo;for&rsquo; loops. The AST nodes themselves only have a <code>traverse</code> method that accepts a visitor, whatever it may be, and calls the appropriate visit methods. This way, the AST node implementations remain simple and relatively stable.</p> <p>As a very simple example, suppose you wanted to count the number of loops used in a program for an unspecified reason. You could add a <code>countLoops</code> method, but then you&rsquo;ve introduced a method to the AST node API for what might be a one-time, throwaway operation. With the visitor pattern, you don&rsquo;t need to do that; you can just create a new class:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">MyVisitor</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">visit</span><span class="p">(</span><span class="k">const</span> <span class="n">Loop</span><span class="o">*</span><span class="p">)</span> <span class="p">{</span> <span class="n">count</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">visit</span><span class="p">(</span><span class="k">const</span> <span class="n">AstNode</span><span class="o">*</span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* do nothing for other nodes */</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">countLoops</span><span class="p">(</span><span class="k">const</span> <span class="n">AstNode</span><span class="o">*</span> <span class="n">root</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">MyVisitor</span> <span class="n">visitor</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">root</span><span class="o">-&gt;</span><span class="n">traverse</span><span class="p">(</span><span class="n">visitor</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">visitor</span><span class="p">.</span><span class="n">count</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></div><p>The <code>traverse</code> method is a nice API, isn&rsquo;t it? It&rsquo;s very easy to add operations that work on your syntax trees, without modifying them. There is still an important open question, though: how does <code>traverse</code> know to call the right <code>visit</code> function?</p> <p>If <code>traverse</code> were only defined on <code>AstNode*</code>, and it simply called <code>visit(this)</code>, we&rsquo;d always end up calling the <code>AstNode</code> version of the <code>visit</code> function. This is because C++ doesn&rsquo;t dynamic dispatch <span class="sidenote"> <label class="sidenote-label" for="vtable-note">based on the types of method arguments.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="vtable-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Obviously, C++ has the ability to pick the right method based on the runtime type of the <em>receiver</em>: that's just <code>virtual</code> functions and <code>vtable</code>s. <span class="sidenote-delimiter">]</span> </span> </span> Statically, the call clearly accepts an <code>AstNode</code>, and nothing more specific. The compiler therefore picks that version of the <code>visit</code> method.</p> <p>The &ldquo;traditional&rdquo; way to solve this problem in a language like C++ or Java is called <em>double dispatch</em>. Using our example as reference, this involves making <em>each</em> AST node class have its own <code>traverse</code> method. This way, calls to <code>visit(this)</code> have more specific type information, and are resolved to the appropriate overload. But that&rsquo;s more boilerplate code: each new AST node will need to have a virtual traverse method that looks something like this:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">MyNode</span><span class="o">::</span><span class="n">traverse</span><span class="p">(</span><span class="n">Visitor</span><span class="o">&amp;</span> <span class="n">v</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">v</span><span class="p">.</span><span class="n">visit</span><span class="p">(</span><span class="k">this</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></div><p>It would also require all visitors to extend from <code>Visitor</code>. So now you have:</p> <ul> <li>Boilerplate code on every AST node that looks the same but needs to be duplicated</li> <li>A parent <code>Visitor</code> class that must have a <code>visit</code> method for each AST node in the language (so that children can override it).</li> <li>To make it easier to write code like our <code>MyVisitor</code> above, the <code>visit</code> methods in the <code>Visitor</code> must be written such that <code>visit(ChildNode*)</code> calls <code>visit(ParentNode*)</code> by default. Otherwise, the <code>Loop</code> overload wouldn&rsquo;t have been called by the <code>DoWhile</code> overload (e.g.).</li> </ul> <p>So there&rsquo;s a fair bit of tedious boilerplate, and more code to manually modify when adding an AST node: you have to go and adjust the <code>Visitor</code> class with new <code>visit</code> stub.</p> <p>The reason all of this is necessary is that everyone (myself included) generally agrees that code like the following is generally a bad idea:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">AstNode</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">traverse</span><span class="p">(</span><span class="n">Visitor</span><span class="o">&amp;</span> <span class="n">visitor</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="k">auto</span> <span class="n">forLoop</span> <span class="o">=</span> <span class="n">toForLoop</span><span class="p">())</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">visitor</span><span class="p">.</span><span class="n">visit</span><span class="p">(</span><span class="n">forLoop</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="k">auto</span> <span class="n">whileLoop</span> <span class="o">=</span> <span class="n">toWhileLoop</span><span class="p">())</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">visitor</span><span class="p">.</span><span class="n">visit</span><span class="p">(</span><span class="n">whileLoop</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 100 more lines like this... </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></div><p>After all, what happens when you add a new AST node? You&rsquo;d still have to modify this list, and since everything still extends <code>Visitor</code>, you&rsquo;d still need to add a new <code>visit</code> stub there. But what if there were no base class? Instead, what if <code>traverse</code> were a template?</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">AstNode</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">template</span> <span class="o">&lt;</span><span class="k">typename</span> <span class="n">VisitorType</span><span class="o">&gt;</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="n">traverse</span><span class="p">(</span><span class="n">VisitorType</span><span class="o">&amp;</span> <span class="n">visitor</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="k">auto</span> <span class="n">forLoop</span> <span class="o">=</span> <span class="n">toForLoop</span><span class="p">())</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">visitor</span><span class="p">.</span><span class="n">visit</span><span class="p">(</span><span class="n">forLoop</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="nf">if</span> <span class="p">(</span><span class="k">auto</span> <span class="n">whileLoop</span> <span class="o">=</span> <span class="n">toWhileLoop</span><span class="p">())</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">visitor</span><span class="p">.</span><span class="n">visit</span><span class="p">(</span><span class="n">whileLoop</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// 100 more lines like this... </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></div><p>Note that this wouldn&rsquo;t be possible to write in C++ if <code>visit</code> were a virtual method; have you ever heard of a virtual template? With code like this, the <code>VisitorType</code> wouldn&rsquo;t need to define <em>every</em> overload, as long as it had a version for <code>AstNode</code>. Furthermore, C++&rsquo;s regular overload resolution rules would take care of calling the <code>Loop</code> overload if a more specific one for <code>DoWhile</code> didn&rsquo;t exist.</p> <p>The only problem that remains is that of having a 100-line if-else (which could be a <code>switch</code> to little aesthetic benefit). But this is exactly where the X Macro pattern shines again! We already have a list of all AST node classes, and the code for invoking them is nearly identical. Thus, the Chapel compiler has a <code>doDispatch</code> function (used by <code>traverse</code>) that looks like this:</p> <div class="highlight-group" data-base-path="%!s(<nil>)" data-file-path="frontend/include/chpl/uast/AstNode.h"> <div class="highlight-label">From <a href="https://github.com/chapel-lang/chapel/blob/cd108338d321d0b3edf6258e0b2a58459d88a348/frontend/include/chpl/uast/AstNode.h#L377">AstNode.h</a>, around line 377</div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="k">static</span> <span class="kt">void</span> <span class="nf">doDispatch</span><span class="p">(</span><span class="k">const</span> <span class="n">AstNode</span><span class="o">*</span> <span class="n">ast</span><span class="p">,</span> <span class="n">Visitor</span><span class="o">&amp;</span> <span class="n">v</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">switch</span> <span class="p">(</span><span class="n">ast</span><span class="o">-&gt;</span><span class="n">tag</span><span class="p">())</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="cp">#define CONVERT(NAME) \ </span></span></span><span class="line"><span class="cl"><span class="cp"> case chpl::uast::asttags::NAME: \ </span></span></span><span class="line"><span class="cl"><span class="cp"> { \ </span></span></span><span class="line"><span class="cl"><span class="cp"> v.visit((const chpl::uast::NAME*) ast); \ </span></span></span><span class="line"><span class="cl"><span class="cp"> return; \ </span></span></span><span class="line"><span class="cl"><span class="cp"> } </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"> <span class="cp">#define IGNORE(NAME) \ </span></span></span><span class="line"><span class="cl"><span class="cp"> case chpl::uast::asttags::NAME: \ </span></span></span><span class="line"><span class="cl"><span class="cp"> { \ </span></span></span><span class="line"><span class="cl"><span class="cp"> CHPL_ASSERT(false &amp;&amp; &#34;this code should never be run&#34;); \ </span></span></span><span class="line"><span class="cl"><span class="cp"> } </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"> <span class="cp">#define AST_NODE(NAME) CONVERT(NAME) </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="cp">#define AST_BEGIN_SUBCLASSES(NAME) IGNORE(START_##NAME) </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="cp">#define AST_END_SUBCLASSES(NAME) IGNORE(END_##NAME) </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"> <span class="cp">#include</span> <span class="cpf">&#34;chpl/uast/uast-classes-list.h&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"> <span class="n">IGNORE</span><span class="p">(</span><span class="n">NUM_AST_TAGS</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">IGNORE</span><span class="p">(</span><span class="n">AST_TAG_UNKNOWN</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="cp">#undef AST_NODE </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="cp">#undef AST_BEGIN_SUBCLASSES </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="cp">#undef AST_END_SUBCLASSES </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="cp">#undef CONVERT </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="cp">#undef IGNORE </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">CHPL_ASSERT</span><span class="p">(</span><span class="nb">false</span> <span class="o">&amp;&amp;</span> <span class="s">&#34;this code should never be run&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span></span></span></code></pre></div> </div> <p>And that&rsquo;s it. We have automatically generated the traversal code, allowing us to use the visitor pattern in what I think is a very elegant way. Assuming a developer adding a new AST node updates the <code>uast-classes-list.h</code> header, the traversal logic will be auto-modified to properly handle the new node.</p> <a href="#generating-a-python-class-hierarchy"> <h4 id="generating-a-python-class-hierarchy">Generating a Python Class Hierarchy</h4> </a> <p>This is a fun one. For a while, in my spare time, I was working on <a href="https://github.com/chapel-lang/chapel/tree/main/tools/chapel-py"class="external-link">Python bindings for Chapel<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. These bindings are oriented towards developing language tooling: it feels much easier to write a language linter, auto-formatter, or maybe even a language server in Python rather than in C++. It&rsquo;s definitely much easier to use Python to develop throwaway scripts that work with Chapel programs, which is something that developers on the Chapel team tend to do quite often.</p> <p>I decided I wanted the Python AST node class hierarchy to match the C++ version. This is convenient for many reasons, including being able to wrap methods on parent AST nodes and have them be available through child AST nodes and having <code>isinstance</code> work properly. It&rsquo;s also advantageous from the point of view of conceptual simplicity. However, I very much did not want to write CPython API code to define the many AST node classes that are available in the Chapel language.</p> <p>Once again, the <code>uast-classes-list.h</code> header came into play here. With little effort, I was able to auto-generate <code>PyTypeObject</code>s for each AST node in the class hierarchy:</p> <div class="highlight-group" data-base-path="%!s(<nil>)" data-file-path="tools/chapel-py/chapel.cpp"> <div class="highlight-label">From <a href="https://github.com/chapel-lang/chapel/blob/31a296e80cfb69bfc0c79a48d5cc9e8891f54818/tools/chapel-py/chapel.cpp#L563">chapel.cpp</a>, around line 563</div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="cp">#define DEFINE_PY_TYPE_FOR(NAME, TAG, FLAGS)\ </span></span></span><span class="line"><span class="cl"><span class="cp"> PyTypeObject NAME##Type = { \ </span></span></span><span class="line"><span class="cl"><span class="cp"> PyVarObject_HEAD_INIT(NULL, 0) \ </span></span></span><span class="line"><span class="cl"><span class="cp"> .tp_name = #NAME, \ </span></span></span><span class="line"><span class="cl"><span class="cp"> .tp_basicsize = sizeof(NAME##Object), \ </span></span></span><span class="line"><span class="cl"><span class="cp"> .tp_itemsize = 0, \ </span></span></span><span class="line"><span class="cl"><span class="cp"> .tp_flags = FLAGS, \ </span></span></span><span class="line"><span class="cl"><span class="cp"> .tp_doc = PyDoc_STR(&#34;A Chapel &#34; #NAME &#34; AST node&#34;), \ </span></span></span><span class="line"><span class="cl"><span class="cp"> .tp_methods = (PyMethodDef*) PerNodeInfo&lt;TAG&gt;::methods, \ </span></span></span><span class="line"><span class="cl"><span class="cp"> .tp_base = parentTypeFor(TAG), \ </span></span></span><span class="line"><span class="cl"><span class="cp"> .tp_init = (initproc) NAME##Object_init, \ </span></span></span><span class="line"><span class="cl"><span class="cp"> .tp_new = PyType_GenericNew, \ </span></span></span><span class="line"><span class="cl"><span class="cp"> }; </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="cp">#define AST_NODE(NAME) DEFINE_PY_TYPE_FOR(NAME, chpl::uast::asttags::NAME, Py_TPFLAGS_DEFAULT) </span></span></span><span class="line"><span class="cl"><span class="cp">#define AST_BEGIN_SUBCLASSES(NAME) DEFINE_PY_TYPE_FOR(NAME, chpl::uast::asttags::START_##NAME, Py_TPFLAGS_BASETYPE) </span></span></span><span class="line"><span class="cl"><span class="cp">#define AST_END_SUBCLASSES(NAME) </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;chpl/uast/uast-classes-list.h&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#undef AST_NODE </span></span></span><span class="line"><span class="cl"><span class="cp">#undef AST_BEGIN_SUBCLASSES </span></span></span><span class="line"><span class="cl"><span class="cp">#undef AST_END_SUBCLASSES</span></span></span></code></pre></div> </div> <p>You may have noticed that I snuck templates into the code above. The motivation there is to avoid writing out the (usually empty) Python method table for every single AST node. In particular, I have a template that, by default, provides an empty method table, which can be specialized per node to add methods when necessary. This detail is useful for application 3 below, but not necessary to understand the use of X Macros here.</p> <p>I used the same <code>&lt;</code> and <code>&gt;</code> trick to generate the <code>parentTypeFor</code> each tag:</p> <div class="highlight-group" data-base-path="%!s(<nil>)" data-file-path="tools/chapel-py/chapel.cpp"> <div class="highlight-label">From <a href="https://github.com/chapel-lang/chapel/blob/31a296e80cfb69bfc0c79a48d5cc9e8891f54818/tools/chapel-py/chapel.cpp#L157">chapel.cpp</a>, around line 157</div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">static</span> <span class="n">PyTypeObject</span><span class="o">*</span> <span class="nf">parentTypeFor</span><span class="p">(</span><span class="n">chpl</span><span class="o">::</span><span class="n">uast</span><span class="o">::</span><span class="n">asttags</span><span class="o">::</span><span class="n">AstTag</span> <span class="n">tag</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"><span class="cp">#define AST_NODE(NAME) </span></span></span><span class="line"><span class="cl"><span class="cp">#define AST_LEAF(NAME) </span></span></span><span class="line"><span class="cl"><span class="cp">#define AST_BEGIN_SUBCLASSES(NAME) </span></span></span><span class="line"><span class="cl"><span class="cp">#define AST_END_SUBCLASSES(NAME) \ </span></span></span><span class="line"><span class="cl"><span class="cp"> if (tag &gt; chpl::uast::asttags::START_##NAME &amp;&amp; tag &lt; chpl::uast::asttags::END_##NAME) { \ </span></span></span><span class="line"><span class="cl"><span class="cp"> return &amp;NAME##Type; \ </span></span></span><span class="line"><span class="cl"><span class="cp"> } </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;chpl/uast/uast-classes-list.h&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;chpl/uast/uast-classes-list.h&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#undef AST_NODE </span></span></span><span class="line"><span class="cl"><span class="cp">#undef AST_LEAF </span></span></span><span class="line"><span class="cl"><span class="cp">#undef AST_BEGIN_SUBCLASSES </span></span></span><span class="line"><span class="cl"><span class="cp">#undef AST_END_SUBCLASSES </span></span></span><span class="line"><span class="cl"><span class="cp"></span> <span class="k">return</span> <span class="o">&amp;</span><span class="n">AstNodeType</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div> </div> <p>A few more invocations of the <code>uast-classes-list.h</code> macro, and I had a working class hierarchy. I didn&rsquo;t explicitly mention any AST node at all; all was derived from the Chapel compiler header. This also meant that as the language changed and the AST class hierarchy developed, the Python bindings&rsquo; code would not need to be updated. As long as it was compiled with an up-to-date version of the header, the hierarchy would match that present within the language.</p> <p>This allows for code like the following to be written in Python:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Python" data-lang="Python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">print_decls</span><span class="p">(</span><span class="n">mod</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="s2">&#34;&#34;&#34; </span></span></span><span class="line"><span class="cl"><span class="s2"> Print all the things declared in this Chapel module. </span></span></span><span class="line"><span class="cl"><span class="s2"> &#34;&#34;&#34;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">child</span> <span class="ow">in</span> <span class="n">mod</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">child</span><span class="p">,</span> <span class="n">NamedDecl</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="nb">print</span><span class="p">(</span><span class="n">child</span><span class="o">.</span><span class="n">name</span><span class="p">())</span> </span></span></code></pre></div><a href="#application-3-cpython-method-tables-and-getters"> <h3 id="application-3-cpython-method-tables-and-getters">Application 3: CPython Method Tables and Getters</h3> </a> <p>The Chapel Python bindings use the X Macro pattern another time, actually. Like I mentioned earlier, I use <a href="https://en.cppreference.com/w/cpp/language/template_specialization"class="external-link">template specialization<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> to reduce the amount of boilerplate code required for declaring Python objects. In particular, there&rsquo;s a general method table declared as follows:</p> <div class="highlight-group" data-base-path="%!s(<nil>)" data-file-path="tools/chapel-py/chapel.cpp"> <div class="highlight-label">From <a href="https://github.com/chapel-lang/chapel/blob/31a296e80cfb69bfc0c79a48d5cc9e8891f54818/tools/chapel-py/chapel.cpp#L541">chapel.cpp</a>, around line 541</div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">template</span> <span class="o">&lt;</span><span class="n">chpl</span><span class="o">::</span><span class="n">uast</span><span class="o">::</span><span class="n">asttags</span><span class="o">::</span><span class="n">AstTag</span> <span class="n">tag</span><span class="o">&gt;</span> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">PerNodeInfo</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">static</span> <span class="k">constexpr</span> <span class="n">PyMethodDef</span> <span class="n">methods</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">}</span> <span class="cm">/* Sentinel */</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span> </span></span><span class="line"><span class="cl"><span class="p">};</span></span></span></code></pre></div> </div> <p>Then, when I need to add methods, I use template specialization by writing something like the following:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">template</span> <span class="o">&lt;&gt;</span> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">PerNodeInfo</span><span class="o">&lt;</span><span class="n">TheAstTag</span><span class="o">&gt;</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">static</span> <span class="k">constexpr</span> <span class="n">PyMethodDef</span> <span class="n">methods</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="s">&#34;method_name&#34;</span><span class="p">,</span> <span class="n">TheNode_method_name</span><span class="p">,</span> <span class="n">METH_NOARGS</span><span class="p">,</span> <span class="s">&#34;Documentation string&#34;</span><span class="p">},</span> </span></span><span class="line"><span class="cl"> <span class="c1">// ... more like the above ... </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">{</span><span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">}</span> <span class="cm">/* Sentinel */</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span></code></pre></div><p>When reviewing a PR that adds more methods to the Python bindings (by defining new <code>TheNode_methodname</code> functions and then including them in the method table), I noticed that in the PR, the developer added some methods but forgot to put them into the respective table, leaving them unusable by the Python client code. This came with the additional observation that there was a moderate amount of duplication when declaring the C++ functions and then listing them in the table. The name (<code>method_name</code> in the code) occurred many times.</p> <p>The developer who opened the PR suggesting using X Macros to combine the information (declaration of function and its use in the corresponding method table) into a single list. This led to the following header file:</p> <div class="highlight-group" data-base-path="%!s(<nil>)" data-file-path="tools/chapel-py/method-tables.h"> <div class="highlight-label">From <a href="https://github.com/chapel-lang/chapel/blob/31a296e80cfb69bfc0c79a48d5cc9e8891f54818/tools/chapel-py/method-tables.h#L323">method-tables.h</a>, around line 323</div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">CLASS_BEGIN</span><span class="p">(</span><span class="n">FnCall</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">METHOD_PROTOTYPE</span><span class="p">(</span><span class="n">FnCall</span><span class="p">,</span> <span class="n">actuals</span><span class="p">,</span> <span class="s">&#34;Get the actuals of this FnCall node&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">PLAIN_GETTER</span><span class="p">(</span><span class="n">FnCall</span><span class="p">,</span> <span class="n">used_square_brackets</span><span class="p">,</span> <span class="s">&#34;Check if this FnCall was made using square brackets&#34;</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;b&#34;</span><span class="p">,</span> <span class="k">return</span> <span class="n">node</span><span class="o">-&gt;</span><span class="n">callUsedSquareBrackets</span><span class="p">())</span> </span></span><span class="line"><span class="cl"><span class="n">CLASS_END</span><span class="p">(</span><span class="n">FnCall</span><span class="p">)</span></span></span></code></pre></div> </div> <p>The <code>PLAIN_GETTER</code> macro in this case is used to define trivial getters (precluding the need for handling the Python-object-to-AST-node conversion, and other CPython-specific things), whereas the <code>METHOD_PROTOTYPE</code> is used to refer to methods that needed explicit implementations. With this, the method tables are generated as follows:</p> <div class="highlight-group" data-base-path="%!s(<nil>)" data-file-path="tools/chapel-py/chapel.cpp"> <div class="highlight-label">From <a href="https://github.com/chapel-lang/chapel/blob/31a296e80cfb69bfc0c79a48d5cc9e8891f54818/tools/chapel-py/chapel.cpp#L548">chapel.cpp</a>, around line 548</div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="cp">#define CLASS_BEGIN(TAG) \ </span></span></span><span class="line"><span class="cl"><span class="cp"> template &lt;&gt; \ </span></span></span><span class="line"><span class="cl"><span class="cp"> struct PerNodeInfo&lt;chpl::uast::asttags::TAG&gt; { \ </span></span></span><span class="line"><span class="cl"><span class="cp"> static constexpr PyMethodDef methods[] = { </span></span></span><span class="line"><span class="cl"><span class="cp">#define CLASS_END(TAG) \ </span></span></span><span class="line"><span class="cl"><span class="cp"> {NULL, NULL, 0, NULL} </span><span class="cm">/* Sentinel */</span><span class="cp"> \ </span></span></span><span class="line"><span class="cl"><span class="cp"> }; \ </span></span></span><span class="line"><span class="cl"><span class="cp"> }; </span></span></span><span class="line"><span class="cl"><span class="cp">#define PLAIN_GETTER(NODE, NAME, DOCSTR, TYPESTR, BODY) \ </span></span></span><span class="line"><span class="cl"><span class="cp"> {#NAME, NODE##Object_##NAME, METH_NOARGS, DOCSTR}, </span></span></span><span class="line"><span class="cl"><span class="cp">#define METHOD_PROTOTYPE(NODE, NAME, DOCSTR) \ </span></span></span><span class="line"><span class="cl"><span class="cp"> {#NAME, NODE##Object_##NAME, METH_NOARGS, DOCSTR}, </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;method-tables.h&#34;</span></span></span></code></pre></div> </div> <p>The <code>CLASS_BEGIN</code> generates the initial <code>template &lt;&gt;</code> header and the code up to the opening curly brace of the table definition. Then, for each method, <code>PLAIN_GETTER</code> and <code>METHOD_PROTOTYPE</code> generate the relevant entries. Finally, <code>CLASS_END</code> inserts the sentinel and the closing curly brace.</p> <p>Another invocation of the macros in <code>method-tables.h</code> is used to generate the implementations of &ldquo;plain getters&rdquo;, which is boilerplate that I won&rsquo;t get into it here, since it&rsquo;s pretty CPython specific.</p> <a href="#discussion"> <h3 id="discussion">Discussion</h3> </a> <p>I&rsquo;ve presented to you a three applications of the pattern, in an order that happens to be from least to most &ldquo;extreme&rdquo;. It&rsquo;s possible that some of these are over the line for using macros, especially for those who think of macros as unfortunate remnants of C++&rsquo;s past. However, I think that what I&rsquo;ve demonstrated demonstrates the versatility of the X Macro pattern &ndash; feel free to apply it to the degree that you find appropriate.</p> <p>The thing I like the most about this pattern is that the header files read quite nicely: you end up with a very declarative &ldquo;scaffold&rdquo; of what&rsquo;s going on. The <code>uast-classes-list.h</code> makes for an excellent and fairly readable reference of all the AST nodes in the Chapel compiler. The <code>method-tables.h</code> header provides a fairly concise summary of what methods are available on what (Python) AST node.</p> <p>Of course, this approach is not without its drawbacks. Drawback zero is the heavy use of macros: to the best of my knowledge, modern C++ tends to discourage the usage of macros in favor of C++-specific features. Of course, this &ldquo;pure C++&rdquo; preference is applicable to variable degrees in different use cases and code bases; because of this, I won&rsquo;t count macros as (too much of) a drawback.</p> <p>The more significant downside is that this approach introduces a lot of dependencies between source files. Any time the header changes, anything that uses any part of the code generated by the header must be recompiled. Thus, if you&rsquo;re generating classes, changing any one class will &ldquo;taint&rdquo; any code that uses <em>any</em> of the generated classes. In the Chapel compiler, touching the AST class hierarchy requires a recompilation of all the AST nodes, and any compiler code that uses the AST nodes (a lot). This is because each AST node needs access to the <code>AstTag</code> enum, and that enum is generated from the hierarchy header.</p> <p>That&rsquo;s all I have for today! Thanks for reading. I hope you got something useful for your day-to-day programming out of this.</p> The "Is Something" Pattern in Agda https://danilafe.com/blog/agda_is_pattern/ Thu, 31 Aug 2023 22:15:34 -0700 https://danilafe.com/blog/agda_is_pattern/ <p>Agda is a functional programming language with a relatively Haskell-like syntax and feature set, so coming into it, I relied on my past experiences with Haskell to get things done. However, the languages are sufficiently different to leave room for useful design patterns in Agda that can&rsquo;t be brought over from Haskell, because they don&rsquo;t exist there. One such pattern will be the focus of this post; it&rsquo;s relatively simple, but I came across it by reading the standard library code. My hope is that by writing it down here, I can save someone the trouble of recognizing it and understanding its purpose. The pattern is &ldquo;unique&rdquo; to Agda (in the sense that it isn&rsquo;t present in Haskell) because it relies on dependent types.</p> <p>In my head, I call this the <code>IsSomething</code> pattern. Before I introduce it, let me try to provide some motivation. I should say that this may not be the only motivation for this pattern; it&rsquo;s just how I arrived at seeing its value.</p> <a href="#type-classes-for-related-operations"> <h3 id="type-classes-for-related-operations">Type Classes for Related Operations</h3> </a> <p>Suppose you wanted to define a type class for &ldquo;a type that has an associative binary operation&rdquo;. In Haskell, this is the famous <code>Semigroup</code> class. Here&rsquo;s a definition I lifted from the <a href="https://hackage.haskell.org/package/base-4.18.0.0/docs/src/GHC.Base.html#Semigroup"class="external-link">Haskell docs<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kr">class</span> <span class="kt">Semigroup</span> <span class="n">a</span> <span class="kr">where</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="o">&lt;&gt;</span><span class="p">)</span> <span class="ow">::</span> <span class="n">a</span> <span class="ow">-&gt;</span> <span class="n">a</span> <span class="ow">-&gt;</span> <span class="n">a</span> </span></span><span class="line"><span class="cl"> <span class="n">a</span> <span class="o">&lt;&gt;</span> <span class="n">b</span> <span class="ow">=</span> <span class="n">sconcat</span> <span class="p">(</span><span class="n">a</span> <span class="kt">:|</span> <span class="p">[</span> <span class="n">b</span> <span class="p">])</span> </span></span></code></pre></div><p>It says that a type <code>a</code> is a semigroup if it has a binary operation, which Haskell calls <code>(&lt;&gt;)</code>. The language isn&rsquo;t expressive enough to encode the associative property of this binary operation, but we won&rsquo;t hold it against Haskell: not every language needs dependent types or SMT-backed refinement types. If we translated this definition into Agda (and encoded the associativity constraint), we&rsquo;d end up with something like this:</p> <div class="highlight-group" data-base-path="" data-file-path="agda-issomething/example.agda" data-first-line="9" data-last-line="13" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/agda-issomething/example.agda#L9-L13">example.agda</a>, lines 9 through 13</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="kr">record</span><span class="w"> </span>Semigroup<span class="w"> </span><span class="o">(</span>A<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">)</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">field</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">_∙_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">isAssociative</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>a₁<span class="w"> </span>a₂<span class="w"> </span>a₃<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>a₁<span class="w"> </span>∙<span class="w"> </span><span class="o">(</span>a₂<span class="w"> </span>∙<span class="w"> </span>a₃<span class="o">)</span><span class="w"> </span>≡<span class="w"> </span><span class="o">(</span>a₁<span class="w"> </span>∙<span class="w"> </span>a₂<span class="o">)</span><span class="w"> </span>∙<span class="w"> </span>a₃</span></span></code></pre></td></tr></table> </div> </div> </div> <p>So far, so good. Now, let&rsquo;s also encode a more specific sort of type-with-binary-operation: one where the operation is associative as before, but also has an identity element. In Haskell, we can write this as:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kr">class</span> <span class="kt">Semigroup</span> <span class="n">a</span> <span class="ow">=&gt;</span> <span class="kt">Monoid</span> <span class="n">a</span> <span class="kr">where</span> </span></span><span class="line"><span class="cl"> <span class="n">mempty</span> <span class="ow">::</span> <span class="n">a</span> </span></span></code></pre></div><p>This brings in all the requirements of <code>Semigroup</code>, with one additional one: an element <code>mempty</code>, which is intended to be the aforementioned identity element for <code>(&lt;&gt;)</code>. Once again, we can&rsquo;t encode the &ldquo;identity element&rdquo; property; I say this only to explain the lack of any additional code in the preceding snippet.</p> <p>In Agda, there isn&rsquo;t really a special syntax for &ldquo;superclass&rdquo;; we just use a field. The &ldquo;transliterated&rdquo; implementation is as follows:</p> <div class="highlight-group" data-base-path="" data-file-path="agda-issomething/example.agda" data-first-line="15" data-last-line="24" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/agda-issomething/example.agda#L15-L24">example.agda</a>, lines 15 through 24</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="kr">record</span><span class="w"> </span>Monoid<span class="w"> </span><span class="o">(</span>A<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">)</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">field</span><span class="w"> </span>semigroup<span class="w"> </span><span class="ow">:</span><span class="w"> </span>Semigroup<span class="w"> </span>A<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">open</span><span class="w"> </span>Semigroup<span class="w"> </span>semigroup<span class="w"> </span>public<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">field</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">zero</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">isIdentityLeft</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>a<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>zero<span class="w"> </span>∙<span class="w"> </span>a<span class="w"> </span>≡<span class="w"> </span>a<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">isIdentityRight</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>a<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>a<span class="w"> </span>∙<span class="w"> </span>zero<span class="w"> </span>≡<span class="w"> </span>a</span></span></code></pre></td></tr></table> </div> </div> </div> <p>This code might require a little bit of explanation. Like I said, the base class is brought in as a field, <code>semigroup</code>. Then, every field of <code>semigroup</code> is also made available within <code>Monoid</code>, as well as to users of <code>Monoid</code>, by using an <code>open public</code> directive. The subsequent fields mimic the Haskell definition amended with proofs of identity.</p> <p>We get our first sign of awkwardness here. We can&rsquo;t refer to the binary operation very easily; it&rsquo;s nested inside of <code>semigroup</code>, and we have to access its fields to get ahold of <code>(∙)</code>. It&rsquo;s not too bad at all &ndash; it just cost us an extra line. However, the bookkeeping of what-operation-is-where gets frustrating quickly.</p> <p>I will demonstrate the frustrations in one final example. I will admit to it being contrived: I am trying to avoid introducing too many definitions and concepts just for the sake of a motivating case. Suppose you are trying to specify a type in which the binary operation has <em>two</em> properties (e.g. it&rsquo;s a monoid <em>and</em> something else). Since the only two type classes I have so far are <code>Monoid</code> and <code>Semigroup</code>, I will use those; note that in this particular instance, using both is a contrivance, since one contains the latter.</p> <div class="highlight-group" data-base-path="" data-file-path="agda-issomething/example.agda" data-first-line="26" data-last-line="32" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/agda-issomething/example.agda#L26-L32">example.agda</a>, lines 26 through 32</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="kr">record</span><span class="w"> </span>ContrivedExample<span class="w"> </span><span class="o">(</span>A<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">)</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">field</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">-- first property</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">monoid</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Monoid<span class="w"> </span>A<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">-- second property; Semigroup is a stand-in.</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">semigroup</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Semigroup<span class="w"> </span>A</span></span></code></pre></td></tr></table> </div> </div> </div> <p>However, there&rsquo;s a problem: nothing in the above definition ensures that the binary operations of the two fields are the same! As far as Agda is concerned (as one would quickly come to realize by trying a few proofs with the code), the two operations are completely separate. One could perhaps add an equality constraint:</p> <div class="highlight-group" data-base-path="" data-file-path="agda-issomething/example.agda" data-first-line="26" data-last-line="34" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/agda-issomething/example.agda#L26-L34">example.agda</a>, lines 26 through 34</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="kr">record</span><span class="w"> </span>ContrivedExample<span class="w"> </span><span class="o">(</span>A<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">)</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">field</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">-- first property</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">monoid</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Monoid<span class="w"> </span>A<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">-- second property; Semigroup is a stand-in.</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">semigroup</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Semigroup<span class="w"> </span>A<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">operationsEqual</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>Monoid._∙_<span class="w"> </span>monoid<span class="w"> </span>≡<span class="w"> </span>Semigroup._∙_<span class="w"> </span>semigroup</span></span></code></pre></td></tr></table> </div> </div> </div> <p>However, this will get tedious quickly. Proofs will need to leverage rewrites (via the <code>rewrite</code> keyword, or via <code>cong</code>) to change one of the binary operations into the other. As you build up more and more complex algebraic structures, in which the various operations are related in nontrivial ways, you start to look for other approaches. That&rsquo;s where the <code>IsSomething</code> pattern comes in.</p> <a href="#the-issomething-pattern-parameterizing-by-operations"> <h3 id="the-issomething-pattern-parameterizing-by-operations">The <code>IsSomething</code> Pattern: Parameterizing By Operations</h3> </a> <p>The pain point of the original approach is data flow. The way it&rsquo;s written, data (operations, elements, etc.) flows from the fields of a record to the record itself: <code>Monoid</code> has to <em>read</em> the <code>(∙)</code> operation from <code>Semigroup</code>. The more fields you add, the more reading and reconciliation you have to do. It would be better if the data flowed the other direction: from <code>Monoid</code> to <code>Semigroup</code>. <code>Monoid</code> could say, &ldquo;here&rsquo;s a binary operation; it must satisfy these constraints, in addition to having an identity element&rdquo;. To <em>provide</em> the binary operation to a field, we use type application; this would look something like this:</p> <div class="highlight-group" data-base-path="" data-file-path="agda-issomething/example.agda" data-first-line="42" data-last-line="42" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/agda-issomething/example.agda#L42-L42">example.agda</a>, line 42</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">42 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="nf">isSemigroup</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsSemigroup<span class="w"> </span>_∙_</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Here&rsquo;s the part that&rsquo;s not possible in Haskell: we have a <code>record</code>, called <code>IsSemigroup</code>, that&rsquo;s parameterized by a <em>value</em> &ndash; the binary operation! This new record is quite similar to our original <code>Semigroup</code>, except that it doesn&rsquo;t need a field for <code>(∙)</code>: it gets that from outside. Note the additional parameter in the <code>record</code> header:</p> <div class="highlight-group" data-base-path="" data-file-path="agda-issomething/example.agda" data-first-line="37" data-last-line="38" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/agda-issomething/example.agda#L37-L38">example.agda</a>, lines 37 through 38</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">37 </span><span class="lnt">38 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="kr">record</span><span class="w"> </span>IsSemigroup<span class="w"> </span><span class="o">{</span>A<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">}</span><span class="w"> </span><span class="o">(</span>_∙_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">field</span><span class="w"> </span>isAssociative<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>a₁<span class="w"> </span>a₂<span class="w"> </span>a₃<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>a₁<span class="w"> </span>∙<span class="w"> </span><span class="o">(</span>a₂<span class="w"> </span>∙<span class="w"> </span>a₃<span class="o">)</span><span class="w"> </span>≡<span class="w"> </span><span class="o">(</span>a₁<span class="w"> </span>∙<span class="w"> </span>a₂<span class="o">)</span><span class="w"> </span>∙<span class="w"> </span>a₃</span></span></code></pre></td></tr></table> </div> </div> </div> <p>We can define an <code>IsMonoid</code> similarly:</p> <div class="highlight-group" data-base-path="" data-file-path="agda-issomething/example.agda" data-first-line="40" data-last-line="47" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/agda-issomething/example.agda#L40-L47">example.agda</a>, lines 40 through 47</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="kr">record</span><span class="w"> </span>IsMonoid<span class="w"> </span><span class="o">{</span>A<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">}</span><span class="w"> </span><span class="o">(</span>zero<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="o">(</span>_∙_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">field</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">isSemigroup</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsSemigroup<span class="w"> </span>_∙_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">isIdentityLeft</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>a<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>zero<span class="w"> </span>∙<span class="w"> </span>a<span class="w"> </span>≡<span class="w"> </span>a<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">isIdentityRight</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>a<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>a<span class="w"> </span>∙<span class="w"> </span>zero<span class="w"> </span>≡<span class="w"> </span>a<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">open</span><span class="w"> </span>IsSemigroup<span class="w"> </span>isSemigroup<span class="w"> </span>public</span></span></code></pre></td></tr></table> </div> </div> </div> <p>We want to make an &ldquo;is&rdquo; version for each algebraic property; this way, if we want to use &ldquo;monoid&rdquo; as part of some other structure, we can pass it the required binary operation the same way we passed it to <code>IsSemigroup</code>. Finally, the contrived motivating example from above becomes:</p> <div class="highlight-group" data-base-path="" data-file-path="agda-issomething/example.agda" data-first-line="49" data-last-line="55" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/agda-issomething/example.agda#L49-L55">example.agda</a>, lines 49 through 55</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="kr">record</span><span class="w"> </span>IsContrivedExample<span class="w"> </span><span class="o">{</span>A<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">}</span><span class="w"> </span><span class="o">(</span>zero<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="o">(</span>_∙_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">field</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">-- first property</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">monoid</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsMonoid<span class="w"> </span>zero<span class="w"> </span>_∙_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">-- second property; Semigroup is a stand-in.</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">semigroup</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsSemigroup<span class="w"> </span>_∙_</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Since we passed the same operation to both <code>IsMonoid</code> and <code>IsSemigroup</code>, we know that we really do have a <em>single</em> operation with <em>both</em> properties, no strange equality witnesses or anything necessary.</p> <p>Of course, these new records are not quite equivalent to our original ones. They need to be passed a binary operation; a &ldquo;complete&rdquo; package should include the binary operation <em>in addition</em> to its properties encoded as <code>IsSemigroup</code> or <code>IsMonoid</code>. Such a complete package would be more-or-less equivalent to our original <code>Semigroup</code> and <code>Monoid</code> instances. Here&rsquo;s what that would look like:</p> <div class="highlight-group" data-base-path="" data-file-path="agda-issomething/example.agda" data-first-line="57" data-last-line="66" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/agda-issomething/example.agda#L57-L66">example.agda</a>, lines 57 through 66</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span><span class="lnt">65 </span><span class="lnt">66 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="w"> </span><span class="kr">record</span><span class="w"> </span>Semigroup<span class="w"> </span><span class="o">(</span>A<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">)</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">field</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">_∙_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">isSemigroup</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsSemigroup<span class="w"> </span>_∙_<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">record</span><span class="w"> </span>Monoid<span class="w"> </span><span class="o">(</span>A<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">)</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">field</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">zero</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">_∙_</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">isMonoid</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsMonoid<span class="w"> </span>zero<span class="w"> </span>_∙_</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Agda calls records that include both the operation and its <code>IsSomething</code> record <em>bundles</em> (see <a href="https://agda.github.io/agda-stdlib/Algebra.Bundles.html"class="external-link"><code>Algebra.Bundles</code><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, for example). Notice that the bundles don&rsquo;t rely on other bundles to define properties; that would lead right back to the &ldquo;bottom-up&rdquo; data flow in which a parent record has to access the operations and values stored in its fields. Hower, bundles do sometimes &ldquo;contain&rdquo; (via a definition, not a field) smaller bundles, in case, for example, you need <em>only</em> a semigroup, but you have a monoid.</p> <a href="#bonus-using-parameterized-modules-to-avoid-repetitive-arguments"> <h3 id="bonus-using-parameterized-modules-to-avoid-repetitive-arguments">Bonus: Using Parameterized Modules to Avoid Repetitive Arguments</h3> </a> <p>One annoying thing about our definitions above is that we had to accept our binary operation, and sometimes the zero element, as an argument to each one, and to thread it through to all the fields that require it. Agda has a nice mechanism to help alleviate some of this repetition: <a href="https://agda.readthedocs.io/en/latest/language/module-system.html#parameterised-modules"class="external-link">parameterized modules<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. We can define a <em>whole module</em> that accepts the binary operation as an argument; it will be implicitly passed as an argument to all of the definitions within. Thus, our entire <code>IsMonoid</code>, <code>IsSemigroup</code>, and <code>IsContrivedExample</code> code could look like this:</p> <div class="highlight-group" data-base-path="" data-file-path="agda-issomething/example.agda" data-first-line="68" data-last-line="87" data-agda-block> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/agda-issomething/example.agda#L68-L87">example.agda</a>, lines 68 through 87</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">68 </span><span class="lnt">69 </span><span class="lnt">70 </span><span class="lnt">71 </span><span class="lnt">72 </span><span class="lnt">73 </span><span class="lnt">74 </span><span class="lnt">75 </span><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span><span class="lnt">79 </span><span class="lnt">80 </span><span class="lnt">81 </span><span class="lnt">82 </span><span class="lnt">83 </span><span class="lnt">84 </span><span class="lnt">85 </span><span class="lnt">86 </span><span class="lnt">87 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Agda" data-lang="Agda"><span class="line"><span class="cl"><span class="kr">module</span><span class="w"> </span><span class="n">ThirdAttempt</span><span class="w"> </span><span class="o">{</span>A<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="o">}</span><span class="w"> </span><span class="o">(</span>_∙_<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="w"> </span><span class="ow">→</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">record</span><span class="w"> </span>IsSemigroup<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">field</span><span class="w"> </span>isAssociative<span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>a₁<span class="w"> </span>a₂<span class="w"> </span>a₃<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>a₁<span class="w"> </span>∙<span class="w"> </span><span class="o">(</span>a₂<span class="w"> </span>∙<span class="w"> </span>a₃<span class="o">)</span><span class="w"> </span>≡<span class="w"> </span><span class="o">(</span>a₁<span class="w"> </span>∙<span class="w"> </span>a₂<span class="o">)</span><span class="w"> </span>∙<span class="w"> </span>a₃<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">record</span><span class="w"> </span>IsMonoid<span class="w"> </span><span class="o">(</span>zero<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">field</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">isSemigroup</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsSemigroup<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">isIdentityLeft</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>a<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>zero<span class="w"> </span>∙<span class="w"> </span>a<span class="w"> </span>≡<span class="w"> </span>a<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">isIdentityRight</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="ow">∀</span><span class="w"> </span><span class="o">(</span>a<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">→</span><span class="w"> </span>a<span class="w"> </span>∙<span class="w"> </span>zero<span class="w"> </span>≡<span class="w"> </span>a<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">open</span><span class="w"> </span>IsSemigroup<span class="w"> </span>isSemigroup<span class="w"> </span>public<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">record</span><span class="w"> </span>IsContrivedExample<span class="w"> </span><span class="o">(</span>zero<span class="w"> </span><span class="ow">:</span><span class="w"> </span>A<span class="o">)</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span><span class="kt">Set</span><span class="w"> </span>a<span class="w"> </span><span class="kr">where</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kr">field</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">-- first property</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">monoid</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsMonoid<span class="w"> </span>zero<span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">-- second property; Semigroup is a stand-in.</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nf">semigroup</span><span class="w"> </span><span class="ow">:</span><span class="w"> </span>IsSemigroup</span></span></code></pre></td></tr></table> </div> </div> </div> <p>The more <code>IsSomething</code> records you declare, the more effective this trick becomes.</p> <a href="#conclusion"> <h3 id="conclusion">Conclusion</h3> </a> <p>That&rsquo;s all I have! The pattern I&rsquo;ve described shows up all over the Agda standard library; the example that made me come across it was the <a href="https://agda.github.io/agda-stdlib/Algebra.Structures.html"class="external-link"><code>Algebra.Structures</code> module<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. I hope you find it useful.</p> <p>Happy (dependently typed) programming!</p> Proving My Compiler Code Incorrect With Alloy https://danilafe.com/blog/dyno_alloy/ Sun, 04 Jun 2023 21:56:00 -0700 https://danilafe.com/blog/dyno_alloy/ <p><strong>Disclaimer:</strong> though &ldquo;my compiler code&rdquo; makes for a fun title, I do not claim exclusive credit for any of the C++ code in the Chapel compiler that I mention in this post. The code is &ldquo;mine&rdquo; in the sense that I was debugging changes I was making, and perhaps also in the sense that I was working with it.</p> <p>I work as a compiler developer on the <a href="https://chapel-lang.org"class="external-link">Chapel<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> team. Recently, while thinking through a change to some code, I caught myself making wishes: &ldquo;if only I could have a computer check this property for me&rdquo;. Having at some point seen <a href="https://www.hillelwayne.com/post/alloy6/"class="external-link">Hillel Wayne&rsquo;s post<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> about the release of <a href="https://alloytools.org/"class="external-link">Alloy 6<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, I thought I&rsquo;d give it a go. In this post, I describe my experience applying Alloy to a real part of the Chapel compiler. I&rsquo;d never touched Alloy before this, so be warned: this is what I came up with on my own attempt, and I may well be doing something fairly silly by the standards of &ldquo;real&rdquo; Alloy users.</p> <a href="#the-problem-at-hand"> <h3 id="the-problem-at-hand">The Problem at Hand</h3> </a> <p>One of the things that a language like Chapel has to do is called <em>resolution</em>, which is the process of figuring out what each identifier, like <code>x</code>, refers to, and what its type is. Even the first part of that is pretty complicated, what with public and private variables, methods (which can be declared outside of their receiver type in Chapel), and more&hellip;</p> <p>Scope resolution in Chapel is further complicated by the fact that the same scope might need to be searched multiple times, in different contexts. Let me start with a few examples to illustrate what I mean. Here&rsquo;s the first program:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Chapel" data-lang="Chapel"><span class="line"><span class="cl"><span class="k">module</span><span class="w"> </span><span class="nc">M</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">class</span><span class="w"> </span><span class="nc">C</span><span class="w"> </span><span class="p">{}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// A regular procedure (not a method) </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="k">proc</span><span class="w"> </span><span class="nf">foo</span><span class="p">()</span><span class="w"> </span><span class="p">{}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// A method on C. </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="k">proc</span><span class="w"> </span><span class="nf">C.foo</span><span class="p">()</span><span class="w"> </span><span class="p">{}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Another method on C. </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="k">proc</span><span class="w"> </span><span class="nf">C.doSomething</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nx">foo</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><p>If you don&rsquo;t know Chapel (and you probably don&rsquo;t!) this program already merits a fair bit of explanation. I&rsquo;ve collapsed it for the sake of visual clarity; feel free to expand the below section to learn more about the language features used in the program above.</p> <details> <summary>Click here for an explanation of the above code snippet</summary><p>A <em>module</em> in Chapel (declared via a <code>module</code> keyword) is just a collection of definitions. Such definitions could include variables, methods, classes and more. Putting them in a module helps group them.</p> <p>A <em>class</em> in Chapel (declared via a <code>class</code> keyword) is much like a class in object oriented languages. The class <code>C</code> that we&rsquo;re creating on line 2 doesn&rsquo;t have any fields or methods &ndash; at least not yet. We will, however, add methods to it using Chapel&rsquo;s <em>secondary method</em> mechanism (more on that in a moment).</p> <p>The <code>proc</code> keyword is used to create functions and methods. On line 5, we create a procedure called <code>foo</code> that does nothing. On line 8, because we write <code>C.foo</code> instead of just <code>foo</code>, we&rsquo;re actually creating a method on the class <code>C</code> we declared earlier. This method does nothing too. Notice that although declaring classes in Chapel works about the same as declaring classes in other languages, it&rsquo;s fairly unusual to be able to declare a class method (like the <code>foo</code> on line 8 in this case) outside of the <code>class C { ... }</code> section of code. This is part of the reason that Chapel method resolution is complicated (methods can be declared anywhere!). The only other language that I know of that supports this feature is Kotlin with its <a href="https://kotlinlang.org/docs/extensions.html"class="external-link">extension function mechanism<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, but it&rsquo;s possible that other languages have similar functionality.</p> </details> <p>The interesting part of the snippet is the body of the <code>doSomething</code> method. It has a call to <code>foo</code>: but which <code>foo</code> is it referring to? There are two: the regular procedure (non-method) <code>foo</code>, declared on line 5, and the method <code>C.foo</code> declared on line 8. In Chapel, the rules dictate that when such a situation arises, and a fitting method is found, the method is preferred to the non-method. In the rewritten version of the Chapel compiler, titled Dyno, this disambiguation is achieved by first searching the scopes visible from the class <code>C</code> for methods only. In this particular example, the two scopes searched will be:</p> <ol> <li>The inside of class <code>C</code>. The class itself doesn&rsquo;t define any methods, so nothing is found.</li> <li>The module in which <code>C</code> is defined (<code>M</code> in this case). This module does have a method, the one on line 8, so that one is returned.</li> </ol> <p>Only if methods are not found are non-methods considered. In this situation, the search order will be as follows:</p> <ol> <li>The inside of <code>C.doSomething</code> will be searched. <code>doSomething</code> doesn&rsquo;t declare anything, so the search will come up empty.</li> <li>The module in which <code>C.doSomething</code> is defined (<code>M</code> again) will be searched. This time, both methods and non-methods will be considered. Since we&rsquo;re considering a hypothetical situation in which the method <code>C.foo</code> isn&rsquo;t there (otherwise it would&rsquo;ve been found earlier), the only thing that will be found will be the non-method <code>foo</code>.</li> </ol> <p>Notice that we&rsquo;ve already had to search the module <code>M</code> twice, looking for different things each time. First, we were looking for only methods, but later, we were looking for anything. However, this isn&rsquo;t as complicated as things can get. The simplifying aspect of this program is that both <code>doSomething</code> and <code>C</code> are defined inside the module <code>M</code>, and therefore have access to its private methods and procedures. If we extracted <code>C.doSomething</code> into its own separate module, the program would look like this.</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Chapel" data-lang="Chapel"><span class="line"><span class="cl"><span class="k">module</span><span class="w"> </span><span class="nc">M1</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">class</span><span class="w"> </span><span class="nc">C</span><span class="w"> </span><span class="p">{}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// A regular procedure (not a method) </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="k">proc</span><span class="w"> </span><span class="nf">foo</span><span class="p">()</span><span class="w"> </span><span class="p">{}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// A method on C. </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="k">proc</span><span class="w"> </span><span class="nf">C.foo</span><span class="p">()</span><span class="w"> </span><span class="p">{}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">module</span><span class="w"> </span><span class="nc">M2</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">use</span><span class="w"> </span><span class="nx">super</span><span class="p">.</span><span class="nx">M1</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Another method on C. </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="k">proc</span><span class="w"> </span><span class="nf">C.doSomething</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nx">foo</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><p>Since <code>doSomething</code> is now in another module, it can&rsquo;t just access the <code>foo</code>s from <code>M1</code> willy-nilly. There are a few ways to get the things that were declared in another module out and make use of them. I opted for a <code>use</code> statement, which, in its simplest form, just brings all the declarations inside the <code>use</code>d module into the current scope. Thus, the <code>use</code> statement on line 11 would bring all things declared in <code>M1</code> into the scope inside <code>M2</code>. There&rsquo;s a catch, though. Since <code>M2</code> is not declared inside <code>M1</code>, a <code>use</code> statement will not be able to bring in <em>private</em> symbols from <code>M1</code> (they&rsquo;re private for a reason!). So, this time, when searching the scope for <code>M1</code>, we will have to search only for public symbols. That&rsquo;s another, different way of searching <code>M1</code>. So far, we&rsquo;ve seen three:</p> <ul> <li>Search <code>M1</code> for any symbol.</li> <li>Search <code>M1</code> for methods only.</li> <li>Search <code>M1</code> for public symbols only.</li> </ul> <p>Dyno introduces more ways to search within a scope, including combinations of search types, such as looking only for public methods. To represent the various search configurations, the Dyno team came up with using a bitfield of <em>flags</em>, each of which indicated a necessary condition for a symbol to be returned. A bitfield with flags set for two properties (like &ldquo;public&rdquo; and &ldquo;method&rdquo;) requires that both such properties be found on each symbol that&rsquo;s returned from a scope. This led to C++ code along the lines of:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">allPublicSymbols</span> <span class="o">=</span> <span class="n">Flags</span><span class="o">::</span><span class="n">PUBLIC</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">allPublicMethods</span> <span class="o">=</span> <span class="n">Flags</span><span class="o">::</span><span class="n">PUBLIC</span> <span class="o">|</span> <span class="n">Flags</span><span class="o">::</span><span class="n">METHOD</span><span class="p">;</span> </span></span></code></pre></div><p>It also turned out convenient to add negative versions of each flag (<code>NOT_PUBLIC</code> for private symbols, <code>NOT_METHOD</code> for regular old procedures and other definitions, and so on. So, some other possible flag combinations include:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">allNonMethods</span> <span class="o">=</span> <span class="n">Flags</span><span class="o">::</span><span class="n">NOT_METHOD</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="k">auto</span> <span class="n">privateMethods</span> <span class="o">=</span> <span class="n">Flags</span><span class="o">::</span><span class="n">NOT_PUBLIC</span> <span class="o">|</span> <span class="n">Flags</span><span class="o">::</span><span class="n">METHOD</span><span class="p">;</span> </span></span></code></pre></div><p>Given these flags, there are some situations in which checking a scope a second time is redundant, in that it is guaranteed to find no additional symbols. For instance, if you search a scope for all public symbols, and then subsequently search for all public methods, you will only find duplicates &ndash; after all, all public methods are public symbols. Most generally, this occurs when a second search has all the flags from a previous search, and maybe more. In math lingo, if the set of flags checked the first time is a subset of the set of flags checked the second time, it&rsquo;s guaranteed not to find anything new.</p> <p>In Dyno, we like to avoid additional work when we can. To do so, we track which scopes have already been searched, and avoid searching them again. Since what comes up from a search depends on the flags, we store the flags alongside the scopes we&rsquo;ve checked. <strong>If we find that the previously-checked bitfield is a subset of the current bitset, we just skip the search</strong>.</p> <p>But then, what if it <em>isn&rsquo;t</em> a subset? Another concern here is avoiding duplicate results (it&rsquo;s easier to check for duplicate definitions if you know a symbol is only returned from a search once). So, another feature of Dyno&rsquo;s scope search is an additional bitfield of what to <em>exclude</em>, which we set to be the previous search&rsquo;s filter. So if the first search looked for symbols matching description \(A\), and the second search is supposed to look for symbols matching description \(B\), <strong>then really we do a search for \(A \land \lnot B\) (that is, \(A\) and not \(B\))</strong>.</p> <p class="dialog"> <span class="message side-question"> <span class="message-sender"><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#moon"/> </svg></span> <span class="message-text"> Hold on, why do you need a whole another bitfield? There are already negated versions of each flag available. Can't you just add those to the filter? </span> </span> <span class="message side-answer"> <span class="message-sender"><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#sun"/> </svg></span> <span class="message-text"> Good question. The difference is a little bit tricky. If we just negated each flag, we'd turn an expression like \(A \land B\) into \(\lnot A \land \lnot B\). However, according to <a href="https://en.wikipedia.org/wiki/De_Morgan%27s_laws">De Morgan's laws</a>, the proper negation of \(A \land B\) is \(\lnot A \lor \lnot B\) (notice the use of "or" instead of "and"). On the other hand, using an "exclude" bitfield negates the whole conjunction, rather than the individual flags, and so gives us the result we need. </span> </span> </p> <p>One last thing: what happens if there were two previous searches? What we need is to to somehow combine the two filters into one. Taking a cue from a previous example, in which &ldquo;public&rdquo; was followed by &ldquo;public methods&rdquo;, we can observe that since the second search has additional flags, it&rsquo;s more restrictive, and thus guaranteed to not find anything. <strong>So we try to create the least restrictive bitfield possible, by taking an intersection of the flags used.</strong></p> <p>Actually, that last point is not quite correct in every possible case (taking the intersection is not always the right thing to do). However, running the code through our test suite, we did not notice any cases in which it misbehaved. So, noting the potential issue in a comment, we moved on to other things.</p> <p>That is, until I decided that it was time to add another possible flag to the bitfield. At that point, sitting and trying to reason about the possible cases, I realized that it would be much nicer to describe this mathematically, and have a model checker generate outlandish scenarios for me.</p> <a href="#modeling-flags-and-bitsets-in-alloy"> <h3 id="modeling-flags-and-bitsets-in-alloy">Modeling Flags and Bitsets in Alloy</h3> </a> <p>Flags are represented on the C++ side as an <code>enum</code> (with custom indexing so as to make each flag be exactly one bit). I checked, and it looked like Alloy had an <code>enum</code> feature, too! I started off by making an enum of the flags I wanted to play with.</p> <div class="highlight-group" data-base-path="" data-file-path="dyno-alloy/DynoAlloy.als" data-first-line="1" data-last-line="1"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dyno-alloy/DynoAlloy.als#L1-L1">DynoAlloy.als</a>, line 1</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Alloy" data-lang="Alloy"><span class="line"><span class="cl"><span class="kd">enum</span><span class="w"> </span><span class="n">Flag</span><span class="w"> </span><span class="o">{</span><span class="n">Method</span><span class="p">,</span><span class="w"> </span><span class="n">MethodOrField</span><span class="p">,</span><span class="w"> </span><span class="n">Public</span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We haven&rsquo;t seen the <code>MethodOrField</code> flag, but it&rsquo;s an important one. It turns out that it&rsquo;s much more common to look for anything that could be part of a class, rather than just its methods. This flag is itself an &ldquo;or&rdquo; of two properties (something being a method and something being a class field). Note that this is not the same as having two flags, <code>Method</code> and <code>Field</code>, and always including them together (because that would be an &ldquo;and&rdquo;, not an &ldquo;or&rdquo;).</p> <p>Notice also that the list of flags doesn&rsquo;t include the negative versions. Since the negative versions are one-for-one with the positive ones, I instead chose to represent bitfields as simply two sets: one set of &ldquo;positive&rdquo; flags, in which the presence of e.g. <code>Method</code> indicates that the <code>METHOD</code> flag was set, and one set of &ldquo;negative&rdquo; flags, in which the presence of <code>Method</code> indicates that <code>NOT_METHOD</code> was set. This way, I&rsquo;m guaranteed that there&rsquo;s a positive and negative version of each flag, automatically. Here&rsquo;s how I wrote that in Alloy.</p> <div class="highlight-group" data-base-path="" data-file-path="dyno-alloy/DynoAlloy.als" data-first-line="6" data-last-line="9"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dyno-alloy/DynoAlloy.als#L6-L9">DynoAlloy.als</a>, lines 6 through 9</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span><span class="lnt">9 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Alloy" data-lang="Alloy"><span class="line"><span class="cl"><span class="kd">sig</span><span class="w"> </span><span class="n">Bitfield</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">positiveFlags</span><span class="p">:</span><span class="w"> </span><span class="k">set</span><span class="w"> </span><span class="n">Flag</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">negativeFlags</span><span class="p">:</span><span class="w"> </span><span class="k">set</span><span class="w"> </span><span class="n">Flag</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This definition (a <em>signature</em> in Alloy terms) specifies what a bitfield is like, but not any operations on it. My next order of business is to define some common functionality on bitfields. Alloy is all about <a href="https://en.wikipedia.org/wiki/Relation_%28mathematics%29"class="external-link">relations<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> and <a href="https://en.wikipedia.org/wiki/Predicate_%28mathematical_logic%29"class="external-link">predicates<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, so for all of these, I had to effectively write something that <em>checks</em> if some condition holds for some arguments. This might seem abstract; as an example, here&rsquo;s <code>bitfieldEmpty</code>, which checks that a bitfield has no flags set.</p> <div class="highlight-group" data-base-path="" data-file-path="dyno-alloy/DynoAlloy.als" data-first-line="26" data-last-line="28"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dyno-alloy/DynoAlloy.als#L26-L28">DynoAlloy.als</a>, lines 26 through 28</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Alloy" data-lang="Alloy"><span class="line"><span class="cl"><span class="k">pred</span><span class="w"> </span><span class="n">bitfieldEmpty</span><span class="o">[</span><span class="n">b</span><span class="p">:</span><span class="w"> </span><span class="n">Bitfield</span><span class="o">]</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">#</span><span class="n">b</span><span class="o">.</span><span class="n">positiveFlags</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="ow">and</span><span class="w"> </span><span class="o">#</span><span class="n">b</span><span class="o">.</span><span class="n">negativeFlags</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The <code>#</code> operator in Alloy is used to check the size of a set. So, to check if a bitfield is empty, I simply check if there are neither positive nor negative flags. Probably the most unusual aspect of this piece of code is that equality is written as <code>=</code>, as opposed to <code>==</code> like in most common languages. This is because, like I said, Alloy is all about relations and predicates, and not at all about imperative manipulation of data. So, there&rsquo;s no need to reserve <code>=</code> for assignment.</p> <p>The next step from here is a predicate that accepts two arguments, <code>bitfieldEqual</code>. As its name suggests, this predicate accepts two bitfields, and makes sure they have exactly the same flags set.</p> <div class="highlight-group" data-base-path="" data-file-path="dyno-alloy/DynoAlloy.als" data-first-line="30" data-last-line="32"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dyno-alloy/DynoAlloy.als#L30-L32">DynoAlloy.als</a>, lines 30 through 32</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Alloy" data-lang="Alloy"><span class="line"><span class="cl"><span class="k">pred</span><span class="w"> </span><span class="n">bitfieldEqual</span><span class="o">[</span><span class="n">b1</span><span class="p">:</span><span class="w"> </span><span class="n">Bitfield</span><span class="p">,</span><span class="w"> </span><span class="n">b2</span><span class="p">:</span><span class="w"> </span><span class="n">Bitfield</span><span class="o">]</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">b1</span><span class="o">.</span><span class="n">positiveFlags</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">b2</span><span class="o">.</span><span class="n">positiveFlags</span><span class="w"> </span><span class="ow">and</span><span class="w"> </span><span class="n">b1</span><span class="o">.</span><span class="n">negativeFlags</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">b2</span><span class="o">.</span><span class="n">negativeFlags</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>So far, this has been pretty similar to just writing boolean functions in a language like C++. However, the similarity is only superficial. An easy way to see that is to try to determine the <em>intersection</em> of two bitfields &ndash; that&rsquo;s the operation we will be having to model, since the Dyno implementation uses <code>&amp;</code> to combine filter sets. In a language like C++, you might write a function like the following, in which you accept two bitfield arguments and return a new bitfield.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">Bitfield</span> <span class="nf">intersection</span><span class="p">(</span><span class="n">Bitfield</span> <span class="n">b1</span><span class="p">,</span> <span class="n">Bitfield</span> <span class="n">b2</span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">}</span> </span></span></code></pre></div><p>However, in Alloy, you can&rsquo;t create a new bitfield, nor return something from a <code>pred</code> that isn&rsquo;t a boolean. Instead, you describe how the inputs will be related to the output. So, to model a binary function, you end up with a three-parameter predicate: two inputs, and one output. But how <em>does</em> the output of a bitfield intersection connect to the two operands being intersected? Well, its two flag sets will be intersections of the flag sets of the inputs!</p> <div class="highlight-group" data-base-path="" data-file-path="dyno-alloy/DynoAlloy.als" data-first-line="34" data-last-line="37"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dyno-alloy/DynoAlloy.als#L34-L37">DynoAlloy.als</a>, lines 34 through 37</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Alloy" data-lang="Alloy"><span class="line"><span class="cl"><span class="k">pred</span><span class="w"> </span><span class="n">bitfieldIntersection</span><span class="o">[</span><span class="n">b1</span><span class="p">:</span><span class="w"> </span><span class="n">Bitfield</span><span class="p">,</span><span class="w"> </span><span class="n">b2</span><span class="p">:</span><span class="w"> </span><span class="n">Bitfield</span><span class="p">,</span><span class="w"> </span><span class="n">b3</span><span class="p">:</span><span class="w"> </span><span class="n">Bitfield</span><span class="o">]</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">b3</span><span class="o">.</span><span class="n">positiveFlags</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">b1</span><span class="o">.</span><span class="n">positiveFlags</span><span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">b2</span><span class="o">.</span><span class="n">positiveFlags</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">b3</span><span class="o">.</span><span class="n">negativeFlags</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">b1</span><span class="o">.</span><span class="n">negativeFlags</span><span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">b2</span><span class="o">.</span><span class="n">negativeFlags</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Next, let&rsquo;s talk about what flags <em>do</em>. They are used to include and exclude symbols based on certain properties. One property is being a method: a <code>METHOD</code> flag requires this property, whereas a <code>NOT_METHOD</code> flag ensures that a symbol does not have it. Another property is being a public definition: if a symbol isn&rsquo;t public, it&rsquo;ll be ignored by searches with the <code>PUBLIC</code> flag set. Just like a bitfield can have multiple flags, a symbol can have multiple properties (e.g., a public method). Unlike our bitfields, though, we won&rsquo;t be modeling symbols as having both positive and negative properties. That is to say, we won&rsquo;t have a &ldquo;not public&rdquo; property: the absence of the &ldquo;public&rdquo; property will be enough to make something private. Here&rsquo;s the Alloy definition for everything I just said:</p> <div class="highlight-group" data-base-path="" data-file-path="dyno-alloy/DynoAlloy.als" data-first-line="59" data-last-line="63"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dyno-alloy/DynoAlloy.als#L59-L63">DynoAlloy.als</a>, lines 59 through 63</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Alloy" data-lang="Alloy"><span class="line"><span class="cl"><span class="kd">enum</span><span class="w"> </span><span class="n">Property</span><span class="w"> </span><span class="o">{</span><span class="w"> </span><span class="n">PMethod</span><span class="p">,</span><span class="w"> </span><span class="n">PField</span><span class="p">,</span><span class="w"> </span><span class="n">PPublic</span><span class="w"> </span><span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">sig</span><span class="w"> </span><span class="n">Symbol</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">properties</span><span class="p">:</span><span class="w"> </span><span class="k">set</span><span class="w"> </span><span class="n">Property</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Now, we can specify how flags in a bitfield relate to properties on a symbol. We can do so by saying which flags match which properties. The <code>Method</code> flag, for instance, will be satisfied by the <code>PMethod</code> property. The <code>MethodOrField</code> flag is more lenient, and will be satisfied by either <code>PMethod</code> or <code>PField</code>. Here&rsquo;s a predicate <code>flagMatchesProperty</code> that encodes all the flag-property combinations:</p> <div class="highlight-group" data-base-path="" data-file-path="dyno-alloy/DynoAlloy.als" data-first-line="65" data-last-line="69"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dyno-alloy/DynoAlloy.als#L65-L69">DynoAlloy.als</a>, lines 65 through 69</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">65 </span><span class="lnt">66 </span><span class="lnt">67 </span><span class="lnt">68 </span><span class="lnt">69 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Alloy" data-lang="Alloy"><span class="line"><span class="cl"><span class="k">pred</span><span class="w"> </span><span class="n">flagMatchesProperty</span><span class="o">[</span><span class="n">flag</span><span class="p">:</span><span class="w"> </span><span class="n">Flag</span><span class="p">,</span><span class="w"> </span><span class="n">property</span><span class="p">:</span><span class="w"> </span><span class="n">Property</span><span class="o">]</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span><span class="n">flag</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Method</span><span class="w"> </span><span class="ow">and</span><span class="w"> </span><span class="n">property</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PMethod</span><span class="o">)</span><span class="w"> </span><span class="ow">or</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span><span class="n">flag</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">MethodOrField</span><span class="w"> </span><span class="ow">and</span><span class="w"> </span><span class="o">(</span><span class="n">property</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PMethod</span><span class="w"> </span><span class="ow">or</span><span class="w"> </span><span class="n">property</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PField</span><span class="o">))</span><span class="w"> </span><span class="ow">or</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span><span class="n">flag</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Public</span><span class="w"> </span><span class="ow">and</span><span class="w"> </span><span class="n">property</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PPublic</span><span class="o">)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>A bitfield matching a symbol is a little bit more complicated. Said informally, the condition for a bitfield matching a symbol is twofold:</p> <ul> <li>Every single positive flag, like <code>METHOD</code>, must be satisfied by a property on the symbol.</li> <li>None of the negative flags, like <code>NOT_METHOD</code>, must be satisfied by a property on the symbol (that is to say, if <code>Method</code> is in the negative flags set, then the symbol must not have <code>PMethod</code> property). It is more conveniently to formulate this &ndash; equivalently &ndash; as follows: for each negative flag, there must not be a property that satisfies it.</li> </ul> <p>Each of the above two conditions translates quite literally into Alloy:</p> <div class="highlight-group" data-base-path="" data-file-path="dyno-alloy/DynoAlloy.als" data-first-line="71" data-last-line="74"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dyno-alloy/DynoAlloy.als#L71-L74">DynoAlloy.als</a>, lines 71 through 74</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">71 </span><span class="lnt">72 </span><span class="lnt">73 </span><span class="lnt">74 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Alloy" data-lang="Alloy"><span class="line"><span class="cl"><span class="k">pred</span><span class="w"> </span><span class="n">bitfieldMatchesProperties</span><span class="o">[</span><span class="n">bitfield</span><span class="p">:</span><span class="w"> </span><span class="n">Bitfield</span><span class="p">,</span><span class="w"> </span><span class="n">symbol</span><span class="p">:</span><span class="w"> </span><span class="n">Symbol</span><span class="o">]</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">all</span><span class="w"> </span><span class="n">flag</span><span class="p">:</span><span class="w"> </span><span class="n">bitfield</span><span class="o">.</span><span class="n">positiveFlags</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">some</span><span class="w"> </span><span class="n">property</span><span class="p">:</span><span class="w"> </span><span class="n">symbol</span><span class="o">.</span><span class="n">properties</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">flagMatchesProperty</span><span class="o">[</span><span class="n">flag</span><span class="p">,</span><span class="w"> </span><span class="n">property</span><span class="o">]</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">all</span><span class="w"> </span><span class="n">flag</span><span class="p">:</span><span class="w"> </span><span class="n">bitfield</span><span class="o">.</span><span class="n">negativeFlags</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">no</span><span class="w"> </span><span class="n">property</span><span class="p">:</span><span class="w"> </span><span class="n">symbol</span><span class="o">.</span><span class="n">properties</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">flagMatchesProperty</span><span class="o">[</span><span class="n">flag</span><span class="p">,</span><span class="w"> </span><span class="n">property</span><span class="o">]</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We can read line 73 as &ldquo;for each flag in a bitfield&rsquo;s positive flags, there must be some property in the symbol that matches it&rdquo;. Similarly, line 74 can be read out loud as &ldquo;for each flag in the negative flags, no property in the symbol must match it&rdquo;.</p> <p>We&rsquo;ve written a fair bit of Alloy. If you&rsquo;re anything like me, you might be getting a bit twitchy: how do we even check that any of this works? For this, we&rsquo;ll need to run our model. We will give Alloy a claim, and ask it to find a situation in which that claim holds true. The simplest claim is &ldquo;there exists a bitfield&rdquo;.</p> <div class="highlight-group" data-base-path="" data-file-path="dyno-alloy/DynoAlloy.als" data-first-line="76" data-last-line="78"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dyno-alloy/DynoAlloy.als#L76-L78">DynoAlloy.als</a>, lines 76 through 78</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Alloy" data-lang="Alloy"><span class="line"><span class="cl"><span class="n">bitfieldExists</span><span class="p">:</span><span class="w"> </span><span class="k">run</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">some</span><span class="w"> </span><span class="n">Bitfield</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Executing this model yields a pretty interesting bitfield: one in which every single flag is set &ndash; both the positive and negative versions.</p> <figure><img src="https://danilafe.com/blog/dyno_alloy/bitfield_exists.png" alt="Alloy&rsquo;s output satisfying &ldquo;a bit field exists&rdquo;"><figcaption> <p>Alloy&rsquo;s output satisfying &ldquo;a bit field exists&rdquo;</p> </figcaption> </figure> <p>That&rsquo;s a little bit ridiculous: this bitfield will never match anything! You can&rsquo;t be and not be a method at the same time, for instance. For for a more interesting example, let&rsquo;s ask for a bitfield that matches some symbol.</p> <div class="highlight-group" data-base-path="" data-file-path="dyno-alloy/DynoAlloy.als" data-first-line="80" data-last-line="82"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dyno-alloy/DynoAlloy.als#L80-L82">DynoAlloy.als</a>, lines 80 through 82</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">80 </span><span class="lnt">81 </span><span class="lnt">82 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Alloy" data-lang="Alloy"><span class="line"><span class="cl"><span class="n">matchingBitfieldExists</span><span class="p">:</span><span class="w"> </span><span class="k">run</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">some</span><span class="w"> </span><span class="n">bitfield</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="n">Bitfield</span><span class="p">,</span><span class="w"> </span><span class="n">symbol</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="n">Symbol</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">bitfieldMatchesProperties</span><span class="o">[</span><span class="n">bitfield</span><span class="p">,</span><span class="w"> </span><span class="n">symbol</span><span class="o">]</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The output here is pretty interesting too. Alloy finds a symbol and a bitfield that matches it, but they&rsquo;re both empty. In effect, it said: &ldquo;if you don&rsquo;t specify any filters, any private definition will match&rdquo;. Fair enough, of course, but a curious departure from the previous maximalist &ldquo;put in all the flags!&rdquo; approach.</p> <figure><img src="https://danilafe.com/blog/dyno_alloy/matching_bitfield_exists.png" alt="Alloy&rsquo;s output satisfying &ldquo;a bit field that matches a symbol exists&rdquo;"><figcaption> <p>Alloy&rsquo;s output satisfying &ldquo;a bit field that matches a symbol exists&rdquo;</p> </figcaption> </figure> <p>Let&rsquo;s try nudge it towards a more interesting case. I&rsquo;m going to ask for a filter with one positive and one negative flag, and a symbol with two properties.</p> <div class="highlight-group" data-base-path="" data-file-path="dyno-alloy/DynoAlloy.als" data-first-line="84" data-last-line="91"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dyno-alloy/DynoAlloy.als#L84-L91">DynoAlloy.als</a>, lines 84 through 91</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">84 </span><span class="lnt">85 </span><span class="lnt">86 </span><span class="lnt">87 </span><span class="lnt">88 </span><span class="lnt">89 </span><span class="lnt">90 </span><span class="lnt">91 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Alloy" data-lang="Alloy"><span class="line"><span class="cl"><span class="n">matchingBitfieldExists2</span><span class="p">:</span><span class="w"> </span><span class="k">run</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">some</span><span class="w"> </span><span class="n">bitfield</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="n">Bitfield</span><span class="p">,</span><span class="w"> </span><span class="n">symbol</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="n">Symbol</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">#</span><span class="n">bitfield</span><span class="o">.</span><span class="n">positiveFlags</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">#</span><span class="n">bitfield</span><span class="o">.</span><span class="n">negativeFlags</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">#</span><span class="n">symbol</span><span class="o">.</span><span class="n">properties</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">bitfieldMatchesProperties</span><span class="o">[</span><span class="n">bitfield</span><span class="p">,</span><span class="w"> </span><span class="n">symbol</span><span class="o">]</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The results are more interesting this time: we get a filter for private methods, and a private symbol that was&hellip; both a field and a method?</p> <figure><img src="https://danilafe.com/blog/dyno_alloy/matching_bitfield_exists_2.png" alt="Alloy&rsquo;s spiced up output satisfying &ldquo;a bit field that matches a symbol exists&rdquo;"><figcaption> <p>Alloy&rsquo;s spiced up output satisfying &ldquo;a bit field that matches a symbol exists&rdquo;</p> </figcaption> </figure> <p>We never told Alloy that a symbol can&rsquo;t be both a field and a method. It had no idea what the flags meant, just that they exist. To let Alloy know what we do &ndash; that the two properties are incompatible &ndash; we can use a <em>fact</em>. To me, the most natural way of phrasing this is &ldquo;there is never a symbol that has both the method and field properties&rdquo;. Alas, Alloy doesn&rsquo;t have a <code>never</code> keyword; it only has <code>always</code>. So I opt instead for an alternative formulation: &ldquo;there are always zero symbols that are both methods and fields&rdquo;. In Alloy, the claim looks like this:</p> <div class="highlight-group" data-base-path="" data-file-path="dyno-alloy/DynoAlloy.als" data-first-line="93" data-last-line="98"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dyno-alloy/DynoAlloy.als#L93-L98">DynoAlloy.als</a>, lines 93 through 98</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">93 </span><span class="lnt">94 </span><span class="lnt">95 </span><span class="lnt">96 </span><span class="lnt">97 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Alloy" data-lang="Alloy"><span class="line"><span class="cl"><span class="k">fact</span><span class="w"> </span><span class="s">&#34;method and field are incompatible&#34;</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">always</span><span class="w"> </span><span class="k">no</span><span class="w"> </span><span class="n">symbol</span><span class="p">:</span><span class="w"> </span><span class="n">Symbol</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">PMethod</span><span class="w"> </span><span class="ow">in</span><span class="w"> </span><span class="n">symbol</span><span class="o">.</span><span class="n">properties</span><span class="w"> </span><span class="ow">and</span><span class="w"> </span><span class="n">PField</span><span class="w"> </span><span class="ow">in</span><span class="w"> </span><span class="n">symbol</span><span class="o">.</span><span class="n">properties</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">}</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Re-running the example program with this fact, Alloy spits out a filter for public non-method symbols, and a symbol that&rsquo;s a public field. Public fields also aren&rsquo;t a thing in Chapel (all fields in a class are publicly readable in the current version of the language). Perhaps it&rsquo;s time for another fact.</p> <div class="highlight-group" data-base-path="" data-file-path="dyno-alloy/DynoAlloy.als" data-first-line="99" data-last-line="103"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dyno-alloy/DynoAlloy.als#L99-L103">DynoAlloy.als</a>, lines 99 through 103</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 99 </span><span class="lnt">100 </span><span class="lnt">101 </span><span class="lnt">102 </span><span class="lnt">103 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Alloy" data-lang="Alloy"><span class="line"><span class="cl"><span class="k">fact</span><span class="w"> </span><span class="s">&#34;public and field are incompatible&#34;</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">always</span><span class="w"> </span><span class="k">no</span><span class="w"> </span><span class="n">symbol</span><span class="p">:</span><span class="w"> </span><span class="n">Symbol</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">PPublic</span><span class="w"> </span><span class="ow">in</span><span class="w"> </span><span class="n">symbol</span><span class="o">.</span><span class="n">properties</span><span class="w"> </span><span class="ow">and</span><span class="w"> </span><span class="n">PField</span><span class="w"> </span><span class="ow">in</span><span class="w"> </span><span class="n">symbol</span><span class="o">.</span><span class="n">properties</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>But now, Alloy fails to come up with anything at all. That makes sense: by restricting the search to a symbol with two properties, and making <code>PField</code> incompatible with the other two possible properties, we&rsquo;ve guaranteed that our symbol would be a public method. But then, we also required a negative flag in the filter; however, all the flags in the list match a public method, so making any of them negative would guarantee that our symbol would not be found. Let&rsquo;s change the example up a bit to only ask for positive flags.</p> <div class="highlight-group" data-base-path="" data-file-path="dyno-alloy/DynoAlloy.als" data-first-line="105" data-last-line="111"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dyno-alloy/DynoAlloy.als#L105-L111">DynoAlloy.als</a>, lines 105 through 111</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">105 </span><span class="lnt">106 </span><span class="lnt">107 </span><span class="lnt">108 </span><span class="lnt">109 </span><span class="lnt">110 </span><span class="lnt">111 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Alloy" data-lang="Alloy"><span class="line"><span class="cl"><span class="n">matchingBitfieldExists3</span><span class="p">:</span><span class="w"> </span><span class="k">run</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">some</span><span class="w"> </span><span class="n">bitfield</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="n">Bitfield</span><span class="p">,</span><span class="w"> </span><span class="n">symbol</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="n">Symbol</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">#</span><span class="n">bitfield</span><span class="o">.</span><span class="n">positiveFlags</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">#</span><span class="n">symbol</span><span class="o">.</span><span class="n">properties</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">bitfieldMatchesProperties</span><span class="o">[</span><span class="n">bitfield</span><span class="p">,</span><span class="w"> </span><span class="n">symbol</span><span class="o">]</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This time, Alloy gives us a symbol that&rsquo;s a public method, and a filter that only looks for public methods. Fair enough.</p> <figure><img src="https://danilafe.com/blog/dyno_alloy/matching_bitfield_exists_3.png" alt="Alloy&rsquo;s spiced up output satisfying &ldquo;a bit field that matches a symbol exists&rdquo;"><figcaption> <p>Alloy&rsquo;s spiced up output satisfying &ldquo;a bit field that matches a symbol exists&rdquo;</p> </figcaption> </figure> <a href="#exploring-possible-search-configurations"> <h3 id="exploring-possible-search-configurations">Exploring Possible Search Configurations</h3> </a> <p>So now we have a descriptioin of filters and symbols in scopes. The next thing on the itinerary is modeling how the filters (include and exclude) are configured during scope search in Dyno. For this, let&rsquo;s take a look at the C++ code in Dyno.</p> <p>I&rsquo;ll be using the branch that I was working on at the time of trying to apply Alloy. First, here&rsquo;s the code in C++ that defines the various flags I&rsquo;d be working with (though I&rsquo;ve omitted flags that are not currently used in the implementation).</p> <div class="highlight-group" data-base-path="%!s(<nil>)" data-file-path="frontend/include/chpl/resolution/scope-types.h"> <div class="highlight-label">From <a href="https://github.com/chapel-lang/chapel/blob/390187df504f58cbf721a87d5632cdf7ea37563f/frontend/include/chpl/resolution/scope-types.h#L45">scope-types.h</a>, around line 45</div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="k">enum</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="cm">/** Public */</span> </span></span><span class="line"><span class="cl"> <span class="n">PUBLIC</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="cm">/** Not public (aka private) */</span> </span></span><span class="line"><span class="cl"> <span class="n">NOT_PUBLIC</span> <span class="o">=</span> <span class="mi">2</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="cm">/** A method or field declaration */</span> </span></span><span class="line"><span class="cl"> <span class="n">METHOD_FIELD</span> <span class="o">=</span> <span class="mi">4</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="cm">/** Something other than (a method or field declaration) */</span> </span></span><span class="line"><span class="cl"> <span class="n">NOT_METHOD_FIELD</span> <span class="o">=</span> <span class="mi">8</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="cm">/** A method declaration */</span> </span></span><span class="line"><span class="cl"> <span class="n">METHOD</span> <span class="o">=</span> <span class="mi">64</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="cm">/** Something other than a method declaration */</span> </span></span><span class="line"><span class="cl"> <span class="n">NOT_METHOD</span> <span class="o">=</span> <span class="mi">128</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span></span></span></code></pre></div> </div> <p>These are the flags that we model using a <code>Bitset</code>: <code>PUBLIC</code>, <code>METHOD_FIELD</code>, and <code>METHOD</code> are modeled using <code>positiveFlags</code>, and <code>NOT_PUBLIC</code>, <code>NOT_METHOD_FIELD</code>, and <code>NOT_METHOD</code> are modeled using <code>negativeFlags</code>. There are a lot of flags here, and it&rsquo;s not hard to imagine that <em>some</em> combination of these flags will cause problems in our system (particularly when we <em>know</em> it&rsquo;s an approximation). However, the flags aren&rsquo;t used arbitrarily; in fact, it wasn&rsquo;t too hard to track down the most important place in the code where bitsets are built.</p> <div class="highlight-group" data-base-path="%!s(<nil>)" data-file-path="frontend/lib/resolution/scope-queries.cpp"> <div class="highlight-label">From <a href="https://github.com/chapel-lang/chapel/blob/390187df504f58cbf721a87d5632cdf7ea37563f/frontend/lib/resolution/scope-queries.cpp#L914">scope-queries.cpp</a>, around line 914</div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="n">IdAndFlags</span><span class="o">::</span><span class="n">Flags</span> <span class="n">curFilter</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="cm">/* ... some unrelated code ... */</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">skipPrivateVisibilities</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">curFilter</span> <span class="o">|=</span> <span class="n">IdAndFlags</span><span class="o">::</span><span class="n">PUBLIC</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">onlyMethodsFields</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">curFilter</span> <span class="o">|=</span> <span class="n">IdAndFlags</span><span class="o">::</span><span class="n">METHOD_FIELD</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="nf">if</span> <span class="p">(</span><span class="o">!</span><span class="n">includeMethods</span> <span class="o">&amp;&amp;</span> <span class="n">receiverScopes</span><span class="p">.</span><span class="n">empty</span><span class="p">())</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">curFilter</span> <span class="o">|=</span> <span class="n">IdAndFlags</span><span class="o">::</span><span class="n">NOT_METHOD</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span></span></span></code></pre></div> </div> <p>The above code converts the current search parameters into <code>Bitfield</code> flags. For instance, if a <code>use</code> statement is being processed that doesn&rsquo;t have access to private fields, <code>skipPrivateVisibilities</code> will be set. On the other hand, if the calling code didn&rsquo;t explicitly ask for methods, and if there&rsquo;s no method receiver, then the last condition will be true. These various conditions are converted into bits and applied to <code>curFilter</code>. Then, <code>curFilter</code> is used for looking up symbols in a scope.</p> <p>It&rsquo;s not too hard to model this by just looking at the code, and enumerating the possibilities. The first <code>if</code> statement can either be true or false, and then the subsequent <code>if</code>-<code>else</code> chain creates three possibilities in each case: either <code>METHOD_FIELD</code> is set, or <code>NOT_METHOD</code>, or nothing.</p> <p>However, I envisioned this condition to possibly grow in complexity as more search configurations became necessary (in that, the <code>NOT_METHOD</code> option was an addition in my new branch). I therefore chose to model the possible <code>Bitfield</code> values more faithfully, by mimicking the imperative C++ code.</p> <p class="dialog"> <span class="message side-question"> <span class="message-sender"><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#moon"/> </svg></span> <span class="message-text"> Wait, something sounds off. Just earlier, you said Alloy "is not at all about imperative manipulation of data". But now, we're going to mimic plain imperative C++ code? </span> </span> <span class="message side-answer"> <span class="message-sender"><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#sun"/> </svg></span> <span class="message-text"> Alloy the programming language is still not imperative. However, we can <em>model</em> imperative behavior in Alloy. The way I see it, doing so requires us to venture a tiny bit into the realm of <em>semantics</em> for programming languages, in particular for imperative languages. This "venture" is very minimal though, and you really don't need to know much about semantics to understand it. </span> </span> <span class="message side-question"> <span class="message-sender"><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#moon"/> </svg></span> <span class="message-text"> Alright. How does one model imperative behavior in Alloy? </span> </span> <span class="message side-answer"> <span class="message-sender"><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#sun"/> </svg></span> <span class="message-text"> On to that next. </span> </span> </p> <p>The essential piece of insight to modeling an imperative language, though it sounds a little bit tautological, is that <em>statements are all about manipulating state</em>. For example, state could be the value of a variable. If you start with the variable <code>x</code> storing the number <code>6</code>, and then execute the statement <code>x = x * 7</code>, the final value of <code>x</code> will be <code>42</code>. Thus, state has changed. To put this in terms Alloy would understand &ndash; relations and sets &ndash; a statement connects (relates) states before it&rsquo;s executed to states after it&rsquo;s executed. In our particular example, the connection would between the state <code>x = 6</code> and the state <code>x = 42</code>. In the case of adding the <code>PUBLIC</code> to <code>curFilter</code>, as on line 917 in the above code block, we could state the relationship as follows:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Alloy" data-lang="Alloy"><span class="line"><span class="cl"><span class="n">addBitfieldFlag</span><span class="o">[</span><span class="n">bitfieldBefore</span><span class="p">,</span><span class="w"> </span><span class="n">bitfieldAfter</span><span class="p">,</span><span class="w"> </span><span class="n">Public</span><span class="o">]</span><span class="w"> </span></span></span></code></pre></div><p>The above code states that <code>bitfieldAfter</code> (the state <em>after</em> line 917) is the same <code>Bitfield</code> as <code>bitfieldBefore</code> (the state <em>before</em> line 917), except that the <code>Public</code> flag has been added to it.</p> <p>Things are a little more complicated when it comes to modeling the whole <code>if</code>-statement on line 916. If we wanted to be very precise, we&rsquo;d need to encode the other variables (such as <code>skipPrivateVisibilities</code>), how they&rsquo;re set, and what values are possible. However, for the sake of keeping the scope of this model manageable for the time being, I&rsquo;m content to do something simpler &ndash; that is, acknowledge that the code on line 917 may or may not run. If it does run, our previous <code>addBitfieldFlag</code> will be the correct restriction on the before and after states. However, if it doesn&rsquo;t, the state shouldn&rsquo;t change at all. Therefore, we can model lines 916 through 918 as follows (notice the <code>or</code>):</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Alloy" data-lang="Alloy"><span class="line"><span class="cl"><span class="n">addBitfieldFlag</span><span class="o">[</span><span class="n">bitfieldBefore</span><span class="p">,</span><span class="w"> </span><span class="n">bitfieldAfter</span><span class="p">,</span><span class="w"> </span><span class="n">Public</span><span class="o">]</span><span class="w"> </span><span class="ow">or</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">bitfieldEqual</span><span class="o">[</span><span class="n">bitfieldBefore</span><span class="p">,</span><span class="w"> </span><span class="n">bitfieldAfter</span><span class="o">]</span><span class="w"> </span></span></span></code></pre></div><p>The next thing to note is that there are two <code>if</code> statements one after another. The state &ldquo;after&rdquo; the first statement is one and the same as the state &ldquo;before&rdquo; the second statement. Using arrows to represent the &ldquo;before-after&rdquo; relationship created by each statement, we can visualize the whole situation as follows:</p> $$ \text{initial state} \xRightarrow{\text{first statement}} \text{middle state} \xRightarrow{\text{second statement}} \text{final state} $$ <p>We&rsquo;ll write our Alloy code to match:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Alloy" data-lang="Alloy"><span class="line"><span class="cl"><span class="cm">/* First if statement */</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">addBitfieldFlag</span><span class="o">[</span><span class="n">bitfieldBefore</span><span class="p">,</span><span class="w"> </span><span class="n">bitfieldMiddle</span><span class="p">,</span><span class="w"> </span><span class="n">Public</span><span class="o">]</span><span class="w"> </span><span class="ow">or</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">bitfieldEqual</span><span class="o">[</span><span class="n">bitfieldBefore</span><span class="p">,</span><span class="w"> </span><span class="n">bitfieldMiddle</span><span class="o">]</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cm">/* ... something connecting bitfieldMiddle and bitfieldAfter ... */</span><span class="w"> </span></span></span></code></pre></div><p>From here, we can handle the second <code>if</code>/<code>else</code> chain in the same way we did the first <code>if</code>-statement: by making all three outcomes of the chain be possible, and creating an <code>or</code> of all of them.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Alloy" data-lang="Alloy"><span class="line"><span class="cl"><span class="cm">/* First if statement */</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">addBitfieldFlag</span><span class="o">[</span><span class="n">bitfieldBefore</span><span class="p">,</span><span class="w"> </span><span class="n">bitfieldMiddle</span><span class="p">,</span><span class="w"> </span><span class="n">Public</span><span class="o">]</span><span class="w"> </span><span class="ow">or</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">bitfieldEqual</span><span class="o">[</span><span class="n">bitfieldBefore</span><span class="p">,</span><span class="w"> </span><span class="n">bitfieldMiddle</span><span class="o">]</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="cm">/* Second if statement */</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">addBitfieldFlag</span><span class="o">[</span><span class="n">bitfieldMiddle</span><span class="p">,</span><span class="w"> </span><span class="n">bitfieldAfter</span><span class="p">,</span><span class="w"> </span><span class="n">MethodOrField</span><span class="o">]</span><span class="w"> </span><span class="ow">or</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">addBitfieldFlagNeg</span><span class="o">[</span><span class="n">bitfieldMiddle</span><span class="p">,</span><span class="w"> </span><span class="n">bitfieldAfter</span><span class="p">,</span><span class="w"> </span><span class="n">Method</span><span class="o">]</span><span class="w"> </span><span class="ow">or</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">bitfieldEqual</span><span class="o">[</span><span class="n">bitfieldMiddle</span><span class="p">,</span><span class="w"> </span><span class="n">bitfieldAfter</span><span class="o">]</span><span class="w"> </span></span></span></code></pre></div><p>So that helps model the relevant Dyno code. However, what we really want is an Alloy predicate that classifies possible outcomes of the piece of code: is a particular combination of flags possible or not? Here&rsquo;s the piece of Alloy that does so:</p> <div class="highlight-group" data-base-path="" data-file-path="dyno-alloy/DynoAlloy.als" data-first-line="113" data-last-line="132"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dyno-alloy/DynoAlloy.als#L113-L132">DynoAlloy.als</a>, lines 113 through 132</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">113 </span><span class="lnt">114 </span><span class="lnt">115 </span><span class="lnt">116 </span><span class="lnt">117 </span><span class="lnt">118 </span><span class="lnt">119 </span><span class="lnt">120 </span><span class="lnt">121 </span><span class="lnt">122 </span><span class="lnt">123 </span><span class="lnt">124 </span><span class="lnt">125 </span><span class="lnt">126 </span><span class="lnt">127 </span><span class="lnt">128 </span><span class="lnt">129 </span><span class="lnt">130 </span><span class="lnt">131 </span><span class="lnt">132 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Alloy" data-lang="Alloy"><span class="line"><span class="cl"><span class="k">pred</span><span class="w"> </span><span class="n">possibleState</span><span class="o">[</span><span class="n">filterState</span><span class="p">:</span><span class="w"> </span><span class="n">FilterState</span><span class="o">]</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">some</span><span class="w"> </span><span class="n">initialState</span><span class="p">:</span><span class="w"> </span><span class="n">FilterState</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Each lookup in scope starts with empty filter flags</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">bitfieldEmpty</span><span class="o">[</span><span class="n">initialState</span><span class="o">.</span><span class="n">curFilter</span><span class="o">]</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// The intermediate states (bitfieldMiddle) are used for sequencing of operations.</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">some</span><span class="w"> </span><span class="n">bitfieldMiddle</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="n">Bitfield</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Add &#34;Public&#34; depending on skipPrivateVisibilities</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">addBitfieldFlag</span><span class="o">[</span><span class="n">initialState</span><span class="o">.</span><span class="n">curFilter</span><span class="p">,</span><span class="w"> </span><span class="n">bitfieldMiddle</span><span class="p">,</span><span class="w"> </span><span class="n">Public</span><span class="o">]</span><span class="w"> </span><span class="ow">or</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">bitfieldEqual</span><span class="o">[</span><span class="n">initialState</span><span class="o">.</span><span class="n">curFilter</span><span class="p">,</span><span class="w"> </span><span class="n">bitfieldMiddle</span><span class="o">]</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// If it&#39;s a method receiver, add method or field restriction</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">addBitfieldFlag</span><span class="o">[</span><span class="n">bitfieldMiddle</span><span class="p">,</span><span class="w"> </span><span class="n">filterState</span><span class="o">.</span><span class="n">curFilter</span><span class="p">,</span><span class="w"> </span><span class="n">MethodOrField</span><span class="o">]</span><span class="w"> </span><span class="ow">or</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// if it&#39;s not a receiver, filter to non-methods (could be overridden)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// addBitfieldFlagNeg[bitfieldMiddle, filterState.curFilter, Method] or</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Maybe methods are not being curFilterd but it&#39;s not a receiver, so no change.</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">bitfieldEqual</span><span class="o">[</span><span class="n">bitfieldMiddle</span><span class="p">,</span><span class="w"> </span><span class="n">filterState</span><span class="o">.</span><span class="n">curFilter</span><span class="o">]</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The <code>FilterState</code> on the first line (and elsewhere, really), is new. I&rsquo;m trying to be explicit about the state in this particular computation. Its definition is very simple: currently, the only state we care about is the <code>Bitfield</code> corresponding to <code>curFilter</code> in the C++ code above.</p> <div class="highlight-group" data-base-path="" data-file-path="dyno-alloy/DynoAlloy.als" data-first-line="12" data-last-line="14"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dyno-alloy/DynoAlloy.als#L12-L14">DynoAlloy.als</a>, lines 12 through 14</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Alloy" data-lang="Alloy"><span class="line"><span class="cl"><span class="kd">sig</span><span class="w"> </span><span class="n">FilterState</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">curFilter</span><span class="p">:</span><span class="w"> </span><span class="n">Bitfield</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>There&rsquo;s not much more to the predicate. It says, in English, that a state <code>filterState</code> is possible if, starting from an empty initial state <code>initialState</code>, the model of our C++ code can end up with its particular set of flags in the <code>curFilter</code> bitfield.</p> <a href="#modeling-search-state"> <h3 id="modeling-search-state">Modeling Search State</h3> </a> <p>Next, I needed to model the behavior the I described earlier: searching for \(A \land \lnot B\), and taking the intersection of past searches when running subsequent searches.</p> <p>Dyno implemented this roughly as follows:</p> <ol> <li>It kept a mapping of (searched scope → search bitfield). Initially, this mapping was empty.</li> <li>When a scope was searched for the first time, its <code>curFilter</code> / search bitfield was stored into the mapping.</li> <li>When a scope was searched after that, the previously-stored flags in the mapping were excluded (that&rsquo;s the \(A\land\lnot B\) behavior), and the bitfield in the mapping was updated to be the intersection of <code>curFilter</code> and the stored flags.</li> </ol> <p>We&rsquo;ll simplify the model by doing away with the mapping, and considering only a single scope that is searched many times. We&rsquo;ll represent the stored flags as a field <code>found</code>, which will be one of two things: either a <code>Bitfield</code> representing the previously-stored search configuration, or a <code>NotSet</code> sentinel value, representing a scope that hasn&rsquo;t been searched yet. The Alloy code:</p> <div class="highlight-group" data-base-path="" data-file-path="dyno-alloy/DynoAlloy.als" data-first-line="21" data-last-line="23"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dyno-alloy/DynoAlloy.als#L21-L23">DynoAlloy.als</a>, lines 21 through 23</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-alloy" data-lang="alloy"><span class="line"><span class="cl"><span class="k">one</span><span class="w"> </span><span class="kd">sig</span><span class="w"> </span><span class="n">SearchState</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="k">var</span><span class="w"> </span><span class="n">found</span><span class="p">:</span><span class="w"> </span><span class="n">Bitfield</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">NotSet</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The <code>NotSet</code> sentinel value is defined in a very simple way:</p> <div class="highlight-group" data-base-path="" data-file-path="dyno-alloy/DynoAlloy.als" data-first-line="17" data-last-line="17"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dyno-alloy/DynoAlloy.als#L17-L17">DynoAlloy.als</a>, line 17</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">17 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Alloy" data-lang="Alloy"><span class="line"><span class="cl"><span class="k">one</span><span class="w"> </span><span class="kd">sig</span><span class="w"> </span><span class="n">NotSet</span><span class="w"> </span><span class="o">{}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Both of these signatures use a new keyword, <code>one</code>. This keyword means that there&rsquo;s only a single instance of both <code>NotSet</code> and <code>SearchState</code> in our model. This is in contrast to a signature like <code>Bitfield,</code> which allows multiple bitfields to exist at the same time. I ended up with a pretty simple predicate that implemented the &ldquo;store if not set, intersect if set&rdquo; behavior in Alloy:</p> <div class="highlight-group" data-base-path="" data-file-path="dyno-alloy/DynoAlloy.als" data-first-line="147" data-last-line="150"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dyno-alloy/DynoAlloy.als#L147-L150">DynoAlloy.als</a>, lines 147 through 150</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">147 </span><span class="lnt">148 </span><span class="lnt">149 </span><span class="lnt">150 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Alloy" data-lang="Alloy"><span class="line"><span class="cl"><span class="k">pred</span><span class="w"> </span><span class="n">updateOrSet</span><span class="o">[</span><span class="n">toSet</span><span class="p">:</span><span class="w"> </span><span class="n">Bitfield</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">NotSet</span><span class="p">,</span><span class="w"> </span><span class="n">setTo</span><span class="p">:</span><span class="w"> </span><span class="n">FilterState</span><span class="o">]</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span><span class="n">toSet</span><span class="w"> </span><span class="ow">in</span><span class="w"> </span><span class="n">NotSet</span><span class="w"> </span><span class="ow">and</span><span class="w"> </span><span class="n">toSet</span><span class="o">&#39;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">setTo</span><span class="o">.</span><span class="n">curFilter</span><span class="o">)</span><span class="w"> </span><span class="ow">or</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">(</span><span class="n">toSet</span><span class="w"> </span><span class="n">not</span><span class="w"> </span><span class="ow">in</span><span class="w"> </span><span class="n">NotSet</span><span class="w"> </span><span class="ow">and</span><span class="w"> </span><span class="n">update</span><span class="o">[</span><span class="n">toSet</span><span class="p">,</span><span class="w"> </span><span class="n">setTo</span><span class="o">])</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>If you look closely, this predicate uses a feature of Alloy we haven&rsquo;t really seen: its ability to reason about time by dipping into <em>temporal logic</em>. Notice that the predicate is written not just in terms of <code>toSet</code>, but also <code>toSet'</code>. The tick (which I personally read as &ldquo;prime&rdquo;) indicates that what we&rsquo;re talking about is not the <em>current</em> value of <code>toSet</code>, but its value at the <em>next moment in time</em>.</p> <p>The first line of the predicate represents the second item from the list above: if a scope hasn&rsquo;t been searched before (represented by the <em>present</em> value of <code>toSet</code> being <code>NotSet</code>) the future value (represented by <code>toSet'</code>) is just the current filter / bitfield. The second line handles the third item from the list, updating a previously-set filter based on new flags. I defined an additional predicate to help with this:</p> <div class="highlight-group" data-base-path="" data-file-path="dyno-alloy/DynoAlloy.als" data-first-line="138" data-last-line="140"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dyno-alloy/DynoAlloy.als#L138-L140">DynoAlloy.als</a>, lines 138 through 140</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">138 </span><span class="lnt">139 </span><span class="lnt">140 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Alloy" data-lang="Alloy"><span class="line"><span class="cl"><span class="k">pred</span><span class="w"> </span><span class="n">update</span><span class="o">[</span><span class="n">toSet</span><span class="p">:</span><span class="w"> </span><span class="n">Bitfield</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">NotSet</span><span class="p">,</span><span class="w"> </span><span class="n">setTo</span><span class="p">:</span><span class="w"> </span><span class="n">FilterState</span><span class="o">]</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">toSet</span><span class="o">&#39;</span><span class="w"> </span><span class="ow">in</span><span class="w"> </span><span class="n">Bitfield</span><span class="w"> </span><span class="ow">and</span><span class="w"> </span><span class="n">bitfieldIntersection</span><span class="o">[</span><span class="n">toSet</span><span class="p">,</span><span class="w"> </span><span class="n">setTo</span><span class="o">.</span><span class="n">curFilter</span><span class="p">,</span><span class="w"> </span><span class="n">toSet</span><span class="o">&#39;]</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>What this predicate says is that <em>at the next moment</em>, the value of <code>toSet</code> will be equal to its present value intersected with <code>curFilter</code>. I also had to specify that the future value of <code>toSet</code>, will still be a <code>Bitfield</code> after the step, and would not revert to a <code>NotSet</code>.</p> <p>With the <code>updateOrSet</code> predicate in hand, we can actually specify how our model will evolve. To do so, we first need to specify the initial conditions. In particular, our scope will start out not having been searched; its flags will be <code>NotSet</code>.</p> <div class="highlight-group" data-base-path="" data-file-path="dyno-alloy/DynoAlloy.als" data-first-line="138" data-last-line="140"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dyno-alloy/DynoAlloy.als#L138-L140">DynoAlloy.als</a>, lines 138 through 140</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">138 </span><span class="lnt">139 </span><span class="lnt">140 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Alloy" data-lang="Alloy"><span class="line"><span class="cl"><span class="k">pred</span><span class="w"> </span><span class="n">update</span><span class="o">[</span><span class="n">toSet</span><span class="p">:</span><span class="w"> </span><span class="n">Bitfield</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">NotSet</span><span class="p">,</span><span class="w"> </span><span class="n">setTo</span><span class="p">:</span><span class="w"> </span><span class="n">FilterState</span><span class="o">]</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">toSet</span><span class="o">&#39;</span><span class="w"> </span><span class="ow">in</span><span class="w"> </span><span class="n">Bitfield</span><span class="w"> </span><span class="ow">and</span><span class="w"> </span><span class="n">bitfieldIntersection</span><span class="o">[</span><span class="n">toSet</span><span class="p">,</span><span class="w"> </span><span class="n">setTo</span><span class="o">.</span><span class="n">curFilter</span><span class="p">,</span><span class="w"> </span><span class="n">toSet</span><span class="o">&#39;]</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Next, we must specify that our <code>SearchState</code> changes in a very particular way: each step, the code invokes a search, and the state is modified to record that the search occurred. Each search is described via <code>curFilter</code> in a <code>filterState</code>. We want to ensure that <code>curFilter</code> is a reasonable filter (that is, it&rsquo;s a combination of flags that can actually arise in the C++ program). To ensure this, we can use the <code>possibleState</code> predicate from earlier. From there, the <code>updateOrSet</code> predicate can be used to specify that this step&rsquo;s <code>curFilter</code> is saved, either as-is (if no searches occurred previously) or as an intersection (if this is not the first search). The whole fact corresponding to this is below:</p> <div class="highlight-group" data-base-path="" data-file-path="dyno-alloy/DynoAlloy.als" data-first-line="161" data-last-line="175"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dyno-alloy/DynoAlloy.als#L161-L175">DynoAlloy.als</a>, lines 161 through 175</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">161 </span><span class="lnt">162 </span><span class="lnt">163 </span><span class="lnt">164 </span><span class="lnt">165 </span><span class="lnt">166 </span><span class="lnt">167 </span><span class="lnt">168 </span><span class="lnt">169 </span><span class="lnt">170 </span><span class="lnt">171 </span><span class="lnt">172 </span><span class="lnt">173 </span><span class="lnt">174 </span><span class="lnt">175 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Alloy" data-lang="Alloy"><span class="line"><span class="cl"><span class="k">fact</span><span class="w"> </span><span class="n">step</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">always</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Model that a new doLookupInScope could&#39;ve occurred, with any combination of flags.</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">all</span><span class="w"> </span><span class="n">searchState</span><span class="p">:</span><span class="w"> </span><span class="n">SearchState</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">some</span><span class="w"> </span><span class="n">fs</span><span class="p">:</span><span class="w"> </span><span class="n">FilterState</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// This is a possible combination of lookup flags</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">possibleState</span><span class="o">[</span><span class="n">fs</span><span class="o">]</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// If a search has been performed before, take the intersection; otherwise,</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// just insert the current filter flags.</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">updateOrSet</span><span class="o">[</span><span class="n">searchState</span><span class="o">.</span><span class="n">found</span><span class="p">,</span><span class="w"> </span><span class="n">fs</span><span class="o">]</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#asking-for-counterexamples"> <h3 id="asking-for-counterexamples">Asking for Counterexamples</h3> </a> <p>As we&rsquo;ve already seen, Alloy works by finding examples: combinations of various variables that match our requirements. It won&rsquo;t be sufficient to ask Alloy for an example of our code doing what we expect: if the code malfunctions nine times out of ten, Alloy will still find us the one case in which it works. It won&rsquo;t tell us much.</p> <p>Instead, we have to ask it to find a <em>counterexample</em>: a case which does <em>not</em> work. If Alloy succeeds in finding such an example, the code we&rsquo;re modeling has a bug. Of course, to make all this work, you need to know what to ask. There&rsquo;s no way to tell Alloy, &ldquo;find me a bug&rdquo; &ndash; we need to be more specific. I had to focus on bugs I was most worried about.</p> <p>If the stored combination of flags (in <code>found</code>) evolves into a bad configuration, things can go wrong in two ways. The first is that we will somehow exclude symbols from the lookup that shouldn&rsquo;t have been excluded. In other words, can past searches break future searches?</p> <p>I came up with the following Alloy (counter)example to model this situation. It&rsquo;s a little bit long; there are comments there to explain what it does, and I&rsquo;ll go through below.</p> <div class="highlight-group" data-base-path="" data-file-path="dyno-alloy/DynoAlloy.als" data-first-line="177" data-last-line="202"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dyno-alloy/DynoAlloy.als#L177-L202">DynoAlloy.als</a>, lines 177 through 202</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">177 </span><span class="lnt">178 </span><span class="lnt">179 </span><span class="lnt">180 </span><span class="lnt">181 </span><span class="lnt">182 </span><span class="lnt">183 </span><span class="lnt">184 </span><span class="lnt">185 </span><span class="lnt">186 </span><span class="lnt">187 </span><span class="lnt">188 </span><span class="lnt">189 </span><span class="lnt">190 </span><span class="lnt">191 </span><span class="lnt">192 </span><span class="lnt">193 </span><span class="lnt">194 </span><span class="lnt">195 </span><span class="lnt">196 </span><span class="lnt">197 </span><span class="lnt">198 </span><span class="lnt">199 </span><span class="lnt">200 </span><span class="lnt">201 </span><span class="lnt">202 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Alloy" data-lang="Alloy"><span class="line"><span class="cl"><span class="n">counterexampleNotFound</span><span class="p">:</span><span class="w"> </span><span class="k">run</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">all</span><span class="w"> </span><span class="n">searchState</span><span class="p">:</span><span class="w"> </span><span class="n">SearchState</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// a way that subsequent results of searching will miss things.</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">eventually</span><span class="w"> </span><span class="k">some</span><span class="w"> </span><span class="n">symbol</span><span class="p">:</span><span class="w"> </span><span class="n">Symbol</span><span class="p">,</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">fs</span><span class="p">:</span><span class="w"> </span><span class="n">FilterState</span><span class="p">,</span><span class="w"> </span><span class="n">fsBroken</span><span class="p">:</span><span class="w"> </span><span class="n">FilterState</span><span class="p">,</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">exclude1</span><span class="p">:</span><span class="w"> </span><span class="n">Bitfield</span><span class="p">,</span><span class="w"> </span><span class="n">exclude2</span><span class="p">:</span><span class="w"> </span><span class="n">Bitfield</span><span class="w"> </span><span class="o">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Some search (fs) will cause a transition / modification of the search state...</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">possibleState</span><span class="o">[</span><span class="n">fs</span><span class="o">]</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">updateOrSet</span><span class="o">[</span><span class="n">searchState</span><span class="o">.</span><span class="n">found</span><span class="p">,</span><span class="w"> </span><span class="n">fs</span><span class="o">]</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">excludeBitfield</span><span class="o">[</span><span class="n">searchState</span><span class="o">.</span><span class="n">found</span><span class="p">,</span><span class="w"> </span><span class="n">exclude1</span><span class="o">]</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Such that a later, valid search... (fsBroken)</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">possibleState</span><span class="o">[</span><span class="n">fsBroken</span><span class="o">]</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">excludeBitfield</span><span class="o">[</span><span class="n">searchState</span><span class="o">.</span><span class="n">found</span><span class="o">&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">exclude2</span><span class="o">]</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Will allow for a symbol ...</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// ... that are left out of the original search...</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">not</span><span class="w"> </span><span class="n">bitfieldMatchesProperties</span><span class="o">[</span><span class="n">searchState</span><span class="o">.</span><span class="n">found</span><span class="p">,</span><span class="w"> </span><span class="n">symbol</span><span class="o">]</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// ... and out of the current search</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">not</span><span class="w"> </span><span class="o">(</span><span class="n">bitfieldMatchesProperties</span><span class="o">[</span><span class="n">fs</span><span class="o">.</span><span class="n">curFilter</span><span class="p">,</span><span class="w"> </span><span class="n">symbol</span><span class="o">]</span><span class="w"> </span><span class="ow">and</span><span class="w"> </span><span class="n">not</span><span class="w"> </span><span class="n">bitfieldMatchesProperties</span><span class="o">[</span><span class="n">exclude1</span><span class="p">,</span><span class="w"> </span><span class="n">symbol</span><span class="o">])</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// But would be matched by the broken search...</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">bitfieldMatchesProperties</span><span class="o">[</span><span class="n">fsBroken</span><span class="o">.</span><span class="n">curFilter</span><span class="p">,</span><span class="w"> </span><span class="n">symbol</span><span class="o">]</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// ... to not be matched by a search with the new state:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">not</span><span class="w"> </span><span class="o">(</span><span class="n">bitfieldMatchesProperties</span><span class="o">[</span><span class="n">fsBroken</span><span class="o">.</span><span class="n">curFilter</span><span class="p">,</span><span class="w"> </span><span class="n">symbol</span><span class="o">]</span><span class="w"> </span><span class="ow">and</span><span class="w"> </span><span class="n">not</span><span class="w"> </span><span class="n">bitfieldMatchesProperties</span><span class="o">[</span><span class="n">exclude2</span><span class="p">,</span><span class="w"> </span><span class="n">symbol</span><span class="o">])</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This example asks that at some point in time, things &ldquo;go wrong&rdquo;. In particular, will there by a symbol (<code>symbol</code>) that hasn&rsquo;t been found yet, such that a search for a particular filter (<code>fs</code>) will break the system, making a subsequent search <code>fsBroken</code> not find <code>symbol</code> even though it should have?</p> <p>The <code>possibleState</code>, <code>updateOrSet</code>, and <code>excludeBitfield</code> lines encode the fact that a search occurred for <code>fs</code>. This must be a valid search, and the search state must be modified appropriately. Furthermore, at the time this search takes place, to make the \(\lnot B\) portion of the algorithm work, the bitfield <code>exclude1</code> will be set based on the previous search state.</p> <p>The next two lines, <code>possibleState</code> and <code>excludeBitfield</code>, set the stage for the broken search: <code>fsBroken</code> is a another valid search, and at the time it happens, the bitfield <code>exclude2</code> is set based on previous search state. Since <code>fsBroken</code> occurs after <code>fs</code>, its &ldquo;previous search state&rdquo; is actually the state <em>after</em> <code>fs</code>, so we use <code>found'</code> instead of <code>found</code>.</p> <p>Finally, the subsequent four lines of code describe the issue: the symbol in question has not been found before <code>fs</code>, and nor will it be found by <code>fs</code>. That means thus far, it hasn&rsquo;t been reported to the user. Therefore, if the symbol matches <code>fsBroken</code>, it ought to be reported: we haven&rsquo;t seen it yet, and here we&rsquo;re being asked for something matching the symbol&rsquo;s description! However, as per the last line of code, searching for <code>fsBroken</code> together with the appropriate set of exclude flags, we still don&rsquo;t find <code>symbol</code>. That&rsquo;s a problem!</p> <p>Unfortunately, Alloy finds a model that satisfies this constraint. There are a lot of moving parts, so the output is a bit difficult to read. I did my best to clean it up by turning off some arrows. Our system is spanning multiple &ldquo;moments&rdquo; in time, so a single picture won&rsquo;t describe the bug entirely. Here&rsquo;s the diagram Alloy outputs for the first state:</p> <figure class="fullwide"><img src="https://danilafe.com/blog/dyno_alloy/bug.png" alt="Figure representing the initial state according to Alloy"><figcaption> <p>Figure representing the initial state according to Alloy</p> </figcaption> </figure> <p>We can get a lot out of this figure. First, the symbol-to-be-lost is a private method (it doesn&rsquo;t have the <code>PPublic</code> property, and it does have the <code>PMethod</code> property). Also, Alloy immediately gives away what <code>fs</code> and <code>fsBroken</code> will be: eventually, when the user searches for all <em>non-methods</em> (<code>negativeFlags: Method</code> are the giveaway there), their subsequent search for <em>anything</em> will fail to come up with our private method, even though it should. To gather more details about this broken case, we can look at the state that follows the initial one.</p> <figure class="fullwide"><img src="https://danilafe.com/blog/dyno_alloy/bug_2.png" alt="Figure representing the second state according to Alloy"><figcaption> <p>Figure representing the second state according to Alloy</p> </figcaption> </figure> <p>The main difference is that <code>found</code> has changed from <code>NotSet</code> (because no searches occurred) to <code>FilterState1</code>. This indicates that the first search was for all <code>Public</code> symbols (which our method is not). There is only one more state after this:</p> <figure class="fullwide"><img src="https://danilafe.com/blog/dyno_alloy/bug_3.png" alt="Figure representing the final state according to Alloy"><figcaption> <p>Figure representing the final state according to Alloy</p> </figcaption> </figure> <p>In the above diagram, <code>found</code> has changed once again, this time to an empty bitfield. This is a valid behavior for our system. Recall that <code>fs</code> was a search for non-methods, and that the intersection of <code>NOT_METHOD</code> and <code>PUBLIC</code> is empty. Thus, <code>found</code> will be set to the empty <code>bitfield</code>, which (incorrectly) indicates that all symbols have been searched for! After this, any search would fail: <code>fsBroken</code> doesn&rsquo;t have any flags set, and still, nothing is reported.</p> <p>Now, this doesn&rsquo;t definitively prove the compiler is broken: it&rsquo;s possible that there isn&rsquo;t a situation in which three searches like this (<code>PUBLIC</code>, then <code>NOT_METHOD</code>, then anything) will occur in practice. However, this gave the &ldquo;motif&rdquo; for reproducing the bug. All I had to do was find a real-life case that matched the counterexample.</p> <p>It was a little easier to find a reproducer for a similar counterexample, actually. By inspection, I noticed that the same bug would occur if the second search was for <code>METHOD_OR_FIELD</code>, and not for <code>NOT_METHOD</code>. I was able to come up with a (fairly convoluted) example of Chapel code that triggered the issue. I include it here as a curiosity; there&rsquo;s no need to understand how exactly it works.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Chapel" data-lang="Chapel"><span class="line"><span class="cl"><span class="k">module</span><span class="w"> </span><span class="nc">TopLevel</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">module</span><span class="w"> </span><span class="nc">XContainerUser</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">public</span><span class="w"> </span><span class="k">use</span><span class="w"> </span><span class="nx">TopLevel</span><span class="p">.</span><span class="nx">XContainer</span><span class="p">;</span><span class="w"> </span><span class="c1">// Will search for public, to no avail. </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">module</span><span class="w"> </span><span class="nc">XContainer</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">private</span><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">x</span><span class="p">:</span><span class="w"> </span><span class="kt">int</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">record</span><span class="w"> </span><span class="nc">R</span><span class="w"> </span><span class="p">{}</span><span class="w"> </span><span class="c1">// R is in the same scope as x so it won&#39;t set public </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="k">module</span><span class="w"> </span><span class="nc">MethodHaver</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">use</span><span class="w"> </span><span class="nx">TopLevel</span><span class="p">.</span><span class="nx">XContainerUser</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">use</span><span class="w"> </span><span class="nx">TopLevel</span><span class="p">.</span><span class="nx">XContainer</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">proc</span><span class="w"> </span><span class="nf">R.foo</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">x</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> </span></span></span></code></pre></div><p>Alas, the two-bitfield system is not just an approximation, it malfunctions in practice. I submitted a PR to fix the issue.</p> Search as a Polynomial https://danilafe.com/blog/search_polynomials/ Mon, 22 May 2023 21:39:00 -0700 https://danilafe.com/blog/search_polynomials/ <p>I read a really neat paper some time ago, and I&rsquo;ve been wanting to write about it ever since. The paper is called <a href="https://dl.acm.org/doi/pdf/10.1145/3473577"class="external-link">Algebras for Weighted Search<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, and it is a tad too deep to dive into in a blog article &ndash; readers of ICFP are rarely the target audience on this site. However, one particular insight I gleaned from the paper merits additional discussion and demonstration. I&rsquo;m going to do that here.</p> <p>In particular, the paper pointed out a connection between polynomials and a general concept of <em>search</em>. In the context of the paper, &ldquo;search&rdquo; simply referred to a way of finding various solutions to some problem, perhaps like &ldquo;what are the ways of getting from one place to another?&rdquo;. In this case, a search would be a computation that explores the space of possible routes.</p> <p>That all sounds very abstract, so let&rsquo;s start with a concrete example. Suppose that you&rsquo;re trying to get from city A to city B, and then from city B to city C. Also suppose that your trips are measured in one-hour intervals (maybe you round trip lengths, turning 2:45 into 3 hours), and that trips of equal duration are considered equivalent (&ldquo;as long as it gets me there!&rdquo;). Now, I give you a list of possible routes from city A to city B, and another list of possible routes from city B to city C, grouped by their length. Given these two lists, what are the possible routes from A to C?</p> <p>Let&rsquo;s make this even more concrete, and start with some actual lists of routes. Maybe there are two routes from A to B that take two hours each, and one &ldquo;quick&rdquo; trip that takes only an hour. On top of this, there&rsquo;s one three-hour trip from B to C, and one two-hour trip. Given these building blocks, the list of possible trips from A to C is as follows.</p> <ol> <li>Two two-hour trips from A to B, followed up by the three-hour trip from B to C.</li> <li>Two two-hour trips from A to B, followed by the shorter two-hour trip from B to C.</li> <li>One one-hour trip from A to B, followed by the three-hour trip from B to C.</li> <li>One one-hour trip from A to B, followed by the shorter two-hour trip from B to C.</li> </ol> <p>In the above, to figure out the various ways of getting from A to C, we had to examine all pairings of A-to-B routes with B-to-C routes. But then, multiple pairings end up having the same total length: the second and third bullet points both describe trips that take four hours. Thus, to give our final report, we need to &ldquo;combine like terms&rdquo; - add up the trips from the two matching bullet points, ending up with total of three four-hour trips.</p> <p>Does this feel a little bit familiar? To me, this bears a rather striking resemblance to an operation we&rsquo;ve seen in high school algebra class: we&rsquo;re multiplying two binomials! Here&rsquo;s the corresponding multiplication:</p> $$ \left(2x^2 &#43; x\right)\left(x^3&#43;x^2\right) = 2x^5 &#43; 2x^4 &#43; x^4 &#43; x^3 = \underline{2x^5&#43;3x^4&#43;x^3} $$ <p>It&rsquo;s not just binomials that correspond to our combining paths between cities. We can represent any combination of trips of various lengths as a polynomial. Each term \(ax^n\) represents \(a\) trips of length \(n\). As we just saw, multiplying two polynomials corresponds to &ldquo;sequencing&rdquo; the trips they represent &ndash; matching each trip in one with each of the trips in the other, and totaling them up.</p> <p>What about adding polynomials, what does that correspond to? The answer there is actually quite simple: if two polynomials both represent (distinct) lists of trips from A to B, then adding them just combines the list. If I know one trip that takes two hours (\(x^2\)) and someone else knows a shortcut (\(x\)), then we can combine that knowledge (\(x^2+x\)).</p> <p class="dialog"> <span class="message side-question"> <span class="message-sender"><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#moon"/> </svg></span> <span class="message-text"> Wait a moment. Sure, we learned about polynomials in algebra class: they're functions! You put in a number for \(x\), and get another number out. But you haven't done that, and in fact you haven't even mentioned functions at all. What's going on? </span> </span> <span class="message side-answer"> <span class="message-sender"><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#sun"/> </svg></span> <span class="message-text"> In this article (and in the paper it's based on), polynomials are viewed in a more general way than you might be used to. The point isn't to think of them as defining functions on numbers, but to make use of their "shape": a sum of certain powers of \(x\), like \(ax^n+bx^m+...\) </span> </span> <span class="message side-question"> <span class="message-sender"><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#moon"/> </svg></span> <span class="message-text"> So we won't be plugging numbers in, or trying to graph the polynomials in this section? </span> </span> <span class="message side-answer"> <span class="message-sender"><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#sun"/> </svg></span> <span class="message-text"> That's right, we won't be. The sort of thing we're doing here is a bit closer to <a href="https://en.wikipedia.org/wiki/Abstract_algebra">abstract algebra</a> than to high school math. Don't worry if you're not familiar with the subject, though: I'm trying to explain everything from first principles. </span> </span> </p> <p>Well, it&rsquo;s a neat little thing that tracking trips corresponds to adding and mulitpying polynomials like that. We can push this observation a bit further, though. Since our trick relies on multiplying two polynomials, we&rsquo;ll need to better understand what that multiplication needs to behave as we expect. In particular, we&rsquo;ll need to know what the &ldquo;bare minimum&rdquo; is for working with polynomial: what arithmetic properties must we bring to the table? Let&rsquo;s take a look at that next.</p> <a href="#polynomials-over-semirings"> <h3 id="polynomials-over-semirings">Polynomials over Semirings</h3> </a> <p>Let&rsquo;s watch what happens when we multiply two binomials, paying really close attention to the operations we&rsquo;re performing. The following (concrete) example should do.</p> $$ \begin{aligned} &amp;amp; (x&#43;1)(1-x)\\ =\ &amp;amp; (x&#43;1)1&#43;(x&#43;1)(-x)\\ =\ &amp;amp; x&#43;1-x^2-x \\ =\ &amp;amp; x-x&#43;1-x^2 \\ =\ &amp;amp; 1-x^2 \end{aligned} $$ <p>The first thing we do is <em>distribute</em> the multiplication over the addition, on the left. We then do that again, on the right this time. After this, we finally get some terms, but they aren&rsquo;t properly grouped together; an \(x\) is at the front, and a \(-x\) is at the very back. We use the fact that addition is <em>commutative</em> (\(a+b=b+a\)) and <em>associative</em> (\(a+(b+c)=(a+b)+c\)) to rearrange the equation, grouping the \(x\) and its negation together. This gives us \((1-1)x=0x=0\). That last step is important: we&rsquo;ve used the fact that multiplication by zero gives zero. Another important property (though we didn&rsquo;t use it here) is that multiplication has to be associative, too.</p> <p>So, what if we didn&rsquo;t use numbers, but rather any <em>thing</em> with two operations, one kind of like \((\times)\) and one kind of like \((+)\)?</p> <p class="dialog"> <span class="message side-question"> <span class="message-sender"><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#moon"/> </svg></span> <span class="message-text"> Here, it seems like you're saying that in the polynomials we've seen so far, it's numbers themselves that need to be commutative, associative, etc.. </span> </span> <span class="message side-answer"> <span class="message-sender"><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#sun"/> </svg></span> <span class="message-text"> That's right, I am saying that. We need the \((+)\) and \((\times)\) operations on numbers to follow the laws I laid out above. </span> </span> <span class="message side-question"> <span class="message-sender"><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#moon"/> </svg></span> <span class="message-text"> Okay, but in your equations above, it's not just numbers that were moved around using commutativity and associativity: it was variables, like \(x\). Just earlier you said that we're thinking of the polynomials in terms of their "shape", and not as functions. If that's the case, why we allowed to blur the lines between polynomial and number like that? </span> </span> <span class="message side-answer"> <span class="message-sender"><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#sun"/> </svg></span> <span class="message-text"> Good question. If you want to get really precise, in the abstract view, adding numbers is not quite the same as adding polynomials. Because of this, saying that addition commutes for numbers does not <em>immediately</em> tel us that it commutes for something like \(x\). However, also in the abstract view, we define how addition and multiplication on polynomials work <em>using</em> addition and multiplication numbers. Thus, properties of numbers make their way into properties of polynomials. </span> </span> </p> <p>As I was saying, what if we used some other kind of <em>thing</em> other than numbers, together with notions of what it means to &ldquo;add&rdquo; and &ldquo;multiply&rdquo; this <em>thing</em>? As long as these operations satisfy the properties we have used so far, we should be able to create polynomials using them, and do this same sort of &ldquo;combining paths&rdquo; we did earlier. Before we get to that, let me just say that &ldquo;things with addition and multiplication that work in the way we described&rdquo; have an established name in math - they&rsquo;re called semirings.</p> <p>A <strong>semiring</strong> is a set equipped with two operations, one called &ldquo;multiplicative&rdquo; (and thus carrying the symbol \(\times)\) and one called &ldquo;additive&rdquo; (and thus written as \(+\)). Both of these operations need to have an &ldquo;identity element&rdquo;. The identity element for multiplication is usually <span class="sidenote"> <label class="sidenote-label" for="written-as-note">written as \(1\),</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="written-as-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> And I do mean "written as": a semiring need not be over numbers. We could define one over <a href="https://en.wikipedia.org/wiki/Graph">graphs</a>, sets, and many other things! Nevertheless, because most of us learn the properties of addition and multiplication much earlier than we learn about other more "esoteric" things, using numbers to stand for special elements seems to help use intuition. <span class="sidenote-delimiter">]</span> </span> </span> and the identity element for addition is written as \(0\). Furthermore, a few equations hold. I&rsquo;ll present them in groups. First, multiplication is associative and multiplying by \(1\) does nothing; in mathematical terms, the set forms a <a href="https://mathworld.wolfram.com/Monoid.html"class="external-link">monoid<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> with multiplication and \(1\). $$ \begin{array}{cl} (a\times b)\times c = a\times(b\times c) &amp;amp; \text{(multiplication associative)}\\ 1\times a = a = a \times 1 &amp;amp; \text{(1 is multiplicative identity)}\\ \end{array} $$ </p> <p>Similarly, addition is associative and adding \(0\) does nothing. Addition must also be commutative; in other words, the set forms a commutative monoid with addition and \(0\). $$ \begin{array}{cl} (a&#43;b)&#43;c = a&#43;(b&#43;c) &amp;amp; \text{(addition associative)}\\ 0&#43;a = a = a&#43;0 &amp;amp; \text{(0 is additive identity)}\\ a&#43;b = b&#43;a &amp;amp; \text{(addition is commutative)}\\ \end{array} $$ </p> <p>Finally, a few equations determine how addition and multiplication interact. $$ \begin{array}{cl} 0\times a = 0 = a \times 0 &amp;amp; \text{(annihilation)}\\ a\times(b&#43;c) = a\times b &#43; a\times c &amp;amp; \text{(left distribution)}\\ (a&#43;b)\times c = a\times c &#43; b\times c &amp;amp; \text{(right distribution)}\\ \end{array} $$ </p> <p>That&rsquo;s it, we&rsquo;ve defined a semiring. First, notice that numbers do indeed form a semiring; all the equations above should be quite familiar from algebra class. When using polynomials with numbers to do our city path finding, we end up tracking how many different ways there are to get from one place to another in a particular number of hours. There are, however, other semirings we can use that yield interesting results, even though we continue to add and multiply polynomials.</p> <p>One last thing before we look at other semirings: given a semiring \(R\), the polynomials using that \(R\), and written in terms of the variable \(x\), are denoted as \(R[x]\).</p> <a href="#the-semiring-of-booleans-mathbbb"> <h4 id="the-semiring-of-booleans-mathbbb">The Semiring of Booleans, \(\mathbb{B}\)</h4> </a> <p>Alright, it&rsquo;s time for our first non-number example. It will be a simple one, though - booleans (that&rsquo;s right, <code>true</code> and <code>false</code> from your favorite programming language!) form a semiring. In this case, addition is the &ldquo;or&rdquo; operation (aka <code>||</code>), in which the result is true if either operand is true, and false otherwise.</p> $$ \begin{array}{c} \text{true} &#43; b = \text{true}\\ b &#43; \text{true} = \text{true}\\ \text{false} &#43; \text{false} = \text{false} \end{array} $$ <p>For addition, the identity element &ndash; our \(0\) &ndash; is \(\text{false}\).</p> <p>Correspondingly, multiplication is the &ldquo;and&rdquo; operation (aka <code>&amp;&amp;</code>), in which the result is false if either operand is false, and true otherwise.</p> $$ \begin{array}{c} \text{false} \times b = \text{false}\\ b \times \text{false} = \text{false}\\ \text{true} \times \text{true} = \text{true} \end{array} $$ <p>For multiplication, the identity element &ndash; the \(1\) &ndash; is \(\text{true}\).</p> <p>It&rsquo;s not hard to see that <em>both</em> operations are commutative - the first and second equations for addition, for instance, can be combined to get \(\text{true}+b=b+\text{true}\), and the third equation clearly shows commutativity when both operands are false. The other properties are easy enough to verify by simple case analysis (there are 8 cases to consider). The set of booleans is usually denoted as \(\mathbb{B}\), which means polynomials using booleans are denoted by \(\mathbb{B}[x]\).</p> <p>Let&rsquo;s try some examples. We can&rsquo;t count how many ways there are to get from A to B in a certain number of hours anymore: booleans aren&rsquo;t numbers! Instead, what we <em>can</em> do is track <em>whether or not</em> there is a way to get from A to B in a certain number of hours (call it \(n\)). If we can, we write that as \(\text{true}\ x^n = 1x^n = x^n\). If we can&rsquo;t, we write that as \(\text{false}\ x^n = 0x^n = 0\). The polynomials corresponding to our introductory problem are \(x^2+x^1\) and \(x^3+x^2\). Multiplying them out gives:</p> $$ (x^2&#43;x^1)(x^3&#43;x^2) = x^5 &#43; x^4 &#43; x^4 &#43; x^3 = x^5 &#43; x^4 &#43; x^2 $$ <p>And that&rsquo;s right; if it&rsquo;s possible to get from A to B in either two hours or one hour, and then from B to C in either three hours or two hours, then it&rsquo;s possible to get from A to C in either five, four, or three hours. In a way, polynomials like this give us <span class="sidenote"> <label class="sidenote-label" for="homomorphism-note">less information than our original ones</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="homomorphism-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> In fact, we can construct a semiring homomorphism (kind of like a <a href="https://en.wikipedia.org/wiki/Ring_homomorphism">ring homomorphism</a>, but for semirings) from \(\mathbb{N}[x]\) to \(\mathbb{B}[x]\) as follows: $$ \sum_{i=0}^n a_ix^i \mapsto \sum_{i=0}^n \text{clamp}(a_i)x^i $$ Where the \(\text{clamp}\) function checks if its argument is non-zero. In the case of city path search, \(\text{clamp}\) asks the questions "are there any routes at all?". $$ \text{clamp}(n) = \begin{cases} \text{false} &amp;amp; n = 0 \\ \text{true} &amp;amp; n &amp;gt; 0 \end{cases} $$ We can't construct the inverse of the above homomorphism (a mapping that would undo our clamping, and take polynomials in \(\mathbb{B}[x]\) to \(\mathbb{N}[x]\)). This fact gives us a more "mathematical" confirmation that we lost information, rather than gained it, but switching to boolean polynomials: we can always recover a boolean polynomial from the natural number one, but not the other way around. <span class="sidenote-delimiter">]</span> </span> </span> (which were \(\mathbb{N}[x]\), polynomials over natural numbers \(\mathbb{N} = \{ 0, 1, 2, ... \}\)), so it&rsquo;s unclear why we&rsquo;d prefer them. However, we&rsquo;re just warming up - there are more interesting semirings for us to consider!</p> <a href="#the-semiring-of-sets-of-paths-mathcalppi"> <h4 id="the-semiring-of-sets-of-paths-mathcalppi">The Semiring of Sets of Paths, \(\mathcal{P}(\Pi)\)</h4> </a> <p>Until now, we explicitly said that &ldquo;all paths of the same length are equivalent&rdquo;. If we&rsquo;re giving directions, though, we might benefit from knowing not just that there <em>is</em> a way, but what roads that way is made up of!</p> <p>To this end, we define the set of paths, \(\Pi\). This set will consist of the empty path (which we will denote \(\circ\), why not?), street names (e.g. \(\text{Mullholland Dr.}\) or \(\text{Sunset Blvd.}\)), and concatenations of paths, written using \(\rightarrow\). For instance, a path that first takes us on \(\text{Highway}\) and then on \(\text{Exit 4b}\) will be written as:</p> $$ \text{Highway}\rightarrow\text{Exit 4b} $$ <p>Furthermore, it&rsquo;s not too much of a stretch to say that adding an empty path to the front or the back of another path doesn&rsquo;t change it. If we use the letter \(\pi\) to denote a path, this means the following equation:</p> $$ \circ \rightarrow \pi = \pi = \pi \rightarrow \circ $$ <p><span class="sidenote"> <label class="sidenote-label" for="paths-monoid-note">So those are paths.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="paths-monoid-note"></input><span class="sidenote-content sidenote-right" style="margin-top:0.25rem"><span class="sidenote-delimiter">[note:</span> Actually, if you clicked through the <a href="https://mathworld.wolfram.com/Monoid.html">monoid</a> link earlier, you might be interested to know that paths as defined here form a monoid with concatenation \(\rightarrow\) and the empty path \(\circ\) as a unit. <span class="sidenote-delimiter">]</span> </span> </span> Paths alone, though, aren&rsquo;t enough for our polynomials; we&rsquo;re tracking different ways to get from one place to another. This is an excellent use case for sets!</p> <p>Our next semiring will be that of <em>sets of paths</em>. Some example elements of this semiring are \(\varnothing\), also known as the empty set, \(\{\circ\}\), the set containing only the empty path, and the set containing a path via the highway, and another path via the suburbs:</p> $$ \{\text{Highway}\rightarrow\text{Exit 4b}, \text{Suburb Rd.}\} $$ <p>So what are the addition and multiplication on sets of paths? Addition is the easier one: it&rsquo;s just the union of sets (the &ldquo;triangle equal sign&rdquo; symbol means &ldquo;defined as&rdquo;):</p> $$ A &#43; B \triangleq A \cup B $$ <p>It&rsquo;s well known (and not hard to verify) that set union is commutative and associative. The additive identity \(0\) is simply the empty set \(\varnothing\). Intuitively, adding &ldquo;no paths&rdquo; to another set of paths doesn&rsquo;t add anything, and thus leaves that other set unchanged.</p> <p>Multiplication is a little bit more interesting, and uses the path concatenation operation we defined earlier. We will use this operation to describe path sequencing; given two sets of paths, \(A\) and \(B\), we&rsquo;ll create a new set of paths consisting of each path from \(A\) concatenated with each path from \(B\):</p> $$ A \times B \triangleq \{ a \rightarrow b\ |\ a \in A, b \in B \} $$ <p>The fact that this definition of multiplication on sets is associative relies on the associativity of path concatenation; if path concatenation weren&rsquo;t associative, the second equality below would not hold.</p> $$ \begin{array}{rcl} A \times (B \times C) &amp;amp; = &amp;amp; \{ a \rightarrow (b \rightarrow c)\ |\ a \in A, b \in B, c \in C \} \\ &amp;amp; \stackrel{?}{=} &amp;amp; \{ (a \rightarrow b) \rightarrow c \ |\ a \in A, b \in B, c \in C \} \\ &amp;amp; = &amp;amp; (A \times B) \times C \end{array} $$ <p>What&rsquo;s the multiplicative identity? Well, since multiplication concatenates all the combinations of paths from two sets, we could try making a set of elements that don&rsquo;t do anything when concatenating. Sound familiar? It should, that&rsquo;s \(\circ\), the empty path element! We thus define our multiplicative identity as \(\{\circ\}\), and verify that it is indeed the identity:</p> $$ \begin{gathered} \{\circ\} \times A = \{ \circ \rightarrow a\ |\ a \in A \} = \{ a \ |\ a \in A \} = A \\ A \times \{\circ\}= \{ a\rightarrow \circ \ |\ a \in A \} = \{ a \ |\ a \in A \} = A \end{gathered} $$ <p>It&rsquo;s not too difficult to verify the annihilation and distribution laws for sets of paths, either; I won&rsquo;t do that here, though. Finally, let&rsquo;s take a look at an example. Like before, we&rsquo;ll try make one that corresponds to our introductory description of paths from A to B and from B to C. Now we need to be a little bit creative, and come up with names for all these different roads between our hypothetical cities. Let&rsquo;s say that \(\text{Highway A}\) and \(\text{Highway B}\) are the two paths from A to B that take two hours each, and then \(\text{Shortcut}\) is the path that takes one hour. As for paths from B to C, let&rsquo;s just call them \(\text{Long}\) for the three-hour path, and \(\text{Short}\) for the two-hour path. Our two polynomials are then:</p> $$ \begin{array}{rcl} P_1 &amp;amp; = &amp;amp; \{\text{Highway A}, \text{Highway B}\}x^2 &#43; \{\text{Shortcut}\}x \\ P_2 &amp;amp; = &amp;amp; \{\text{Long}\}x^3 &#43; \{\text{Short}\}x^2 \end{array} $$ <p>Multiplying them gives: $$ \begin{array}{rl} &amp;amp; \{\text{Highway A} \rightarrow \text{Long}, \text{Highway B} \rightarrow \text{Long}\}x^5\\ &#43; &amp;amp; \{\text{Highway A} \rightarrow \text{Short}, \text{Highway B} \rightarrow \text{Short}, \text{Shortcut} \rightarrow \text{Long}\}x^4\\ &#43; &amp;amp; \{\text{Shortcut} \rightarrow \text{Short}\}x^3 \end{array} $$ </p> <p>This resulting polynomial gives us all the paths from city A to city C, grouped by their length!</p> <a href="#the-tropical-semiring-mathbbr"> <h4 id="the-tropical-semiring-mathbbr">The Tropical Semiring, \(\mathbb{R}\)</h4> </a> <p>I only have one last semiring left to show you. It&rsquo;s a fun semiring though, as even its name might suggest: we&rsquo;ll take a look at a <em>tropical semiring</em>.</p> <p>In this semiring, we go back to numbers; particularly, real numbers (e.g., \(1.34\), \(163\), \(e\), that kind of thing). We even use addition &ndash; sort of. In the tropical semiring, addition serves as the <em>multiplicative</em> operation! This is even confusing to write, so I&rsquo;m going to switch up notation; in the rest of this section, I&rsquo;ll use \(\otimes\) to represent the multiplicative operation in semirings, and \(\oplus\) to represent the additive one. The symbols \(\times\) and \(+\) will be used to represent the regular operations on real numbers. With that, the operations on our tropical semiring over real numbers are defined as follows:</p> $$ \begin{array}{rcl} x \otimes y &amp;amp; \triangleq &amp;amp; x &#43; y\\ x \oplus y &amp;amp; \triangleq &amp;amp; \min(x,y) \end{array} $$ <p>What is this new semiring good for? How about this: suppose that in addition to the duration of the trip, you&rsquo;d like to track the distance you must travel for each route (shorter routes do sometimes have more traffic!). Let&rsquo;s watch what happens when we add and multiply polynomials over this semiring. When we add terms with the same power but different coefficients, like \(ax\oplus bx\), we end up with a term \(\min(a,b)x\). In other words, for each trip duration, we pick the shortest length. When we multiply two polynomials, like \(ax\otimes bx\), we get \((a+b)x\); in other words, when sequencing two trips, we add up the distances to get the combined distance, just like we&rsquo;d expect.</p> <p>We can, of course, come up with a polynomial to match our initial example. Say that the trips from A to B are represented by \(2.0x^2\oplus1.5x\) (the shortest two-hour trip is \(2\) units of distance long, and the one-hour trip is \(1.5\) units long), and that the trips from B to C are represented by \(4.0x^3\oplus1.0x^2\). Multiplying the two polynomials out gives:</p> $$ \begin{array}{rcl} (2.0x^2\oplus1.5x)(4.0x^3\oplus1.0x^2) &amp;amp; = &amp;amp; 6.0x^5 \oplus \min(2.0&#43;1.0, 1.5&#43;4.0)x^4 \oplus 2.5x^3 \\ &amp;amp; = &amp;amp; 6.0x^5 \oplus 3.0x^4 \oplus 2.5x^3 \end{array} $$ <p>The only time we used the additive operation in this case was to pick between two trips of equal druation but different length (two-hour trip from A to B followed by a two-hour trip from B to C, or one-hour trip from A to C followed by a three-hour trip from B to C). The first trip wins out, since it requires only \(3.0\) units of distance.</p> <a href="#anything-but-routes"> <h3 id="anything-but-routes">Anything but Routes</h3> </a> <p>So far, all I&rsquo;ve done can be reduced to variations on a theme: keeping track of some aspects of a trip between cities, using polynomials for structure. However, that&rsquo;s just the beginning. This sort of trick can be be made even more powerful by further relaxing the notion of a &ldquo;polynomial&rdquo;. By doing so, we can make our polynomials represent arbitrary <em>effects</em> (in the computer science sense &ndash; things like errors, logging to a console, storing and accessing information from a database). Relying for just a little longer on our example of journeys between cities, we might be able to represent trips with random variation (traffic can be unpredicatable!), or maybe cities where you will get stuck. But the point isn&rsquo;t routes: the same approach can be used to represent traversing a binary tree, performing Prolog-like proof search, or evaluating a non-deterministic program. The sky&rsquo;s the limit!</p> <p>Unfortunately, doing so would require even more background and buildup, for which I just don&rsquo;t have space for in this article. I&rsquo;ll save these things for next time, though &ndash; stay tuned!</p> Generalizing Folds in Haskell https://danilafe.com/blog/haskell_catamorphisms/ Fri, 22 Apr 2022 12:19:22 -0700 https://danilafe.com/blog/haskell_catamorphisms/ <p>Have you encountered Haskell&rsquo;s <code>foldr</code> function? Did you know that you can use it to express any function on a list? What&rsquo;s more, there&rsquo;s a way to derive similar functions for <span class="sidenote"> <label class="sidenote-label" for="positive-note">a large class of data types in Haskell.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="positive-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Specifically, this is the class of <a href="https://en.wikipedia.org/wiki/Inductive_type">inductive types</a>. <span class="sidenote-delimiter">]</span> </span> </span> This is precisely the focus of this post. Before we get into the details, it&rsquo;s good to review the underlying concepts in a more familiar setting: functions.</p> <a href="#recursive-functions"> <h3 id="recursive-functions">Recursive Functions</h3> </a> <p>Let&rsquo;s start off with a little bit of a warmup, and take a look at a simple recursive function: <code>length</code>. Here&rsquo;s a straightforward definition:</p> <div class="highlight-group" data-base-path="" data-file-path="catamorphisms/Cata.hs" data-first-line="4" data-last-line="6"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/catamorphisms/Cata.hs#L4-L6">Cata.hs</a>, lines 4 through 6</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">length</span> <span class="ow">::</span> <span class="p">[</span><span class="n">a</span><span class="p">]</span> <span class="ow">-&gt;</span> <span class="kt">Int</span> </span></span><span class="line"><span class="cl"><span class="nf">length</span> <span class="kt">[]</span> <span class="ow">=</span> <span class="mi">0</span> </span></span><span class="line"><span class="cl"><span class="nf">length</span> <span class="p">(</span><span class="kr">_</span><span class="kt">:</span><span class="n">xs</span><span class="p">)</span> <span class="ow">=</span> <span class="mi">1</span> <span class="o">+</span> <span class="n">length</span> <span class="n">xs</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Haskell is nice because it allows for clean definitions of recursive functions; <code>length</code> can just reference itself in its definition, and everything works out in the end. In the underlying lambda calculus, though, a function definition doesn&rsquo;t come with a name &ndash; you only get anonymous functions via the lambda abstraction. There&rsquo;s no way for such functions to just refer to themselves by their name in their body. But the lambda calculus is Turing complete, so something is making recursive definitions possible.</p> <p>The trick is to rewrite your recursive function in such a way that instead of calling itself by its name (which, with anonymous functions, is hard to come by), it receives a reference to itself as an argument. As a concrete example:</p> <div class="highlight-group" data-base-path="" data-file-path="catamorphisms/Cata.hs" data-first-line="8" data-last-line="10"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/catamorphisms/Cata.hs#L8-L10">Cata.hs</a>, lines 8 through 10</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">lengthF</span> <span class="ow">::</span> <span class="p">([</span><span class="n">a</span><span class="p">]</span> <span class="ow">-&gt;</span> <span class="kt">Int</span><span class="p">)</span> <span class="ow">-&gt;</span> <span class="p">[</span><span class="n">a</span><span class="p">]</span> <span class="ow">-&gt;</span> <span class="kt">Int</span> </span></span><span class="line"><span class="cl"><span class="nf">lengthF</span> <span class="n">rec</span> <span class="kt">[]</span> <span class="ow">=</span> <span class="mi">0</span> </span></span><span class="line"><span class="cl"><span class="nf">lengthF</span> <span class="n">rec</span> <span class="p">(</span><span class="kr">_</span><span class="kt">:</span><span class="n">xs</span><span class="p">)</span> <span class="ow">=</span> <span class="mi">1</span> <span class="o">+</span> <span class="n">rec</span> <span class="n">xs</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This new function can easily me anonymous; if we enable the <code>LambdaCase</code> extension, we can write it using only lambda functions as:</p> <div class="highlight-group" data-base-path="" data-file-path="catamorphisms/Cata.hs" data-first-line="12" data-last-line="14"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/catamorphisms/Cata.hs#L12-L14">Cata.hs</a>, lines 12 through 14</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">lengthF&#39;</span> <span class="ow">=</span> <span class="nf">\</span><span class="n">rec</span> <span class="ow">-&gt;</span> <span class="nf">\</span><span class="kr">case</span> </span></span><span class="line"><span class="cl"> <span class="kt">[]</span> <span class="ow">-&gt;</span> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="kr">_</span><span class="kt">:</span><span class="n">xs</span> <span class="ow">-&gt;</span> <span class="mi">1</span> <span class="o">+</span> <span class="n">rec</span> <span class="n">xs</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This function is not equivalent to <code>length</code>, however. It expects &ldquo;itself&rdquo;, or a function which has type <code>[a] -&gt; Int</code>, to be passed in as the first argument. Once fed this <code>rec</code> argument, though, <code>lengthF</code> returns a length function. Let&rsquo;s try feed it something, then!</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">lengthF</span> <span class="n">_something</span> </span></span></code></pre></div><p>But if <code>lengthF</code> produces a length function when given this <em>something</em>, why can&rsquo;t we feed this newly-produced length function back to it?</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">lengthF</span> <span class="p">(</span><span class="n">lengthF</span> <span class="n">_something</span><span class="p">)</span> </span></span></code></pre></div><p>And again:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">lengthF</span> <span class="p">(</span><span class="n">lengthF</span> <span class="p">(</span><span class="n">lengthF</span> <span class="n">_something</span><span class="p">))</span> </span></span></code></pre></div><p>If we kept going with this process infinitely, we&rsquo;d eventually have what we need:</p> $$ \text{length} = \text{lengthF}(\text{lengthF}(\text{lengthF}(...))) $$ <p>But hey, the stuff inside the first set of parentheses is still an infinite sequence of applications of the function \(\text{lengthF}\), and we have just defined this to be \(\text{length}\). Thus, we can rewrite the above equation as:</p> $$ \text{length} = \text{lengthF}(\text{length}) $$ <p>What we have just discovered is that the actual function that we want, <code>length</code>, is a <a href="https://mathworld.wolfram.com/FixedPoint.html"class="external-link">fixed point<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> of the non-recursive function <code>lengthF</code>. Fortunately, Haskell comes with a function that can find such a fixed point. It&rsquo;s defined like this:</p> <div class="highlight-group" data-base-path="" data-file-path="catamorphisms/Cata.hs" data-first-line="16" data-last-line="16"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/catamorphisms/Cata.hs#L16-L16">Cata.hs</a>, line 16</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">16 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">fix</span> <span class="n">f</span> <span class="ow">=</span> <span class="kr">let</span> <span class="n">x</span> <span class="ow">=</span> <span class="n">f</span> <span class="n">x</span> <span class="kr">in</span> <span class="n">x</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This definition is as declarative as can be; <code>fix</code> returns the \(x\) such that \(x = f(x)\). With this, we finally write:</p> <div class="highlight-group" data-base-path="" data-file-path="catamorphisms/Cata.hs" data-first-line="18" data-last-line="18"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/catamorphisms/Cata.hs#L18-L18">Cata.hs</a>, line 18</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">18 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">length&#39;</span> <span class="ow">=</span> <span class="n">fix</span> <span class="n">lengthF</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Loading up the file in GHCi, and running the above function, we get exactly the expected results.</p> <pre tabindex="0"><code>ghci&gt; Main.length&#39; [1,2,3] 3 </code></pre><p>You may be dissatisfied with the way we handled <code>fix</code> here; we went through and pretended that we didn&rsquo;t have recursive function definitions, but then used a recursive <code>let</code>-expression in the body <code>fix</code>! This is a valid criticism, so I&rsquo;d like to briefly talk about how <code>fix</code> is used in the context of the lambda calculus.</p> <p>In the untyped typed lambda calculus, we can just define a term that behaves like <code>fix</code> does. The most common definition is the \(Y\) combinator, defined as follows:</p> $$ Y = \lambda f. (\lambda x. f (x x)) (\lambda x. f (x x )) $$ <p>When applied to a function, this combinator goes through the following evaluation steps:</p> $$ Y f = f (Y f) = f (f (Y f)) =\ ... $$ <p>This is the exact sort of infinite series of function applications that we saw above with \(\text{lengthF}\).</p> <a href="#recursive-data-types"> <h3 id="recursive-data-types">Recursive Data Types</h3> </a> <p>We have now seen how we can rewrite a recursive function as a fixed point of some non-recursive function. Another cool thing we can do, though, is to transform recursive <strong>data types</strong> in a similar manner! Let&rsquo;s start with something pretty simple.</p> <div class="highlight-group" data-base-path="" data-file-path="catamorphisms/Cata.hs" data-first-line="20" data-last-line="20"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/catamorphisms/Cata.hs#L20-L20">Cata.hs</a>, line 20</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">20 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kr">data</span> <span class="kt">MyList</span> <span class="ow">=</span> <span class="kt">MyNil</span> <span class="o">|</span> <span class="kt">MyCons</span> <span class="kt">Int</span> <span class="kt">MyList</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Just like we did with functions, we can extract the recursive occurrences of <code>MyList</code> into a parameter.</p> <div class="highlight-group" data-base-path="" data-file-path="catamorphisms/Cata.hs" data-first-line="21" data-last-line="21"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/catamorphisms/Cata.hs#L21-L21">Cata.hs</a>, line 21</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">21 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kr">data</span> <span class="kt">MyListF</span> <span class="n">a</span> <span class="ow">=</span> <span class="kt">MyNilF</span> <span class="o">|</span> <span class="kt">MyConsF</span> <span class="kt">Int</span> <span class="n">a</span> </span></span></code></pre></td></tr></table> </div> </div> </div> <p>Just like <code>lengthF</code>, <code>MyListF</code> isn&rsquo;t really a list. We can&rsquo;t write a function <code>sum :: MyListF -&gt; Int</code>. <code>MyListF</code> requires <em>something</em> as an argument, and once given that, produces a type of integer lists. Once again, let&rsquo;s try feeding it:</p> <pre tabindex="0"><code>MyListF a </code></pre><p>From the definition, we can clearly see that <code>a</code> is where the &ldquo;rest of the list&rdquo; is in the original <code>MyList</code>. So, let&rsquo;s try fill <code>a</code> with a list that we can get out of <code>MyListF</code>:</p> <pre tabindex="0"><code>MyListF (MyListF a) </code></pre><p>And again:</p> <pre tabindex="0"><code>MyListF (MyListF (MyListF a)) </code></pre><p>Much like we used a <code>fix</code> function to turn our <code>lengthF</code> into <code>length</code>, we need a data type, which we&rsquo;ll call <code>Fix</code> (and which has been <a href="https://hackage.haskell.org/package/data-fix-0.3.2/docs/Data-Fix.html"class="external-link">implemented before<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>). Here&rsquo;s the definition:</p> <div class="highlight-group" data-base-path="" data-file-path="catamorphisms/Cata.hs" data-first-line="23" data-last-line="23"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/catamorphisms/Cata.hs#L23-L23">Cata.hs</a>, line 23</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">23 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kr">newtype</span> <span class="kt">Fix</span> <span class="n">f</span> <span class="ow">=</span> <span class="kt">Fix</span> <span class="p">{</span> <span class="n">unFix</span> <span class="ow">::</span> <span class="n">f</span> <span class="p">(</span><span class="kt">Fix</span> <span class="n">f</span><span class="p">)</span> <span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Looking past the constructors and accessors, we might write the above in pseudo-Haskell as follows:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kr">newtype</span> <span class="kt">Fix</span> <span class="n">f</span> <span class="ow">=</span> <span class="n">f</span> <span class="p">(</span><span class="kt">Fix</span> <span class="n">f</span><span class="p">)</span> </span></span></code></pre></div><p>This is just like the lambda calculus \(Y\) combinator above! Unfortunately, we <em>do</em> have to deal with the cruft induced by the constructors here. Thus, to write down the list <code>[1,2,3]</code> using <code>MyListF</code>, we&rsquo;d have to produce the following:</p> <div class="highlight-group" data-base-path="" data-file-path="catamorphisms/Cata.hs" data-first-line="25" data-last-line="26"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/catamorphisms/Cata.hs#L25-L26">Cata.hs</a>, lines 25 through 26</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">25 </span><span class="lnt">26 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">testList</span> <span class="ow">::</span> <span class="kt">Fix</span> <span class="kt">MyListF</span> </span></span><span class="line"><span class="cl"><span class="nf">testList</span> <span class="ow">=</span> <span class="kt">Fix</span> <span class="p">(</span><span class="kt">MyConsF</span> <span class="mi">1</span> <span class="p">(</span><span class="kt">Fix</span> <span class="p">(</span><span class="kt">MyConsF</span> <span class="mi">2</span> <span class="p">(</span><span class="kt">Fix</span> <span class="p">(</span><span class="kt">MyConsF</span> <span class="mi">3</span> <span class="p">(</span><span class="kt">Fix</span> <span class="kt">MyNilF</span><span class="p">))))))</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This is actually done in practice when using some approaches to help address the <a href="https://en.wikipedia.org/wiki/Expression_problem"class="external-link">expression problem<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>; however, it&rsquo;s quite unpleasant to write code in this way, so we&rsquo;ll set it aside.</p> <p>Let&rsquo;s go back to our infinite chain of type applications. We&rsquo;ve a similar pattern before, with \(\text{length}\) and \(\text{lengthF}\). Just like we did then, it seems like we might be able to write something like the following:</p> $$ \begin{aligned} &amp;amp; \text{MyList} = \text{MyListF}(\text{MyListF}(\text{MyListF}(...))) \\ \Leftrightarrow\ &amp;amp; \text{MyList} = \text{MyListF}(\text{MyList}) \end{aligned} $$ <p>In something like Haskell, though, the above is not quite true. <code>MyListF</code> is a non-recursive data type, with a different set of constructors to <code>MyList</code>; they aren&rsquo;t <em>really</em> equal. Instead of equality, though, we use the next-best thing: isomorphism.</p> $$ \text{MyList} \cong \text{MyListF}(\text{MyList}) $$ <p>Two types are isomorphic when there exist a <span class="sidenote"> <label class="sidenote-label" for="fix-isomorphic-note">pair of functions, \(f\) and \(g\),</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="fix-isomorphic-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Let's a look at the types of <code>Fix</code> and <code>unFix</code>, by the way. Suppose that we <em>did</em> define <code>MyList</code> to be <code>Fix MyListF</code>. Let's specialize the <code>f</code> type parameter of <code>Fix</code> to <code>MyListF</code> for a moment, and check:<br><br> In one direction, <code>Fix :: MyListF MyList -> MyList</code><br> And in the other, <code>unFix :: MyList -> MyListF MyList</code><br><br> The two mutual inverses \(f\) and \(g\) fall out of the definition of the <code>Fix</code> data type! If we didn't have to deal with the constructor cruft, this would be more ergonomic than writing our own <code>myIn</code> and <code>myOut</code> functions. <span class="sidenote-delimiter">]</span> </span> </span> that take you from one type to the other (and vice versa), such that applying \(f\) after \(g\), or \(g\) after \(f\), gets you right back where you started. That is, \(f\) and \(g\) need to be each other&rsquo;s inverses. For our specific case, let&rsquo;s call the two functions <code>myOut</code> and <code>myIn</code> (I&rsquo;m matching the naming in <a href="https://maartenfokkinga.github.io/utwente/mmf91m.pdf"class="external-link">this paper<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>). They are not hard to define:</p> <div class="highlight-group" data-base-path="" data-file-path="catamorphisms/Cata.hs" data-first-line="28" data-last-line="34"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/catamorphisms/Cata.hs#L28-L34">Cata.hs</a>, lines 28 through 34</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">myOut</span> <span class="ow">::</span> <span class="kt">MyList</span> <span class="ow">-&gt;</span> <span class="kt">MyListF</span> <span class="kt">MyList</span> </span></span><span class="line"><span class="cl"><span class="nf">myOut</span> <span class="kt">MyNil</span> <span class="ow">=</span> <span class="kt">MyNilF</span> </span></span><span class="line"><span class="cl"><span class="nf">myOut</span> <span class="p">(</span><span class="kt">MyCons</span> <span class="n">i</span> <span class="n">xs</span><span class="p">)</span> <span class="ow">=</span> <span class="kt">MyConsF</span> <span class="n">i</span> <span class="n">xs</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="nf">myIn</span> <span class="ow">::</span> <span class="kt">MyListF</span> <span class="kt">MyList</span> <span class="ow">-&gt;</span> <span class="kt">MyList</span> </span></span><span class="line"><span class="cl"><span class="nf">myIn</span> <span class="kt">MyNilF</span> <span class="ow">=</span> <span class="kt">MyNil</span> </span></span><span class="line"><span class="cl"><span class="nf">myIn</span> <span class="p">(</span><span class="kt">MyConsF</span> <span class="n">i</span> <span class="n">xs</span><span class="p">)</span> <span class="ow">=</span> <span class="kt">MyCons</span> <span class="n">i</span> <span class="n">xs</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>By the way, when a data type is a fixed point of some other, non-recursive type constructor, this second type constructor is called a <strong>base functor</strong>. We can verify that <code>MyListF</code> is a functor by providing an instance (which is rather straightforward):</p> <div class="highlight-group" data-base-path="" data-file-path="catamorphisms/Cata.hs" data-first-line="36" data-last-line="38"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/catamorphisms/Cata.hs#L36-L38">Cata.hs</a>, lines 36 through 38</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kr">instance</span> <span class="kt">Functor</span> <span class="kt">MyListF</span> <span class="kr">where</span> </span></span><span class="line"><span class="cl"> <span class="n">fmap</span> <span class="n">f</span> <span class="kt">MyNilF</span> <span class="ow">=</span> <span class="kt">MyNilF</span> </span></span><span class="line"><span class="cl"> <span class="n">fmap</span> <span class="n">f</span> <span class="p">(</span><span class="kt">MyConsF</span> <span class="n">i</span> <span class="n">a</span><span class="p">)</span> <span class="ow">=</span> <span class="kt">MyConsF</span> <span class="n">i</span> <span class="p">(</span><span class="n">f</span> <span class="n">a</span><span class="p">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#recursive-functions-with-base-functors"> <h3 id="recursive-functions-with-base-functors">Recursive Functions with Base Functors</h3> </a> <p>One neat thing you can do with a base functor is define recursive functions on the actual data type!</p> <p>Let&rsquo;s go back to the very basics. When we write recursive functions, we try to think of it as solving a problem, assuming that we are given solutions to the sub-problems that make it up. In the more specific case of recursive functions on data types, we think of it as performing a given operation, assuming that we know how to perform this operation on the smaller pieces of the data structure. Some quick examples:</p> <ol> <li>When writing a <code>sum</code> function on a list, we assume we know how to find the sum of the list&rsquo;s tail (<code>sum xs</code>), and add to it the current element (<code>x+</code>). Of course, if we&rsquo;re looking at a part of a data structure that&rsquo;s not recursive, we don&rsquo;t need to perform any work on its constituent pieces. <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">sum</span> <span class="kt">[]</span> <span class="ow">=</span> <span class="mi">0</span> </span></span><span class="line"><span class="cl"><span class="nf">sum</span> <span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="n">xs</span><span class="p">)</span> <span class="ow">=</span> <span class="n">x</span> <span class="o">+</span> <span class="n">sum</span> <span class="n">xs</span> </span></span></code></pre></div></li> <li>When writing a function to invert a binary tree, we assume that we can invert the left and right children of a non-leaf node. We might write: <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">invert</span> <span class="kt">Leaf</span> <span class="ow">=</span> <span class="kt">Leaf</span> </span></span><span class="line"><span class="cl"><span class="nf">invert</span> <span class="p">(</span><span class="kt">Node</span> <span class="n">l</span> <span class="n">r</span><span class="p">)</span> <span class="ow">=</span> <span class="kt">Node</span> <span class="p">(</span><span class="n">invert</span> <span class="n">r</span><span class="p">)</span> <span class="p">(</span><span class="n">invert</span> <span class="n">l</span><span class="p">)</span> </span></span></code></pre></div></li> </ol> <p>What does this have to do with base functors? Well, recall how we arrived at <code>MyListF</code> from <code>MyList</code>: we replaced every occurrence of <code>MyList</code> in the definition with a type parameter <code>a</code>. Let me reiterate: wherever we had a sub-list in our definition, we replaced it with <code>a</code>. The <code>a</code> in <code>MyListF</code> marks the locations where we <em>would</em> have to use recursion if we were to define a function on <code>MyList</code>.</p> <p>What if instead of a stand-in for the list type (as it was until now), we use <code>a</code> to represent the result of the recursive call on that sub-list? To finish computing the sum of the list, then, the following would suffice:</p> <div class="highlight-group" data-base-path="" data-file-path="catamorphisms/Cata.hs" data-first-line="40" data-last-line="42"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/catamorphisms/Cata.hs#L40-L42">Cata.hs</a>, lines 40 through 42</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">mySumF</span> <span class="ow">::</span> <span class="kt">MyListF</span> <span class="kt">Int</span> <span class="ow">-&gt;</span> <span class="kt">Int</span> </span></span><span class="line"><span class="cl"><span class="nf">mySumF</span> <span class="kt">MyNilF</span> <span class="ow">=</span> <span class="mi">0</span> </span></span><span class="line"><span class="cl"><span class="nf">mySumF</span> <span class="p">(</span><span class="kt">MyConsF</span> <span class="n">i</span> <span class="n">rest</span><span class="p">)</span> <span class="ow">=</span> <span class="n">i</span> <span class="o">+</span> <span class="n">rest</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Actually, this is enough to define the whole <code>sum</code> function. First things first, let&rsquo;s use <code>myOut</code> to unpack one level of the <code>Mylist</code> type:</p> <div class="highlight-group" data-base-path="" data-file-path="catamorphisms/Cata.hs" data-first-line="28" data-last-line="28"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/catamorphisms/Cata.hs#L28-L28">Cata.hs</a>, line 28</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">28 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">myOut</span> <span class="ow">::</span> <span class="kt">MyList</span> <span class="ow">-&gt;</span> <span class="kt">MyListF</span> <span class="kt">MyList</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We know that <code>MyListF</code> is a functor; we can thus use <code>fmap sum</code> to compute the sum of the remaining list:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">fmap</span> <span class="n">mySum</span> <span class="ow">::</span> <span class="kt">MyListF</span> <span class="kt">MyList</span> <span class="ow">-&gt;</span> <span class="kt">MyListF</span> <span class="kt">Int</span> </span></span></code></pre></div><p>Finally, we can use our <code>mySumF</code> to handle the last addition:</p> <div class="highlight-group" data-base-path="" data-file-path="catamorphisms/Cata.hs" data-first-line="40" data-last-line="40"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/catamorphisms/Cata.hs#L40-L40">Cata.hs</a>, line 40</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">40 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">mySumF</span> <span class="ow">::</span> <span class="kt">MyListF</span> <span class="kt">Int</span> <span class="ow">-&gt;</span> <span class="kt">Int</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Let&rsquo;s put all of these together:</p> <div class="highlight-group" data-base-path="" data-file-path="catamorphisms/Cata.hs" data-first-line="44" data-last-line="45"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/catamorphisms/Cata.hs#L44-L45">Cata.hs</a>, lines 44 through 45</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">44 </span><span class="lnt">45 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">mySum</span> <span class="ow">::</span> <span class="kt">MyList</span> <span class="ow">-&gt;</span> <span class="kt">Int</span> </span></span><span class="line"><span class="cl"><span class="nf">mySum</span> <span class="ow">=</span> <span class="n">mySumF</span> <span class="o">.</span> <span class="n">fmap</span> <span class="n">mySum</span> <span class="o">.</span> <span class="n">myOut</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Notice, though, that the exact same approach would work for <em>any</em> function with type:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kt">MyListF</span> <span class="n">a</span> <span class="ow">-&gt;</span> <span class="n">a</span> </span></span></code></pre></div><p>We can thus write a generalized version of <code>mySum</code> that, instead of using <code>mySumF</code>, uses some arbitrary function <code>f</code> with the aforementioned type:</p> <div class="highlight-group" data-base-path="" data-file-path="catamorphisms/Cata.hs" data-first-line="47" data-last-line="48"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/catamorphisms/Cata.hs#L47-L48">Cata.hs</a>, lines 47 through 48</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">47 </span><span class="lnt">48 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">myCata</span> <span class="ow">::</span> <span class="p">(</span><span class="kt">MyListF</span> <span class="n">a</span> <span class="ow">-&gt;</span> <span class="n">a</span><span class="p">)</span> <span class="ow">-&gt;</span> <span class="kt">MyList</span> <span class="ow">-&gt;</span> <span class="n">a</span> </span></span><span class="line"><span class="cl"><span class="nf">myCata</span> <span class="n">f</span> <span class="ow">=</span> <span class="n">f</span> <span class="o">.</span> <span class="n">fmap</span> <span class="p">(</span><span class="n">myCata</span> <span class="n">f</span><span class="p">)</span> <span class="o">.</span> <span class="n">myOut</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Let&rsquo;s use <code>myCata</code> to write a few other functions:</p> <div class="highlight-group" data-base-path="" data-file-path="catamorphisms/Cata.hs" data-first-line="50" data-last-line="60"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/catamorphisms/Cata.hs#L50-L60">Cata.hs</a>, lines 50 through 60</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">myLength</span> <span class="ow">=</span> <span class="n">myCata</span> <span class="o">$</span> <span class="nf">\</span><span class="kr">case</span> </span></span><span class="line"><span class="cl"> <span class="kt">MyNilF</span> <span class="ow">-&gt;</span> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="kt">MyConsF</span> <span class="kr">_</span> <span class="n">l</span> <span class="ow">-&gt;</span> <span class="mi">1</span> <span class="o">+</span> <span class="n">l</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="nf">myMax</span> <span class="ow">=</span> <span class="n">myCata</span> <span class="o">$</span> <span class="nf">\</span><span class="kr">case</span> </span></span><span class="line"><span class="cl"> <span class="kt">MyNilF</span> <span class="ow">-&gt;</span> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="kt">MyConsF</span> <span class="n">x</span> <span class="n">y</span> <span class="ow">-&gt;</span> <span class="n">max</span> <span class="n">x</span> <span class="n">y</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="nf">myMin</span> <span class="ow">=</span> <span class="n">myCata</span> <span class="o">$</span> <span class="nf">\</span><span class="kr">case</span> </span></span><span class="line"><span class="cl"> <span class="kt">MyNilF</span> <span class="ow">-&gt;</span> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="kt">MyConsF</span> <span class="n">x</span> <span class="n">y</span> <span class="ow">-&gt;</span> <span class="n">min</span> <span class="n">x</span> <span class="n">y</span></span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#its-just-a-foldr"> <h4 id="its-just-a-foldr">It&rsquo;s just a <code>foldr</code>!</h4> </a> <p>When you write a function with the type <code>MyListF a -&gt; a</code>, you are actually providing two things: a &ldquo;base case&rdquo; element of type <code>a</code>, for when you match <code>MyNilF</code>, and a &ldquo;combining function&rdquo; with type <code>Int -&gt; a -&gt; a</code>, for when you match <code>MyConsF</code>. We can thus define:</p> <div class="highlight-group" data-base-path="" data-file-path="catamorphisms/Cata.hs" data-first-line="64" data-last-line="66"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/catamorphisms/Cata.hs#L64-L66">Cata.hs</a>, lines 64 through 66</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">64 </span><span class="lnt">65 </span><span class="lnt">66 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">pack</span> <span class="ow">::</span> <span class="n">a</span> <span class="ow">-&gt;</span> <span class="p">(</span><span class="kt">Int</span> <span class="ow">-&gt;</span> <span class="n">a</span> <span class="ow">-&gt;</span> <span class="n">a</span><span class="p">)</span> <span class="ow">-&gt;</span> <span class="kt">MyListF</span> <span class="n">a</span> <span class="ow">-&gt;</span> <span class="n">a</span> </span></span><span class="line"><span class="cl"><span class="nf">pack</span> <span class="n">b</span> <span class="n">f</span> <span class="kt">MyNilF</span> <span class="ow">=</span> <span class="n">b</span> </span></span><span class="line"><span class="cl"><span class="nf">pack</span> <span class="n">b</span> <span class="n">f</span> <span class="p">(</span><span class="kt">MyConsF</span> <span class="n">x</span> <span class="n">y</span><span class="p">)</span> <span class="ow">=</span> <span class="n">f</span> <span class="n">x</span> <span class="n">y</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We could also go in the opposite direction, by writing:</p> <div class="highlight-group" data-base-path="" data-file-path="catamorphisms/Cata.hs" data-first-line="68" data-last-line="69"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/catamorphisms/Cata.hs#L68-L69">Cata.hs</a>, lines 68 through 69</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">68 </span><span class="lnt">69 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">unpack</span> <span class="ow">::</span> <span class="p">(</span><span class="kt">MyListF</span> <span class="n">a</span> <span class="ow">-&gt;</span> <span class="n">a</span><span class="p">)</span> <span class="ow">-&gt;</span> <span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="kt">Int</span> <span class="ow">-&gt;</span> <span class="n">a</span> <span class="ow">-&gt;</span> <span class="n">a</span><span class="p">)</span> </span></span><span class="line"><span class="cl"><span class="nf">unpack</span> <span class="n">f</span> <span class="ow">=</span> <span class="p">(</span><span class="n">f</span> <span class="kt">MyNilF</span><span class="p">,</span> <span class="nf">\</span><span class="n">i</span> <span class="n">a</span> <span class="ow">-&gt;</span> <span class="n">f</span> <span class="p">(</span><span class="kt">MyConsF</span> <span class="n">i</span> <span class="n">a</span><span class="p">))</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Hey, what was it that we said about types with two functions between them, which are inverses of each other? That&rsquo;s right, <code>MyListF a -&gt; a</code> and <code>(a, Int -&gt; a -&gt; a)</code> are isomorphic. The function <code>myCata</code>, and the &ldquo;traditional&rdquo; definition of <code>foldr</code> are equivalent!</p> <a href="#base-functors-for-all"> <h4 id="base-functors-for-all">Base Functors for All!</h4> </a> <p>We&rsquo;ve been playing with <code>MyList</code> for a while now, but it&rsquo;s kind of getting boring: it&rsquo;s just a list of integers! Furthermore, we&rsquo;re not <em>really</em> getting anything out of this new &ldquo;generalization&rdquo; procedure &ndash; <code>foldr</code> is part of the standard library, and we&rsquo;ve just reinvented the wheel.</p> <p>But you see, we haven&rsquo;t quite. This is because, while we&rsquo;ve only been working with <code>MyListF</code>, the base functor for <code>MyList</code>, our approach works for <em>any recursive data type</em>, provided an <code>out</code> function. Let&rsquo;s define a type class, <code>Cata</code>, which pairs a data type <code>a</code> with its base functor <code>f</code>, and specifies how to &ldquo;unpack&rdquo; <code>a</code>:</p> <div class="highlight-group" data-base-path="" data-file-path="catamorphisms/Cata.hs" data-first-line="71" data-last-line="72"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/catamorphisms/Cata.hs#L71-L72">Cata.hs</a>, lines 71 through 72</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">71 </span><span class="lnt">72 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kr">class</span> <span class="kt">Functor</span> <span class="n">f</span> <span class="ow">=&gt;</span> <span class="kt">Cata</span> <span class="n">a</span> <span class="n">f</span> <span class="kr">where</span> </span></span><span class="line"><span class="cl"> <span class="n">out</span> <span class="ow">::</span> <span class="n">a</span> <span class="ow">-&gt;</span> <span class="n">f</span> <span class="n">a</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We can now provide a more generic version of our <code>myCata</code>, one that works for all types with a base functor:</p> <div class="highlight-group" data-base-path="" data-file-path="catamorphisms/Cata.hs" data-first-line="74" data-last-line="75"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/catamorphisms/Cata.hs#L74-L75">Cata.hs</a>, lines 74 through 75</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">74 </span><span class="lnt">75 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">cata</span> <span class="ow">::</span> <span class="kt">Cata</span> <span class="n">a</span> <span class="n">f</span> <span class="ow">=&gt;</span> <span class="p">(</span><span class="n">f</span> <span class="n">b</span> <span class="ow">-&gt;</span> <span class="n">b</span><span class="p">)</span> <span class="ow">-&gt;</span> <span class="n">a</span> <span class="ow">-&gt;</span> <span class="n">b</span> </span></span><span class="line"><span class="cl"><span class="nf">cata</span> <span class="n">f</span> <span class="ow">=</span> <span class="n">f</span> <span class="o">.</span> <span class="n">fmap</span> <span class="p">(</span><span class="n">cata</span> <span class="n">f</span><span class="p">)</span> <span class="o">.</span> <span class="n">out</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Clearly, <code>MyList</code> and <code>MyListF</code> are one instance of this type class:</p> <div class="highlight-group" data-base-path="" data-file-path="catamorphisms/Cata.hs" data-first-line="77" data-last-line="78"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/catamorphisms/Cata.hs#L77-L78">Cata.hs</a>, lines 77 through 78</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">77 </span><span class="lnt">78 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kr">instance</span> <span class="kt">Cata</span> <span class="kt">MyList</span> <span class="kt">MyListF</span> <span class="kr">where</span> </span></span><span class="line"><span class="cl"> <span class="n">out</span> <span class="ow">=</span> <span class="n">myOut</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We can also write a base functor for Haskell&rsquo;s built-in list type, <code>[a]</code>:</p> <div class="highlight-group" data-base-path="" data-file-path="catamorphisms/Cata.hs" data-first-line="80" data-last-line="84"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/catamorphisms/Cata.hs#L80-L84">Cata.hs</a>, lines 80 through 84</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">80 </span><span class="lnt">81 </span><span class="lnt">82 </span><span class="lnt">83 </span><span class="lnt">84 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kr">data</span> <span class="kt">ListF</span> <span class="n">a</span> <span class="n">b</span> <span class="ow">=</span> <span class="kt">Nil</span> <span class="o">|</span> <span class="kt">Cons</span> <span class="n">a</span> <span class="n">b</span> <span class="kr">deriving</span> <span class="kt">Functor</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kr">instance</span> <span class="kt">Cata</span> <span class="p">[</span><span class="n">a</span><span class="p">]</span> <span class="p">(</span><span class="kt">ListF</span> <span class="n">a</span><span class="p">)</span> <span class="kr">where</span> </span></span><span class="line"><span class="cl"> <span class="n">out</span> <span class="kt">[]</span> <span class="ow">=</span> <span class="kt">Nil</span> </span></span><span class="line"><span class="cl"> <span class="n">out</span> <span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="n">xs</span><span class="p">)</span> <span class="ow">=</span> <span class="kt">Cons</span> <span class="n">x</span> <span class="n">xs</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We can use our <code>cata</code> function for regular lists to define a generic <code>sum</code>:</p> <div class="highlight-group" data-base-path="" data-file-path="catamorphisms/Cata.hs" data-first-line="86" data-last-line="89"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/catamorphisms/Cata.hs#L86-L89">Cata.hs</a>, lines 86 through 89</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">86 </span><span class="lnt">87 </span><span class="lnt">88 </span><span class="lnt">89 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">sum</span> <span class="ow">::</span> <span class="kt">Num</span> <span class="n">a</span> <span class="ow">=&gt;</span> <span class="p">[</span><span class="n">a</span><span class="p">]</span> <span class="ow">-&gt;</span> <span class="n">a</span> </span></span><span class="line"><span class="cl"><span class="nf">sum</span> <span class="ow">=</span> <span class="n">cata</span> <span class="o">$</span> <span class="nf">\</span><span class="kr">case</span> </span></span><span class="line"><span class="cl"> <span class="kt">Nil</span> <span class="ow">-&gt;</span> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="kt">Cons</span> <span class="n">x</span> <span class="n">xs</span> <span class="ow">-&gt;</span> <span class="n">x</span> <span class="o">+</span> <span class="n">xs</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>It works perfectly:</p> <pre tabindex="0"><code>ghci&gt; Main.sum [1,2,3] 6 ghci&gt; Main.sum [1,2,3.0] 6.0 ghci&gt; Main.sum [1,2,3.0,-1] 5.0 </code></pre><p>What about binary trees, which served as our second example of a recursive data structure? We can do that, too:</p> <div class="highlight-group" data-base-path="" data-file-path="catamorphisms/Cata.hs" data-first-line="91" data-last-line="96"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/catamorphisms/Cata.hs#L91-L96">Cata.hs</a>, lines 91 through 96</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">91 </span><span class="lnt">92 </span><span class="lnt">93 </span><span class="lnt">94 </span><span class="lnt">95 </span><span class="lnt">96 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kr">data</span> <span class="kt">BinaryTree</span> <span class="n">a</span> <span class="ow">=</span> <span class="kt">Node</span> <span class="n">a</span> <span class="p">(</span><span class="kt">BinaryTree</span> <span class="n">a</span><span class="p">)</span> <span class="p">(</span><span class="kt">BinaryTree</span> <span class="n">a</span><span class="p">)</span> <span class="o">|</span> <span class="kt">Leaf</span> <span class="kr">deriving</span> <span class="p">(</span><span class="kt">Show</span><span class="p">,</span> <span class="kt">Foldable</span><span class="p">)</span> </span></span><span class="line"><span class="cl"><span class="kr">data</span> <span class="kt">BinaryTreeF</span> <span class="n">a</span> <span class="n">b</span> <span class="ow">=</span> <span class="kt">NodeF</span> <span class="n">a</span> <span class="n">b</span> <span class="n">b</span> <span class="o">|</span> <span class="kt">LeafF</span> <span class="kr">deriving</span> <span class="kt">Functor</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kr">instance</span> <span class="kt">Cata</span> <span class="p">(</span><span class="kt">BinaryTree</span> <span class="n">a</span><span class="p">)</span> <span class="p">(</span><span class="kt">BinaryTreeF</span> <span class="n">a</span><span class="p">)</span> <span class="kr">where</span> </span></span><span class="line"><span class="cl"> <span class="n">out</span> <span class="p">(</span><span class="kt">Node</span> <span class="n">a</span> <span class="n">l</span> <span class="n">r</span><span class="p">)</span> <span class="ow">=</span> <span class="kt">NodeF</span> <span class="n">a</span> <span class="n">l</span> <span class="n">r</span> </span></span><span class="line"><span class="cl"> <span class="n">out</span> <span class="kt">Leaf</span> <span class="ow">=</span> <span class="kt">LeafF</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Given this, here&rsquo;s an implementation of that <code>invert</code> function we mentioned earlier:</p> <div class="highlight-group" data-base-path="" data-file-path="catamorphisms/Cata.hs" data-first-line="98" data-last-line="101"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/catamorphisms/Cata.hs#L98-L101">Cata.hs</a>, lines 98 through 101</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 98 </span><span class="lnt"> 99 </span><span class="lnt">100 </span><span class="lnt">101 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">invert</span> <span class="ow">::</span> <span class="kt">BinaryTree</span> <span class="n">a</span> <span class="ow">-&gt;</span> <span class="kt">BinaryTree</span> <span class="n">a</span> </span></span><span class="line"><span class="cl"><span class="nf">invert</span> <span class="ow">=</span> <span class="n">cata</span> <span class="o">$</span> <span class="nf">\</span><span class="kr">case</span> </span></span><span class="line"><span class="cl"> <span class="kt">LeafF</span> <span class="ow">-&gt;</span> <span class="kt">Leaf</span> </span></span><span class="line"><span class="cl"> <span class="kt">NodeF</span> <span class="n">a</span> <span class="n">l</span> <span class="n">r</span> <span class="ow">-&gt;</span> <span class="kt">Node</span> <span class="n">a</span> <span class="n">r</span> <span class="n">l</span></span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#degenerate-cases"> <h4 id="degenerate-cases">Degenerate Cases</h4> </a> <p>Actually, the data types we consider don&rsquo;t have to be recursive. We can apply the same procedure of replacing recursive occurrences in a data type&rsquo;s definition with a new type parameter to <code>Maybe</code>; the only difference is that now the new parameter will not be used!</p> <div class="highlight-group" data-base-path="" data-file-path="catamorphisms/Cata.hs" data-first-line="103" data-last-line="107"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/catamorphisms/Cata.hs#L103-L107">Cata.hs</a>, lines 103 through 107</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">103 </span><span class="lnt">104 </span><span class="lnt">105 </span><span class="lnt">106 </span><span class="lnt">107 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kr">data</span> <span class="kt">MaybeF</span> <span class="n">a</span> <span class="n">b</span> <span class="ow">=</span> <span class="kt">NothingF</span> <span class="o">|</span> <span class="kt">JustF</span> <span class="n">a</span> <span class="kr">deriving</span> <span class="kt">Functor</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kr">instance</span> <span class="kt">Cata</span> <span class="p">(</span><span class="kt">Maybe</span> <span class="n">a</span><span class="p">)</span> <span class="p">(</span><span class="kt">MaybeF</span> <span class="n">a</span><span class="p">)</span> <span class="kr">where</span> </span></span><span class="line"><span class="cl"> <span class="n">out</span> <span class="kt">Nothing</span> <span class="ow">=</span> <span class="kt">NothingF</span> </span></span><span class="line"><span class="cl"> <span class="n">out</span> <span class="p">(</span><span class="kt">Just</span> <span class="n">x</span><span class="p">)</span> <span class="ow">=</span> <span class="kt">JustF</span> <span class="n">x</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>And then we can define a function on <code>Maybe</code> using <code>cata</code>:</p> <div class="highlight-group" data-base-path="" data-file-path="catamorphisms/Cata.hs" data-first-line="109" data-last-line="112"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/catamorphisms/Cata.hs#L109-L112">Cata.hs</a>, lines 109 through 112</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">109 </span><span class="lnt">110 </span><span class="lnt">111 </span><span class="lnt">112 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">getOrDefault</span> <span class="ow">::</span> <span class="n">a</span> <span class="ow">-&gt;</span> <span class="kt">Maybe</span> <span class="n">a</span> <span class="ow">-&gt;</span> <span class="n">a</span> </span></span><span class="line"><span class="cl"><span class="nf">getOrDefault</span> <span class="n">d</span> <span class="ow">=</span> <span class="n">cata</span> <span class="o">$</span> <span class="nf">\</span><span class="kr">case</span> </span></span><span class="line"><span class="cl"> <span class="kt">NothingF</span> <span class="ow">-&gt;</span> <span class="n">d</span> </span></span><span class="line"><span class="cl"> <span class="kt">JustF</span> <span class="n">a</span> <span class="ow">-&gt;</span> <span class="n">a</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This isn&rsquo;t <em>really</em> useful, since we&rsquo;re still pattern matching on a type that looks identical to <code>Maybe</code> itself. There is one reason that I bring it up, though. Remember how <code>foldr</code> was equivalent to <code>cata</code> for <code>MyList</code>, because defining a function <code>MyListF a -&gt; a</code> was the same as providing a base case <code>a</code> and a &ldquo;combining function&rdquo; <code>Int -&gt; a -&gt; a</code>? Well, defining a function <code>MaybeF x a -&gt; a</code> is the same as providing a base case <code>a</code> (for <code>NothingF</code>) and a handler for the contained value, <code>x -&gt; a</code>. So we might imagine the <code>foldr</code> function for <code>Maybe</code> to have type:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">maybeFold</span> <span class="ow">::</span> <span class="n">a</span> <span class="ow">-&gt;</span> <span class="p">(</span><span class="n">x</span> <span class="ow">-&gt;</span> <span class="n">a</span><span class="p">)</span> <span class="ow">-&gt;</span> <span class="kt">Maybe</span> <span class="n">x</span> <span class="ow">-&gt;</span> <span class="n">a</span> </span></span></code></pre></div><p>This is exactly the function <a href="https://hackage.haskell.org/package/base-4.16.1.0/docs/Data-Maybe.html#v:maybe"class="external-link"><code>maybe</code> from <code>Data.Maybe</code><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>! Hopefully you can follow a similar process in your head to arrive at &ldquo;fold&rdquo; functions for <code>Either</code> and <code>Bool</code>. Indeed, there are functions that correspond to these data types in the Haskell standard library, named <a href="https://hackage.haskell.org/package/base-4.16.1.0/docs/Data-Either.html#v:either"class="external-link"><code>either</code><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> and <a href="https://hackage.haskell.org/package/base-4.16.1.0/docs/Data-Bool.html#v:bool"class="external-link"><code>bool</code><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. Much like <code>fold</code> can be used to represent any function on lists, <code>maybe</code>, <code>either</code>, and <code>bool</code> can be used to represent any function on their corresponding data types. I think that&rsquo;s neat.</p> <a href="#what-about-foldable"> <h4 id="what-about-foldable">What About <code>Foldable</code>?</h4> </a> <p>If you&rsquo;ve been around the Haskell ecosystem, you may know the <code>Foldable</code> type class. Isn&rsquo;t this exactly what we&rsquo;ve been working towards here? No, not at all. Take a look at how the documentation describes <a href="https://hackage.haskell.org/package/base-4.16.1.0/docs/Data-Foldable.html"class="external-link"><code>Data.Foldable</code><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>:</p> <blockquote> <p>The Foldable class represents data structures that can be reduced to a summary value one element at a time.</p> </blockquote> <p>One at a time, huh? Take a look at the signature of <code>foldMap</code>, which is sufficient for an instance of <code>Foldable</code>:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">foldMap</span> <span class="ow">::</span> <span class="kt">Monoid</span> <span class="n">m</span> <span class="ow">=&gt;</span> <span class="p">(</span><span class="n">a</span> <span class="ow">-&gt;</span> <span class="n">m</span><span class="p">)</span> <span class="ow">-&gt;</span> <span class="n">t</span> <span class="n">a</span> <span class="ow">-&gt;</span> <span class="n">m</span> </span></span></code></pre></div><p>A <code>Monoid</code> is just a type with an associative binary operation that has an identity element. Then, <code>foldMap</code> simply visits the data structure in order, and applies this binary operation pairwise to each monoid produced via <code>f</code>. Alas, this function is not enough to be able to implement something like inverting a binary tree; there are different configurations of binary tree that, when visited in-order, result in the same sequence of elements. For example:</p> <pre tabindex="0"><code>ghci&gt; fold (Node &#34;Hello&#34; Leaf (Node &#34;, &#34; Leaf (Node &#34;World!&#34; Leaf Leaf))) &#34;Hello, World!&#34; ghci&gt; fold (Node &#34;Hello&#34; (Node &#34;, &#34; Leaf Leaf) (Node &#34;World!&#34; Leaf Leaf)) &#34;Hello, World!&#34; </code></pre><p>As far as <code>fold</code> (which is just <code>foldMap id</code>) is concerned, the two trees are equivalent. They are very much not equivalent for the purposes of inversion! Thus, whereas <code>Foldable</code> helps us work with list-like data types, the <code>Cata</code> type class lets us express <em>any</em> function on a recursive data type similarly to how we&rsquo;d do it with <code>foldr</code> and lists.</p> <a href="#catamorphisms"> <h4 id="catamorphisms">Catamorphisms</h4> </a> <p>Why is the type class called <code>Cata</code>, and the function <code>cata</code>? Well, a function that performs a computation by recursively visiting the data structure is called a catamorphism. Indeed, <code>foldr f b</code>, for function <code>f</code> an &ldquo;base value&rdquo; <code>b</code> is an example of a list catamorophism. It&rsquo;s a fancy word, and there are some fancier descriptions of what it is, especially when you step into category theory (check out the <a href="https://en.wikipedia.org/wiki/Catamorphism"class="external-link">Wikipedia entry<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> if you want to know what I mean). However, for our purposes, a catamorphism is just a generalization of <code>foldr</code> from lists to any data type!</p> Declaratively Deploying Multiple Blog Versions with NixOS and Flakes https://danilafe.com/blog/blog_with_nix/ Sun, 10 Apr 2022 00:24:58 -0700 https://danilafe.com/blog/blog_with_nix/ <a href="#prologue"> <h3 id="prologue">Prologue</h3> </a> <p>You can skip this section if you&rsquo;d like.</p> <p>For the last few days, I&rsquo;ve been stuck inside of my room due to some kind of cold or flu, which or <span class="sidenote"> <label class="sidenote-label" for="pcr-note">may or may not be COVID™.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="pcr-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> The results of the PCR test are pending at the time of writing. <span class="sidenote-delimiter">]</span> </span> </span> In seeming correspondence with the progression of my cold, a thought occurred in the back of my mind: &ldquo;<em>Your blog deployment is kind of a mess</em>&rdquo;. On the first day, when I felt only a small tingling in my throat, I waved that thought away pretty easily. On the second day, feeling unwell and staying in bed, I couldn&rsquo;t help but start to look up Nix documentation. And finally, on the third day, between coughing fits and overconsumption of oral analgesic, I got to work.</p> <p>In short, this post is the closest thing I&rsquo;ve written to a fever dream.</p> <a href="#the-constraints"> <h3 id="the-constraints">The Constraints</h3> </a> <p>I run several versions of this site. The first is, of course, the &ldquo;production&rdquo; version, hosted at the time of writing on <code>danilafe.com</code> and containing the articles that I would like to share with the world. The second is a version of this site on which drafts are displayed - this way, I can share posts with my friends before they are published, get feedback, and even just re-read what I wrote from any device that has an internet connection. The third is the Russian version of my blog. It&rsquo;s rather empty, because translation is hard work, so it only exists so far as another &ldquo;draft&rdquo; website.</p> <p>Currently, only my main site is behind HTTPS. However, I would like for it to be possible to adjust this, and possibly even switch my hosts without changing any of the code that actually builds my blog.</p> <p>I wanted to be able to represent all of this complexity in my NixOS configuration file, and that&rsquo;s what this post is about!</p> <a href="#why-flakes"> <h3 id="why-flakes">Why Flakes</h3> </a> <p>I decided to use Nix flakes to manage my configuration. But what is it that made me do so? Well, two things:</p> <ul> <li><strong>Adding custom packages</strong>. The Nix code for my blog provides a package / derivation for each version of my website, and I want to use these packages in my <code>configuration.nix</code>. Adding custom packages is typically done using overlays; however, how should my system configuration get my overlay Nix expression? I would like to be able to separate my build-the-blog code from my describe-the-server code, and so I need a clean way to let my system access the former from the latter. Flakes solve this issue by letting me specify a blog flake, and pull it in as one of the system configuration&rsquo;s inputs.</li> <li><strong>Versioning</strong>. My process for deploying new versions of the site prior to flakes boiled down to fetching the latest commit from the <code>master</code> branch of my blog repository, and updating the <code>default.nix</code> file with the corresponding hash. This way, I could reliably fetch the version of my site that I wanted published. Flakes do the same thing: the <code>flake.lock</code> file contains the hashes of the Git-based dependencies of a flake, and thus prevents builds from accidentally pulling in something else. However, unlike my approach, which relies on custom scripts and extra tools such as <code>jq</code>, the locking mechanism used by flakes is provided with standard Nix tooling. Using flakes also guarantees that my build process won&rsquo;t break with updates to Hugo or Ruby, since the <code>nixpkgs</code> version is stored in <code>flake.lock</code>, too.</li> </ul> <a href="#the-final-result"> <h3 id="the-final-result">The Final Result</h3> </a> <p>Here&rsquo;s the relevant section of my configuration:</p> <div class="highlight-group" data-base-path="server-config" data-file-path="server-config/configuration.nix" data-first-line="42" data-last-line="59"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Nix-Configs/server-config/src/commit/98cffe09546aee1678f7baebdea5eb5fef288935/configuration.nix#L42-L59">configuration.nix</a>, lines 42 through 59</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Nix" data-lang="Nix"><span class="line"><span class="cl"> <span class="n">services</span><span class="o">.</span><span class="n">danilafe-blog</span> <span class="o">=</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">enable</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">challengePath</span> <span class="o">=</span> <span class="s2">&#34;/var/www/challenges&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">sites</span> <span class="o">=</span> <span class="p">[</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">builders</span><span class="o">.</span><span class="n">english</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">ssl</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">host</span> <span class="o">=</span> <span class="s2">&#34;danilafe.com&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">})</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">builders</span><span class="o">.</span><span class="n">english</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">drafts</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">host</span> <span class="o">=</span> <span class="s2">&#34;drafts.danilafe.com&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">})</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">builders</span><span class="o">.</span><span class="n">russian</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">drafts</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">host</span> <span class="o">=</span> <span class="s2">&#34;drafts.ru.danilafe.com&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">})</span> </span></span><span class="line"><span class="cl"> <span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>I really like how this turned out for three reasons. First, it&rsquo;s very clear from the configuration what I want from my server: three virtual hosts, one with HTTPS, one with drafts, and one with drafts and <em>in Russian</em>. Second, there&rsquo;s plenty of code reuse. I&rsquo;m using two builder functions, <code>english</code> and <code>russian</code>, but under the hood, the exact same code is being used to run Hugo and perform all the necessary post-processing. Finally, all of this can be used pretty much immediately given my blog flake, which reduces the amount of glue code I have to write.</p> <a href="#getting-there"> <h3 id="getting-there">Getting There</h3> </a> <a href="#a-derivation-builder"> <h4 id="a-derivation-builder">A Derivation Builder</h4> </a> <p>As I mentioned earlier, I need to generate multiple versions of my blog. All of these use pretty much the same build process &ndash; run Hugo on the Markdown files, then do some post-processing (in particular, convert the LaTeX in the resulting pages into MathML and nice-looking HTML). I didn&rsquo;t want to write this logic multiple times, so I settled for a function that takes some settings, and returns a derivation:</p> <div class="highlight-group" data-base-path="blog-static-flake" data-file-path="blog-static-flake/lib.nix" data-first-line="6" data-last-line="21"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Nix-Configs/blog-static-flake/src/commit/67b47d9c298e7476c2ca211aac5c5fd961637b7b/lib.nix#L6-L21">lib.nix</a>, lines 6 through 21</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Nix" data-lang="Nix"><span class="line"><span class="cl"> <span class="n">website</span> <span class="o">=</span> <span class="n">settings</span><span class="p">:</span> <span class="n">stdenv</span><span class="o">.</span><span class="n">mkDerivation</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">inherit</span> <span class="p">(</span><span class="n">settings</span><span class="p">)</span> <span class="n">src</span> <span class="n">ssl</span> <span class="n">host</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">name</span> <span class="o">=</span> <span class="s2">&#34;blog-static&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">version</span> <span class="o">=</span> <span class="n">settings</span><span class="o">.</span><span class="n">src</span><span class="o">.</span><span class="n">rev</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">urlSub</span> <span class="o">=</span> </span></span><span class="line"><span class="cl"> <span class="k">let</span> </span></span><span class="line"><span class="cl"> <span class="n">regexEscape</span> <span class="o">=</span> <span class="n">lib</span><span class="o">.</span><span class="n">escape</span> <span class="p">[</span> <span class="s2">&#34;/&#34;</span> <span class="s2">&#34;(&#34;</span> <span class="s2">&#34;)&#34;</span> <span class="s2">&#34;[&#34;</span> <span class="s2">&#34;]&#34;</span> <span class="s2">&#34;+&#34;</span> <span class="s2">&#34;*&#34;</span> <span class="s2">&#34;</span><span class="se">\\</span><span class="s2">&#34;</span> <span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="k">in</span> </span></span><span class="line"><span class="cl"> <span class="k">with</span> <span class="n">settings</span><span class="o">.</span><span class="n">replaceUrl</span><span class="p">;</span> <span class="s2">&#34;s/</span><span class="si">${</span><span class="n">regexEscape</span> <span class="n">from</span><span class="si">}</span><span class="s2">/</span><span class="si">${</span><span class="n">regexEscape</span> <span class="n">to</span><span class="si">}</span><span class="s2">/g&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">publicPath</span> <span class="o">=</span> <span class="n">settings</span><span class="o">.</span><span class="n">path</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">extraFlags</span> <span class="o">=</span> <span class="p">(</span><span class="k">if</span> <span class="n">settings</span><span class="o">.</span><span class="n">drafts</span> <span class="k">then</span> <span class="s2">&#34; -D &#34;</span> <span class="k">else</span> <span class="s2">&#34;&#34;</span><span class="p">)</span> <span class="o">+</span> <span class="n">settings</span><span class="o">.</span><span class="n">extraFlags</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">builder</span> <span class="o">=</span> <span class="sr">./build/builder.sh</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">buildInputs</span> <span class="o">=</span> <span class="p">[</span> </span></span><span class="line"><span class="cl"> <span class="n">hugo</span> <span class="n">katex-html</span> </span></span><span class="line"><span class="cl"> <span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>There are a few things here:</p> <ul> <li>On line 7, the settings <code>src</code>, <code>ssl</code>, and <code>host</code> are inherited into the derivation. The <code>src</code> setting provides a handle on the source code of the blog. I haven&rsquo;t had much time to test and fine-tune the changes enabling multi-language support on the site, so they reside on a separate branch. It&rsquo;s up to the caller to specify which version of the source code should be used for building. The <code>host</code> and <code>ssl</code> settings are interesting because <strong>they don&rsquo;t actually matter for the derivation itself</strong> &ndash; they just aren&rsquo;t used in the builder. However, attributes given to a derivation are accessible from &ldquo;outside&rdquo;, and these settings will play a role later.</li> <li>Lines 10 through 14 deal with setting the base URL of the site. Hugo does not know how to interpret the <code>--baseURL</code> option when a blog has multiple languages. What this means is that in the end, it is impossible to configure the base URL used in links from the command line. I need to apply some manual changes to the configuration file. It&rsquo;s necessary to adjust the base URL because each version of my website is hosted in a different place: the default (English) website is hosted on <code>danilafe.com</code>, the version with drafts on <code>drafts.danilafe.com</code>, and so on. However, the configuration file only knows one base URL per language, and so it <em>doesn&rsquo;t</em> know when or when not to use the <code>drafts.</code> prefix. The <code>urlSub</code> variable is used in the builder.</li> <li>On line 15, the <code>publicPath</code> variable is set; while single-language Hugo puts all the generated HTML into the <code>public</code> folder, the multi-language configuration places it into <code>public/[language-code]</code>. Thus, depending on the configuration, the builder needs to look in a different place for final output.</li> </ul> <p>This new <code>website</code> function is general enough to represent all my blog versions, but it&rsquo;s too low-level. Do I really want to specify the <code>publicPath</code> each time I want to describe a version of the site? What about <code>settings.replaceUrl</code>, or the source code? Just like I would in any garden variety language, I defined two helper functions:</p> <div class="highlight-group" data-base-path="blog-static-flake" data-file-path="blog-static-flake/lib.nix" data-first-line="25" data-last-line="48"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Nix-Configs/blog-static-flake/src/commit/67b47d9c298e7476c2ca211aac5c5fd961637b7b/lib.nix#L25-L48">lib.nix</a>, lines 25 through 48</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Nix" data-lang="Nix"><span class="line"><span class="cl"> <span class="n">english</span> <span class="o">=</span> <span class="n">settings</span><span class="p">:</span> <span class="n">website</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">inherit</span> <span class="p">(</span><span class="n">settings</span><span class="p">)</span> <span class="n">host</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">ssl</span> <span class="o">=</span> <span class="n">settings</span><span class="o">.</span><span class="n">ssl</span> <span class="n">or</span> <span class="no">false</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">drafts</span> <span class="o">=</span> <span class="n">settings</span><span class="o">.</span><span class="n">drafts</span> <span class="n">or</span> <span class="no">false</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">src</span> <span class="o">=</span> <span class="n">blog-source</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">path</span> <span class="o">=</span> <span class="s2">&#34;.&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">extraFlags</span> <span class="o">=</span> <span class="s2">&#34;--config=config.toml,config-gen.toml&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">replaceUrl</span> <span class="o">=</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">from</span> <span class="o">=</span> <span class="s2">&#34;https://danilafe.com&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">to</span> <span class="o">=</span> <span class="n">wrapHost</span> <span class="p">(</span><span class="n">settings</span><span class="o">.</span><span class="n">ssl</span> <span class="n">or</span> <span class="no">false</span><span class="p">)</span> <span class="n">settings</span><span class="o">.</span><span class="n">host</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span> </span></span><span class="line"><span class="cl"> <span class="n">russian</span> <span class="o">=</span> <span class="n">settings</span><span class="p">:</span> <span class="n">website</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">inherit</span> <span class="p">(</span><span class="n">settings</span><span class="p">)</span> <span class="n">host</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">ssl</span> <span class="o">=</span> <span class="n">settings</span><span class="o">.</span><span class="n">ssl</span> <span class="n">or</span> <span class="no">false</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">drafts</span> <span class="o">=</span> <span class="n">settings</span><span class="o">.</span><span class="n">drafts</span> <span class="n">or</span> <span class="no">false</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">src</span> <span class="o">=</span> <span class="n">blog-source-localized</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">path</span> <span class="o">=</span> <span class="s2">&#34;ru&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">extraFlags</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">replaceUrl</span> <span class="o">=</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">from</span> <span class="o">=</span> <span class="s2">&#34;https://ru.danilafe.com&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">to</span> <span class="o">=</span> <span class="n">wrapHost</span> <span class="p">(</span><span class="n">settings</span><span class="o">.</span><span class="n">ssl</span> <span class="n">or</span> <span class="no">false</span><span class="p">)</span> <span class="n">settings</span><span class="o">.</span><span class="n">host</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Both of these simply make a call to the <code>website</code> function (and thus return derivations), but they make some decisions for the caller, and provide a nicer interface by allowing attributes to be omitted. Specifically, by default, a site version is assumed to be HTTP-only, and to contain non-draft articles. Furthermore, since each function corresponds to a language, there&rsquo;s no need for the caller to provide a blog version, and thus also the output <code>path</code>, or even to specify the &ldquo;from&rdquo; part of <code>replaceUrl</code>. The <code>wrapHost</code> function, not included in the snippet, simply adds <code>http</code> or <code>https</code> to the <code>host</code> parameter, which does not otherwise include this information. These functions can now be called to describe different versions of my site:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Nix" data-lang="Nix"><span class="line"><span class="cl"><span class="c1"># Default version, hosted on the main site and using HTTPS</span> </span></span><span class="line"><span class="cl"><span class="n">english</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">ssl</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">host</span> <span class="o">=</span> <span class="s2">&#34;danilafe.com&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># English draft version, hosted on draft domain and not using HTTPS.</span> </span></span><span class="line"><span class="cl"><span class="n">english</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">drafts</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">host</span> <span class="o">=</span> <span class="s2">&#34;drafts.danilafe.com&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># Russian draft version, hosted on draft (russian) domain, and not using HTTPS.</span> </span></span><span class="line"><span class="cl"><span class="n">russian</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">drafts</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">host</span> <span class="o">=</span> <span class="s2">&#34;drafts.ru.danilafe.com&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></div><a href="#configuring-nginx"> <h4 id="configuring-nginx">Configuring Nginx</h4> </a> <p>The above functions are already a pretty big win (in my opinion) when it comes to describing my blog. However, by themselves, they aren&rsquo;t quite enough to clean up my system configuration: for each of these blog versions, I&rsquo;d need to add an Nginx <code>virtualHosts</code> entry where I&rsquo;d pass in the corresponding host (like <code>danilafe.com</code> or <code>drafts.danilafe.com</code>), configure SSL, and so on. At one point, too, all paths in <code>/var</code> were by default mounted as read-only by NixOS, which meant that it was necessary to tell <code>systemd</code> that <code>/var/www/challenges</code> should be writeable so that the SSL certificate for the site could be properly renewed. Overall, this was a lot of detail that I didn&rsquo;t want front-and-center in my server configuration.</p> <p>However, with the additional &ldquo;ghost&rdquo; attributes, my derivations already contain most of the information required to configure Nginx. The virtual host, for instance, is the same as <code>replaceUrl.to</code> (since I&rsquo;d want the Nginx virtual host for a blog version to handle links within that version). The <code>ssl</code> ghost parameter corresponds precisely to whether or not a virtual host will need SSL (and thus ACME, and thus the <code>systemd</code> setting). For each derivation built using <code>website</code>, I can access the attributes like <code>ssl</code> or <code>host</code> to generate the corresponding piece of the Nginx configuration.</p> <p>To make this <em>really</em> nice, I wanted all of this to be &ldquo;just another section of my configuration file&rdquo;. That is, I wanted to control my site deployment via regular old attributes in <code>configuration.nix</code>. To this end, I needed a module. Xe recently <a href="https://christine.website/blog/nix-flakes-3-2022-04-07"class="external-link">wrote about NixOS modules in flakes<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, and what I do here is very similar. In essence, a module has two bits:</p> <ul> <li>The <em>options</em>, which specify what kind of attributes this module understands. The most common option is <code>enable</code>, which tells a module that it should apply its configuration changes.</li> <li>The <em>configuration</em>, which consists of the various system settings that this module will itself set. These typically depend on the options.</li> </ul> <p>In short, a module describes the sort of options it will accept, and then provides a way to convert these newly-described options into changes to the system configuration. It may help if I showed you the concrete options that my newly-created blog module provides:</p> <div class="highlight-group" data-base-path="blog-static-flake" data-file-path="blog-static-flake/module.nix" data-first-line="32" data-last-line="43"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Nix-Configs/blog-static-flake/src/commit/67b47d9c298e7476c2ca211aac5c5fd961637b7b/module.nix#L32-L43">module.nix</a>, lines 32 through 43</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Nix" data-lang="Nix"><span class="line"><span class="cl"> <span class="n">options</span><span class="o">.</span><span class="n">services</span><span class="o">.</span><span class="n">danilafe-blog</span> <span class="o">=</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">enable</span> <span class="o">=</span> <span class="n">mkEnableOption</span> <span class="s2">&#34;Daniel&#39;s blog service&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">sites</span> <span class="o">=</span> <span class="n">mkOption</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type</span> <span class="o">=</span> <span class="n">types</span><span class="o">.</span><span class="n">listOf</span> <span class="n">types</span><span class="o">.</span><span class="n">package</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">default</span> <span class="o">=</span> <span class="p">{};</span> </span></span><span class="line"><span class="cl"> <span class="n">description</span> <span class="o">=</span> <span class="s2">&#34;List of versions of this blog that should be enabled.&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span> </span></span><span class="line"><span class="cl"> <span class="n">challengePath</span> <span class="o">=</span> <span class="n">mkOption</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type</span> <span class="o">=</span> <span class="n">types</span><span class="o">.</span><span class="n">str</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">description</span> <span class="o">=</span> <span class="s2">&#34;The location for ACME challenges.&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>There are three options here:</p> <ul> <li><code>enable</code>, a boolean-valued input that determines whether or not the module should make any changes to the system configuration at all.</li> <li><code>sites</code>, which, as written in the code, accepts a list of derivations. These derivations correspond to the various versions of my site that should be served to the outside world.</li> <li><code>challengePath</code>, a string to configure where ACME will place files during automatic SSL renewal.</li> </ul> <p>Now, while these are the only three options the user will need to set, the changes to the system configuration are quite involved. For instance, for each site (derivation) in the <code>sites</code> list, the resulting configuration needs to have a <code>virtualHost</code> in the <code>services.nginx</code> namespace. To this end, I defined a function that accepts a site derivation and produces the necessary settings:</p> <div class="highlight-group" data-base-path="blog-static-flake" data-file-path="blog-static-flake/module.nix" data-first-line="7" data-last-line="19"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Nix-Configs/blog-static-flake/src/commit/67b47d9c298e7476c2ca211aac5c5fd961637b7b/module.nix#L7-L19">module.nix</a>, lines 7 through 19</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Nix" data-lang="Nix"><span class="line"><span class="cl"> <span class="n">virtualHost</span> <span class="o">=</span> <span class="n">package</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">virtualHosts</span><span class="o">.</span><span class="s2">&#34;</span><span class="si">${</span><span class="n">package</span><span class="o">.</span><span class="n">host</span><span class="si">}</span><span class="s2">&#34;</span> <span class="o">=</span> <span class="n">mkMerge</span> <span class="p">[</span> </span></span><span class="line"><span class="cl"> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">root</span> <span class="o">=</span> <span class="n">package</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">mkIf</span> <span class="p">(</span><span class="n">sslForSite</span> <span class="n">package</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">addSSL</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">enableACME</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">acmeRoot</span> <span class="o">=</span> <span class="n">cfg</span><span class="o">.</span><span class="n">challengePath</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">})</span> </span></span><span class="line"><span class="cl"> <span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Each virtual host always has a <code>root</code> option (where Nginx should look for HTML files), but only those sites for which SSL is enabled need to specify <code>addSSL</code>, <code>enableACME</code>, and <code>acmeRoot</code>. All the virtual hosts are assembled into a single array (below, <code>cfg</code> refers to the options that the user provided to the module, as specified above).</p> <div class="highlight-group" data-base-path="blog-static-flake" data-file-path="blog-static-flake/module.nix" data-first-line="28" data-last-line="28"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Nix-Configs/blog-static-flake/src/commit/67b47d9c298e7476c2ca211aac5c5fd961637b7b/module.nix#L28-L28">module.nix</a>, line 28</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">28 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Nix" data-lang="Nix"><span class="line"><span class="cl"> <span class="n">virtualHosts</span> <span class="o">=</span> <span class="nb">map</span> <span class="n">virtualHost</span> <span class="n">cfg</span><span class="o">.</span><span class="n">sites</span><span class="p">;</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>If the <code>enable</code> option is set, we enable Nginx, and provide it with a list of all of the virtual hosts we generated. Below, <code>config</code> (not to be confused with <code>cfg</code>) is the namespace for the module&rsquo;s configuration.</p> <div class="highlight-group" data-base-path="blog-static-flake" data-file-path="blog-static-flake/module.nix" data-first-line="45" data-last-line="51"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Nix-Configs/blog-static-flake/src/commit/67b47d9c298e7476c2ca211aac5c5fd961637b7b/module.nix#L45-L51">module.nix</a>, lines 45 through 51</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Nix" data-lang="Nix"><span class="line"><span class="cl"> <span class="n">config</span><span class="o">.</span><span class="n">services</span><span class="o">.</span><span class="n">nginx</span> <span class="o">=</span> <span class="n">mkIf</span> <span class="n">cfg</span><span class="o">.</span><span class="n">enable</span> <span class="p">(</span><span class="n">mkMerge</span> <span class="p">(</span><span class="n">virtualHosts</span> <span class="o">++</span> <span class="p">[</span> </span></span><span class="line"><span class="cl"> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1"># Always enable nginx.</span> </span></span><span class="line"><span class="cl"> <span class="n">enable</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">recommendedGzipSettings</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">]));</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>In a similar manner to this, I generate a list of <code>systemd</code> services which are used to configure the challenge path to be writeable. Click the <code>module.nix</code> link above to check out the full file.</p> <a href="#creating-a-flake"> <h4 id="creating-a-flake">Creating a Flake</h4> </a> <p>We now have two &ldquo;things&rdquo; that handle the deployment of the blog: the builder functions <code>english</code> and <code>russian</code> which help describe various blog versions, and the NixOS module that configures the server&rsquo;s Nginx to serve said versions. We now want to expose these to the NixOS system configuration, which describes the entire server. This is where flakes finally come in. <a href="https://blog.ysndr.de/posts/internals/2021-01-01-flake-ification/index.html"class="external-link">Yanik Sander<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> wrote up a pretty comprehensive explanation of how their blog is deployed using flakes, which I often consulted while getting started &ndash; check it out if you are looking for more details.</p> <p>In brief, a Nix flake has <em>inputs</em> and <em>outputs</em>. Inputs can be other flakes or source files that the flake needs access to, and outputs are simply Nix expressions that the flake provides.</p> <p>The nice thing about flakes&rsquo; inputs is that they can reference other flakes via Git. This means that, should I write a flake for my blog (as I am about to do) I will be able to reference its git URL in another flake, and Nix will automatically clone and import it. This helps achieve the <strong>adding custom packages</strong> goal, since I can now easily write Nix expressions and reference them from my system configuration.</p> <p>Importantly, flakes track the versions of their inputs in a <code>flake.lock</code> file; this means that, unless explicitly told to do otherwise, they will use the same version of their inputs. This achieves the <strong>versioning</strong> goal for my blog, too, since now it will pull the pre-defined commit from Git until I tell it to fetch the updated site. In addition to pinning the version of my blog, though, the flake also locks down the version of <code>nixpkgs</code> itself. This means that the same packages will be used in the build process, instead of those found on the host system at the time. This has the nice effect of preventing updates to dependencies from breaking the build; it&rsquo;s a nice step towards purity and reproducibility.</p> <p>Let&rsquo;s take a look at the inputs of my blog flake:</p> <div class="highlight-group" data-base-path="blog-static-flake" data-file-path="blog-static-flake/flake.nix" data-first-line="2" data-last-line="19"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Nix-Configs/blog-static-flake/src/commit/67b47d9c298e7476c2ca211aac5c5fd961637b7b/flake.nix#L2-L19">flake.nix</a>, lines 2 through 19</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Nix" data-lang="Nix"><span class="line"><span class="cl"> <span class="n">inputs</span> <span class="o">=</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">nixpkgs</span><span class="o">.</span><span class="n">url</span> <span class="o">=</span> <span class="s2">&#34;github:nixos/nixpkgs&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">flake-utils</span><span class="o">.</span><span class="n">url</span> <span class="o">=</span> <span class="s2">&#34;github:numtide/flake-utils&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">katex-html</span><span class="o">.</span><span class="n">url</span> <span class="o">=</span> <span class="s2">&#34;git+https://dev.danilafe.com/Nix-Configs/katex-html&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">blog-source</span> <span class="o">=</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">flake</span> <span class="o">=</span> <span class="no">false</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">url</span> <span class="o">=</span> <span class="s2">&#34;https://dev.danilafe.com/Web-Projects/blog-static.git&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type</span> <span class="o">=</span> <span class="s2">&#34;git&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">submodules</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span> </span></span><span class="line"><span class="cl"> <span class="n">blog-source-localized</span> <span class="o">=</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">flake</span> <span class="o">=</span> <span class="no">false</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">url</span> <span class="o">=</span> <span class="s2">&#34;https://dev.danilafe.com/Web-Projects/blog-static.git&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">ref</span> <span class="o">=</span> <span class="s2">&#34;localization&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type</span> <span class="o">=</span> <span class="s2">&#34;git&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">submodules</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Two of these inputs are my blog source code, pulled from its usual Git host. They are marked as <code>flake = false</code> (my blog is just a Hugo project!), and both require submodules to be fetched. One of them is set to the <code>localization</code> branch, once again because localization is not yet stabilized and thus not merged into my blog&rsquo;s <code>master</code> branch. The other three inputs are flakes, one of which is just <code>nixpkgs</code>. The <code>flake-utils</code> flake provides some convenient functions for writing other flakes, and <code>katex-html</code> is my own creation, a KaTeX-to-HTML conversion script that I use to post-process the blog.</p> <p>So what outputs should this flake provide? Well, we&rsquo;ve already defined a NixOS module for the blog, and we&rsquo;d like our flake to expose this module to the world. But the module alone is not enough; its configuration requires a list of packages created using our builders. Where does one procure such a list? The caller will need access to the builders themselves. To make all of this work, I ended up with the following expression for my <code>outputs</code>:</p> <div class="highlight-group" data-base-path="blog-static-flake" data-file-path="blog-static-flake/flake.nix" data-first-line="21" data-last-line="34"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Nix-Configs/blog-static-flake/src/commit/67b47d9c298e7476c2ca211aac5c5fd961637b7b/flake.nix#L21-L34">flake.nix</a>, lines 21 through 34</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Nix" data-lang="Nix"><span class="line"><span class="cl"> <span class="n">outputs</span> <span class="o">=</span> <span class="p">{</span> <span class="n">self</span><span class="o">,</span> <span class="n">blog-source</span><span class="o">,</span> <span class="n">blog-source-localized</span><span class="o">,</span> <span class="n">nixpkgs</span><span class="o">,</span> <span class="n">flake-utils</span><span class="o">,</span> <span class="n">katex-html</span> <span class="p">}:</span> </span></span><span class="line"><span class="cl"> <span class="k">let</span> </span></span><span class="line"><span class="cl"> <span class="n">buildersFor</span> <span class="o">=</span> <span class="n">system</span><span class="p">:</span> <span class="kn">import</span> <span class="sr">./lib.nix</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">inherit</span> <span class="n">blog-source</span> <span class="n">blog-source-localized</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">pkgs</span> <span class="o">=</span> <span class="kn">import</span> <span class="n">nixpkgs</span> <span class="p">{</span> <span class="k">inherit</span> <span class="n">system</span><span class="p">;</span> <span class="p">};</span> </span></span><span class="line"><span class="cl"> <span class="n">katex-html</span> <span class="o">=</span> <span class="n">katex-html</span><span class="o">.</span><span class="n">defaultPackage</span><span class="o">.</span><span class="si">${</span><span class="n">system</span><span class="si">}</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span> </span></span><span class="line"><span class="cl"> <span class="k">in</span> </span></span><span class="line"><span class="cl"> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">inherit</span> <span class="n">buildersFor</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">nixosModule</span> <span class="o">=</span> <span class="p">(</span><span class="kn">import</span> <span class="sr">./module.nix</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="o">//</span> <span class="n">flake-utils</span><span class="o">.</span><span class="n">lib</span><span class="o">.</span><span class="n">eachDefaultSystem</span> <span class="p">(</span><span class="n">system</span><span class="p">:</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">defaultPackage</span> <span class="o">=</span> <span class="p">(</span><span class="n">buildersFor</span> <span class="n">system</span><span class="p">)</span><span class="o">.</span><span class="n">english</span> <span class="p">{</span> <span class="n">host</span> <span class="o">=</span> <span class="s2">&#34;danilafe.com&#34;</span><span class="p">;</span> <span class="p">};</span> </span></span><span class="line"><span class="cl"> <span class="p">});</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The flake <code>output</code> schema provides a standard option for exposing modules, <code>nixosModule</code>. Then, exposing my <code>module.nix</code> file from the flake is simply a matter of importing it, as on line 31. There is, however, no standard way for exposing a <em>function</em>. The good news is that any attribute defined on a flake is accessible from code that imports that flake. Thus, I simply added a <code>buildersFor</code> function, which fetches the <code>nixpkgs</code> collection and LaTeX builder script for a given system, and feeds them to the file that defines the <code>english</code> and <code>russian</code> builders. This <code>buildersFor</code> function also provides the builders with the two different blog sources they reference, <code>blog-source</code> and <code>blog-source-localized</code>.</p> <p>The <code>system</code> parameter to <code>buildersFor</code> is necessary because the set of packages from <code>nixpkgs</code> depends on it. Thus, if the builders use any packages from the collection (they do), they must know which system to pull packages for. This is a common pattern in flakes: the <code>packages</code> attribute is typically a system-to-package mapping, too.</p> <p>Finally, the last little bit on lines 32 through 34 defines a default package for the flake. This is the package that is built if a user runs <code>nix build .#</code>. This isn&rsquo;t strictly necessary for my purposes, but it&rsquo;s nice to be able to test that the builders still work by running a test build. The <code>eachDefaultSystem</code> function generates a <code>defaultPackage</code> attribute for each of the &ldquo;default&rdquo; systems, so that the package is buildable on more than just my server architecture.</p> <p>And that&rsquo;s it for the blog flake! I simply push it to Git, and move on to actually <em>using</em> it from elsewhere.</p> <a href="#using-the-module"> <h4 id="using-the-module">Using the Module</h4> </a> <p>In my server configuration (which is, itself, a flake), I simply list my <code>blog-static-flake</code> as one of the inputs:</p> <div class="highlight-group" data-base-path="server-config" data-file-path="server-config/flake.nix" data-first-line="4" data-last-line="4"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Nix-Configs/server-config/src/commit/98cffe09546aee1678f7baebdea5eb5fef288935/flake.nix#L4-L4">flake.nix</a>, line 4</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">4 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Nix" data-lang="Nix"><span class="line"><span class="cl"> <span class="n">blog</span><span class="o">.</span><span class="n">url</span> <span class="o">=</span> <span class="s2">&#34;git+https://dev.danilafe.com/DanilaFe/blog-static-flake&#34;</span><span class="p">;</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Then, in the <code>modules</code> attribute, I include <code>blog.nixosModule</code>, making NixOS aware of its options and configuration. The final little piece is to provide the <code>english</code> and <code>russian</code> builders to the system configuration; this can be done using the <code>specialArgs</code> attribute. The whole <code>flake.nix</code> file is pretty short:</p> <div class="highlight-group" data-base-path="server-config" data-file-path="server-config/flake.nix"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Nix-Configs/server-config/src/commit/98cffe09546aee1678f7baebdea5eb5fef288935/flake.nix">flake.nix</a>, entire file</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Nix" data-lang="Nix"><span class="line"><span class="cl"><span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">inputs</span> <span class="o">=</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">nixpkgs</span><span class="o">.</span><span class="n">url</span> <span class="o">=</span> <span class="s2">&#34;github:nixos/nixpkgs/nixos-unstable&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">blog</span><span class="o">.</span><span class="n">url</span> <span class="o">=</span> <span class="s2">&#34;git+https://dev.danilafe.com/DanilaFe/blog-static-flake&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span> </span></span><span class="line"><span class="cl"> <span class="n">outputs</span> <span class="o">=</span> <span class="p">{</span> <span class="n">self</span><span class="o">,</span> <span class="n">nixpkgs</span><span class="o">,</span> <span class="n">blog</span> <span class="p">}:</span> </span></span><span class="line"><span class="cl"> <span class="k">let</span> </span></span><span class="line"><span class="cl"> <span class="n">system</span> <span class="o">=</span> <span class="s2">&#34;x86_64-linux&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">builders</span> <span class="o">=</span> <span class="n">blog</span><span class="o">.</span><span class="n">buildersFor</span> <span class="n">system</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">in</span> </span></span><span class="line"><span class="cl"> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">nixosConfigurations</span><span class="o">.</span><span class="n">nixos-droplet-v2</span> <span class="o">=</span> <span class="n">nixpkgs</span><span class="o">.</span><span class="n">lib</span><span class="o">.</span><span class="n">nixosSystem</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">inherit</span> <span class="n">system</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">specialArgs</span> <span class="o">=</span> <span class="p">{</span> <span class="k">inherit</span> <span class="n">system</span> <span class="n">builders</span><span class="p">;</span> <span class="p">};</span> </span></span><span class="line"><span class="cl"> <span class="n">modules</span> <span class="o">=</span> <span class="p">[</span> <span class="sr">./configuration.nix</span> <span class="n">blog</span><span class="o">.</span><span class="n">nixosModule</span> <span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div> </div> <p>Finally, in <code>configuration.nix</code>, taking <code>builders</code> as one of the inputs, I write what you saw above:</p> <div class="highlight-group" data-base-path="server-config" data-file-path="server-config/configuration.nix" data-first-line="42" data-last-line="59"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Nix-Configs/server-config/src/commit/98cffe09546aee1678f7baebdea5eb5fef288935/configuration.nix#L42-L59">configuration.nix</a>, lines 42 through 59</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Nix" data-lang="Nix"><span class="line"><span class="cl"> <span class="n">services</span><span class="o">.</span><span class="n">danilafe-blog</span> <span class="o">=</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">enable</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">challengePath</span> <span class="o">=</span> <span class="s2">&#34;/var/www/challenges&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">sites</span> <span class="o">=</span> <span class="p">[</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">builders</span><span class="o">.</span><span class="n">english</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">ssl</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">host</span> <span class="o">=</span> <span class="s2">&#34;danilafe.com&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">})</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">builders</span><span class="o">.</span><span class="n">english</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">drafts</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">host</span> <span class="o">=</span> <span class="s2">&#34;drafts.danilafe.com&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">})</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">builders</span><span class="o">.</span><span class="n">russian</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">drafts</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">host</span> <span class="o">=</span> <span class="s2">&#34;drafts.ru.danilafe.com&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">})</span> </span></span><span class="line"><span class="cl"> <span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span></span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#wrapping-up"> <h3 id="wrapping-up">Wrapping Up</h3> </a> <p>So there you have it, a flake-based multi-version blog deployment written in a declarative style. You can check out both my <a href="https://dev.danilafe.com/Nix-Configs/server-config"class="external-link">system configuration flake<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> and my <a href="https://dev.danilafe.com/Nix-Configs/blog-static-flake"class="external-link">blog flake<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> on my Git server. If you want more, check out the articles by Xe and Yannik linked above. Thanks for reading!</p> Digit Sum Patterns and Modular Arithmetic https://danilafe.com/blog/modulo_patterns/ Thu, 30 Dec 2021 15:42:40 -0800 https://danilafe.com/blog/modulo_patterns/ <p>When I was in elementary school, our class was briefly visited by our school&rsquo;s headmaster. He was there for a demonstration, probably intended to get us to practice our multiplication tables. <em>&ldquo;Pick a number&rdquo;</em>, he said, <em>&ldquo;And I&rsquo;ll teach you how to draw a pattern from it.&rdquo;</em></p> <p>The procedure was rather simple:</p> <ol> <li>Pick a number between 2 and 8 (inclusive).</li> <li>Start generating positive multiples of this number. If you picked 8, your multiples would be 8, 16, 24, and so on.</li> <li>If a multiple is more than one digit long, sum its digits. For instance, for 16, write 1+6=7. If the digits add up to a number that&rsquo;s still more than 1 digit long, add up the digits of <em>that</em> number (and so on).</li> <li>Start drawing on a grid. For each resulting number, draw that many squares in one direction, and then &ldquo;turn&rdquo;. Using 8 as our example, we could draw 8 up, 7 to the right, 6 down, 5 to the left, and so on.</li> <li>As soon as you come back to where you started (<em>&ldquo;And that will always happen&rdquo;</em>, said my headmaster), you&rsquo;re done. You should have drawn a pretty pattern!</li> </ol> <p>Sticking with our example of 8, the pattern you&rsquo;d end up with would be something like this:</p> <figure class="tiny"><img src="https://danilafe.com/blog/modulo_patterns/pattern_8.svg" alt="Pattern generated by the number 8."><figcaption> <p>Pattern generated by the number 8.</p> </figcaption> </figure> <p>Before we go any further, let&rsquo;s observe that it&rsquo;s not too hard to write code to do this. For instance, the &ldquo;add digits&rdquo; algorithm can be naively written by turning the number into a string (<code>17</code> becomes <code>&quot;17&quot;</code>), splitting that string into characters (<code>&quot;17&quot;</code> becomes <code>[&quot;1&quot;, &quot;7&quot;]</code>), turning each of these character back into numbers (the array becomes <code>[1, 7]</code>) and then computing the sum of the array, leaving <code>8</code>.</p> <div class="highlight-group" data-base-path="" data-file-path="patterns/patterns.rb" data-first-line="3" data-last-line="8"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/patterns/patterns.rb#L3-L8">patterns.rb</a>, lines 3 through 8</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Ruby" data-lang="Ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">sum_digits</span><span class="p">(</span><span class="n">n</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="n">n</span> <span class="o">&gt;</span> <span class="mi">9</span> </span></span><span class="line"><span class="cl"> <span class="n">n</span> <span class="o">=</span> <span class="n">n</span><span class="o">.</span><span class="n">to_s</span><span class="o">.</span><span class="n">chars</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:to_i</span><span class="p">)</span><span class="o">.</span><span class="n">sum</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"> <span class="n">n</span> </span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We may now encode the &ldquo;drawing&rdquo; logic. At any point, there&rsquo;s a &ldquo;direction&rdquo; we&rsquo;re going - which I&rsquo;ll denote by the Ruby symbols <code>:top</code>, <code>:bottom</code>, <code>:left</code>, and <code>:right</code>. Each step, we take the current <code>x</code>,<code>y</code> coordinates (our position on the grid), and shift them by <code>n</code> in a particular direction <code>dir</code>. We also return the new direction alongside the new coordinates.</p> <div class="highlight-group" data-base-path="" data-file-path="patterns/patterns.rb" data-first-line="10" data-last-line="21"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/patterns/patterns.rb#L10-L21">patterns.rb</a>, lines 10 through 21</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Ruby" data-lang="Ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">step</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">dir</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="n">dir</span> </span></span><span class="line"><span class="cl"> <span class="k">when</span> <span class="ss">:top</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">[</span><span class="n">x</span><span class="p">,</span><span class="n">y</span><span class="o">+</span><span class="n">n</span><span class="p">,</span><span class="ss">:right</span><span class="o">]</span> </span></span><span class="line"><span class="cl"> <span class="k">when</span> <span class="ss">:right</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">[</span><span class="n">x</span><span class="o">+</span><span class="n">n</span><span class="p">,</span><span class="n">y</span><span class="p">,</span><span class="ss">:bottom</span><span class="o">]</span> </span></span><span class="line"><span class="cl"> <span class="k">when</span> <span class="ss">:bottom</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">[</span><span class="n">x</span><span class="p">,</span><span class="n">y</span><span class="o">-</span><span class="n">n</span><span class="p">,</span><span class="ss">:left</span><span class="o">]</span> </span></span><span class="line"><span class="cl"> <span class="k">when</span> <span class="ss">:left</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">[</span><span class="n">x</span><span class="o">-</span><span class="n">n</span><span class="p">,</span><span class="n">y</span><span class="p">,</span><span class="ss">:top</span><span class="o">]</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The top-level algorithm is captured by the following code, which produces a list of coordinates in the order that you&rsquo;d visit them.</p> <div class="highlight-group" data-base-path="" data-file-path="patterns/patterns.rb" data-first-line="23" data-last-line="35"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/patterns/patterns.rb#L23-L35">patterns.rb</a>, lines 23 through 35</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Ruby" data-lang="Ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">run_number</span><span class="p">(</span><span class="n">number</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">counter</span> <span class="o">=</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">dir</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="ss">:top</span> </span></span><span class="line"><span class="cl"> <span class="n">line_stack</span> <span class="o">=</span> <span class="o">[[</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="o">]]</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kp">loop</span> <span class="k">do</span> </span></span><span class="line"><span class="cl"> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">dir</span> <span class="o">=</span> <span class="n">step</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="n">y</span><span class="p">,</span> <span class="n">sum_digits</span><span class="p">(</span><span class="n">counter</span><span class="o">*</span><span class="n">number</span><span class="p">),</span> <span class="n">dir</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">line_stack</span> <span class="o">&lt;&lt;</span> <span class="o">[</span><span class="n">x</span><span class="p">,</span><span class="n">y</span><span class="o">]</span> </span></span><span class="line"><span class="cl"> <span class="n">counter</span> <span class="o">+=</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="k">break</span> <span class="k">if</span> <span class="n">x</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">y</span> <span class="o">==</span> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">make_svg</span><span class="p">(</span><span class="n">line_stack</span><span class="p">)</span> </span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>I will omit the code for generating SVGs from the body of the article &ndash; you can always find the complete source code in this blog&rsquo;s Git repo (or by clicking the link in the code block above). Let&rsquo;s run the code on a few other numbers. Here&rsquo;s one for 4, for instance:</p> <figure class="tiny"><img src="https://danilafe.com/blog/modulo_patterns/pattern_4.svg" alt="Pattern generated by the number 4."><figcaption> <p>Pattern generated by the number 4.</p> </figcaption> </figure> <p>And one more for 2, which I don&rsquo;t find as pretty.</p> <figure class="tiny"><img src="https://danilafe.com/blog/modulo_patterns/pattern_2.svg" alt="Pattern generated by the number 2."><figcaption> <p>Pattern generated by the number 2.</p> </figcaption> </figure> <p>It really does always work out! Young me was amazed, though I would often run out of space on my grid paper to complete the pattern, or miscount the length of my lines partway in. It was only recently that I started thinking about <em>why</em> it works, and I think I figured it out. Let&rsquo;s take a look!</p> <a href="#is-a-number-divisible-by-3"> <h3 id="is-a-number-divisible-by-3">Is a number divisible by 3?</h3> </a> <p>You might find the whole &ldquo;add up the digits of a number&rdquo; thing familiar, and for good reason: it&rsquo;s one way to check if a number is divisible by 3. The quick summary of this result is,</p> <blockquote> <p>If the sum of the digits of a number is divisible by 3, then so is the whole number.</p> </blockquote> <p>For example, the sum of the digits of 72 is 9, which is divisible by 3; 72 itself is correspondingly also divisible by 3, since 24*3=72. On the other hand, the sum of the digits of 82 is 10, which is <em>not</em> divisible by 3; 82 isn&rsquo;t divisible by 3 either (it&rsquo;s one more than 81, which <em>is</em> divisible by 3).</p> <p>Why does <em>this</em> work? Let&rsquo;s talk remainders.</p> <p>If a number doesn&rsquo;t cleanly divide another (we&rsquo;re sticking to integers here), what&rsquo;s left behind is the remainder. For instance, dividing 7 by 3 leaves us with a remainder 1. On the other hand, if the remainder is zero, then that means that our dividend is divisible by the divisor (what a mouthful). In mathematics, we typically use \(a|b\) to say \(a\) divides \(b\), or, as we have seen above, that the remainder of dividing \(b\) by \(a\) is zero.</p> <p>Working with remainders actually comes up pretty frequently in discrete math. A well-known example I&rsquo;m aware of is the <a href="https://en.wikipedia.org/wiki/RSA_%28cryptosystem%29"class="external-link">RSA algorithm<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, which works with remainders resulting from dividing by a product of two large prime numbers. But what&rsquo;s a good way to write, in numbers and symbols, the claim that &ldquo;\(a\) divides \(b\) with remainder \(r\)&rdquo;? Well, we know that dividing yields a quotient (possibly zero) and a remainder (also possibly zero). Let&rsquo;s call the quotient \(q\). <span class="sidenote"> <label class="sidenote-label" for="r-less-note">Then, we know that when dividing \(b\) by \(a\) we have:</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="r-less-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> It's important to point out that for the equation in question to represent division with quotient \(q\) and remainder \(r\), it must be that \(r\) is less than \(a\). Otherwise, you could write \(r = s + a\) for some \(s\), and end up with $$ \begin{aligned} &amp;amp; b = qa &#43; r \\ \Rightarrow\ &amp;amp; b = qa &#43; (s &#43; a) \\ \Rightarrow\ &amp;amp; b = (q&#43;1)a &#43; s \end{aligned} $$ In plain English, if \(r\) is bigger than \(a\) after you've divided, you haven't taken out "as much \(a\) from your dividend as you could", and the actual quotient is larger than \(q\). <span class="sidenote-delimiter">]</span> </span> </span> </p> $$ \begin{aligned} &amp;amp; b = qa &#43; r \\ \Rightarrow\ &amp;amp; b-r = qa \\ \end{aligned} $$ <p>We only really care about the remainder here, not the quotient, since it&rsquo;s the remainder that determines if something is divisible or not. From the form of the second equation, we can deduce that \(b-r\) is divisible by \(a\) (it&rsquo;s literally equal to \(a\) times \(q\), so it must be divisible). Thus, we can write:</p> $$ a|(b-r) $$ <p>There&rsquo;s another notation for this type of statement, though. To say that the difference between two numbers is divisible by a third number, we write:</p> $$ b \equiv r\ (\text{mod}\ a) $$ <p>Some things that <em>seem</em> like they would work from this &ldquo;equation-like&rdquo; notation do, indeed, work. For instance, we can &ldquo;add two equations&rdquo; (I&rsquo;ll omit the proof here; jump down to <a href="#adding-two-congruences"class="same-page-link">this section</a> to see how it works):</p> $$ \textbf{if}\ a \equiv b\ (\text{mod}\ k)\ \textbf{and}\ c \equiv d, (\text{mod}\ k),\ \textbf{then}\ a&#43;c \equiv b&#43;d\ (\text{mod}\ k). $$ <p>Multiplying both sides by the same number (call it \(n\)) also works (once again, you can find the proof in <a href="#multiplying-both-sides-of-a-congruence"class="same-page-link">this section below</a>).</p> $$ \textbf{if}\ a \equiv b\ (\text{mod}\ k),\ \textbf{then}\ na \equiv nb\ (\text{mod}\ k). $$ <p>Ok, that&rsquo;s a lot of notation and other <em>stuff</em>. Let&rsquo;s talk specifics. Of particular interest is the number 10, since our number system is <em>base ten</em> (the value of a digit is multiplied by 10 for every place it moves to the left). The remainder of 10 when dividing by 3 is 1. Thus, we have:</p> $$ 10 \equiv 1\ (\text{mod}\ 3) $$ <p>From this, we can deduce that multiplying by 10, when it comes to remainders from dividing by 3, is the same as multiplying by 1. We can clearly see this by multiplying both sides by \(n\). In our notation:</p> $$ 10n \equiv n\ (\text{mod}\ 3) $$ <p>But wait, there&rsquo;s more. Take any power of ten, be it a hundred, a thousand, or a million. Multiplying by that number is <em>also</em> equivalent to multiplying by 1!</p> $$ 10^kn = 10\times10\times...\times 10n \equiv n\ (\text{mod}\ 3) $$ <p>We can put this to good use. Let&rsquo;s take a large number that&rsquo;s divisible by 3. This number will be made of multiple digits, like \(d_2d_1d_0\). Note that I do <strong>not</strong> mean multiplication here, but specifically that each \(d_i\) is a number between 0 and 9 in a particular place in the number &ndash; it&rsquo;s a digit. Now, we can write:</p> $$ \begin{aligned} 0 &amp;amp;\equiv d_2d_1d_0 \\ &amp;amp; = 100d_2 &#43; 10d_1 &#43; d_0 \\ &amp;amp; \equiv d_2 &#43; d_1 &#43; d_0 \end{aligned} $$ <p>We have just found that \(d_2+d_1+d_0 \equiv 0\ (\text{mod}\ 3)\), or that the sum of the digits is also divisible by 3. The logic we use works in the other direction, too: if the sum of the digits is divisible, then so is the actual number.</p> <p>There&rsquo;s only one property of the number 3 we used for this reasoning: that \(10 \equiv 1\ (\text{mod}\ 3)\). But it so happens that there&rsquo;s another number that has this property: 9. This means that to check if a number is divisible by <em>nine</em>, we can also check if the sum of the digits is divisible by 9. Try it on 18, 27, 81, and 198.</p> <p>Here&rsquo;s the main takeaway: <strong>summing the digits in the way described by my headmaster is the same as figuring out the remainder of the number from dividing by 9</strong>. Well, almost. The difference is the case of 9 itself: the <strong>remainder</strong> here is 0, but we actually use 9 to draw our line. We can actually try just using 0. Here&rsquo;s the updated <code>sum_digits</code> code:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Ruby" data-lang="Ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">sum_digits</span><span class="p">(</span><span class="n">n</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">n</span> <span class="o">%</span> <span class="mi">9</span> </span></span><span class="line"><span class="cl"><span class="k">end</span> </span></span></code></pre></div><p>The results are similarly cool:</p> <p><figure class="tiny"><img src="https://danilafe.com/blog/modulo_patterns/pattern_8_mod.svg" alt="Pattern generated by the number 8 by just using remainders."><figcaption> <p>Pattern generated by the number 8.</p> </figcaption> </figure> <figure class="tiny"><img src="https://danilafe.com/blog/modulo_patterns/pattern_4_mod.svg" alt="Pattern generated by the number 4 by just using remainders."><figcaption> <p>Pattern generated by the number 4.</p> </figcaption> </figure> <figure class="tiny"><img src="https://danilafe.com/blog/modulo_patterns/pattern_2_mod.svg" alt="Pattern generated by the number 2 by just using remainders."><figcaption> <p>Pattern generated by the number 2.</p> </figcaption> </figure> </p> <a href="#sequences-of-remainders"> <h3 id="sequences-of-remainders">Sequences of Remainders</h3> </a> <p>So now we know what the digit-summing algorithm is really doing. But that algorithm isn&rsquo;t all there is to it! We&rsquo;re repeatedly applying this algorithm over and over to multiples of another number. How does this work, and why does it always loop around? Why don&rsquo;t we ever spiral farther and farther from the center?</p> <p>First, let&rsquo;s take a closer look at our sequence of multiples. Suppose we&rsquo;re working with multiples of some number \(n\). Let&rsquo;s write \(a_i\) for the \(i\)th multiple. Then, we end up with:</p> $$ \begin{aligned} a_1 &amp;amp;= n \\ a_2 &amp;amp;= 2n \\ a_3 &amp;amp;= 3n \\ a_4 &amp;amp;= 4n \\ ... \\ a_i &amp;amp;= in \end{aligned} $$ <p>This is actually called an <a href="https://mathworld.wolfram.com/ArithmeticProgression.html"class="external-link">arithmetic sequence<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>; for each multiple, the number increases by \(n\).</p> <p>Here&rsquo;s a first seemingly trivial point: at some time, the remainder of \(a_i\) will repeat. There are only so many remainders when dividing by nine: specifically, the only possible remainders are the numbers 0 through 8. We can invoke the <a href="https://en.wikipedia.org/wiki/Pigeonhole_principle"class="external-link">pigeonhole principle<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> and say that after 9 multiples, we will have to have looped. Another way of seeing this is as follows:</p> $$ \begin{aligned} &amp;amp; 9 \equiv 0\ (\text{mod}\ 9) \\ \Rightarrow\ &amp;amp; 9n \equiv 0\ (\text{mod}\ 9) \\ \Rightarrow\ &amp;amp; 10n \equiv n\ (\text{mod}\ 9) \\ \end{aligned} $$ <p>The 10th multiple is equivalent to n, and will thus have the same remainder. The looping may happen earlier: the simplest case is if we pick 9 as our \(n\), in which case the remainder will always be 0.</p> <p>Repeating remainders alone do not guarantee that we will return to the center. The repeating sequence 1,2,3,4 will certainly cause a spiral. The reason is that, if we start facing &ldquo;up&rdquo;, we will always move up 1 and down 3 after four steps, leaving us 2 steps below where we started. Next, the cycle will repeat, and since turning four times leaves us facing &ldquo;up&rdquo; again, we&rsquo;ll end up getting <em>further</em> away. Here&rsquo;s a picture that captures this behvior:</p> <figure class="tiny"><img src="https://danilafe.com/blog/modulo_patterns/pattern_1_4.svg" alt="Spiral generated by the number 1 by summing digits."><figcaption> <p>Spiral generated by the number 1 with divisor 4.</p> </figcaption> </figure> <p>And here&rsquo;s one more where the cycle repeats after 8 steps instead of 4. You can see that it also leads to a spiral:</p> <figure class="tiny"><img src="https://danilafe.com/blog/modulo_patterns/pattern_1_8.svg" alt="Spiral generated by the number 1 by summing digits."><figcaption> <p>Spiral generated by the number 1 with divisor 8.</p> </figcaption> </figure> <p>From this, we can devise a simple condition to prevent spiraling &ndash; the <em>length</em> of the sequence before it repeats <em>cannot be a multiple of 4</em>. This way, whenever the cycle restarts, it will do so in a different direction: backwards, turned once to the left, or turned once to the right. Clearly repeating the sequence backwards is guaranteed to take us back to the start. The same is true for the left and right-turn sequences, though it&rsquo;s less obvious. If drawing our sequence once left us turned to the right, drawing our sequence twice will leave us turned more to the right. On a grid, two right turns are the same as turning around. The third repetition will then undo the effects of the first one (since we&rsquo;re facing backwards now), and the fourth will undo the effects of the second.</p> <p>There is an exception to this multiple-of-4 rule: if a sequence makes it back to the origin right before it starts over. In that case, even if it&rsquo;s facing the very same direction it started with, all is well &ndash; things are just like when it first started, and the cycle repeats. I haven&rsquo;t found a sequence that does this, so for our purposes, we&rsquo;ll stick with avoiding multiples of 4.</p> <p>Okay, so we want to avoid cycles with lengths divisible by four. What does it mean for a cycle to be of length <em>k</em>? It effectively means the following:</p> $$ \begin{aligned} &amp;amp; a_{k&#43;1} \equiv a_1\ (\text{mod}\ 9) \\ \Rightarrow\ &amp;amp; (k&#43;1)n \equiv n\ (\text{mod}\ 9) \\ \Rightarrow\ &amp;amp; kn \equiv 0\ (\text{mod}\ 9) \\ \end{aligned} $$ <p>If we could divide both sides by \(k\), we could go one more step:</p> $$ n \equiv 0\ (\text{mod}\ 9) \\ $$ <p>That is, \(n\) would be divisible by 9! This would contradict our choice of \(n\) to be between 2 and 8. What went wrong? Turns out, it&rsquo;s that last step: we can&rsquo;t always divide by \(k\). Some values of \(k\) are special, and it&rsquo;s only <em>those</em> values that can serve as cycle lengths without causing a contradiction. So, what are they?</p> <p>They&rsquo;re values that have a common factor with 9 (an incomplete explanation is in <a href="#invertible-numbers-textmod-d-share-no-factors-with-d"class="same-page-link">this section below</a>). There are many numbers that have a common factor with 9; 3, 6, 9, 12, and so on. However, those can&rsquo;t all serve as cycle lengths: as we said, cycles can&rsquo;t get longer than 9. This leaves us with 3, 6, and 9 as <em>possible</em> cycle lengths, none of which are divisible by 4. We&rsquo;ve eliminated the possibility of spirals!</p> <a href="#generalizing-to-arbitrary-divisors"> <h3 id="generalizing-to-arbitrary-divisors">Generalizing to Arbitrary Divisors</h3> </a> <p>The trick was easily executable on paper because there&rsquo;s an easy way to compute the remainder of a number when dividing by 9 (adding up the digits). However, we have a computer, and we don&rsquo;t need to fall back on such cool-but-complicated techniques. To replicate our original behavior, we can just write:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Ruby" data-lang="Ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">sum_digits</span><span class="p">(</span><span class="n">n</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">x</span> <span class="o">=</span> <span class="n">n</span> <span class="o">%</span> <span class="mi">9</span> </span></span><span class="line"><span class="cl"> <span class="n">x</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">?</span> <span class="mi">9</span> <span class="p">:</span> <span class="n">x</span> </span></span><span class="line"><span class="cl"><span class="k">end</span> </span></span></code></pre></div><p>But now, we can change the <code>9</code> to something else. There are some numbers we&rsquo;d like to avoid - specifically, we want to avoid those numbers that would allow for cycles of length 4 (or of a length divisible by 4). If we didn&rsquo;t avoid them, we might run into infinite loops, where our pencil might end up moving further and further from the center.</p> <p>Actually, let&rsquo;s revisit that. When we were playing with paths of length \(k\) while dividing by 9, we noted that the only <em>possible</em> values of \(k\) are those that share a common factor with 9, specifically 3, 6 and 9. But that&rsquo;s not quite as strong as it could be: try as you might, but you will not find a cycle of length 6 when dividing by 9. The same is true if we pick 6 instead of 9, and try to find a cycle of length 4. Even though 4 <em>does</em> have a common factor with 6, and thus is not ruled out as a valid cycle by our previous condition, we don&rsquo;t find any cycles of length 4.</p> <p>So what is it that <em>really</em> determines if there can be cycles or not?</p> <p>Let&rsquo;s do some more playing around. What are the actual cycle lengths when we divide by 9? For all but two numbers, the cycle lengths are 9. The two special numbers are 6 and 3, and they end up with a cycle length of 3. From this, we can say that the cycle length seems to depend on whether or not our \(n\) has any common factors with the divisor.</p> <p>Let&rsquo;s explore this some more with a different divisor, say 12. We fill find that 8 has a cycle length of 3, 7 has a cycle length of 12, 9 has a cycle length of 4. What&rsquo;s happening here? To see, let&rsquo;s divide 12 <strong>by these cycle lengths</strong>. For 8, we get (12/3) = 4. For 7, this works out to 1. For 9, it works out to 3. These new numbers, 4, 1, and 3, are actually the <strong>greatest common factors</strong> of 8, 7, and 3 with 12, respectively. The greatest common factor of two numbers is the largest number that divides them both. We thus write down our guess for the length of a cycle:</p> $$ k = \frac{d}{\text{gcd}(d,n)} $$ <p>Where \(d\) is our divisor, which has been 9 until just recently, and \(\text{gcd}(d,n)\) is the greatest common factor of \(d\) and \(n\). This equation is in agreement with our experiment for \(d = 9\), too. Why might this be? Recall that sequences with period \(k\) imply the following congruence:</p> $$ kn \equiv 0\ (\text{mod}\ d) $$ <p>Here I&rsquo;ve replaced 9 with \(d\), since we&rsquo;re trying to make it work for <em>any</em> divisor, not just 9. Now, suppose the greatest common divisor of \(n\) and \(d\) is some number \(f\). Then, since this number divides \(n\) and \(d\), we can write \(n=fm\) for some \(m\), and \(d=fg\) for some \(g\). We can rewrite our congruence as follows:</p> $$ kfm \equiv 0\ (\text{mod}\ fg) $$ <p>We can simplify this a little bit. Recall that what this congruence really means is that the difference of \(kfm\) and \(0\), which is just \(kfm\), is divisible by \(fg\):</p> $$ fg|kfm $$ <p>But if \(fg\) divides \(kfm\), it must be that \(g\) divides \(km\)! This, in turn, means we can write:</p> $$ g|km $$ <p>Can we distill this statement even further? It turns out that we can. Remember that we got \(g\) and \(m\) by dividing \(d\) and \(n\) by their greatest common factor, \(f\). This, in turn, means that \(g\) and \(m\) have no more common factors that aren&rsquo;t equal to 1 (see <a href="#numbers-divided-by-their-textgcd-have-no-common-factors"class="same-page-link">this section below</a>). From this, in turn, we can deduce that \(m\) is not relevant to \(g\) dividing \(km\), and we get:</p> $$ g|k $$ <p>That is, we get that \(k\) must be divisible by \(g\). Recall that we got \(g\) by dividing \(d\) by \(f\), which is our largest common factor &ndash; aka \(\text{gcd}(d,n)\). We can thus write:</p> $$ \frac{d}{\text{gcd}(d,n)}|k $$ <p>Let&rsquo;s stop and appreciate this result. We have found a condition that is required for a sequnce of remainders from dividing by \(d\) (which was 9 in the original problem) to repeat after \(k\) numbers. Furthermore, all of our steps can be performed in reverse, which means that if a \(k\) matches this conditon, we can work backwards and determine that a sequence of numbers has to repeat after \(k\) steps.</p> <p>Multiple \(k\)s will match this condition, and that&rsquo;s not surprising. If a sequence repeats after 5 steps, it also repeats after 10, 15, and so on. We&rsquo;re interested in the first time our sequences repeat after taking any steps, which means we have to pick the smallest possible non-zero value of \(k\). The smallest number divisible by \(d/\text{gcd}(d,n)\) is \(d/\text{gcd}(d,n)\) itself. We thus confirm our hypothesis:</p> $$ k = \frac{d}{\text{gcd}(d,n)} $$ <p>Lastly, recall that our patterns would spiral away from the center whenever a \(k\) is a multiple of 4. Now that we know what \(k\) is, we can restate this as &ldquo;\(d/\text{gcd}(d,n)\) is divisible by 4&rdquo;. But if we pick \(n=d-1\), the greatest common factor has to be \(1\) (see <a href="#divisors-of-n-and-n-1"class="same-page-link">this section below</a>), so we can even further simplify this &ldquo;\(d\) is divisible by 4&rdquo;. Thus, we can state simply that any divisor divisible by 4 is off-limits, as it will induce loops. For example, pick \(d=4\). Running our algorithm <span class="sidenote"> <label class="sidenote-label" for="constructive-note">for \(n=d-1=3\),</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="constructive-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Did you catch that? From our work above, we didn't just find a condition that would prevent spirals; we also found the precise number that would result in a spiral if this condition were violated! This is because our proof is <em>constructive</em>: instead of just claiming the existence of a thing, it also shows how to get that thing. Our proof in the earlier section (which claimed that the divisor 9 would never create spirals) went by contradiction, which was <em>not</em> constructive. Repeating that proof for a general \(d\) wouldn't have told us the specific numbers that would spiral.<br> <br> This is the reason that direct proofs tend to be preferred over proofs by contradiction. <span class="sidenote-delimiter">]</span> </span> </span> we indeed find an infinite spiral:</p> <figure class="tiny"><img src="https://danilafe.com/blog/modulo_patterns/pattern_3_4.svg" alt="Spiral generated by the number 3 by summing digits."><figcaption> <p>Spiral generated by the number 3 with divisor 4.</p> </figcaption> </figure> <p>Let&rsquo;s try again. Pick \(d=8\); then, for \(n=d-1=7\), we also get a spiral:</p> <figure class="tiny"><img src="https://danilafe.com/blog/modulo_patterns/pattern_7_8.svg" alt="Spiral generated by the number 7 by summing digits."><figcaption> <p>Spiral generated by the number 7 with divisor 8.</p> </figcaption> </figure> <p>A poem comes to mind:</p> <blockquote> <p>Turning and turning in the widening gyre</p> <p>The falcon cannot hear the falconner;</p> </blockquote> <p>Fortunately, there are plenty of numbers that are not divisible by four, and we can pick any of them! I&rsquo;ll pick primes for good measure. Here are a few good ones from using 13 (which corresponds to summing digits of base-14 numbers):</p> <p><figure class="tiny"><img src="https://danilafe.com/blog/modulo_patterns/pattern_8_13.svg" alt="Pattern generated by the number 8 by summing digits."><figcaption> <p>Pattern generated by the number 8 in base 14.</p> </figcaption> </figure> <figure class="tiny"><img src="https://danilafe.com/blog/modulo_patterns/pattern_4_13.svg" alt="Pattern generated by the number 4 by summing digits."><figcaption> <p>Pattern generated by the number 4 in base 14.</p> </figcaption> </figure> </p> <p>Here&rsquo;s one from dividing by 17 (base-18 numbers).</p> <figure class="tiny"><img src="https://danilafe.com/blog/modulo_patterns/pattern_5_17.svg" alt="Pattern generated by the number 5 by summing digits."><figcaption> <p>Pattern generated by the number 5 in base 18.</p> </figcaption> </figure> <p>Finally, base-30:</p> <figure class="tiny"><img src="https://danilafe.com/blog/modulo_patterns/pattern_2_29.svg" alt="Pattern generated by the number 2 by summing digits."><figcaption> <p>Pattern generated by the number 2 in base 30.</p> </figcaption> </figure> <figure class="tiny"><img src="https://danilafe.com/blog/modulo_patterns/pattern_6_29.svg" alt="Pattern generated by the number 6 by summing digits."><figcaption> <p>Pattern generated by the number 6 in base 30.</p> </figcaption> </figure> <a href="#generalizing-to-arbitrary-numbers-of-directions"> <h3 id="generalizing-to-arbitrary-numbers-of-directions">Generalizing to Arbitrary Numbers of Directions</h3> </a> <p>What if we didn&rsquo;t turn 90 degrees each time? What, if, instead, we turned 120 degrees (so that turning 3 times, not 4, would leave you facing the same direction you started)? We can pretty easily do that, too. Let&rsquo;s call this number of turns \(c\). Up until now, we had \(c=4\).</p> <p>First, let&rsquo;s update our condition. Before, we had &ldquo;\(d\) cannot be divisible by 4&rdquo;. Now, we aren&rsquo;t constraining ourselves to only 4, but rather using a generic variable \(c\). We then end up with &ldquo;\(d\) cannot be divisible by \(c\)&rdquo;. For instance, suppose we kept our divisor as 9 for the time being, but started turning 3 times instead of 4. This violates our divisibility condtion, and we once again end up with a spiral:</p> <figure class="tiny"><img src="https://danilafe.com/blog/modulo_patterns/pattern_8_9_t3.svg" alt="Pattern generated by the number 3 by summing digits and turning 120 degrees."><figcaption> <p>Pattern generated by the number 8 in base 10 while turning 3 times.</p> </figcaption> </figure> <p>If, on the other hand, we pick \(d=8\) and \(c=3\), we get patterns for all numbers just like we hoped. Here&rsquo;s one such pattern:</p> <figure class="tiny"><img src="https://danilafe.com/blog/modulo_patterns/pattern_7_8_t3.svg" alt="Pattern generated by the number 7 by summing digits in base 9 and turning 120 degrees."><figcaption> <p>Pattern generated by the number 7 in base 9 while turning 3 times.</p> </figcaption> </figure> <p>Hold on a moment; it&rsquo;s actully not so obvious why our condition <em>still</em> works. When we just turned on a grid, things were simple. As long as we didn&rsquo;t end up facing the same way we started, we will eventually perform the exact same motions in reverse. The same is not true when turning 120 degrees, like we suggested. Here&rsquo;s an animated circle all of the turns we would make:</p> <figure class="small"><img src="https://danilafe.com/blog/modulo_patterns/turn_3_1.gif" alt="Possible orientations when turning 120 degrees."><figcaption> <p>Orientations when turning 120 degrees</p> </figcaption> </figure> <p>We never quite do the exact <em>opposite</em> of any one of our movements. So then, will we come back to the origin anyway? Well, let&rsquo;s start simple. Suppose we always turn by exactly one 120-degree increment (we might end up turning more or less, just like we may end up turning left, right, or back in the 90 degree case). Each time you face a particular direciton, after performing a cycle, you will have moved some distance away from when you started, and turned 120 degrees. If you then repeat the cycle, you will once again move by the same offset as before, but this time the offset will be rotated 120 degrees, and you will have rotated a total of 240 degrees. Finally, performing the cycle a third time, you&rsquo;ll have moved by the same offset (rotated 240 degrees).</p> <p>If you overaly each offset such that their starting points overlap, they will look very similar to that circle above. And now, here&rsquo;s the beauty: you can arrange these rotated offsets into a triangle:</p> <figure class="small"><img src="https://danilafe.com/blog/modulo_patterns/turn_3_anim.gif" alt="Triangle formed by three 120-degree turns."><figcaption> <p>Triangle formed by three 120-degree turns.</p> </figcaption> </figure> <p>As long as you rotate by the same amount each time (and you will, since the cycle length determines how many times you turn, and the cycle length never changes), you can do so for any number of directions. For instance, here&rsquo;s a similar visualization in which there are 5 possible directions, and where each turn is consequently 72 degrees:</p> <figure class="small"><img src="https://danilafe.com/blog/modulo_patterns/turn_5_anim.gif" alt="Pentagon formed by five 72-degree turns."><figcaption> <p>Pentagon formed by five 72-degree turns.</p> </figcaption> </figure> <p>Each of these polygon shapes forms a loop. If you walk along its sides, you will eventually end up exactly where you started. This confirms that if you end up making one turn at the end of each cycle, you will eventually end up right where you started.</p> <p>Things aren&rsquo;t always as simple as making a single turn, though. Let&rsquo;s go back to the version of the problem in which we have 3 possible directions, and think about what would happen if we turned by 240 degrees at a time: 2 turns instead of 1?</p> <p>Even though we first turn a whole 240 degrees, the second time we turn we &ldquo;overshoot&rdquo; our initial bearing, and end up at 120 degrees compared to it. As soon as we turn 240 more degrees (turning the third time), we end up back at 0. In short, even though we &ldquo;visited&rdquo; each bearing in a different order, we visited them all, and exactly once at that. Here&rsquo;s a visualization:</p> <figure class="small"><img src="https://danilafe.com/blog/modulo_patterns/turn_3_2.gif" alt="Possible orientations when turning 120 degrees, twice at a time."><figcaption> <p>Orientations when turning 120 degrees, twice at a time</p> </figcaption> </figure> <p>Note that even though in the above picture it looks like we&rsquo;re just turning left instead of right, that&rsquo;s not the case; a single turn of 240 degrees is more than half the circle, so our second bearing ends up on the left side of the circle even though we turn right.</p> <p>Just to make sure we really see what&rsquo;s happening, let&rsquo;s try this when there are 5 possible directions, and when we still make two turns (now of 72 degrees each)</p> <figure class="small"><img src="https://danilafe.com/blog/modulo_patterns/turn_5_2.gif" alt="Possible orientations when turning 72 degrees, twice at a time."><figcaption> <p>Orientations when turning 72 degrees, twice at a time</p> </figcaption> </figure> <p>Let&rsquo;s try put some mathematical backing to this &ldquo;visited them all&rdquo; idea, and turning in general. First, observe that as soon as we turn 360 degrees, it&rsquo;s as good as not turning at all - we end up facing up again. If we turned 480 degrees (that is, two turns of 240 degrees each), the first 360 can be safely ignored, since it puts us where we started; only the 120 degrees that remain are needed to figure out our final bearing. In short, the final direction we&rsquo;re facing is the remainder from dividing by 360. We already know how to formulate this using modular arithmetic: if we turn \(t\) degrees \(k\) times, and end up at final bearing (remainder) \(b\), this is captured by:</p> $$ kt \equiv b\ (\text{mod}\ 360) $$ <p>Of course, if we end up facing the same way we started, we get the familiar equivalence:</p> $$ kt \equiv 0\ (\text{mod}\ 360) $$ <p>Even though the variables in this equivalence mean different things now than they did last time we saw it, the mathematical properties remain the same. For instance, we can say that after \(360/\text{gcd}(360, t)\) turns, we&rsquo;ll end up facing the way that we started.</p> <p>So far, so good. What I don&rsquo;t like about this, though, is that we have all of these numbers of degrees all over our equations: 72 degrees, 144 degrees, and so forth. However, something like 73 degrees (if there are five possible directions) is just not a valid bearing, and nor is 71. We have so many possible degrees (360 of them, to be exact), but we&rsquo;re only using a handful! That&rsquo;s wasteful. Instead, observe that for \(c\) possible turns, the smallest possible turn angle is \(360/c\). Let&rsquo;s call this angle \(\theta\) (theta). Now, notice that we always turn in multiples of \(\theta\): a single turn moves us \(\theta\) degrees, two turns move us \(2\theta\) degrees, and so on. If we define \(r\) to be the number of turns that we find ourselves rotated by after a single cycle, we have \(t=r\theta\), and our turning equation can be written as:</p> $$ kr\theta \equiv 0\ (\text{mod}\ c\theta) $$ <p>Now, once again, recall that the above equivalence is just notation for the following:</p> $$ \begin{aligned} &amp;amp; c\theta|kr\theta \\ \Leftrightarrow\ &amp;amp; c|kr \end{aligned} $$ <p>And finally, observing that \(kr=kr-0\), we have:</p> $$ kr \equiv 0\ (\text{mod}\ c) $$ <p>This equivalence says the same thing as our earlier one; however, instead of being in terms of degrees, it&rsquo;s in terms of the number of turns \(c\) and the turns-per-cycle \(r\). Now, recall once again that the smallest number of steps \(k>0\) for which this equivalence holds is \(k = c/\text{gcd}(c,r)\).</p> <p>We&rsquo;re close now: we have a sequence of \(k\) steps that will lead us back to the beginning. What&rsquo;s left is to show that these \(k\) steps are evenly distributed throughout our circle, which is the key property that makes it possible for us to make a polygon out of them (and thus end up back where we started).</p> <p>To show this, say that we have a largest common divisor \(f=\text{gcd}(c,r)\), and that \(c=fe\) and \(r=fs\). We can once again &ldquo;divide through&rdquo; by \(f\), and get:</p> $$ ks \equiv 0\ (\text{mod}\ e) $$ <p>Now, we know that \(\text{gcd}(e,s)=1\) (<a href="#numbers-divided-by-their-textgcd-have-no-common-factors"class="same-page-link">see this section below</a>), and thus:</p> $$ k = e/\text{gcd}(e,s) = e $$ <p>That is, our cycle will repeat after \(e\) remainders. But wait, we&rsquo;ve only got \(e\) possible remainders: the numbers \(0\) through \(e-1\)! Thus, for a cycle to repeat after \(e\) remainders, all possible remainders must occur. For a concrete example, take \(e=5\); our remainders will be the set \(\{0,1,2,3,4\}\). Now, let&rsquo;s &ldquo;multiply back through&rdquo; by \(f\):</p> $$ kfs \equiv 0\ (\text{mod}\ fe) $$ <p>We still have \(e\) possible remainders, but this time they are multiplied by \(f\). For example, taking \(e\) to once again be equal to \(5\), we have the set of possible remainders \(\{0, f, 2f, 3f, 4f\}\). The important bit is that these remainders are all evenly spaced, and that space between them is \(f=\text{gcd}(c,r)\).</p> <p>Let&rsquo;s recap: we have confirmed that for \(c\) possible turns (4 in our original formulation), and \(r\) turns at a time, we will always loop after \(k=c/\text{gcd}(c,r)\) steps, evenly spaced out at \(\text{gcd}(c,r)\) turns. No specific properties from \(c\) or \(r\) are needed for this to work. Finally, recall from the previous section that \(r\) is zero (and thus, our pattern breaks down) whenever the divisor \(d\) (9 in our original formulation) is itself divisible by \(c\). And so, <strong>as long as we pick a system with \(c\) possible directions and divisor \(d\), we will always loop back and create a pattern as long as \(c\nmid d\) (\(c\) does not divide \(d\))</strong>.</p> <p>Let&rsquo;s try it out! There&rsquo;s a few pictures below. When reading the captions, keep in mind that the <em>base</em> is one more than the <em>divisor</em> (we started with numbers in the usual base 10, but divided by 9).</p> <figure class="tiny"><img src="https://danilafe.com/blog/modulo_patterns/pattern_1_7_t5.svg" alt="Pattern generated by the number 1 by summing digits in base 8 and turning 72 degrees."><figcaption> <p>Pattern generated by the number 1 in base 8 while turning 5 times.</p> </figcaption> </figure> <figure class="tiny"><img src="https://danilafe.com/blog/modulo_patterns/pattern_3_4_t7.svg" alt="Pattern generated by the number 3 by summing digits in base 5 and turning 51 degrees."><figcaption> <p>Pattern generated by the number 3 in base 5 while turning 7 times.</p> </figcaption> </figure> <figure class="tiny"><img src="https://danilafe.com/blog/modulo_patterns/pattern_3_11_t6.svg" alt="Pattern generated by the number 3 by summing digits in base 12 and turning 60 degrees."><figcaption> <p>Pattern generated by the number 3 in base 12 while turning 6 times.</p> </figcaption> </figure> <figure class="tiny"><img src="https://danilafe.com/blog/modulo_patterns/pattern_2_11_t7.svg" alt="Pattern generated by the number 2 by summing digits in base 12 and turning 51 degrees."><figcaption> <p>Pattern generated by the number 2 in base 12 while turning 7 times.</p> </figcaption> </figure> <a href="#conclusion"> <h3 id="conclusion">Conclusion</h3> </a> <p>Today we peeked under the hood of a neat mathematical trick that was shown to me by my headmaster over 10 years ago now. Studying what it was that made this trick work led us to play with the underlying mathematics some more, and extend the trick to more situations (and prettier patterns). I hope you found this as interesting as I did!</p> <p>By the way, the kind of math that we did in this article is most closely categorized as <em>number theory</em>. Check it out if you&rsquo;re interested!</p> <p>Finally, a huge thank you to Arthur for checking my math, helping me with proofs, and proofreading the article.</p> <p>All that remains are some proofs I omitted from the original article since they were taking up a lot of space (and were interrupting the flow of the explanation). They are listed below.</p> <a href="#referenced-proofs"> <h3 id="referenced-proofs">Referenced Proofs</h3> </a> <a href="#adding-two-congruences"> <h4 id="adding-two-congruences">Adding Two Congruences</h4> </a> <p><strong>Claim</strong>: If for some numbers \(a\), \(b\), \(c\), \(d\), and \(k\), we have \(a \equiv b\ (\text{mod}\ k)\) and \(c \equiv d\ (\text{mod}\ k)\), then it&rsquo;s also true that \(a+c \equiv b+d\ (\text{mod}\ k)\).</p> <p><strong>Proof</strong>: By definition, we have \(k|(a-b)\) and \(k|(c-d)\). This, in turn, means that for some \(i\) and \(j\), \(a-b=ik\) and \(c-d=jk\). Add both sides to get: $$ \begin{aligned} &amp;amp; (a-b)&#43;(c-d) = ik&#43;jk \\ \Rightarrow\ &amp;amp; (a&#43;c)-(b&#43;d) = (i&#43;j)k \\ \Rightarrow\ &amp;amp; k\ |\left[(a&#43;c)-(b&#43;d)\right]\\ \Rightarrow\ &amp;amp; a&#43;c \equiv b&#43;d\ (\text{mod}\ k) \\ \end{aligned} $$ \(\blacksquare\)</p> <a href="#multiplying-both-sides-of-a-congruence"> <h4 id="multiplying-both-sides-of-a-congruence">Multiplying Both Sides of a Congruence</h4> </a> <p><strong>Claim</strong>: If for some numbers \(a\), \(b\), \(n\) and \(k\), we have \(a \equiv b\ (\text{mod}\ k)\) then we also have that \(an \equiv bn\ (\text{mod}\ k)\).</p> <p><strong>Proof</strong>: By definition, we have \(k|(a-b)\). Since multiplying \(a-b\) but \(n\) cannot make it <em>not</em> divisible by \(k\), we also have \(k|\left[n(a-b)\right]\). Distributing \(n\), we have \(k|(na-nb)\). By definition, this means \(na\equiv nb\ (\text{mod}\ k)\).</p> <p>\(\blacksquare\)</p> <a href="#invertible-numbers-textmod-d-share-no-factors-with-d"> <h4 id="invertible-numbers-textmod-d-share-no-factors-with-d">Invertible Numbers \(\text{mod}\ d\) Share no Factors with \(d\)</h4> </a> <p><strong>Claim</strong>: A number \(k\) is only invertible (can be divided by) in \(\text{mod}\ d\) if \(k\) and \(d\) share no common factors (except 1).</p> <p><strong>Proof</strong>: Write \(\text{gcd}(k,d)\) for the greatest common factor divisor of \(k\) and \(d\). Another important fact (not proven here, but see something <a href="https://sharmaeklavya2.github.io/theoremdep/nodes/number-theory/gcd/gcd-is-min-lincomb.html"class="external-link">like this<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>), is that if \(\text{gcd}(k,d) = r\), then the smallest possible number that can be made by adding and subtracting \(k\)s and \(d\)s is \(r\). That is, for some \(i\) and \(j\), the smallest possible positive value of \(ik + jd\) is \(r\).</p> <p>Now, note that \(d \equiv 0\ (\text{mod}\ d)\). Multiplying both sides by \(j\), get \(jd\equiv 0\ (\text{mod}\ d)\). This, in turn, means that the smallest possible value of \(ik+jd \equiv ik\) is \(r\). If \(r\) is bigger than 1 (i.e., if \(k\) and \(d\) have common factors), then we can&rsquo;t pick \(i\) such that \(ik\equiv1\), since we know that \(r>1\) is the least possible value we can make. There is therefore no multiplicative inverse to \(k\). Alternatively worded, we cannot divide by \(k\).</p> <p>\(\blacksquare\)</p> <a href="#numbers-divided-by-their-textgcd-have-no-common-factors"> <h4 id="numbers-divided-by-their-textgcd-have-no-common-factors">Numbers Divided by Their \(\text{gcd}\) Have No Common Factors</h4> </a> <p><strong>Claim</strong>: For any two numbers \(a\) and \(b\) and their largest common factor \(f\), if \(a=fc\) and \(b=fd\), then \(c\) and \(d\) have no common factors other than 1 (i.e., \(\text{gcd}(c,d)=1\)).</p> <p><strong>Proof</strong>: Suppose that \(c\) and \(d\) do have sommon factor, \(e\neq1\). In that case, we have \(c=ei\) and \(d=ej\) for some \(i\) and \(j\). Then, we have \(a=fei\), and \(b=fej\). From this, it&rsquo;s clear that both \(a\) and \(b\) are divisible by \(fe\). Since \(e\) is greater than \(1\), \(fe\) is greater than \(f\). But our assumptions state that \(f\) is the greatest common divisor of \(a\) and \(b\)! We have arrived at a contradiction.</p> <p>Thus, \(c\) and \(d\) cannot have a common factor other than 1.</p> <p>\(\blacksquare\)</p> <a href="#divisors-of-n-and-n-1"> <h4 id="divisors-of-n-and-n-1">Divisors of \(n\) and \(n-1\).</h4> </a> <p><strong>Claim</strong>: For any \(n\), \(\text{gcd}(n,n-1)=1\). That is, \(n\) and \(n-1\) share no common divisors.</p> <p><strong>Proof</strong>: Suppose some number \(f\) divides both \(n\) and \(n-1\). In that case, we can write \(n=af\), and \((n-1)=bf\) for some \(a\) and \(b\). Subtracting one equation from the other:</p> <p>$$ 1 = (a-b)f $$ But this means that 1 is divisible by \(f\)! That&rsquo;s only possible if \(f=1\). Thus, the only number that divides \(n\) and \(n-1\) is 1; that&rsquo;s our greatest common factor.</p> <p>\(\blacksquare\)</p> Introducing Matrix Highlight https://danilafe.com/blog/introducing_highlight/ Mon, 13 Dec 2021 16:49:42 -0800 https://danilafe.com/blog/introducing_highlight/ <p>I wanted to briefly introduce a project that I&rsquo;ve been working on in my spare time over the past couple of months. It&rsquo;s called <strong>Matrix Highlight</strong>, though this is a working title. However, it does exactly what the title claims: this little project is a browser extension to annotate the web, using <a href="https://matrix.org"class="external-link">Matrix<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> as a communication and storage protocol. My goal with this project is a <strong>decentralized, federated, collaborative annotation system for web pages and documents (that can be self-hosted).</strong> See the image below for a quick sneak peek at what it looks like.</p> <figure class="fullwide"><img src="https://danilafe.com/blog/introducing_highlight/mhl_many.png" alt="Text randomly highlighted with Matrix Highlight"><figcaption> <p>Text randomly highlighted with Matrix Highlight</p> </figcaption> </figure> <a href="#project-goals"> <h3 id="project-goals">Project Goals</h3> </a> <a href="#decentralized"> <h4 id="decentralized">Decentralized</h4> </a> <p>Quite literally, the word &ldquo;decentralized&rdquo; lies in opposition to &ldquo;centralized&rdquo;. In a centralized application, the data, or computation, or anything really, is controlled by a single entity. An example of this might be Google Docs: Google is in charge of Docs, and no one else. You log in through Google, and Google manages your various documents and edits to them authoritatively.</p> <p>I don&rsquo;t think that&rsquo;s a good idea. Although convenient, this kind of arrangement shifts control out of your hands as a user, and into the hands of the entity running your software. Google has the power, if they wanted, to ban you from using Google Docs, or to manage your content in ways you don&rsquo;t like. They can (and do) impose storage limits. You are, in a sense, at their mercy. Furthermore, in the (admittedly unlikely) case that Google goes down, Google Docs goes down for everyone. There&rsquo;s a single point of failure. Whereas it&rsquo;s hard to imagine Google itself having any real trouble (although the recent AWS outage proves that such a thing is possible), most services aren&rsquo;t Google.</p> <p>A decentralized application does not suffer from these problems. The failure of a single server in a decentralized system does not bring it down for everyone. Furthermore, users have the ability to switch between different servers or providers if one becomes abusive (or simply stops existing). Users have more choices, and more control.</p> <p><strong>For Matrix Highlight specifically</strong>, this means not having to rely on one specific group or company for storing and managing your annotations or notes.</p> <a href="#federated"> <h4 id="federated">Federated</h4> </a> <p>Decentralization by itself does not make for useful software. There might very well be multiple servers providing access to a particular piece of software. However, there&rsquo;s no guarantee that users of one such server can meaningfully interact with users of another server. Microsoft&rsquo;s Office 365 has collaborative document editing, and so does Google Docs. However, users of the two services cannot collaborate with <em>each other</em>.</p> <p>In a federated system, the various providers establish a way of working together. The <a href="https://en.wikipedia.org/wiki/Fediverse"class="external-link">Fediverse<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> is a big example of this. Users of various <a href="https://joinmastodon.org/"class="external-link">Mastodon<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> servers can see each other&rsquo;s messages and posts, despite residing on servers with differing rules and administration. Users of Matrix can send messages between servers, with only one account.</p> <p><strong>For Matrix Highlight</strong>, this means that users who choose to use different servers or providers are still able to collaboratively highlight and annotate pages together.</p> <a href="#self-hosted"> <h4 id="self-hosted">Self-Hosted</h4> </a> <p>Self-hosting is the practice of running the various software you use yourself. This allows you yourself to be in charge of your data, instead of <em>any</em> other entity, however trustworthy. A popular self-hosted solution is <a href="https://nextcloud.com/"class="external-link">Nextcloud<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, which may be used, among other things, as a Google Drive replacement that you run on your own server. With Nextcloud, your files are completely under your own management, rather than that of some other person or company elsewhere.</p> <p><strong>For Matrix Highlight</strong>, this means that users can choose to run all the necessary software themselves, and thus remain in complete control of their annotation and other data.</p> <a href="#what-it-looks-like"> <h3 id="what-it-looks-like">What it Looks Like</h3> </a> <p>First of all, you can watch a little demo video I recorded here:</p> <div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"> <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/Q3h5A0DsE1s?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe> </div> <p>You already got a little taste of Matrix Highlight in the opening screenshot. However, I&rsquo;d like to show you some more of what I have so far. The most important aspect of the tool is the ability to annotate web pages. The tool can be brought up on any page; I typically test it on my blog, but that&rsquo;s just because it&rsquo;s convenient. Selecting some text brings up a little highlighting tooltip:</p> <figure><img src="https://danilafe.com/blog/introducing_highlight/mhl_tooltip.png" alt="A matrix highlighting tooltip appearing over one of the sidenotes in a different article."><figcaption> <p>A matrix highlighting tooltip appearing over one of the sidenotes in a different article.</p> </figcaption> </figure> <p>Selecting one of the colors in the tooltip creates a new highlight of the text you had selected:</p> <figure><img src="https://danilafe.com/blog/introducing_highlight/mhl_highlight.png" alt="The result of clicking a color in the previous screenshot."><figcaption> <p>The result of clicking a color in the previous screenshot.</p> </figcaption> </figure> <p>Annotations applied in this way are shared across all active instances of a Matrix Highlight page, including those shared with other users.</p> <figure class="fullwide"><img src="https://danilafe.com/blog/introducing_highlight/mhl_multi.png" alt="Two chrome windows with the same annotations."><figcaption> <p>Two chrome windows with the same annotations.</p> </figcaption> </figure> <p>Highlights created by users can also be browsed as a list:</p> <figure class="medium"><img src="https://danilafe.com/blog/introducing_highlight/mhl_quotelist.png" alt="A list of highlights from another page."><figcaption> <p>A list of highlights from another page.</p> </figcaption> </figure> <p>Highlights are stored in Matrix rooms. Since Matrix rooms are effectively chat rooms, they are built for being shared with other users. Thus, it is very simple to give another user access to the current list of highlights.</p> <figure class="medium"><img src="https://danilafe.com/blog/introducing_highlight/mhl_userlist.png" alt="A list of users for a particular page."><figcaption> <p>A list of users for a particular page.</p> </figcaption> </figure> <p>This also means that a single page can have multiple independent sets of highlights, allowing you to organize them however you like. For instance, if you&rsquo;re proofreading a page of your own, you may have a highlight set (Matrix room) for every editing pass. The rooms can be switched at a moment&rsquo;s notice:</p> <figure class="medium"><img src="https://danilafe.com/blog/introducing_highlight/mhl_roomlist.png" alt="A list of rooms for a particular page."><figcaption> <p>A list of rooms for a particular page.</p> </figcaption> </figure> <a href="#current-and-planned-features"> <h3 id="current-and-planned-features">Current and Planned Features</h3> </a> <p>The following are the current and planned features for Matrix Highlight:</p> <ul> <li><strong>Current</strong>: Create and send website annotations over Matrix.</li> <li><strong>Current</strong>: Store data in a decentralized and federated manner.</li> <li><strong>Current</strong>: Share highlights with other users, including those on other servers.</li> <li><strong>Current</strong>: Group annotations together and create multiple annotation groups</li> <li><strong>Planned</strong>: Use Matrix&rsquo;s End-to-End encryption to ensure the secure transmission and storage of highlight data.</li> <li><strong>Planned</strong>: Leverage the new <a href="https://github.com/matrix-org/matrix-doc/blob/gsouquet/threading-via-relations/proposals/3440-threading-via-relations.md"class="external-link"><code>m.thread</code> MSC<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> to allow users to comment on and discuss highlights.</li> <li><strong>Planned</strong>: Use something like <a href="https://archivebox.io/"class="external-link">ArchiveBox<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> to cache the current version of a website and prevent annotations from breaking.</li> <li><strong>Planned</strong> Highlight PDFs in addition to web pages.</li> </ul> <a href="#project-status-and-conclusion"> <h3 id="project-status-and-conclusion">Project Status and Conclusion</h3> </a> <p>For the moment, I&rsquo;m refraining from publishing the project&rsquo;s source or output extensions. This is a hobby project, and I don&rsquo;t want to share something half-baked with the world. However, I fully intend to share the code for the project as soon as I think it&rsquo;s ready (which would probably be when I feel perfectly comfortable using it for my own needs).</p> A Verified Evaluator for the Untyped Concatenative Calculus https://danilafe.com/blog/coq_dawn_eval/ Sat, 27 Nov 2021 20:24:57 -0800 https://danilafe.com/blog/coq_dawn_eval/ <p>Earlier, I wrote <a href="https://danilafe.com/blog/coq_dawn/">an article</a> in which I used Coq to encode the formal semantics of <a href="https://www.dawn-lang.org/posts/foundations-ucc/"class="external-link">Dawn&rsquo;s Untyped Concatenative Calculus<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, and to prove a few things about the mini-language. Though I&rsquo;m quite happy with how that turned out, my article was missing something that&rsquo;s present on the original Dawn page &ndash; an evaluator. In this article, I&rsquo;ll define an evaluator function in Coq, prove that it&rsquo;s equivalent to Dawn&rsquo;s formal semantics, and extract all of this into usable Haskell code.</p> <a href="#changes-since-last-time"> <h3 id="changes-since-last-time">Changes Since Last Time</h3> </a> <p>In trying to write and verify this evaluator, I decided to make changes to the way I formalized the UCC. Remember how we used a predicate, <code>IsValue</code>, to tag expressions that were values? It turns out that this is a very cumbersome approach. For one thing, this formalization allows for the case in which the exact same expression is a value for two different reasons. Although <code>IsValue</code> has only one constructor (<code>Val_quote</code>), it&rsquo;s actually <span class="sidenote"> <label class="sidenote-label" for="hott-note">not provable</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="hott-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Interestingly, it's also not provable that any two proofs of \(a = b\) are equal, even though equality only has one constructor, \(\text{refl}\ :\ a \rightarrow (a = a) \). Under the <a href="https://homotopytypetheory.org/book/">homotopic interpretation</a> of type theory, this corresponds to the fact that two paths from \(a\) to \(b\) need not be homotopic (continuously deformable) to each other.<br> <br> As an intuitive example, imagine wrapping a string around a pole. Holding the ends of the string in place, there's nothing you can do to "unwrap" the string. This string is thus not deformable into a string that starts and stops at the same points, but does not go around the pole. <span class="sidenote-delimiter">]</span> </span> </span> that any two proofs of <code>IsValue e</code> are equal. I ended up getting into a lot of losing arguments with the Coq runtime about whether or not two stacks are actually the same. Also, some of the semantic rules expected a value on the stack with a particular proof for <code>IsValue</code>, and rejected the exact same stack with a generic value.</p> <p>Thus, I switched from our old implementation:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/Dawn.v" data-first-line="19" data-last-line="22"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Dawn.v#L19-L22">Dawn.v</a>, lines 19 through 22</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Inductive</span> <span class="n">IsValue</span> <span class="o">:</span> <span class="n">expr</span> <span class="o">-&gt;</span> <span class="kt">Prop</span> <span class="o">:=</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">Val_quote</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">{</span><span class="n">e</span> <span class="o">:</span> <span class="n">expr</span><span class="o">},</span> <span class="n">IsValue</span> <span class="o">(</span><span class="n">e_quote</span> <span class="n">e</span><span class="o">).</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kn">Definition</span> <span class="n">value</span> <span class="o">:=</span> <span class="o">{</span> <span class="n">v</span> <span class="o">:</span> <span class="n">expr</span> <span class="o">&amp;</span> <span class="n">IsValue</span> <span class="n">v</span> <span class="o">}.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>To the one I originally had in mind:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/DawnV2.v" data-first-line="19" data-last-line="19"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/DawnV2.v#L19-L19">DawnV2.v</a>, line 19</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">19 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Inductive</span> <span class="n">value</span> <span class="o">:=</span> <span class="n">v_quote</span> <span class="o">(</span><span class="n">e</span> <span class="o">:</span> <span class="n">expr</span><span class="o">).</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>I then had the following function to convert a value back into an equivalent expression:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/DawnV2.v" data-first-line="22" data-last-line="25"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/DawnV2.v#L22-L25">DawnV2.v</a>, lines 22 through 25</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Definition</span> <span class="n">value_to_expr</span> <span class="o">(</span><span class="n">v</span> <span class="o">:</span> <span class="n">value</span><span class="o">)</span> <span class="o">:</span> <span class="n">expr</span> <span class="o">:=</span> </span></span><span class="line"><span class="cl"> <span class="k">match</span> <span class="n">v</span> <span class="k">with</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">v_quote</span> <span class="n">e</span> <span class="o">=&gt;</span> <span class="n">e_quote</span> <span class="n">e</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>I replaced instances of <code>projT1</code> with instances of <code>value_to_expr</code>.</p> <a href="#where-we-are"> <h3 id="where-we-are">Where We Are</h3> </a> <p>At the end of my previous article, we ended up with a Coq encoding of the big-step <a href="https://en.wikipedia.org/wiki/Operational_semantics"class="external-link">operational semantics<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> of UCC, as well as some proofs of correctness about the derived forms from the article (like \(\text{quote}_3\) and \(\text{rotate}_3\)). The trouble is, despite having our operational semantics, we can&rsquo;t make our Coq code run anything. This is for several reasons:</p> <ol> <li>Our definitions only let us to <em>confirm</em> that given some initial stack, a program ends up in some other final stack. We even have a little <code>Ltac2</code> tactic to help us automate this kind of proof. However, in an evaluator, the final stack is not known until the program finishes running. We can&rsquo;t confirm the result of evaluation, we need to <em>find</em> it.</li> <li>To address the first point, we could try write a function that takes a program and an initial stack, and produces a final stack, as well as a proof that the program would evaluate to this stack under our semantics. However, it&rsquo;s quite easy to write a non-terminating UCC program, whereas functions in Coq <em>have to terminate!</em> We can&rsquo;t write a terminating function to run non-terminating code.</li> </ol> <p>So, is there anything we can do? No, there isn&rsquo;t. Writing an evaluator in Coq is just not possible. The end, thank you for reading.</p> <p>Just kidding &ndash; there&rsquo;s definitely a way to get our code evaluating, but it will look a little bit strange.</p> <a href="#a-step-by-step-evaluator"> <h3 id="a-step-by-step-evaluator">A Step-by-Step Evaluator</h3> </a> <p>The trick is to recognize that program evaluation occurs in steps. There may well be an infinite number of steps, if the program is non-terminating, but there&rsquo;s always a step we can take. That is, unless an invalid instruction is run, like trying to clone from an empty stack, or unless the program finished running. You don&rsquo;t need a non-terminating function to just give you a next step, if one exists. We can write such a function in Coq.</p> <p>Here&rsquo;s a new data type that encodes the three situations we mentioned in the previous paragraph. Its constructors (one per case) are as follows:</p> <ol> <li><code>err</code> - there are no possible evaluation steps due to an error.</li> <li><code>middle e s</code> - the evaluation took a step; the stack changed to <code>s</code>, and the rest of the program is <code>e</code>.</li> <li><code>final s</code> - there are no possible evaluation steps because the evaluation is complete, leaving a final stack <code>s</code>.</li> </ol> <div class="highlight-group" data-base-path="" data-file-path="dawn/DawnEval.v" data-first-line="6" data-last-line="9"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/DawnEval.v#L6-L9">DawnEval.v</a>, lines 6 through 9</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span><span class="lnt">9 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Inductive</span> <span class="n">step_result</span> <span class="o">:=</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">err</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">middle</span> <span class="o">(</span><span class="n">e</span> <span class="o">:</span> <span class="n">expr</span><span class="o">)</span> <span class="o">(</span><span class="n">s</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">final</span> <span class="o">(</span><span class="n">s</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">).</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We can now write a function that tries to execute a single step given an expression.</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/DawnEval.v" data-first-line="11" data-last-line="27"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/DawnEval.v#L11-L27">DawnEval.v</a>, lines 11 through 27</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Fixpoint</span> <span class="n">eval_step</span> <span class="o">(</span><span class="n">s</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">)</span> <span class="o">(</span><span class="n">e</span> <span class="o">:</span> <span class="n">expr</span><span class="o">)</span> <span class="o">:</span> <span class="n">step_result</span> <span class="o">:=</span> </span></span><span class="line"><span class="cl"> <span class="k">match</span> <span class="n">e</span><span class="o">,</span> <span class="n">s</span> <span class="k">with</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">e_int</span> <span class="n">swap</span><span class="o">,</span> <span class="n">v&#39;</span> <span class="o">::</span> <span class="n">v</span> <span class="o">::</span> <span class="n">vs</span> <span class="o">=&gt;</span> <span class="n">final</span> <span class="o">(</span><span class="n">v</span> <span class="o">::</span> <span class="n">v&#39;</span> <span class="o">::</span> <span class="n">vs</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">e_int</span> <span class="n">clone</span><span class="o">,</span> <span class="n">v</span> <span class="o">::</span> <span class="n">vs</span> <span class="o">=&gt;</span> <span class="n">final</span> <span class="o">(</span><span class="n">v</span> <span class="o">::</span> <span class="n">v</span> <span class="o">::</span> <span class="n">vs</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">e_int</span> <span class="n">drop</span><span class="o">,</span> <span class="n">v</span> <span class="o">::</span> <span class="n">vs</span> <span class="o">=&gt;</span> <span class="n">final</span> <span class="n">vs</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">e_int</span> <span class="n">quote</span><span class="o">,</span> <span class="n">v</span> <span class="o">::</span> <span class="n">vs</span> <span class="o">=&gt;</span> <span class="n">final</span> <span class="o">(</span><span class="n">v_quote</span> <span class="o">(</span><span class="n">value_to_expr</span> <span class="n">v</span><span class="o">)</span> <span class="o">::</span> <span class="n">vs</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">e_int</span> <span class="n">compose</span><span class="o">,</span> <span class="o">(</span><span class="n">v_quote</span> <span class="n">v2</span><span class="o">)</span> <span class="o">::</span> <span class="o">(</span><span class="n">v_quote</span> <span class="n">v1</span><span class="o">)</span> <span class="o">::</span> <span class="n">vs</span> <span class="o">=&gt;</span> <span class="n">final</span> <span class="o">(</span><span class="n">v_quote</span> <span class="o">(</span><span class="n">e_comp</span> <span class="n">v1</span> <span class="n">v2</span><span class="o">)</span> <span class="o">::</span> <span class="n">vs</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">e_int</span> <span class="k">apply</span><span class="o">,</span> <span class="o">(</span><span class="n">v_quote</span> <span class="n">v1</span><span class="o">)</span> <span class="o">::</span> <span class="n">vs</span> <span class="o">=&gt;</span> <span class="n">middle</span> <span class="n">v1</span> <span class="n">vs</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">e_quote</span> <span class="n">e&#39;</span><span class="o">,</span> <span class="n">vs</span> <span class="o">=&gt;</span> <span class="n">final</span> <span class="o">(</span><span class="n">v_quote</span> <span class="n">e&#39;</span> <span class="o">::</span> <span class="n">vs</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">e_comp</span> <span class="n">e1</span> <span class="n">e2</span><span class="o">,</span> <span class="n">vs</span> <span class="o">=&gt;</span> </span></span><span class="line"><span class="cl"> <span class="k">match</span> <span class="n">eval_step</span> <span class="n">vs</span> <span class="n">e1</span> <span class="k">with</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">final</span> <span class="n">vs&#39;</span> <span class="o">=&gt;</span> <span class="n">middle</span> <span class="n">e2</span> <span class="n">vs&#39;</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">middle</span> <span class="n">e1&#39;</span> <span class="n">vs&#39;</span> <span class="o">=&gt;</span> <span class="n">middle</span> <span class="o">(</span><span class="n">e_comp</span> <span class="n">e1&#39;</span> <span class="n">e2</span><span class="o">)</span> <span class="n">vs&#39;</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">err</span> <span class="o">=&gt;</span> <span class="n">err</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="o">_,</span> <span class="o">_</span> <span class="o">=&gt;</span> <span class="n">err</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Most intrinsics, by themselves, complete after just one step. For instance, a program consisting solely of \(\text{swap}\) will either fail (if the stack doesn&rsquo;t have enough values), or it will swap the top two values and be done. We list only &ldquo;correct&rdquo; cases, and resort to a &ldquo;catch-all&rdquo; case on line 26 that returns an error. The one multi-step intrinsic is \(\text{apply}\), which can evaluate an arbitrary expression from the stack. In this case, the &ldquo;one step&rdquo; consists of popping the quoted value from the stack; the &ldquo;remaining program&rdquo; is precisely the expression that was popped.</p> <p>Quoting an expression also always completes in one step (it simply places the quoted expression on the stack). The really interesting case is composition. Expressions are evaluated left-to-right, so we first determine what kind of step the left expression (<code>e1</code>) can take. We may need more than one step to finish up with <code>e1</code>, so there&rsquo;s a good chance it returns a &ldquo;rest program&rdquo; <code>e1'</code> and a stack <code>vs'</code>. If this happens, to complete evaluation of \(e_1\ e_2\), we need to first finish evaluating \(e_1'\), and then evaluate \(e_2\). Thus, the &ldquo;rest of the program&rdquo; is \(e_1'\ e_2\), or <code>e_comp e1' e2</code>. On the other hand, if <code>e1</code> finished evaluating, we still need to evaluate <code>e2</code>, so our &ldquo;rest of the program&rdquo; is \(e_2\), or <code>e2</code>. If evaluating <code>e1</code> led to an error, then so did evaluating <code>e_comp e1 e2</code>, and we return <code>err</code>.</p> <a href="#extracting-code"> <h3 id="extracting-code">Extracting Code</h3> </a> <p>Just knowing a single step is not enough to run the code. We need something that repeatedly tries to take a step, as long as it&rsquo;s possible. However, this part is once again not possible in Coq, as it brings back the possibility of non-termination. So if we can&rsquo;t use Coq, why don&rsquo;t we use another language? Coq&rsquo;s extraction mechanism allows us to do just that.</p> <p>I added the following code to the end of my file:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/DawnEval.v" data-first-line="231" data-last-line="235"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/DawnEval.v#L231-L235">DawnEval.v</a>, lines 231 through 235</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">231 </span><span class="lnt">232 </span><span class="lnt">233 </span><span class="lnt">234 </span><span class="lnt">235 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Require</span> <span class="n">Extraction</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Require</span> <span class="kn">Import</span> <span class="n">ExtrHaskellBasic</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="n">Extraction</span> <span class="n">Language</span> <span class="n">Haskell</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Set</span> <span class="n">Extraction</span> <span class="n">KeepSingleton</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="n">Extraction</span> <span class="s2">&#34;UccGen.hs&#34;</span> <span class="n">expr</span> <span class="n">eval_step</span> <span class="bp">true</span> <span class="bp">false</span> <span class="n">or</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Coq happily produces a new file, <code>UccGen.hs</code> with a lot of code. It&rsquo;s not exactly the most aesthetic; here&rsquo;s a quick peek:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kr">data</span> <span class="kt">Intrinsic</span> <span class="ow">=</span> </span></span><span class="line"><span class="cl"> <span class="kt">Swap</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="kt">Clone</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="kt">Drop</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="kt">Quote</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="kt">Compose</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="kt">Apply</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kr">data</span> <span class="kt">Expr</span> <span class="ow">=</span> </span></span><span class="line"><span class="cl"> <span class="kt">E_int</span> <span class="kt">Intrinsic</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="kt">E_quote</span> <span class="kt">Expr</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="kt">E_comp</span> <span class="kt">Expr</span> <span class="kt">Expr</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kr">data</span> <span class="kt">Value</span> <span class="ow">=</span> </span></span><span class="line"><span class="cl"> <span class="kt">V_quote</span> <span class="kt">Expr</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1">-- ... more</span> </span></span></code></pre></div><p>All that&rsquo;s left is to make a new file, <code>Ucc.hs</code>. I use a different file so that Coq doesn&rsquo;t overwrite my changes every time I re-run extraction. In this file, we place the &ldquo;glue code&rdquo; that tries running one step after another:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/Ucc.hs" data-first-line="46" data-last-line="51"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Ucc.hs#L46-L51">Ucc.hs</a>, lines 46 through 51</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="n">eval</span> <span class="o">::</span> <span class="o">[</span><span class="n">Value</span><span class="o">]</span> <span class="o">-&gt;</span> <span class="n">Expr</span> <span class="o">-&gt;</span> <span class="n">Maybe</span> <span class="o">[</span><span class="n">Value</span><span class="o">]</span> </span></span><span class="line"><span class="cl"><span class="n">eval</span> <span class="n">s</span> <span class="n">e</span> <span class="o">=</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="n">eval_step</span> <span class="n">s</span> <span class="n">e</span> <span class="k">of</span> </span></span><span class="line"><span class="cl"> <span class="n">Err</span> <span class="o">-&gt;</span> <span class="n">Nothing</span> </span></span><span class="line"><span class="cl"> <span class="n">Final</span> <span class="n">s&#39;</span> <span class="o">-&gt;</span> <span class="n">Just</span> <span class="n">s&#39;</span> </span></span><span class="line"><span class="cl"> <span class="n">Middle</span> <span class="n">e&#39;</span> <span class="n">s&#39;</span> <span class="o">-&gt;</span> <span class="n">eval</span> <span class="n">s&#39;</span> <span class="n">e&#39;</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Finally, loading up GHCi using <code>ghci Ucc.hs</code>, I can run the following commands:</p> <pre tabindex="0"><code>ghci&gt; fromList = foldl1 E_comp ghci&gt; test = eval [] $ fromList [true, false, UccGen.or] ghci&gt; :f test test = Just [V_quote (E_comp (E_int Swap) (E_int Drop))] </code></pre><p>That is, applying <code>or</code> to <code>true</code> and <code>false</code> results a stack with only <code>true</code> on top. As expected, and proven by our semantics!</p> <a href="#proving-equivalence"> <h3 id="proving-equivalence">Proving Equivalence</h3> </a> <p>Okay, so <code>true false or</code> evaluates to <code>true</code>, much like our semantics claims. However, does our evaluator <em>always</em> match the semantics? So far, we have not claimed or verified that it does. Let&rsquo;s try giving it a shot.</p> <a href="#first-steps-and-evaluation-chains"> <h4 id="first-steps-and-evaluation-chains">First Steps and Evaluation Chains</h4> </a> <p>The first thing we can do is show that if we have a proof that <code>e</code> takes initial stack <code>vs</code> to final stack <code>vs'</code>, then each <code>eval_step</code> &ldquo;makes progress&rdquo; towards <code>vs'</code> (it doesn&rsquo;t simply <em>return</em> <code>vs'</code>, since it only takes a single step and doesn&rsquo;t always complete the evaluation). We also want to show that if the semantics dictates <code>e</code> finishes in stack <code>vs'</code>, then <code>eval_step</code> will never return an error. Thus, we have two possibilities:</p> <ul> <li> <p><code>eval_step</code> returns <code>final</code>. In this case, for it to match our semantics, the final stack must be the same as <code>vs'</code>. Here&rsquo;s the relevant section from the Coq file:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/DawnEval.v" data-first-line="30" data-last-line="30"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/DawnEval.v#L30-L30">DawnEval.v</a>, line 30</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">30 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="o">(</span><span class="n">eval_step</span> <span class="n">vs</span> <span class="n">e</span> <span class="o">=</span> <span class="n">final</span> <span class="n">vs&#39;</span><span class="o">)</span> <span class="o">\/</span></span></span></code></pre></td></tr></table> </div> </div> </div> </li> <li> <p><code>eval_step</code> returns <code>middle</code>. In this case, the &ldquo;rest of the program&rdquo; needs to evaluate to <code>vs'</code> according to our semantics (otherwise, taking a step has changed the program&rsquo;s final outcome, which should not happen). We need to quantify the new variables (specifically, the &ldquo;rest of the program&rdquo;, which we&rsquo;ll call <code>ei</code>, and the &ldquo;stack after one step&rdquo;, <code>vsi</code>), for which we use Coq&rsquo;s <code>exists</code> clause. The whole relevant statement is as follows:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/DawnEval.v" data-first-line="31" data-last-line="33"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/DawnEval.v#L31-L33">DawnEval.v</a>, lines 31 through 33</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="o">(</span><span class="k">exists</span> <span class="o">(</span><span class="n">ei</span> <span class="o">:</span> <span class="n">expr</span><span class="o">)</span> <span class="o">(</span><span class="n">vsi</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> </span></span><span class="line"><span class="cl"> <span class="n">eval_step</span> <span class="n">vs</span> <span class="n">e</span> <span class="o">=</span> <span class="n">middle</span> <span class="n">ei</span> <span class="n">vsi</span> <span class="o">/\</span> </span></span><span class="line"><span class="cl"> <span class="n">Sem_expr</span> <span class="n">vsi</span> <span class="n">ei</span> <span class="n">vs&#39;</span><span class="o">).</span></span></span></code></pre></td></tr></table> </div> </div> </div> </li> </ul> <p>The whole theorem statement is as follows:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/DawnEval.v" data-first-line="29" data-last-line="33"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/DawnEval.v#L29-L33">DawnEval.v</a>, lines 29 through 33</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Theorem</span> <span class="n">eval_step_correct</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">e</span> <span class="o">:</span> <span class="n">expr</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="n">vs&#39;</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> <span class="n">Sem_expr</span> <span class="n">vs</span> <span class="n">e</span> <span class="n">vs&#39;</span> <span class="o">-&gt;</span> </span></span><span class="line"><span class="cl"> <span class="o">(</span><span class="n">eval_step</span> <span class="n">vs</span> <span class="n">e</span> <span class="o">=</span> <span class="n">final</span> <span class="n">vs&#39;</span><span class="o">)</span> <span class="o">\/</span> </span></span><span class="line"><span class="cl"> <span class="o">(</span><span class="k">exists</span> <span class="o">(</span><span class="n">ei</span> <span class="o">:</span> <span class="n">expr</span><span class="o">)</span> <span class="o">(</span><span class="n">vsi</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> </span></span><span class="line"><span class="cl"> <span class="n">eval_step</span> <span class="n">vs</span> <span class="n">e</span> <span class="o">=</span> <span class="n">middle</span> <span class="n">ei</span> <span class="n">vsi</span> <span class="o">/\</span> </span></span><span class="line"><span class="cl"> <span class="n">Sem_expr</span> <span class="n">vsi</span> <span class="n">ei</span> <span class="n">vs&#39;</span><span class="o">).</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>I have the Coq proof script for this (in fact, you can click the link at the top of the code block to view the original source file). However, there&rsquo;s something unsatisfying about this statement. In particular, how do we prove that an entire sequence of steps evaluates to something? We&rsquo;d have to examine the first step, checking if it&rsquo;s a &ldquo;final&rdquo; step or a &ldquo;middle&rdquo; step; if it&rsquo;s a &ldquo;middle&rdquo; step, we&rsquo;d have to move on to the &ldquo;rest of the program&rdquo; and repeat the process. Each time we&rsquo;d have to &ldquo;retrieve&rdquo; <code>ei</code> and <code>vsi</code> from <code>eval_step_correct</code>, and feed it back to <code>eval_step</code>.</p> <p>I&rsquo;ll do you one better: how do we even <em>say</em> that an expression &ldquo;evaluates step-by-step to final stack <code>vs'</code>&rdquo;? For one step, we can say:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="n">eval_step</span> <span class="n">vs</span> <span class="n">e</span> <span class="o">=</span> <span class="n">final</span> <span class="n">vs&#39;</span> </span></span></code></pre></div><p>Here&rsquo;s a picture so you can start visualizing what it looks like. The black line represents a single &ldquo;step&rdquo;.</p> <figure class="small"><img src="https://danilafe.com/blog/coq_dawn_eval/coq_eval_empty.png" alt="Two dots connected by a line. One dot is labeled &#34;vs&#34;, and the other &#34;vs1&#34;."><figcaption> <p>Visual representation of a single-step evaluation.</p> </figcaption> </figure> <p>For two steps, we&rsquo;d have to assert the existence of an intermediate expression (the &ldquo;rest of the program&rdquo; after the first step):</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="k">exists</span> <span class="n">ei</span> <span class="n">vsi</span><span class="o">,</span> </span></span><span class="line"><span class="cl"> <span class="n">eval_step</span> <span class="n">vs</span> <span class="n">e</span> <span class="o">=</span> <span class="n">middle</span> <span class="n">ei</span> <span class="n">vsi</span> <span class="o">/\</span> <span class="c">(* First step to intermediate expression. *)</span> </span></span><span class="line"><span class="cl"> <span class="n">eval_step</span> <span class="n">vsi</span> <span class="n">ei</span> <span class="o">=</span> <span class="n">final</span> <span class="n">vs&#39;</span> <span class="c">(* Second step to final state. *)</span> </span></span></code></pre></div><p>Once again, here&rsquo;s a picture. I&rsquo;ve highlighted the intermediate state, <code>vsi</code>, in a brighter color. <figure class="small"><img src="https://danilafe.com/blog/coq_dawn_eval/coq_eval_one.png" alt="Three dots connected by lines. The first dot is labeled &#34;vs&#34;, the next &#34;vsi&#34;, and the last &#34;vs1&#34;. The second dot is highlighted."><figcaption> <p>Visual representation of a two-step evaluation.</p> </figcaption> </figure> </p> <p>For three steps:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="k">exists</span> <span class="n">ei1</span> <span class="n">ei2</span> <span class="n">vsi1</span> <span class="n">vsi2</span><span class="o">,</span> </span></span><span class="line"><span class="cl"> <span class="n">eval_step</span> <span class="n">vs</span> <span class="n">e</span> <span class="o">=</span> <span class="n">middle</span> <span class="n">ei1</span> <span class="n">vsi1</span> <span class="o">/\</span> <span class="c">(* First step to intermediate expression. *)</span> </span></span><span class="line"><span class="cl"> <span class="n">eval_step</span> <span class="n">vsi1</span> <span class="n">ei1</span> <span class="o">=</span> <span class="n">middle</span> <span class="n">ei2</span> <span class="n">vsi2</span> <span class="o">/\</span> <span class="c">(* Second intermediate step *)</span> </span></span><span class="line"><span class="cl"> <span class="n">eval_step</span> <span class="n">vsi2</span> <span class="n">ei2</span> <span class="o">=</span> <span class="n">final</span> <span class="n">vs&#39;</span> <span class="c">(* Second step to final state. *)</span> </span></span></code></pre></div><p>I can imagine that you&rsquo;re getting the idea, but here&rsquo;s one last picture: <figure class="small"><img src="https://danilafe.com/blog/coq_dawn_eval/coq_eval_two.png" alt="Four dots connected by lines. The first dot is labeled &#34;vs&#34;, the next &#34;vsi1&#34;, the one after &#34;vsi2&#34;, and the last &#34;vs1&#34;. The second and third dots are highlighted."><figcaption> <p>Visual representation of a three-step evaluation.</p> </figcaption> </figure> </p> <p>The Coq code for this is awful! Not only is this a lot of writing, but it also makes various sequences of steps have a different &ldquo;shape&rdquo;. This way, we can&rsquo;t make proofs about evaluations of an <em>arbitrary</em> number of steps. Not all is lost, though: if we squint a little at the last example (three steps), a pattern starts to emerge. First, let&rsquo;s re-arrange the <code>exists</code> quantifiers:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="k">exists</span> <span class="n">ei1</span> <span class="n">vsi1</span><span class="o">,</span> <span class="n">eval_step</span> <span class="n">vs</span> <span class="n">e</span> <span class="o">=</span> <span class="n">middle</span> <span class="n">ei1</span> <span class="n">vsi1</span> <span class="o">/\</span> <span class="c">(* Cons *)</span> </span></span><span class="line"><span class="cl"><span class="k">exists</span> <span class="n">ei2</span> <span class="n">vsi2</span><span class="o">,</span> <span class="n">eval_step</span> <span class="n">vsi1</span> <span class="n">ei1</span> <span class="o">=</span> <span class="n">middle</span> <span class="n">ei2</span> <span class="n">vsi2</span> <span class="o">/\</span> <span class="c">(* Cons *)</span> </span></span><span class="line"><span class="cl"><span class="n">eval_step</span> <span class="n">vsi2</span> <span class="n">ei2</span> <span class="o">=</span> <span class="n">final</span> <span class="n">vs&#39;</span> <span class="c">(* Nil *)</span> </span></span></code></pre></div><p>If you squint at this, it kind of looks like a list! The &ldquo;empty&rdquo; part of a list is the final step, while the &ldquo;cons&rdquo; part is a middle step. The analogy holds up for another reason: an &ldquo;empty&rdquo; sequence has zero intermediate expressions, while each &ldquo;cons&rdquo; introduces a single new intermediate program.</p> <figure class="large"><img src="https://danilafe.com/blog/coq_dawn_eval/coq_eval_lists.png" alt="The three previous figures are drawn together, each next to its list representation."><figcaption> <p>Evaluation sequences as lists.</p> </figcaption> </figure> <p>Perhaps we can define a new data type that matches this? Indeed we can!</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/DawnEval.v" data-first-line="64" data-last-line="67"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/DawnEval.v#L64-L67">DawnEval.v</a>, lines 64 through 67</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">64 </span><span class="lnt">65 </span><span class="lnt">66 </span><span class="lnt">67 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Inductive</span> <span class="n">eval_chain</span> <span class="o">(</span><span class="n">vs</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">)</span> <span class="o">(</span><span class="n">e</span> <span class="o">:</span> <span class="n">expr</span><span class="o">)</span> <span class="o">(</span><span class="n">vs&#39;</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">)</span> <span class="o">:</span> <span class="kt">Prop</span> <span class="o">:=</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">chain_final</span> <span class="o">(</span><span class="n">P</span> <span class="o">:</span> <span class="n">eval_step</span> <span class="n">vs</span> <span class="n">e</span> <span class="o">=</span> <span class="n">final</span> <span class="n">vs&#39;</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">chain_middle</span> <span class="o">(</span><span class="n">ei</span> <span class="o">:</span> <span class="n">expr</span><span class="o">)</span> <span class="o">(</span><span class="n">vsi</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="o">(</span><span class="n">P</span> <span class="o">:</span> <span class="n">eval_step</span> <span class="n">vs</span> <span class="n">e</span> <span class="o">=</span> <span class="n">middle</span> <span class="n">ei</span> <span class="n">vsi</span><span class="o">)</span> <span class="o">(</span><span class="n">rest</span> <span class="o">:</span> <span class="n">eval_chain</span> <span class="n">vsi</span> <span class="n">ei</span> <span class="n">vs&#39;</span><span class="o">).</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The new data type is parameterized by the initial and final stacks, as well as the expression that starts in the former and ends in the latter. Then, consider the following <em>type</em>:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="n">eval_chain</span> <span class="n">nil</span> <span class="o">(</span><span class="n">e_comp</span> <span class="o">(</span><span class="n">e_comp</span> <span class="bp">true</span> <span class="bp">false</span><span class="o">)</span> <span class="n">or</span><span class="o">)</span> <span class="o">(</span><span class="bp">true</span> <span class="o">::</span> <span class="n">nil</span><span class="o">)</span> </span></span></code></pre></div><p>This is the type of sequences (or chains) of steps corresponding to the evaluation of the program \(\text{true}\ \text{false}\ \text{or}\), starting in an empty stack and evaluating to a stack with only \(\text{true}\) on top. Thus to say that an expression evaluates to some final stack <code>vs'</code>, in <em>some unknown number of steps</em>, it&rsquo;s sufficient to write:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="n">eval_chain</span> <span class="n">vs</span> <span class="n">e</span> <span class="n">vs&#39;</span> </span></span></code></pre></div><p>Evaluation chains have a couple of interesting properties. First and foremost, they can be &ldquo;concatenated&rdquo;. This is analogous to the <code>Sem_e_comp</code> rule in our original semantics: if an expression <code>e1</code> starts in stack <code>vs</code> and finishes in stack <code>vs'</code>, and another expression starts in stack <code>vs'</code> and finishes in stack <code>vs''</code>, then we can compose these two expressions, and the result will start in <code>vs</code> and finish in <code>vs''</code>.</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/DawnEval.v" data-first-line="69" data-last-line="75"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/DawnEval.v#L69-L75">DawnEval.v</a>, lines 69 through 75</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">69 </span><span class="lnt">70 </span><span class="lnt">71 </span><span class="lnt">72 </span><span class="lnt">73 </span><span class="lnt">74 </span><span class="lnt">75 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Lemma</span> <span class="n">eval_chain_merge</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">e1</span> <span class="n">e2</span> <span class="o">:</span> <span class="n">expr</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="n">vs&#39;</span> <span class="n">vs&#39;&#39;</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> </span></span><span class="line"><span class="cl"> <span class="n">eval_chain</span> <span class="n">vs</span> <span class="n">e1</span> <span class="n">vs&#39;</span> <span class="o">-&gt;</span> <span class="n">eval_chain</span> <span class="n">vs&#39;</span> <span class="n">e2</span> <span class="n">vs&#39;&#39;</span> <span class="o">-&gt;</span> <span class="n">eval_chain</span> <span class="n">vs</span> <span class="o">(</span><span class="n">e_comp</span> <span class="n">e1</span> <span class="n">e2</span><span class="o">)</span> <span class="n">vs&#39;&#39;</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Proof</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">intros</span> <span class="n">e1</span> <span class="n">e2</span> <span class="n">vs</span> <span class="n">vs&#39;</span> <span class="n">vs&#39;&#39;</span> <span class="n">ch1</span> <span class="n">ch2</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">induction</span> <span class="n">ch1</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="k">eapply</span> <span class="n">chain_middle</span><span class="o">;</span> <span class="k">simpl</span><span class="o">;</span> <span class="kr">try</span> <span class="o">(</span><span class="k">rewrite</span> <span class="n">P</span><span class="o">);</span> <span class="k">auto</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Qed</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <figure class="large"><img src="https://danilafe.com/blog/coq_dawn_eval/coq_eval_chain_merge.png" alt="Two evaluation chains, one starting in &#34;vs&#34; and ending in &#34;vs&#39;&#34;, and one starting in &#34;vs&#39;&#34; and ending in &#34;vs&#39;&#39;&#34;, are combined into one. The new chain starts in &#34;vs&#34;, ends in &#34;vs&#39;&#39;&#34;, and has &#34;vs&#39;&#34; in the middle. "><figcaption> <p>Merging evaluation chains.</p> </figcaption> </figure> <p>The proof is very short. We go <span class="sidenote"> <label class="sidenote-label" for="concat-note">by induction on the left evaluation chain</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="concat-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> It's not a coincidence that defining something like <code>(++)</code> (list concatenation) in Haskell typically starts by pattern matching on the <em>left</em> list. In fact, proofs by <code>induction</code> actually correspond to recursive functions! It's tough putting code blocks in sidenotes, but if you're playing with the <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/DawnEval.v"><code>DawnEval.v</code></a> file, try running <code>Print eval_chain_ind</code>, and note the presence of <code>fix</code>, the <a href="https://en.wikipedia.org/wiki/Fixed-point_combinator">fixed point combinator</a> used to implement recursion. <span class="sidenote-delimiter">]</span> </span> </span> (the one for <code>e1</code>). Coq takes care of most of the rest with <code>auto</code>. The key to this proof is that whatever <code>P</code> is contained within a &ldquo;node&rdquo; in the left chain determines how <code>eval_step (e_comp e1 e2)</code> behaves. Whether <code>e1</code> evaluates to <code>final</code> or <code>middle</code>, the composition evaluates to <code>middle</code> (a composition of two expressions cannot be done in one step), so we always <span class="sidenote"> <label class="sidenote-label" for="cons-note">create a new &ldquo;cons&rdquo; node.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="cons-note"></input><span class="sidenote-content sidenote-left"><span class="sidenote-delimiter">[note:</span> This is <em>unlike</em> list concatenation, since we typically don't create new nodes when appending to an empty list. <span class="sidenote-delimiter">]</span> </span> </span> via <code>chain_middle</code>. Then, the two cases are as follows.</p> <p>If the step was <code>final</code>, then the rest of the steps use only <code>e2</code>, and good news, we already have a chain for that! <figure class="large"><img src="https://danilafe.com/blog/coq_dawn_eval/coq_eval_chain_base.png" alt="A single-step chain connected to another by a line labeled &#34;chain_middle&#34;."><figcaption> <p>Merging evaluation chains when the first chain only has one step.</p> </figcaption> </figure> </p> <p>Otherwise, the step was <code>middle</code>, an we still have a chain for some intermediate program <code>ei</code> that starts in some stack <code>vsi</code> and ends in <code>vs'</code>. By induction, we know that <em>this</em> chain can be concatenated with the one for <code>e2</code>, resulting in a chain for <code>e_comp ei e2</code>. All that remains is to attach to this sub-chain the one step from <code>(vs, e1)</code> to <code>(vsi, ei)</code>, which is handled by <code>chain_middle</code>. <figure class="large"><img src="https://danilafe.com/blog/coq_dawn_eval/coq_eval_chain_inductive.png" alt="Visualization of the inductive case of merging chains."><figcaption> <p>Merging evaluation chains when the first chain has a middle step and others.</p> </figcaption> </figure> </p> <p>The <code>merge</code> operation is reversible; chains can be split into two pieces, one for each composed expression:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/DawnEval.v" data-first-line="77" data-last-line="78"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/DawnEval.v#L77-L78">DawnEval.v</a>, lines 77 through 78</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">77 </span><span class="lnt">78 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Lemma</span> <span class="n">eval_chain_split</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">e1</span> <span class="n">e2</span> <span class="o">:</span> <span class="n">expr</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="n">vs&#39;&#39;</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> </span></span><span class="line"><span class="cl"> <span class="n">eval_chain</span> <span class="n">vs</span> <span class="o">(</span><span class="n">e_comp</span> <span class="n">e1</span> <span class="n">e2</span><span class="o">)</span> <span class="n">vs&#39;&#39;</span> <span class="o">-&gt;</span> <span class="k">exists</span> <span class="n">vs&#39;</span><span class="o">,</span> <span class="o">(</span><span class="n">eval_chain</span> <span class="n">vs</span> <span class="n">e1</span> <span class="n">vs&#39;</span><span class="o">)</span> <span class="o">/\</span> <span class="o">(</span><span class="n">eval_chain</span> <span class="n">vs&#39;</span> <span class="n">e2</span> <span class="n">vs&#39;&#39;</span><span class="o">).</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>While interesting, this particular fact isn&rsquo;t used anywhere else in this post, and I will omit the proof here.</p> <a href="#the-forward-direction"> <h4 id="the-forward-direction">The Forward Direction</h4> </a> <p>Let&rsquo;s try rewording our very first proof, <code>eval_step_correct</code>, using chains. The <em>direction</em> remains the same: given that an expression produces a final stack under the formal semantics, we need to prove that this expression <em>evaluates</em> to the same final stack using a sequence of <code>eval_step</code>.</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/DawnEval.v" data-first-line="93" data-last-line="96"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/DawnEval.v#L93-L96">DawnEval.v</a>, lines 93 through 96</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">93 </span><span class="lnt">94 </span><span class="lnt">95 </span><span class="lnt">96 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Theorem</span> <span class="n">val_step_sem</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">e</span> <span class="o">:</span> <span class="n">expr</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="n">vs&#39;</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> </span></span><span class="line"><span class="cl"> <span class="n">Sem_expr</span> <span class="n">vs</span> <span class="n">e</span> <span class="n">vs&#39;</span> <span class="o">-&gt;</span> <span class="n">eval_chain</span> <span class="n">vs</span> <span class="n">e</span> <span class="n">vs&#39;</span> </span></span><span class="line"><span class="cl"><span class="k">with</span> <span class="n">eval_step_int</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">i</span> <span class="o">:</span> <span class="n">intrinsic</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="n">vs&#39;</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> </span></span><span class="line"><span class="cl"> <span class="n">Sem_int</span> <span class="n">vs</span> <span class="n">i</span> <span class="n">vs&#39;</span> <span class="o">-&gt;</span> <span class="n">eval_chain</span> <span class="n">vs</span> <span class="o">(</span><span class="n">e_int</span> <span class="n">i</span><span class="o">)</span> <span class="n">vs&#39;</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The power of this theorem is roughly the same as that of the original one: we can use <code>eval_step_correct</code> to build up a chain by applying it over and over, and we can take the &ldquo;first element&rdquo; of a chain to serve as a witness for <code>eval_step_correct</code>. However, the formulation is arguably significantly cleaner, and contains a proof for <em>all</em> steps right away, rather than a single one.</p> <p>Before we go through the proof, notice that there&rsquo;s actually <em>two</em> theorems being stated here, which depend on each other. This is not surprising given that our semantics are given using two data types, <code>Sem_expr</code> and <code>Sem_int</code>, each of which contains the other. Regular proofs by induction, which work on only one of the data types, break down because we can&rsquo;t make claims &ldquo;by induction&rdquo; about the <em>other</em> type. For example, while going by induction on <code>Sem_expr</code>, we run into issues in the <code>e_int</code> case while handling \(\text{apply}\). We know a single step &ndash; popping the value being run from the stack. But what then? The rule for \(\text{apply}\) in <code>Sem_int</code> contains another <code>Sem_expr</code> which confirms that the quoted value properly evaluates. But this other <code>Sem_expr</code> isn&rsquo;t directly a child of the &ldquo;bigger&rdquo; <code>Sem_expr</code>, and so we don&rsquo;t get an inductive hypothesis about it. We know nothing about it; we&rsquo;re stuck.</p> <p>We therefore have two theorems declared together using <code>with</code> (just like we used <code>with</code> to declare <code>Sem_expr</code> and <code>Sem_int</code>). We have to prove both, which is once again a surprisingly easy task thanks to Coq&rsquo;s <code>auto</code>. Let&rsquo;s start with the first theorem, the one for expression semantics.</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/DawnEval.v" data-first-line="98" data-last-line="107"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/DawnEval.v#L98-L107">DawnEval.v</a>, lines 98 through 107</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 98 </span><span class="lnt"> 99 </span><span class="lnt">100 </span><span class="lnt">101 </span><span class="lnt">102 </span><span class="lnt">103 </span><span class="lnt">104 </span><span class="lnt">105 </span><span class="lnt">106 </span><span class="lnt">107 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">intros</span> <span class="n">e</span> <span class="n">vs</span> <span class="n">vs&#39;</span> <span class="n">Hsem</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">induction</span> <span class="n">Hsem</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="c">(* This is an intrinsic, which is handled by the second </span></span></span><span class="line"><span class="cl"><span class="c"> theorem, eval_step_int. This lemma is used here. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="c">(* A quote doesn&#39;t have a next step, and so is final. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">apply</span> <span class="n">chain_final</span><span class="o">.</span> <span class="k">auto</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="c">(* In composition, by induction, we know that the two sub-expressions produce </span></span></span><span class="line"><span class="cl"><span class="c"> proper evaluation chains. Chains can be composed (via eval_chain_merge). *)</span> </span></span><span class="line"><span class="cl"> <span class="k">eapply</span> <span class="n">eval_chain_merge</span><span class="o">;</span> <span class="k">eauto</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We go by induction on the semantics data type. There are therefore three cases: intrinsics, quotes, and composition. The intrinsic case is handed right off to the second theorem (which we&rsquo;ll see momentarily). The quote case is very simple since quoted expressions are simply pushed onto the stack in a single step (we thus use <code>chain_final</code>). Finally, in the composition case, we have two sub-proofs, one for each expression being evaluated. By induction, we know that each of these sub-proofs can be turned into a chain, and we use <code>eval_chain_merge</code> to combine these two chains into one. That&rsquo;s it.</p> <p>Now, let&rsquo;s try the second theorem. The code is even shorter:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/DawnEval.v" data-first-line="108" data-last-line="115"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/DawnEval.v#L108-L115">DawnEval.v</a>, lines 108 through 115</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">108 </span><span class="lnt">109 </span><span class="lnt">110 </span><span class="lnt">111 </span><span class="lnt">112 </span><span class="lnt">113 </span><span class="lnt">114 </span><span class="lnt">115 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">intros</span> <span class="n">i</span> <span class="n">vs</span> <span class="n">vs&#39;</span> <span class="n">Hsem</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="c">(* The evaluation chain depends on the specific intrinsic in use. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">inversion</span> <span class="n">Hsem</span><span class="o">;</span> <span class="k">subst</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="c">(* Most intrinsics produce a final value, and the evaluation chain is trivial. *)</span> </span></span><span class="line"><span class="cl"> <span class="kr">try</span> <span class="o">(</span><span class="k">apply</span> <span class="n">chain_final</span><span class="o">;</span> <span class="k">auto</span><span class="o">;</span> <span class="n">fail</span><span class="o">).</span> </span></span><span class="line"><span class="cl"> <span class="c">(* Only apply is non-final. The first step is popping the quote from the stack, </span></span></span><span class="line"><span class="cl"><span class="c"> and the rest of the steps are given by the evaluation of the code in the quote. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">apply</span> <span class="n">chain_middle</span> <span class="k">with</span> <span class="n">e</span> <span class="n">vs0</span><span class="o">;</span> <span class="k">auto</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The first command, <code>inversion Hsem</code>, lets us go case-by-case on the various ways an intrinsic can be evaluated. Most intrinsics are quite boring; in our evaluator, they only need a single step, and their semantics rules guarantee that the stack has the exact kind of data that the evaluator expects. We dismiss such cases with <code>apply chain_final; auto</code>. The only time this doesn&rsquo;t work is when we encounter the \(\text{apply}\) intrinsic; in that case, however, we can simply use the first theorem we proved.</p> <a href="#a-quick-aside-automation-using-ltac2"> <h4 id="a-quick-aside-automation-using-ltac2">A Quick Aside: Automation Using <code>Ltac2</code></h4> </a> <p>Going in the other direction will involve lots of situations in which we <em>know</em> that <code>eval_step</code> evaluated to something. Here&rsquo;s a toy proof built around one such case:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/DawnEval.v" data-first-line="237" data-last-line="246"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/DawnEval.v#L237-L246">DawnEval.v</a>, lines 237 through 246</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">237 </span><span class="lnt">238 </span><span class="lnt">239 </span><span class="lnt">240 </span><span class="hl"><span class="lnt">241 </span></span><span class="hl"><span class="lnt">242 </span></span><span class="hl"><span class="lnt">243 </span></span><span class="hl"><span class="lnt">244 </span></span><span class="lnt">245 </span><span class="lnt">246 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Remark</span> <span class="n">eval_swap_two_values</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">vs</span> <span class="n">vs&#39;</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> </span></span><span class="line"><span class="cl"> <span class="n">eval_step</span> <span class="n">vs</span> <span class="o">(</span><span class="n">e_int</span> <span class="n">swap</span><span class="o">)</span> <span class="o">=</span> <span class="n">final</span> <span class="n">vs&#39;</span> <span class="o">-&gt;</span> <span class="k">exists</span> <span class="n">v1</span> <span class="n">v2</span> <span class="n">vst</span><span class="o">,</span> <span class="n">vs</span> <span class="o">=</span> <span class="n">v1</span> <span class="o">::</span> <span class="n">v2</span> <span class="o">::</span> <span class="n">vst</span> <span class="o">/\</span> <span class="n">vs&#39;</span> <span class="o">=</span> <span class="n">v2</span> <span class="o">::</span> <span class="n">v1</span> <span class="o">::</span> <span class="n">vst</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Proof</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">intros</span> <span class="n">vs</span> <span class="n">vs&#39;</span> <span class="n">Hev</span><span class="o">.</span> </span></span><span class="line hl"><span class="cl"> <span class="c">(* Can&#39;t proceed until we know more about the stack. *)</span> </span></span><span class="line hl"><span class="cl"> <span class="k">destruct</span> <span class="n">vs</span> <span class="k">as</span> <span class="o">[|</span><span class="n">v1</span> <span class="o">[|</span><span class="n">v2</span> <span class="n">vs</span><span class="o">]].</span> </span></span><span class="line hl"><span class="cl"> <span class="o">-</span> <span class="c">(* Invalid case; empty stack. *)</span> <span class="k">inversion</span> <span class="n">Hev</span><span class="o">.</span> </span></span><span class="line hl"><span class="cl"> <span class="o">-</span> <span class="c">(* Invalid case; stack only has one value. *)</span> <span class="k">inversion</span> <span class="n">Hev</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="c">(* Valid case: the stack has two values. *)</span> <span class="k">injection</span> <span class="n">Hev</span><span class="o">.</span> <span class="k">eauto</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Qed</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The proof claims that if the <code>swap</code> instruction was evaluated to something, then the initial stack must contain two values, and the final stack must have those two values on top but flipped. This is very easy to prove, since that&rsquo;s the exact behavior of <code>eval_step</code> for the <code>swap</code> intrinsic. However, notice how much boilerplate we&rsquo;re writing: lines 241 through 244 deal entirely with ensuring that our stack does, indeed, have two values. This is trivially true: if the stack <em>didn&rsquo;t</em> have two values, it wouldn&rsquo;t evaluate to <code>final</code>, but to <code>error</code>. However, it takes some effort to convince Coq of this.</p> <p>This was just for a single intrinsic. What if we&rsquo;re trying to prove something for <em>every</em> intrinsic? Things will get messy very quickly. We can&rsquo;t even re-use our <code>destruct vs</code> code, because different intrinsics need stacks with different numbers of values (they have a different <em>arity</em>); if we try to handle all cases with at most 2 values on the stack, we&rsquo;d have to prove the same thing twice for simpler intrinsics. In short, proofs will look like a mess.</p> <p>Things don&rsquo;t have to be this way, though! The boilerplate code is very repetitive, and this makes it a good candidate for automation. For this, Coq has <code>Ltac2</code>.</p> <p>In short, <code>Ltac2</code> is another mini-language contained within Coq. We can use it to put into code the decision making that we&rsquo;d be otherwise doing on our own. For example, here&rsquo;s a tactic that checks if the current proof has a hypothesis in which an intrinsic is evaluated to something:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/DawnEval.v" data-first-line="141" data-last-line="148"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/DawnEval.v#L141-L148">DawnEval.v</a>, lines 141 through 148</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">141 </span><span class="lnt">142 </span><span class="lnt">143 </span><span class="lnt">144 </span><span class="lnt">145 </span><span class="lnt">146 </span><span class="lnt">147 </span><span class="lnt">148 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="n">Ltac2</span> <span class="n">ensure_valid_stack</span> <span class="bp">()</span> <span class="o">:=</span> <span class="n">Control</span><span class="o">.</span><span class="n">enter</span> <span class="o">(</span><span class="k">fun</span> <span class="bp">()</span> <span class="o">=&gt;</span> </span></span><span class="line"><span class="cl"> <span class="k">match</span><span class="o">!</span> <span class="n">goal</span> <span class="k">with</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="o">[</span><span class="n">h</span> <span class="o">:</span> <span class="n">eval_step</span> <span class="o">?</span><span class="n">a</span> <span class="o">(</span><span class="n">e_int</span> <span class="o">?</span><span class="n">b</span><span class="o">)</span> <span class="o">=</span> <span class="o">?</span><span class="n">c</span> <span class="o">|-</span> <span class="o">_]</span> <span class="o">=&gt;</span> </span></span><span class="line"><span class="cl"> <span class="k">let</span> <span class="n">h</span> <span class="o">:=</span> <span class="n">Control</span><span class="o">.</span><span class="n">hyp</span> <span class="n">h</span> <span class="k">in</span> </span></span><span class="line"><span class="cl"> <span class="n">destruct_int_stack</span> <span class="n">b</span> <span class="n">a</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="kr">try</span> <span class="o">(</span><span class="k">inversion</span> <span class="o">$</span><span class="n">h</span><span class="o">;</span> <span class="n">fail</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="o">[|-</span> <span class="o">_</span> <span class="o">]</span> <span class="o">=&gt;</span> <span class="bp">()</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span><span class="o">).</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p><code>Ltac2</code> has a pattern matching construct much like Coq itself does, but it can pattern match on Coq expressions and proof goals. When pattern matching on goals, we use the following notation: <code>[ h1 : t1, ..., hn : tn |- g ]</code>, which reads:</p> <blockquote> <p>Given hypotheses <code>h1</code> through <code>hn</code>, of types <code>t1</code> through <code>tn</code> respectively, we need to prove <code>g</code>.</p> </blockquote> <p>In our pattern match, then, we&rsquo;re ignoring the actual thing we&rsquo;re supposed to prove (the tactic we&rsquo;re writing won&rsquo;t be that smart; its user &ndash; ourselves &ndash; will need to know when to use it). We are, however, searching for a hypothesis in the form <code>eval_step ?a (e_int ?b) = ?c</code>. The three variables, <code>?a</code>, <code>?b</code>, and <code>?c</code> are placeholders which can be matched with anything. Thus, we expect any hypothesis in which an intrinsic is evaluated to anything else (by the way, Coq checks all hypotheses one-by-one, starting at the most recent and finishing with the oldest). The code on lines 144 through 146 actually performs the <code>destruct</code> calls, as well as the <code>inversion</code> attempts that complete any proofs with an inconsistent assumption (like <code>err = final vs'</code>).</p> <p>So how do these lines work? Well, first we need to get a handle on the hypothesis we found. Various tactics can manipulate the proof state, and cause hypotheses to disappear; by calling <code>Control.hyp</code> on <code>h</code>, we secure a more permanent hold on our evaluation assumption. In the next line, we call <em>another</em> <code>Ltac2</code> function, <code>destruct_int_stack</code>, which unpacks the stack exactly as many times as necessary. We&rsquo;ll look at this function in a moment. Finally, we use regular old tactics. Specifically, we use <code>inversion</code> (as mentioned before). I use <code>fail</code> after <code>inversion</code> to avoid cluttering the proof in case there&rsquo;s <em>not</em> a contradiction.</p> <p>On to <code>destruct_int_stack</code>. The definition is very simple:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/DawnEval.v" data-first-line="139" data-last-line="139"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/DawnEval.v#L139-L139">DawnEval.v</a>, line 139</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">139 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="n">Ltac2</span> <span class="n">destruct_int_stack</span> <span class="o">(</span><span class="n">int</span> <span class="o">:</span> <span class="n">constr</span><span class="o">)</span> <span class="o">(</span><span class="n">va</span><span class="o">:</span> <span class="n">constr</span><span class="o">)</span> <span class="o">:=</span> <span class="n">destruct_n</span> <span class="o">(</span><span class="n">int_arity</span> <span class="n">int</span><span class="o">)</span> <span class="n">va</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The main novelty is that this function takes two arguments, both of which are Coq expressions: <code>int</code>, or the intrinsic being evaluated, and <code>va</code>, the stack that&rsquo;s being analyzed. The <code>constr</code> type in Coq holds terms. Let&rsquo;s look at <code>int_arity</code> next:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/DawnEval.v" data-first-line="128" data-last-line="137"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/DawnEval.v#L128-L137">DawnEval.v</a>, lines 128 through 137</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">128 </span><span class="lnt">129 </span><span class="lnt">130 </span><span class="lnt">131 </span><span class="lnt">132 </span><span class="lnt">133 </span><span class="lnt">134 </span><span class="lnt">135 </span><span class="lnt">136 </span><span class="lnt">137 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="n">Ltac2</span> <span class="n">int_arity</span> <span class="o">(</span><span class="n">int</span> <span class="o">:</span> <span class="n">constr</span><span class="o">)</span> <span class="o">:</span> <span class="n">int</span> <span class="o">:=</span> </span></span><span class="line"><span class="cl"> <span class="k">match</span><span class="o">!</span> <span class="n">int</span> <span class="k">with</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">swap</span> <span class="o">=&gt;</span> <span class="n">2</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">clone</span> <span class="o">=&gt;</span> <span class="n">1</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">drop</span> <span class="o">=&gt;</span> <span class="n">1</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">quote</span> <span class="o">=&gt;</span> <span class="n">1</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">compose</span> <span class="o">=&gt;</span> <span class="n">2</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="k">apply</span> <span class="o">=&gt;</span> <span class="n">1</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="o">_</span> <span class="o">=&gt;</span> <span class="n">Control</span><span class="o">.</span><span class="n">throw</span> <span class="n">Not_intrinsic</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This is a pretty standard pattern match that assigns to each expression its arity. However, we&rsquo;re case analyzing over <em>literally any possible Coq expression</em>, so we need to handle the case in which the expression isn&rsquo;t actually a specific intrinsic. For this, we use <code>Control.throw</code> with the <code>Not_intrinsic</code> exception.</p> <p>Wait a moment, does Coq just happen to include a <code>Not_intrinsic</code> exception? No, it does not. We have to register that one ourselves. In <code>Ltac2</code>, the type of exception (<code>exn</code>) is <em>extensible</em>, which means we can add on to it. We add just a single constructor with no arguments:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/DawnEval.v" data-first-line="118" data-last-line="118"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/DawnEval.v#L118-L118">DawnEval.v</a>, line 118</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">118 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="n">Ltac2</span> <span class="kt">Type</span> <span class="n">exn</span> <span class="o">::=</span> <span class="o">[</span> <span class="o">|</span> <span class="n">Not_intrinsic</span> <span class="o">].</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Finally, unpacking the value stack. Here&rsquo;s the code for that:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/DawnEval.v" data-first-line="120" data-last-line="126"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/DawnEval.v#L120-L126">DawnEval.v</a>, lines 120 through 126</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">120 </span><span class="lnt">121 </span><span class="lnt">122 </span><span class="lnt">123 </span><span class="lnt">124 </span><span class="lnt">125 </span><span class="lnt">126 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="n">Ltac2</span> <span class="n">rec</span> <span class="n">destruct_n</span> <span class="o">(</span><span class="n">n</span> <span class="o">:</span> <span class="n">int</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="o">:</span> <span class="n">constr</span><span class="o">)</span> <span class="o">:</span> <span class="kt">unit</span> <span class="o">:=</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">Int</span><span class="o">.</span><span class="n">le</span> <span class="n">n</span> <span class="n">0</span> <span class="k">then</span> <span class="bp">()</span> <span class="k">else</span> </span></span><span class="line"><span class="cl"> <span class="k">let</span> <span class="n">v</span> <span class="o">:=</span> <span class="n">Fresh</span><span class="o">.</span><span class="n">in_goal</span> <span class="o">@</span><span class="n">v</span> <span class="k">in</span> </span></span><span class="line"><span class="cl"> <span class="k">let</span> <span class="n">vs&#39;</span> <span class="o">:=</span> <span class="n">Fresh</span><span class="o">.</span><span class="n">in_goal</span> <span class="o">@</span><span class="n">vs</span> <span class="k">in</span> </span></span><span class="line"><span class="cl"> <span class="k">destruct</span> <span class="o">$</span><span class="n">vs</span> <span class="k">as</span> <span class="o">[|$</span><span class="n">v</span> <span class="o">$</span><span class="n">vs&#39;</span><span class="o">];</span> <span class="n">Control</span><span class="o">.</span><span class="n">enter</span> <span class="o">(</span><span class="k">fun</span> <span class="bp">()</span> <span class="o">=&gt;</span> </span></span><span class="line"><span class="cl"> <span class="kr">try</span> <span class="o">(</span><span class="n">destruct_n</span> <span class="o">(</span><span class="n">Int</span><span class="o">.</span><span class="n">sub</span> <span class="n">n</span> <span class="n">1</span><span class="o">)</span> <span class="o">(</span><span class="n">Control</span><span class="o">.</span><span class="n">hyp</span> <span class="n">vs&#39;</span><span class="o">))</span> </span></span><span class="line"><span class="cl"> <span class="o">).</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This function accepts the arity of an operation, and unpacks the stack that many times. <code>Ltac2</code> doesn&rsquo;t have a way to pattern match on numbers, so we resort to good old &ldquo;less than or equal&rdquo;, and and <code>if</code>/<code>else</code>. If the arity is zero (or less than zero, since it&rsquo;s an integer), we don&rsquo;t need to unpack anymore. This is our base case. Otherwise, we generate two new free variables (we can&rsquo;t just hardcode <code>v1</code> and <code>v2</code>, since they may be in use elsewhere in the proof). To this end we use <code>Fresh.in_goal</code> and give it a &ldquo;base symbol&rdquo; to build on. We then use <code>destruct</code>, passing it our &ldquo;scrutinee&rdquo; (the expression being destructed) and the names we generated for its components. This generates multiple goals; the <code>Control.enter</code> tactic is used to run code for each one of these goals. In the non-empty list case, we try to break apart its tail as necessary by recursively calling <code>destruct_n</code>.</p> <p>That&rsquo;s pretty much it! We can now use our tactic from Coq like any other. Rewriting our earlier proof about \(\text{swap}\), we now only need to handle the valid case:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/DawnEval.v" data-first-line="248" data-last-line="254"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/DawnEval.v#L248-L254">DawnEval.v</a>, lines 248 through 254</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">248 </span><span class="lnt">249 </span><span class="lnt">250 </span><span class="lnt">251 </span><span class="lnt">252 </span><span class="lnt">253 </span><span class="lnt">254 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Remark</span> <span class="n">eval_swap_two_values&#39;</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">vs</span> <span class="n">vs&#39;</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> </span></span><span class="line"><span class="cl"> <span class="n">eval_step</span> <span class="n">vs</span> <span class="o">(</span><span class="n">e_int</span> <span class="n">swap</span><span class="o">)</span> <span class="o">=</span> <span class="n">final</span> <span class="n">vs&#39;</span> <span class="o">-&gt;</span> <span class="k">exists</span> <span class="n">v1</span> <span class="n">v2</span> <span class="n">vst</span><span class="o">,</span> <span class="n">vs</span> <span class="o">=</span> <span class="n">v1</span> <span class="o">::</span> <span class="n">v2</span> <span class="o">::</span> <span class="n">vst</span> <span class="o">/\</span> <span class="n">vs&#39;</span> <span class="o">=</span> <span class="n">v2</span> <span class="o">::</span> <span class="n">v1</span> <span class="o">::</span> <span class="n">vst</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Proof</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">intros</span> <span class="n">vs</span> <span class="n">vs&#39;</span> <span class="n">Hev</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="n">ensure_valid_stack</span> <span class="bp">()</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">injection</span> <span class="n">Hev</span><span class="o">.</span> <span class="k">eauto</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Qed</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We&rsquo;ll be using this new <code>ensure_valid_stack</code> tactic in our subsequent proofs.</p> <a href="#the-backward-direction"> <h4 id="the-backward-direction">The Backward Direction</h4> </a> <p>After our last proof before the <code>Ltac2</code> diversion, we know that our evaluator matches our semantics, but only when a <code>Sem_expr</code> object exists for our program. However, invalid programs don&rsquo;t have a <code>Sem_expr</code> object (there&rsquo;s no way to prove that they evaluate to anything). Does our evaluator behave properly on &ldquo;bad&rdquo; programs, too? We&rsquo;ve not ruled out that our evaluator produces junk <code>final</code> or <code>middle</code> outputs whenever it encounters an error. We need another theorem:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/DawnEval.v" data-first-line="215" data-last-line="216"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/DawnEval.v#L215-L216">DawnEval.v</a>, lines 215 through 216</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">215 </span><span class="lnt">216 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Theorem</span> <span class="n">eval_step_sem_back</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">e</span> <span class="o">:</span> <span class="n">expr</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="n">vs&#39;</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> </span></span><span class="line"><span class="cl"> <span class="n">eval_chain</span> <span class="n">vs</span> <span class="n">e</span> <span class="n">vs&#39;</span> <span class="o">-&gt;</span> <span class="n">Sem_expr</span> <span class="n">vs</span> <span class="n">e</span> <span class="n">vs&#39;</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>That is, if our evalutor reaches a final state, this state matches our semantics. This proof is most conveniently split into two pieces. The first piece says that if our evaluator completes in a single step, then this step matches our semantics.</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/DawnEval.v" data-first-line="158" data-last-line="175"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/DawnEval.v#L158-L175">DawnEval.v</a>, lines 158 through 175</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">158 </span><span class="lnt">159 </span><span class="lnt">160 </span><span class="lnt">161 </span><span class="lnt">162 </span><span class="lnt">163 </span><span class="lnt">164 </span><span class="lnt">165 </span><span class="lnt">166 </span><span class="lnt">167 </span><span class="lnt">168 </span><span class="lnt">169 </span><span class="lnt">170 </span><span class="lnt">171 </span><span class="lnt">172 </span><span class="lnt">173 </span><span class="lnt">174 </span><span class="lnt">175 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Theorem</span> <span class="n">eval_step_final_sem</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">e</span> <span class="o">:</span> <span class="n">expr</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="n">vs&#39;</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> </span></span><span class="line"><span class="cl"> <span class="n">eval_step</span> <span class="n">vs</span> <span class="n">e</span> <span class="o">=</span> <span class="n">final</span> <span class="n">vs&#39;</span> <span class="o">-&gt;</span> <span class="n">Sem_expr</span> <span class="n">vs</span> <span class="n">e</span> <span class="n">vs&#39;</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Proof</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">intros</span> <span class="n">e</span> <span class="n">vs</span> <span class="n">vs&#39;</span> <span class="n">Hev</span><span class="o">.</span> <span class="k">destruct</span> <span class="n">e</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">destruct</span> <span class="n">i</span><span class="o">;</span> <span class="n">ensure_valid_stack</span> <span class="bp">()</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="c">(* Get rid of trivial cases that match one-to-one. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">simpl</span> <span class="k">in</span> <span class="n">Hev</span><span class="o">;</span> <span class="kr">try</span> <span class="o">(</span><span class="k">injection</span> <span class="n">Hev</span> <span class="k">as</span> <span class="n">Hinj</span><span class="o">;</span> <span class="k">subst</span><span class="o">;</span> <span class="n">solve_basic</span> <span class="bp">()</span><span class="o">).</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="c">(* compose with one quoted value is not final, but an error. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">destruct</span> <span class="n">v</span><span class="o">.</span> <span class="k">inversion</span> <span class="n">Hev</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="c">(* compose with two quoted values. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">destruct</span> <span class="n">v</span><span class="o">;</span> <span class="k">destruct</span> <span class="n">v0</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">injection</span> <span class="n">Hev</span> <span class="k">as</span> <span class="n">Hinj</span><span class="o">;</span> <span class="k">subst</span><span class="o">;</span> <span class="n">solve_basic</span> <span class="bp">()</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="c">(* Apply is not final. *)</span> <span class="k">destruct</span> <span class="n">v</span><span class="o">.</span> <span class="k">inversion</span> <span class="n">Hev</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="c">(* Quote is always final, trivially, and the semantics match easily. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">simpl</span> <span class="k">in</span> <span class="n">Hev</span><span class="o">.</span> <span class="k">injection</span> <span class="n">Hev</span> <span class="k">as</span> <span class="n">Hinj</span><span class="o">;</span> <span class="k">subst</span><span class="o">.</span> <span class="n">solve_basic</span> <span class="bp">()</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="c">(* Compose is never final, so we don&#39;t need to handle it here. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">simpl</span> <span class="k">in</span> <span class="n">Hev</span><span class="o">.</span> <span class="k">destruct</span> <span class="o">(</span><span class="n">eval_step</span> <span class="n">vs</span> <span class="n">e1</span><span class="o">);</span> <span class="k">inversion</span> <span class="n">Hev</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Qed</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The statement for a non-<code>final</code> step is more involved; the one step by itself need not match the semantics, since it only carries out a part of a computation. However, we <em>can</em> say that if the &ldquo;rest of the program&rdquo; matches the semantics, then so does the whole expression.</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/DawnEval.v" data-first-line="177" data-last-line="213"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/DawnEval.v#L177-L213">DawnEval.v</a>, lines 177 through 213</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">177 </span><span class="lnt">178 </span><span class="lnt">179 </span><span class="lnt">180 </span><span class="lnt">181 </span><span class="lnt">182 </span><span class="lnt">183 </span><span class="lnt">184 </span><span class="lnt">185 </span><span class="lnt">186 </span><span class="lnt">187 </span><span class="lnt">188 </span><span class="lnt">189 </span><span class="lnt">190 </span><span class="lnt">191 </span><span class="lnt">192 </span><span class="lnt">193 </span><span class="lnt">194 </span><span class="lnt">195 </span><span class="lnt">196 </span><span class="lnt">197 </span><span class="lnt">198 </span><span class="lnt">199 </span><span class="lnt">200 </span><span class="lnt">201 </span><span class="lnt">202 </span><span class="lnt">203 </span><span class="lnt">204 </span><span class="lnt">205 </span><span class="lnt">206 </span><span class="lnt">207 </span><span class="lnt">208 </span><span class="lnt">209 </span><span class="lnt">210 </span><span class="lnt">211 </span><span class="lnt">212 </span><span class="lnt">213 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Theorem</span> <span class="n">eval_step_middle_sem</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">e</span> <span class="n">ei</span><span class="o">:</span> <span class="n">expr</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="n">vsi</span> <span class="n">vs&#39;</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> </span></span><span class="line"><span class="cl"> <span class="n">eval_step</span> <span class="n">vs</span> <span class="n">e</span> <span class="o">=</span> <span class="n">middle</span> <span class="n">ei</span> <span class="n">vsi</span> <span class="o">-&gt;</span> </span></span><span class="line"><span class="cl"> <span class="n">Sem_expr</span> <span class="n">vsi</span> <span class="n">ei</span> <span class="n">vs&#39;</span> <span class="o">-&gt;</span> </span></span><span class="line"><span class="cl"> <span class="n">Sem_expr</span> <span class="n">vs</span> <span class="n">e</span> <span class="n">vs&#39;</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Proof</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">intros</span> <span class="n">e</span><span class="o">.</span> <span class="k">induction</span> <span class="n">e</span><span class="o">;</span> <span class="k">intros</span> <span class="n">ei</span> <span class="n">vs</span> <span class="n">vsi</span> <span class="n">vs&#39;</span> <span class="n">Hev</span> <span class="n">Hsem</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">destruct</span> <span class="n">i</span><span class="o">;</span> <span class="n">ensure_valid_stack</span> <span class="bp">()</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="c">(* compose with one quoted value; invalid. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">destruct</span> <span class="n">v</span><span class="o">.</span> <span class="k">inversion</span> <span class="n">Hev</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="c">(* compose with two quoted values; not a middle step. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">destruct</span> <span class="n">v</span><span class="o">;</span> <span class="k">destruct</span> <span class="n">v0</span><span class="o">.</span> <span class="k">inversion</span> <span class="n">Hev</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="c">(* Apply *)</span> </span></span><span class="line"><span class="cl"> <span class="k">destruct</span> <span class="n">v</span><span class="o">.</span> <span class="k">injection</span> <span class="n">Hev</span> <span class="k">as</span> <span class="n">Hinj</span><span class="o">;</span> <span class="k">subst</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="n">solve_basic</span> <span class="bp">()</span><span class="o">.</span> <span class="k">auto</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="c">(* quoting an expression is not middle. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">inversion</span> <span class="n">Hev</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">simpl</span> <span class="k">in</span> <span class="n">Hev</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">destruct</span> <span class="o">(</span><span class="n">eval_step</span> <span class="n">vs</span> <span class="n">e1</span><span class="o">)</span> <span class="n">eqn</span><span class="o">:</span><span class="n">Hev1</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="c">(* Step led to an error, which can&#39;t happen in a chain. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">inversion</span> <span class="n">Hev</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="c">(* Left expression makes a non-final step. Milk this for equalities first. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">injection</span> <span class="n">Hev</span> <span class="k">as</span> <span class="n">Hinj</span><span class="o">;</span> <span class="k">subst</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="c">(* The rest of the program (e_comp e e2) evaluates using our semantics, </span></span></span><span class="line"><span class="cl"><span class="c"> which means that both e and e2 evaluate using our semantics. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">inversion</span> <span class="n">Hsem</span><span class="o">;</span> <span class="k">subst</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="c">(* By induction, e1 evaluates using our semantics if e does, which we just confirmed. *)</span> </span></span><span class="line"><span class="cl"> <span class="n">specialize</span> <span class="o">(</span><span class="n">IHe1</span> <span class="n">e</span> <span class="n">vs</span> <span class="n">vsi</span> <span class="n">vs2</span> <span class="n">Hev1</span> <span class="n">H2</span><span class="o">).</span> </span></span><span class="line"><span class="cl"> <span class="c">(* The composition rule can now be applied. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">eapply</span> <span class="n">Sem_e_comp</span><span class="o">;</span> <span class="k">eauto</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="c">(* Left expression makes a final step. Milk this for equalities first. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">injection</span> <span class="n">Hev</span> <span class="k">as</span> <span class="n">Hinj</span><span class="o">;</span> <span class="k">subst</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="c">(* Using eval_step_final, we know that e1 evaluates to the intermediate </span></span></span><span class="line"><span class="cl"><span class="c"> state given our semantics. *)</span> </span></span><span class="line"><span class="cl"> <span class="n">specialize</span> <span class="o">(</span><span class="n">eval_step_final_sem</span> <span class="n">e1</span> <span class="n">vs</span> <span class="n">vsi</span> <span class="n">Hev1</span><span class="o">)</span> <span class="k">as</span> <span class="n">Hsem1</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="c">(* The composition rule can now be applied. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">eapply</span> <span class="n">Sem_e_comp</span><span class="o">;</span> <span class="k">eauto</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Qed</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Finally, we snap these two pieces together in <code>eval_step_sem_back</code>:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/DawnEval.v" data-first-line="215" data-last-line="222"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/DawnEval.v#L215-L222">DawnEval.v</a>, lines 215 through 222</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">215 </span><span class="lnt">216 </span><span class="lnt">217 </span><span class="lnt">218 </span><span class="lnt">219 </span><span class="lnt">220 </span><span class="lnt">221 </span><span class="lnt">222 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Theorem</span> <span class="n">eval_step_sem_back</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">e</span> <span class="o">:</span> <span class="n">expr</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="n">vs&#39;</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> </span></span><span class="line"><span class="cl"> <span class="n">eval_chain</span> <span class="n">vs</span> <span class="n">e</span> <span class="n">vs&#39;</span> <span class="o">-&gt;</span> <span class="n">Sem_expr</span> <span class="n">vs</span> <span class="n">e</span> <span class="n">vs&#39;</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Proof</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">intros</span> <span class="n">e</span> <span class="n">vs</span> <span class="n">vs&#39;</span> <span class="n">ch</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="n">ltac1</span><span class="o">:(</span><span class="n">dependent</span> <span class="k">induction</span> <span class="n">ch</span><span class="o">).</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">apply</span> <span class="n">eval_step_final_sem</span><span class="o">.</span> <span class="k">auto</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="n">specialize</span> <span class="o">(</span><span class="n">eval_step_middle_sem</span> <span class="n">e</span> <span class="n">ei</span> <span class="n">vs</span> <span class="n">vsi</span> <span class="n">vs&#39;</span> <span class="n">P</span> <span class="n">IHch</span><span class="o">).</span> <span class="k">auto</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Qed</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We have now proved complete equivalence: our evaluator completes in a final state if and only if our semantics lead to this same final state. As a corollary of this, we can see that if a program is invalid (it doesn&rsquo;t evaluate to anything under our semantics), then our evaluator won&rsquo;t finish in a <code>final</code> state:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/DawnEval.v" data-first-line="224" data-last-line="229"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/DawnEval.v#L224-L229">DawnEval.v</a>, lines 224 through 229</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">224 </span><span class="lnt">225 </span><span class="lnt">226 </span><span class="lnt">227 </span><span class="lnt">228 </span><span class="lnt">229 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Corollary</span> <span class="n">eval_step_no_sem</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">e</span> <span class="o">:</span> <span class="n">expr</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="n">vs&#39;</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> </span></span><span class="line"><span class="cl"> <span class="o">~(</span><span class="n">Sem_expr</span> <span class="n">vs</span> <span class="n">e</span> <span class="n">vs&#39;</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="o">~(</span><span class="n">eval_chain</span> <span class="n">vs</span> <span class="n">e</span> <span class="n">vs&#39;</span><span class="o">).</span> </span></span><span class="line"><span class="cl"><span class="kn">Proof</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">intros</span> <span class="n">e</span> <span class="n">vs</span> <span class="n">vs&#39;</span> <span class="n">Hnsem</span> <span class="n">Hch</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="n">specialize</span> <span class="o">(</span><span class="n">eval_step_sem_back</span> <span class="o">_</span> <span class="o">_</span> <span class="o">_</span> <span class="n">Hch</span><span class="o">).</span> <span class="k">auto</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Qed</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#making-a-mini-repl"> <h3 id="making-a-mini-repl">Making a Mini-REPL</h3> </a> <p>We can now be pretty confident about our evaluator. However, this whole business with using GHCi to run our programs is rather inconvenient; I&rsquo;d rather write <code>[drop]</code> than <code>E_quote (E_int Drop)</code>. I&rsquo;d also rather <em>read</em> the former instead of the latter. We can do some work in Haskell to make playing with our interpreter more convenient.</p> <a href="#pretty-printing-code"> <h4 id="pretty-printing-code">Pretty Printing Code</h4> </a> <p>Coq generated Haskell data types that correspond precisely to the data types we defined for our proofs. We can use the standard type class mechanism in Haskell to define how they should be printed:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/Ucc.hs" data-first-line="8" data-last-line="22"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Ucc.hs#L8-L22">Ucc.hs</a>, lines 8 through 22</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kr">instance</span> <span class="kt">Show</span> <span class="kt">Intrinsic</span> <span class="kr">where</span> </span></span><span class="line"><span class="cl"> <span class="n">show</span> <span class="kt">Swap</span> <span class="ow">=</span> <span class="s">&#34;swap&#34;</span> </span></span><span class="line"><span class="cl"> <span class="n">show</span> <span class="kt">Clone</span> <span class="ow">=</span> <span class="s">&#34;clone&#34;</span> </span></span><span class="line"><span class="cl"> <span class="n">show</span> <span class="kt">Drop</span> <span class="ow">=</span> <span class="s">&#34;drop&#34;</span> </span></span><span class="line"><span class="cl"> <span class="n">show</span> <span class="kt">Quote</span> <span class="ow">=</span> <span class="s">&#34;quote&#34;</span> </span></span><span class="line"><span class="cl"> <span class="n">show</span> <span class="kt">Compose</span> <span class="ow">=</span> <span class="s">&#34;compose&#34;</span> </span></span><span class="line"><span class="cl"> <span class="n">show</span> <span class="kt">Apply</span> <span class="ow">=</span> <span class="s">&#34;apply&#34;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kr">instance</span> <span class="kt">Show</span> <span class="kt">Expr</span> <span class="kr">where</span> </span></span><span class="line"><span class="cl"> <span class="n">show</span> <span class="p">(</span><span class="kt">E_int</span> <span class="n">i</span><span class="p">)</span> <span class="ow">=</span> <span class="n">show</span> <span class="n">i</span> </span></span><span class="line"><span class="cl"> <span class="n">show</span> <span class="p">(</span><span class="kt">E_quote</span> <span class="n">e</span><span class="p">)</span> <span class="ow">=</span> <span class="s">&#34;[&#34;</span> <span class="o">++</span> <span class="n">show</span> <span class="n">e</span> <span class="o">++</span> <span class="s">&#34;]&#34;</span> </span></span><span class="line"><span class="cl"> <span class="n">show</span> <span class="p">(</span><span class="kt">E_comp</span> <span class="n">e1</span> <span class="n">e2</span><span class="p">)</span> <span class="ow">=</span> <span class="n">show</span> <span class="n">e1</span> <span class="o">++</span> <span class="s">&#34; &#34;</span> <span class="o">++</span> <span class="n">show</span> <span class="n">e2</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kr">instance</span> <span class="kt">Show</span> <span class="kt">Value</span> <span class="kr">where</span> </span></span><span class="line"><span class="cl"> <span class="n">show</span> <span class="p">(</span><span class="kt">V_quote</span> <span class="n">e</span><span class="p">)</span> <span class="ow">=</span> <span class="n">show</span> <span class="p">(</span><span class="kt">E_quote</span> <span class="n">e</span><span class="p">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Now our expressions and values print a lot nicer:</p> <pre tabindex="0"><code>ghci&gt; true [swap drop] ghci&gt; false [drop] ghci&gt; UccGen.or clone apply </code></pre><a href="#reading-code-in"> <h4 id="reading-code-in">Reading Code In</h4> </a> <p>The Parsec library in Haskell can be used to convert text back into data structures. It&rsquo;s not too hard to create a parser for UCC:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/Ucc.hs" data-first-line="24" data-last-line="44"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Ucc.hs#L24-L44">Ucc.hs</a>, lines 24 through 44</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kr">type</span> <span class="kt">Parser</span> <span class="n">a</span> <span class="ow">=</span> <span class="kt">ParsecT</span> <span class="kt">String</span> <span class="nb">()</span> <span class="kt">Identity</span> <span class="n">a</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="nf">intrinsic</span> <span class="ow">::</span> <span class="kt">Parser</span> <span class="kt">Intrinsic</span> </span></span><span class="line"><span class="cl"><span class="nf">intrinsic</span> <span class="ow">=</span> <span class="p">(</span><span class="o">&lt;*</span> <span class="n">spaces</span><span class="p">)</span> <span class="o">$</span> <span class="n">foldl1</span> <span class="p">(</span><span class="o">&lt;|&gt;</span><span class="p">)</span> <span class="o">$</span> <span class="n">map</span> <span class="p">(</span><span class="nf">\</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">i</span><span class="p">)</span> <span class="ow">-&gt;</span> <span class="n">try</span> <span class="p">(</span><span class="n">string</span> <span class="n">s</span> <span class="o">&gt;&gt;</span> <span class="n">return</span> <span class="n">i</span><span class="p">))</span> </span></span><span class="line"><span class="cl"> <span class="p">[</span> <span class="p">(</span><span class="s">&#34;swap&#34;</span><span class="p">,</span> <span class="kt">Swap</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="p">(</span><span class="s">&#34;clone&#34;</span><span class="p">,</span> <span class="kt">Clone</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="p">(</span><span class="s">&#34;drop&#34;</span><span class="p">,</span> <span class="kt">Drop</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="p">(</span><span class="s">&#34;quote&#34;</span><span class="p">,</span> <span class="kt">Quote</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="p">(</span><span class="s">&#34;compose&#34;</span><span class="p">,</span> <span class="kt">Compose</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="p">(</span><span class="s">&#34;apply&#34;</span><span class="p">,</span> <span class="kt">Apply</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">]</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="nf">expression</span> <span class="ow">::</span> <span class="kt">Parser</span> <span class="kt">Expr</span> </span></span><span class="line"><span class="cl"><span class="nf">expression</span> <span class="ow">=</span> <span class="n">foldl1</span> <span class="kt">E_comp</span> <span class="o">&lt;$&gt;</span> <span class="n">many1</span> <span class="n">single</span> </span></span><span class="line"><span class="cl"> <span class="kr">where</span> </span></span><span class="line"><span class="cl"> <span class="n">single</span> </span></span><span class="line"><span class="cl"> <span class="ow">=</span> <span class="p">(</span><span class="kt">E_int</span> <span class="o">&lt;$&gt;</span> <span class="n">intrinsic</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">&lt;|&gt;</span> <span class="p">(</span><span class="n">fmap</span> <span class="kt">E_quote</span> <span class="o">$</span> <span class="n">char</span> <span class="sc">&#39;[&#39;</span> <span class="o">*&gt;</span> <span class="n">spaces</span> <span class="o">*&gt;</span> <span class="n">expression</span> <span class="o">&lt;*</span> <span class="n">char</span> <span class="sc">&#39;]&#39;</span> <span class="o">&lt;*</span> <span class="n">spaces</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="nf">parseExpression</span> <span class="ow">::</span> <span class="kt">String</span> <span class="ow">-&gt;</span> <span class="kt">Either</span> <span class="kt">ParseError</span> <span class="kt">Expr</span> </span></span><span class="line"><span class="cl"><span class="nf">parseExpression</span> <span class="ow">=</span> <span class="n">runParser</span> <span class="n">expression</span> <span class="nb">()</span> <span class="s">&#34;&lt;inline&gt;&#34;</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Now, <code>parseExpression</code> can be used to read code:</p> <pre tabindex="0"><code>ghci&gt; parseExpression &#34;[drop] [drop]&#34; Right [drop] [drop] </code></pre><a href="#the-repl"> <h4 id="the-repl">The REPL</h4> </a> <p>We now literally have the three pieces of a read-evaluate-print loop. All that&rsquo;s left is putting them together:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/Ucc.hs" data-first-line="53" data-last-line="64"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Ucc.hs#L53-L64">Ucc.hs</a>, lines 53 through 64</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">main</span> <span class="ow">::</span> <span class="kt">IO</span> <span class="nb">()</span> </span></span><span class="line"><span class="cl"><span class="nf">main</span> <span class="ow">=</span> <span class="kr">do</span> </span></span><span class="line"><span class="cl"> <span class="n">putStr</span> <span class="s">&#34;&gt; &#34;</span> </span></span><span class="line"><span class="cl"> <span class="n">hFlush</span> <span class="n">stdout</span> </span></span><span class="line"><span class="cl"> <span class="n">str</span> <span class="ow">&lt;-</span> <span class="n">getLine</span> </span></span><span class="line"><span class="cl"> <span class="kr">case</span> <span class="n">parseExpression</span> <span class="n">str</span> <span class="kr">of</span> </span></span><span class="line"><span class="cl"> <span class="kt">Right</span> <span class="n">e</span> <span class="ow">-&gt;</span> </span></span><span class="line"><span class="cl"> <span class="kr">case</span> <span class="n">eval</span> <span class="kt">[]</span> <span class="n">e</span> <span class="kr">of</span> </span></span><span class="line"><span class="cl"> <span class="kt">Just</span> <span class="n">st</span> <span class="ow">-&gt;</span> <span class="n">putStrLn</span> <span class="o">$</span> <span class="n">show</span> <span class="n">st</span> </span></span><span class="line"><span class="cl"> <span class="kr">_</span> <span class="ow">-&gt;</span> <span class="n">putStrLn</span> <span class="s">&#34;Evaluation error&#34;</span> </span></span><span class="line"><span class="cl"> <span class="kr">_</span> <span class="ow">-&gt;</span> <span class="n">putStrLn</span> <span class="s">&#34;Parse error&#34;</span> </span></span><span class="line"><span class="cl"> <span class="n">main</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We now have our own little verified evaluator:</p> <pre tabindex="0"><code>$ ghci Ucc $ ghc -main-is Ucc Ucc $ ./Ucc &gt; [drop] [drop] clone apply [[drop]] &gt; [drop] [swap drop] clone apply [[swap drop]] &gt; [swap drop] [drop] clone apply [[swap drop]] &gt; [swap drop] [swap drop] clone apply [[swap drop]] </code></pre><a href="#potential-future-work"> <h3 id="potential-future-work">Potential Future Work</h3> </a> <p>We now have a verified UCC evaluator in Haskell. What next?</p> <ol> <li> <p>We only verified the evaluation component of our REPL. In fact, the whole thing is technically a bit buggy due to the parser:</p> <pre tabindex="0"><code>&gt; [drop] drop coq is a weird name to say out loud in interviews [] </code></pre><p>Shouldn&rsquo;t this be an error? Do you see why it&rsquo;s not? There&rsquo;s work in writing formally verified parsers; some time ago I saw a Galois talk about a <a href="https://galois.com/blog/2019/09/tech-talk-a-verified-ll1-parser-generator/"class="external-link">formally verified parser generator<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. We could use this formally verified parser generator to create our parsers, and then be sure that our grammar is precisely followed.</p> </li> <li> <p>We could try make our evaluator a bit smarter. One thing we could definitely do is maker our REPL support variables. Then, we would be able to write:</p> <pre tabindex="0"><code>&gt; true = [swap drop] &gt; false = [drop] &gt; or = clone apply &gt; true false or [swap drop] </code></pre><p>There are different ways to go about this. One way is to extend our <code>expr</code> data type with a variable constructor. This would complicate the semantics (a <em>lot</em>), but it would allow us to prove facts about variables. Alternatively, we could implement expressions as syntax sugar in our parser. Using a variable would be the same as simply pasting in the variable&rsquo;s definition. This is pretty much what the Dawn article seems to be doing.</p> </li> <li> <p>We could prove more things. Can we confirm, once and for all, the correctness of \(\text{quote}_n\), for <em>any</em> \(n\)? Is there is a generalized way of converting inductive data types into a UCC encoding? Or could we perhaps formally verify the following comment from Lobsters:</p> <blockquote> <p>with the encoding of natural numbers given, n+1 contains the definition of n duplicated two times. This means that the encoding of n has size exponential in n, which seems extremely impractical.</p> </blockquote> <p>The world&rsquo;s our oyster!</p> </li> </ol> <p>Despite all of these exciting possibilities, this is where I stop, for now. I hope you enjoyed this article, and thank you for reading!</p> Formalizing Dawn in Coq https://danilafe.com/blog/coq_dawn/ Sat, 20 Nov 2021 19:04:57 -0800 https://danilafe.com/blog/coq_dawn/ <p>The <a href="https://www.dawn-lang.org/posts/foundations-ucc/"class="external-link"><em>Foundations of Dawn</em><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> article came up on <a href="https://lobste.rs/s/clatuv/foundations_dawn_untyped_concatenative"class="external-link">Lobsters<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> recently. In this article, the author of Dawn defines a core calculus for the language, and provides its semantics. The core calculus is called the <em>untyped concatenative calculus</em>, or UCC. The definitions in the semantics seemed so clean and straightforward that I wanted to try my hand at translating them into machine-checked code. I am most familiar with <a href="https://coq.inria.fr/"class="external-link">Coq<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, and that&rsquo;s what I reached for when making this attempt.</p> <a href="#defining-the-syntax"> <h3 id="defining-the-syntax">Defining the Syntax</h3> </a> <a href="#expressions-and-intrinsics"> <h4 id="expressions-and-intrinsics">Expressions and Intrinsics</h4> </a> <p>This is mostly the easy part. A UCC expression is one of three things:</p> <ul> <li>An &ldquo;intrinsic&rdquo;, written \(i\), which is akin to a built-in function or command.</li> <li>A &ldquo;quote&rdquo;, written \([e]\), which takes a UCC expression \(e\) and moves it onto the stack (UCC is stack-based).</li> <li>A composition of several expressions, written \(e_1\ e_2\ \ldots\ e_n\), which effectively evaluates them in order.</li> </ul> <p>This is straightforward to define in Coq, but I&rsquo;m going to make a little simplifying change. Instead of making &ldquo;composition of \(n\) expressions&rdquo; a core language feature, I&rsquo;ll only allow &ldquo;composition of \(e_1\) and \(e_2\)&rdquo;, written \(e_1\ e_2\). This change does not in any way reduce the power of the language; we can still <span class="sidenote"> <label class="sidenote-label" for="assoc-note">write \(e_1\ e_2\ \ldots\ e_n\) as \((e_1\ e_2)\ \ldots\ e_n\).</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="assoc-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> The same expression can, of course, be written as \(e_1\ \ldots\ (e_{n-1}\ e_n)\). So, which way should we <em>really</em> use when translating the many-expression composition from the Dawn article into the two-expression composition I am using here? Well, the answer is, it doesn't matter; expression composition is <em>associative</em>, so both ways effectively mean the same thing.<br> <br> This is quite similar to what we do in algebra: the regular old addition operator, \(+\) is formally only defined for pairs of numbers, like \(a+b\). However, no one really bats an eye when we write \(1+2+3\), because we can just insert parentheses any way we like, and get the same result: \((1+2)+3\) is the same as \(1+(2+3)\). <span class="sidenote-delimiter">]</span> </span> </span> With that in mind, we can translate each of the three types of expressions in UCC into cases of an inductive data type in Coq.</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/Dawn.v" data-first-line="12" data-last-line="15"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Dawn.v#L12-L15">Dawn.v</a>, lines 12 through 15</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Inductive</span> <span class="n">expr</span> <span class="o">:=</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">e_int</span> <span class="o">(</span><span class="n">i</span> <span class="o">:</span> <span class="n">intrinsic</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">e_quote</span> <span class="o">(</span><span class="n">e</span> <span class="o">:</span> <span class="n">expr</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">e_comp</span> <span class="o">(</span><span class="n">e1</span> <span class="n">e2</span> <span class="o">:</span> <span class="n">expr</span><span class="o">).</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Why do we need <code>e_int</code>? We do because a token like \(\text{swap}\) can be viewed as belonging to the set of intrinsics \(i\), or the set of expressions, \(e\). While writing down the rules in mathematical notation, what exactly the token means is inferred from context - clearly \(\text{swap}\ \text{drop}\) is an expression built from two other expressions. In statically-typed functional languages like Coq or Haskell, however, the same expression can&rsquo;t belong to two different, arbitrary types. Thus, to turn an intrinsic into an expression, we need to wrap it up in a constructor, which we called <code>e_int</code> here. Other than that, <code>e_quote</code> accepts as argument another expression, <code>e</code> (the thing being quoted), and <code>e_comp</code> accepts two expressions, <code>e1</code> and <code>e2</code> (the two sub-expressions being composed).</p> <p>The definition for intrinsics themselves is even simpler:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/Dawn.v" data-first-line="4" data-last-line="10"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Dawn.v#L4-L10">Dawn.v</a>, lines 4 through 10</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Inductive</span> <span class="n">intrinsic</span> <span class="o">:=</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">swap</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">clone</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">drop</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">quote</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">compose</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="k">apply</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We simply define a constructor for each of the six intrinsics. Since none of the intrinsic names are reserved in Coq, we can just call our constructors exactly the same as their names in the written formalization.</p> <a href="#values-and-value-stacks"> <h4 id="values-and-value-stacks">Values and Value Stacks</h4> </a> <p>Values are up next. My initial thought was to define a value much like I defined an intrinsic expression: by wrapping an expression in a constructor for a new data type. Something like:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Inductive</span> <span class="n">value</span> <span class="o">:=</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">v_quot</span> <span class="o">(</span><span class="n">e</span> <span class="o">:</span> <span class="n">expr</span><span class="o">).</span> </span></span></code></pre></div><p>Then, <code>v_quot (e_int swap)</code> would be the Coq translation of the expression \([\text{swap}]\). However, I didn&rsquo;t decide on this approach for two reasons:</p> <ul> <li>There are now two ways to write a quoted expression: either <code>v_quote e</code> to represent a quoted expression that is a value, or <code>e_quote e</code> to represent a quoted expression that is just an expression. In the extreme case, the value \([[e]]\) would be represented by <code>v_quote (e_quote e)</code> - two different constructors for the same concept, in the same expression!</li> <li>When formalizing the lambda calculus, <a href="https://softwarefoundations.cis.upenn.edu/plf-current/Stlc.html"class="external-link">Programming Language Foundations<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> uses an inductively-defined property to indicate values. In the simply typed lambda calculus, much like in UCC, values are a subset of expressions.</li> </ul> <p>I took instead the approach from Programming Language Foundations: a value is merely an expression for which some predicate, <code>IsValue</code>, holds. We will define this such that <code>IsValue (e_quote e)</code> is provable, but also such that here is no way to prove <code>IsValue (e_int swap)</code>, since <em>that</em> expression is not a value. But what does &ldquo;provable&rdquo; mean, here?</p> <p>By the <a href="https://en.wikipedia.org/wiki/Curry%E2%80%93Howard_correspondence"class="external-link">Curry-Howard correspondence<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, a predicate is just a function that takes <em>something</em> and returns a type. Thus, if \(\text{Even}\) is a predicate, then \(\text{Even}\ 3\) is actually a type. Since \(\text{Even}\) takes numbers in, it is a predicate on numbers. Our \(\text{IsValue}\) predicate will be a predicate on expressions, instead. In Coq, we can write this as:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/Dawn.v" data-first-line="19" data-last-line="19"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Dawn.v#L19-L19">Dawn.v</a>, line 19</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">19 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Inductive</span> <span class="n">IsValue</span> <span class="o">:</span> <span class="n">expr</span> <span class="o">-&gt;</span> <span class="kt">Prop</span> <span class="o">:=</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>You might be thinking,</p> <blockquote> <p>Huh, <code>Prop</code>? But you just said that predicates return types!</p> </blockquote> <p>This is a good observation; In Coq, <code>Prop</code> is a special sort of type that corresponds to logical propositions. It&rsquo;s special for a few reasons, but those reasons are beyond the scope of this post; for our purposes, it&rsquo;s sufficient to think of <code>IsValue e</code> as a type.</p> <p>Alright, so what good is this new <code>IsValue e</code> type? Well, we will define <code>IsValue</code> such that this type is only <em>inhabited</em> if <code>e</code> is a value according to the UCC specification. A type is inhabited if and only if we can find a value of that type. For instance, the type of natural numbers, <code>nat</code>, is inhabited, because any number, like <code>0</code>, has this type. Uninhabited types are harder to come by, but take as an example the type <code>3 = 4</code>, the type of proofs that three is equal to four. Three is <em>not</em> equal to four, so we can never find a proof of equality, and thus, <code>3 = 4</code> is uninhabited. As I said, <code>IsValue e</code> will only be inhabited if <code>e</code> is a value per the formal specification of UCC; specifically, this means that <code>e</code> is a quoted expression, like <code>e_quote e'</code>.</p> <p>To this end, we define <code>IsValue</code> as follows:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/Dawn.v" data-first-line="19" data-last-line="20"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Dawn.v#L19-L20">Dawn.v</a>, lines 19 through 20</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">19 </span><span class="lnt">20 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Inductive</span> <span class="n">IsValue</span> <span class="o">:</span> <span class="n">expr</span> <span class="o">-&gt;</span> <span class="kt">Prop</span> <span class="o">:=</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">Val_quote</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">{</span><span class="n">e</span> <span class="o">:</span> <span class="n">expr</span><span class="o">},</span> <span class="n">IsValue</span> <span class="o">(</span><span class="n">e_quote</span> <span class="n">e</span><span class="o">).</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Now, <code>IsValue</code> is a new data type with only only constructor, <code>ValQuote</code>. For any expression <code>e</code>, this constructor creates a value of type <code>IsValue (e_quote e)</code>. Two things are true here:</p> <ul> <li>Since <code>Val_quote</code> accepts any expression <code>e</code> to be put inside <code>e_quote</code>, we can use <code>Val_quote</code> to create an <code>IsValue</code> instance for any quoted expression.</li> <li>Because <code>Val_quote</code> is the <em>only</em> constructor, and because it always returns <code>IsValue (e_quote e)</code>, there&rsquo;s no way to get <code>IsValue (e_int i)</code>, or anything else.</li> </ul> <p>Thus, <code>IsValue e</code> is inhabited if and only if <code>e</code> is a UCC value, as we intended.</p> <p>Just one more thing. A value is just an expression, but Coq only knows about this as long as there&rsquo;s an <code>IsValue</code> instance around to vouch for it. To be able to reason about values, then, we will need both the expression and its <code>IsValue</code> proof. Thus, we define the type <code>value</code> to mean a pair of two things: an expression <code>v</code> and a proof that it&rsquo;s a value, <code>IsValue v</code>:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/Dawn.v" data-first-line="22" data-last-line="22"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Dawn.v#L22-L22">Dawn.v</a>, line 22</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">22 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Definition</span> <span class="n">value</span> <span class="o">:=</span> <span class="o">{</span> <span class="n">v</span> <span class="o">:</span> <span class="n">expr</span> <span class="o">&amp;</span> <span class="n">IsValue</span> <span class="n">v</span> <span class="o">}.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>A value stack is just a list of values:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/Dawn.v" data-first-line="23" data-last-line="23"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Dawn.v#L23-L23">Dawn.v</a>, line 23</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">23 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Definition</span> <span class="n">value_stack</span> <span class="o">:=</span> <span class="kt">list</span> <span class="n">value</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#semantics"> <h3 id="semantics">Semantics</h3> </a> <p>Remember our <code>IsValue</code> predicate? Well, it&rsquo;s not just any predicate, it&rsquo;s a <em>unary</em> predicate. <em>Unary</em> means that it&rsquo;s a predicate that only takes one argument, an expression in our case. However, this is far from the only type of predicate. Here are some examples:</p> <ul> <li>Equality, <code>=</code>, is a binary predicate in Coq. It takes two arguments, say <code>x</code> and <code>y</code>, and builds a type <code>x = y</code> that is only inhabited if <code>x</code> and <code>y</code> are equal.</li> <li>The mathematical &ldquo;less than&rdquo; relation is also a binary predicate, and it&rsquo;s called <code>le</code> in Coq. It takes two numbers <code>n</code> and <code>m</code> and returns a type <code>le n m</code> that is only inhabited if <code>n</code> is less than or equal to <code>m</code>.</li> <li>The evaluation relation in UCC is a ternary predicate. It takes two stacks, <code>vs</code> and <code>vs'</code>, and an expression, <code>e</code>, and creates a type that&rsquo;s inhabited if and only if evaluating <code>e</code> starting at a stack <code>vs</code> results in the stack <code>vs'</code>.</li> </ul> <p>Binary predicates are just functions of two inputs that return types. For instance, here&rsquo;s what Coq has to say about the type of <code>eq</code>:</p> <pre tabindex="0"><code>eq : ?A -&gt; ?A -&gt; Prop </code></pre><p>By a similar logic, ternary predicates, much like UCC&rsquo;s evaluation relation, are functions of three inputs. We can thus write the type of our evaluation relation as follows:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/Dawn.v" data-first-line="35" data-last-line="35"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Dawn.v#L35-L35">Dawn.v</a>, line 35</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">35 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="k">with</span> <span class="n">Sem_expr</span> <span class="o">:</span> <span class="n">value_stack</span> <span class="o">-&gt;</span> <span class="n">expr</span> <span class="o">-&gt;</span> <span class="n">value_stack</span> <span class="o">-&gt;</span> <span class="kt">Prop</span> <span class="o">:=</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We define the constructors just like we did in our <code>IsValue</code> predicate. For each evaluation rule in UCC, such as:</p> $$ \langle V, v, v&amp;#39;\rangle\ \text{swap}\ \rightarrow\ \langle V, v&amp;#39;, v \rangle $$ <p>We introduce a constructor. For the <code>swap</code> rule mentioned above, the constructor looks like this:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/Dawn.v" data-first-line="28" data-last-line="28"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Dawn.v#L28-L28">Dawn.v</a>, line 28</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">28 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">Sem_swap</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">v</span> <span class="n">v&#39;</span> <span class="o">:</span> <span class="n">value</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> <span class="n">Sem_int</span> <span class="o">(</span><span class="n">v&#39;</span> <span class="o">::</span> <span class="n">v</span> <span class="o">::</span> <span class="n">vs</span><span class="o">)</span> <span class="n">swap</span> <span class="o">(</span><span class="n">v</span> <span class="o">::</span> <span class="n">v&#39;</span> <span class="o">::</span> <span class="n">vs</span><span class="o">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Although the stacks are written in reverse order (which is just a consequence of Coq&rsquo;s list notation), I hope that the correspondence is fairly clear. If it&rsquo;s not, try reading this rule out loud:</p> <blockquote> <p>The rule <code>Sem_swap</code> says that for every two values <code>v</code> and <code>v'</code>, and for any stack <code>vs</code>, evaluating <code>swap</code> in the original stack <code>v' :: v :: vs</code>, aka \(\langle V, v, v'\rangle\), results in a final stack <code>v :: v' :: vs</code>, aka \(\langle V, v', v\rangle\).</p> </blockquote> <p>With that in mind, here&rsquo;s a definition of a predicate <code>Sem_int</code>, the evaluation predicate for intrinsics:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/Dawn.v" data-first-line="27" data-last-line="33"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Dawn.v#L27-L33">Dawn.v</a>, lines 27 through 33</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Inductive</span> <span class="n">Sem_int</span> <span class="o">:</span> <span class="n">value_stack</span> <span class="o">-&gt;</span> <span class="n">intrinsic</span> <span class="o">-&gt;</span> <span class="n">value_stack</span> <span class="o">-&gt;</span> <span class="kt">Prop</span> <span class="o">:=</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">Sem_swap</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">v</span> <span class="n">v&#39;</span> <span class="o">:</span> <span class="n">value</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> <span class="n">Sem_int</span> <span class="o">(</span><span class="n">v&#39;</span> <span class="o">::</span> <span class="n">v</span> <span class="o">::</span> <span class="n">vs</span><span class="o">)</span> <span class="n">swap</span> <span class="o">(</span><span class="n">v</span> <span class="o">::</span> <span class="n">v&#39;</span> <span class="o">::</span> <span class="n">vs</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">Sem_clone</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">v</span> <span class="o">:</span> <span class="n">value</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> <span class="n">Sem_int</span> <span class="o">(</span><span class="n">v</span> <span class="o">::</span> <span class="n">vs</span><span class="o">)</span> <span class="n">clone</span> <span class="o">(</span><span class="n">v</span> <span class="o">::</span> <span class="n">v</span> <span class="o">::</span> <span class="n">vs</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">Sem_drop</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">v</span> <span class="o">:</span> <span class="n">value</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> <span class="n">Sem_int</span> <span class="o">(</span><span class="n">v</span> <span class="o">::</span> <span class="n">vs</span><span class="o">)</span> <span class="n">drop</span> <span class="n">vs</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">Sem_quote</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">v</span> <span class="o">:</span> <span class="n">value</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> <span class="n">Sem_int</span> <span class="o">(</span><span class="n">v</span> <span class="o">::</span> <span class="n">vs</span><span class="o">)</span> <span class="n">quote</span> <span class="o">((</span><span class="n">v_quote</span> <span class="o">(</span><span class="n">projT1</span> <span class="n">v</span><span class="o">))</span> <span class="o">::</span> <span class="n">vs</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">Sem_compose</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">e</span> <span class="n">e&#39;</span> <span class="o">:</span> <span class="n">expr</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> <span class="n">Sem_int</span> <span class="o">(</span><span class="n">v_quote</span> <span class="n">e&#39;</span> <span class="o">::</span> <span class="n">v_quote</span> <span class="n">e</span> <span class="o">::</span> <span class="n">vs</span><span class="o">)</span> <span class="n">compose</span> <span class="o">(</span><span class="n">v_quote</span> <span class="o">(</span><span class="n">e_comp</span> <span class="n">e</span> <span class="n">e&#39;</span><span class="o">)</span> <span class="o">::</span> <span class="n">vs</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">Sem_apply</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">e</span> <span class="o">:</span> <span class="n">expr</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="n">vs&#39;</span><span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> <span class="n">Sem_expr</span> <span class="n">vs</span> <span class="n">e</span> <span class="n">vs&#39;</span> <span class="o">-&gt;</span> <span class="n">Sem_int</span> <span class="o">(</span><span class="n">v_quote</span> <span class="n">e</span> <span class="o">::</span> <span class="n">vs</span><span class="o">)</span> <span class="k">apply</span> <span class="n">vs&#39;</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Hey, what&rsquo;s all this with <code>v_quote</code> and <code>projT1</code>? It&rsquo;s just a little bit of bookkeeping. Given a value &ndash; a pair of an expression <code>e</code> and a proof <code>IsValue e</code> &ndash; the function <code>projT1</code> just returns the expression <code>e</code>. That is, it&rsquo;s basically a way of converting a value back into an expression. The function <code>v_quote</code> takes us in the other direction: given an expression \(e\), it constructs a quoted expression \([e]\), and combines it with a proof that the newly constructed quote is a value.</p> <p>The above two function in combination help us define the <code>quote</code> intrinsic, which wraps a value on the stack in an additional layer of quotes. When we create a new quote, we need to push it onto the value stack, so it needs to be a value; we thus use <code>v_quote</code>. However, <code>v_quote</code> needs an expression to wrap in a quote, so we use <code>projT1</code> to extract the expression from the value on top of the stack.</p> <p>In addition to intrinsics, we also define the evaluation relation for actual expressions.</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/Dawn.v" data-first-line="35" data-last-line="39"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Dawn.v#L35-L39">Dawn.v</a>, lines 35 through 39</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="k">with</span> <span class="n">Sem_expr</span> <span class="o">:</span> <span class="n">value_stack</span> <span class="o">-&gt;</span> <span class="n">expr</span> <span class="o">-&gt;</span> <span class="n">value_stack</span> <span class="o">-&gt;</span> <span class="kt">Prop</span> <span class="o">:=</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">Sem_e_int</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">i</span> <span class="o">:</span> <span class="n">intrinsic</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="n">vs&#39;</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> <span class="n">Sem_int</span> <span class="n">vs</span> <span class="n">i</span> <span class="n">vs&#39;</span> <span class="o">-&gt;</span> <span class="n">Sem_expr</span> <span class="n">vs</span> <span class="o">(</span><span class="n">e_int</span> <span class="n">i</span><span class="o">)</span> <span class="n">vs&#39;</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">Sem_e_quote</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">e</span> <span class="o">:</span> <span class="n">expr</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> <span class="n">Sem_expr</span> <span class="n">vs</span> <span class="o">(</span><span class="n">e_quote</span> <span class="n">e</span><span class="o">)</span> <span class="o">(</span><span class="n">v_quote</span> <span class="n">e</span> <span class="o">::</span> <span class="n">vs</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">Sem_e_comp</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">e1</span> <span class="n">e2</span> <span class="o">:</span> <span class="n">expr</span><span class="o">)</span> <span class="o">(</span><span class="n">vs1</span> <span class="n">vs2</span> <span class="n">vs3</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> </span></span><span class="line"><span class="cl"> <span class="n">Sem_expr</span> <span class="n">vs1</span> <span class="n">e1</span> <span class="n">vs2</span> <span class="o">-&gt;</span> <span class="n">Sem_expr</span> <span class="n">vs2</span> <span class="n">e2</span> <span class="n">vs3</span> <span class="o">-&gt;</span> <span class="n">Sem_expr</span> <span class="n">vs1</span> <span class="o">(</span><span class="n">e_comp</span> <span class="n">e1</span> <span class="n">e2</span><span class="o">)</span> <span class="n">vs3</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Here, we may as well go through the three constructors to explain what they mean:</p> <ul> <li><code>Sem_e_int</code> says that if the expression being evaluated is an intrinsic, and if the intrinsic has an effect on the stack as described by <code>Sem_int</code> above, then the effect of the expression itself is the same.</li> <li><code>Sem_e_quote</code> says that if the expression is a quote, then a corresponding quoted value is placed on top of the stack.</li> <li><code>Sem_e_comp</code> says that if one expression <code>e1</code> changes the stack from <code>vs1</code> to <code>vs2</code>, and if another expression <code>e2</code> takes this new stack <code>vs2</code> and changes it into <code>vs3</code>, then running the two expressions one after another (i.e. composing them) means starting at stack <code>vs1</code> and ending in stack <code>vs3</code>.</li> </ul> <a href="#texttrue-textfalse-textor-and-proofs"> <h3 id="texttrue-textfalse-textor-and-proofs">\(\text{true}\), \(\text{false}\), \(\text{or}\) and Proofs</h3> </a> <p>Now it&rsquo;s time for some fun! The UCC language specification starts by defining two values: true and false. Why don&rsquo;t we do the same thing?</p> <span class="fold-table"></span> <table> <thead> <tr> <th>UCC Spec</th> <th>Coq encoding</th> </tr> </thead> <tbody> <tr> <td>\(\text{false}\)=\([\text{drop}]\)</td> <td> <div class="highlight-group" data-base-path="" data-file-path="dawn/Dawn.v" data-first-line="41" data-last-line="42"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Dawn.v#L41-L42">Dawn.v</a>, lines 41 through 42</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">41 </span><span class="lnt">42 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Definition</span> <span class="bp">false</span> <span class="o">:</span> <span class="n">expr</span> <span class="o">:=</span> <span class="n">e_quote</span> <span class="o">(</span><span class="n">e_int</span> <span class="n">drop</span><span class="o">).</span> </span></span><span class="line"><span class="cl"><span class="kn">Definition</span> <span class="bp">false</span><span class="o">_</span><span class="n">v</span> <span class="o">:</span> <span class="n">value</span> <span class="o">:=</span> <span class="n">v_quote</span> <span class="o">(</span><span class="n">e_int</span> <span class="n">drop</span><span class="o">).</span></span></span></code></pre></td></tr></table> </div> </div> </div> </td> </tr> <tr> <td>\(\text{true}\)=\([\text{swap} \ \text{drop}]\)</td> <td> <div class="highlight-group" data-base-path="" data-file-path="dawn/Dawn.v" data-first-line="44" data-last-line="45"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Dawn.v#L44-L45">Dawn.v</a>, lines 44 through 45</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">44 </span><span class="lnt">45 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Definition</span> <span class="bp">true</span> <span class="o">:</span> <span class="n">expr</span> <span class="o">:=</span> <span class="n">e_quote</span> <span class="o">(</span><span class="n">e_comp</span> <span class="o">(</span><span class="n">e_int</span> <span class="n">swap</span><span class="o">)</span> <span class="o">(</span><span class="n">e_int</span> <span class="n">drop</span><span class="o">)).</span> </span></span><span class="line"><span class="cl"><span class="kn">Definition</span> <span class="bp">true</span><span class="o">_</span><span class="n">v</span> <span class="o">:</span> <span class="n">value</span> <span class="o">:=</span> <span class="n">v_quote</span> <span class="o">(</span><span class="n">e_comp</span> <span class="o">(</span><span class="n">e_int</span> <span class="n">swap</span><span class="o">)</span> <span class="o">(</span><span class="n">e_int</span> <span class="n">drop</span><span class="o">)).</span></span></span></code></pre></td></tr></table> </div> </div> </div> </td> </tr> </tbody> </table> <p>Let&rsquo;s try prove that these two work as intended.</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/Dawn.v" data-first-line="47" data-last-line="53"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Dawn.v#L47-L53">Dawn.v</a>, lines 47 through 53</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Theorem</span> <span class="bp">false</span><span class="o">_</span><span class="n">correct</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">v</span> <span class="n">v&#39;</span> <span class="o">:</span> <span class="n">value</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> <span class="n">Sem_expr</span> <span class="o">(</span><span class="n">v&#39;</span> <span class="o">::</span> <span class="n">v</span> <span class="o">::</span> <span class="n">vs</span><span class="o">)</span> <span class="o">(</span><span class="n">e_comp</span> <span class="bp">false</span> <span class="o">(</span><span class="n">e_int</span> <span class="k">apply</span><span class="o">))</span> <span class="o">(</span><span class="n">v</span> <span class="o">::</span> <span class="n">vs</span><span class="o">).</span> </span></span><span class="line"><span class="cl"><span class="kn">Proof</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">intros</span> <span class="n">v</span> <span class="n">v&#39;</span> <span class="n">vs</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">eapply</span> <span class="n">Sem_e_comp</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">apply</span> <span class="n">Sem_e_quote</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">apply</span> <span class="n">Sem_e_int</span><span class="o">.</span> <span class="k">apply</span> <span class="n">Sem_apply</span><span class="o">.</span> <span class="k">apply</span> <span class="n">Sem_e_int</span><span class="o">.</span> <span class="k">apply</span> <span class="n">Sem_drop</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Qed</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This is the first real proof in this article. Rather than getting into the technical details, I invite you to take a look at the &ldquo;shape&rdquo; of the proof:</p> <ul> <li>After the initial use of <code>intros</code>, which brings the variables <code>v</code>, <code>v</code>, and <code>vs</code> into scope, we start by applying <code>Sem_e_comp</code>. Intuitively, this makes sense - at the top level, our expression, \(\text{false}\ \text{apply}\), is a composition of two other expressions, \(\text{false}\) and \(\text{apply}\). Because of this, we need to use the rule from our semantics that corresponds to composition.</li> <li>The composition rule requires that we describe the individual effects on the stack of the two constituent expressions (recall that the first expression takes us from the initial stack <code>v1</code> to some intermediate stack <code>v2</code>, and the second expression takes us from that stack <code>v2</code> to the final stack <code>v3</code>). Thus, we have two &ldquo;bullet points&rdquo;: <ul> <li>The first expression, \(\text{false}\), is just a quoted expression. Thus, the rule <code>Sem_e_quote</code> applies, and the contents of the quote are puhsed onto the stack.</li> <li>The second expression, \(\text{apply}\), is an intrinsic, so we need to use the rule <code>Sem_e_int</code>, which handles the intrinsic case. This, in turn, requires that we show the effect of the intrinsic itself; the <code>apply</code> intrinsic evaluates the quoted expression on the stack. The quoted expression contains the body of false, or \(\text{drop}\). This is once again an intrinsic, so we use <code>Sem_e_int</code>; the intrinsic in question is \(\text{drop}\), so the <code>Sem_drop</code> rule takes care of that.</li> </ul> </li> </ul> <p>Following these steps, we arrive at the fact that evaluating <code>false</code> on the stack simply drops the top element, as specified. The proof for \(\text{true}\) is very similar in spirit:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/Dawn.v" data-first-line="55" data-last-line="63"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Dawn.v#L55-L63">Dawn.v</a>, lines 55 through 63</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Theorem</span> <span class="bp">true</span><span class="o">_</span><span class="n">correct</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">v</span> <span class="n">v&#39;</span> <span class="o">:</span> <span class="n">value</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> <span class="n">Sem_expr</span> <span class="o">(</span><span class="n">v&#39;</span> <span class="o">::</span> <span class="n">v</span> <span class="o">::</span> <span class="n">vs</span><span class="o">)</span> <span class="o">(</span><span class="n">e_comp</span> <span class="bp">true</span> <span class="o">(</span><span class="n">e_int</span> <span class="k">apply</span><span class="o">))</span> <span class="o">(</span><span class="n">v&#39;</span> <span class="o">::</span> <span class="n">vs</span><span class="o">).</span> </span></span><span class="line"><span class="cl"><span class="kn">Proof</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">intros</span> <span class="n">v</span> <span class="n">v&#39;</span> <span class="n">vs</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">eapply</span> <span class="n">Sem_e_comp</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">apply</span> <span class="n">Sem_e_quote</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">apply</span> <span class="n">Sem_e_int</span><span class="o">.</span> <span class="k">apply</span> <span class="n">Sem_apply</span><span class="o">.</span> <span class="k">eapply</span> <span class="n">Sem_e_comp</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">*</span> <span class="k">apply</span> <span class="n">Sem_e_int</span><span class="o">.</span> <span class="k">apply</span> <span class="n">Sem_swap</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">*</span> <span class="k">apply</span> <span class="n">Sem_e_int</span><span class="o">.</span> <span class="k">apply</span> <span class="n">Sem_drop</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Qed</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We can also formalize the \(\text{or}\) operator:</p> <span class="fold-table"></span> <table> <thead> <tr> <th>UCC Spec</th> <th>Coq encoding</th> </tr> </thead> <tbody> <tr> <td>\(\text{or}\)=\(\text{clone}\ \text{apply}\)</td> <td> <div class="highlight-group" data-base-path="" data-file-path="dawn/Dawn.v" data-first-line="65" data-last-line="65"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Dawn.v#L65-L65">Dawn.v</a>, line 65</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">65 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Definition</span> <span class="n">or</span> <span class="o">:</span> <span class="n">expr</span> <span class="o">:=</span> <span class="n">e_comp</span> <span class="o">(</span><span class="n">e_int</span> <span class="n">clone</span><span class="o">)</span> <span class="o">(</span><span class="n">e_int</span> <span class="k">apply</span><span class="o">).</span></span></span></code></pre></td></tr></table> </div> </div> </div> </td> </tr> </tbody> </table> <p>We can write two top-level proofs about how this works: the first says that \(\text{or}\), when the first argument is \(\text{false}\), just returns the second argument (this is in agreement with the truth table, since \(\text{false}\) is the identity element of \(\text{or}\)). The proof proceeds much like before:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/Dawn.v" data-first-line="67" data-last-line="73"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Dawn.v#L67-L73">Dawn.v</a>, lines 67 through 73</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">67 </span><span class="lnt">68 </span><span class="lnt">69 </span><span class="lnt">70 </span><span class="lnt">71 </span><span class="lnt">72 </span><span class="lnt">73 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Theorem</span> <span class="n">or_false_v</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">v</span> <span class="o">:</span> <span class="n">value</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> <span class="n">Sem_expr</span> <span class="o">(</span><span class="bp">false</span><span class="o">_</span><span class="n">v</span> <span class="o">::</span> <span class="n">v</span> <span class="o">::</span> <span class="n">vs</span><span class="o">)</span> <span class="n">or</span> <span class="o">(</span><span class="n">v</span> <span class="o">::</span> <span class="n">vs</span><span class="o">).</span> </span></span><span class="line"><span class="cl"><span class="kn">Proof</span> <span class="k">with</span> <span class="k">apply</span> <span class="n">Sem_e_int</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">intros</span> <span class="n">v</span> <span class="n">vs</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">eapply</span> <span class="n">Sem_e_comp</span><span class="o">...</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">apply</span> <span class="n">Sem_clone</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">apply</span> <span class="n">Sem_apply</span><span class="o">...</span> <span class="k">apply</span> <span class="n">Sem_drop</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Qed</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>To shorten the proof a little bit, I used the <code>Proof with</code> construct from Coq, which runs an additional <em>tactic</em> (like <code>apply</code>) whenever <code>...</code> is used. Because of this, in this proof writing <code>apply Sem_apply...</code> is the same as <code>apply Sem_apply. apply Sem_e_int</code>. Since the <code>Sem_e_int</code> rule is used a lot, this makes for a very convenient shorthand.</p> <p>Similarly, we prove that \(\text{or}\) applied to \(\text{true}\) always returns \(\text{true}\).</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/Dawn.v" data-first-line="75" data-last-line="83"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Dawn.v#L75-L83">Dawn.v</a>, lines 75 through 83</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">75 </span><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span><span class="lnt">79 </span><span class="lnt">80 </span><span class="lnt">81 </span><span class="lnt">82 </span><span class="lnt">83 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Theorem</span> <span class="n">or_true</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">v</span> <span class="o">:</span> <span class="n">value</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> <span class="n">Sem_expr</span> <span class="o">(</span><span class="bp">true</span><span class="o">_</span><span class="n">v</span> <span class="o">::</span> <span class="n">v</span> <span class="o">::</span> <span class="n">vs</span><span class="o">)</span> <span class="n">or</span> <span class="o">(</span><span class="bp">true</span><span class="o">_</span><span class="n">v</span> <span class="o">::</span> <span class="n">vs</span><span class="o">).</span> </span></span><span class="line"><span class="cl"><span class="kn">Proof</span> <span class="k">with</span> <span class="k">apply</span> <span class="n">Sem_e_int</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">intros</span> <span class="n">v</span> <span class="n">vs</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">eapply</span> <span class="n">Sem_e_comp</span><span class="o">...</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">apply</span> <span class="n">Sem_clone</span><span class="o">...</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">apply</span> <span class="n">Sem_apply</span><span class="o">.</span> <span class="k">eapply</span> <span class="n">Sem_e_comp</span><span class="o">...</span> </span></span><span class="line"><span class="cl"> <span class="o">*</span> <span class="k">apply</span> <span class="n">Sem_swap</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">*</span> <span class="k">apply</span> <span class="n">Sem_drop</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Qed</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Finally, the specific facts (like \(\text{false}\ \text{or}\ \text{false}\) evaluating to \(\text{false}\)) can be expressed using our two new proofs, <code>or_false_v</code> and <code>or_true</code>.</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/Dawn.v" data-first-line="85" data-last-line="88"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Dawn.v#L85-L88">Dawn.v</a>, lines 85 through 88</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">85 </span><span class="lnt">86 </span><span class="lnt">87 </span><span class="lnt">88 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Definition</span> <span class="n">or_false_false</span> <span class="o">:=</span> <span class="n">or_false_v</span> <span class="bp">false</span><span class="o">_</span><span class="n">v</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Definition</span> <span class="n">or_false_true</span> <span class="o">:=</span> <span class="n">or_false_v</span> <span class="bp">true</span><span class="o">_</span><span class="n">v</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Definition</span> <span class="n">or_true_false</span> <span class="o">:=</span> <span class="n">or_true</span> <span class="bp">false</span><span class="o">_</span><span class="n">v</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Definition</span> <span class="n">or_true_true</span> <span class="o">:=</span> <span class="n">or_true</span> <span class="bp">true</span><span class="o">_</span><span class="n">v</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#derived-expressions"> <h3 id="derived-expressions">Derived Expressions</h3> </a> <a href="#quotes"> <h4 id="quotes">Quotes</h4> </a> <p>The UCC specification defines \(\text{quote}_n\) to make it more convenient to quote multiple terms. For example, \(\text{quote}_2\) composes and quotes the first two values on the stack. This is defined in terms of other UCC expressions as follows:</p> $$ \text{quote}_n = \text{quote}_{n-1}\ \text{swap}\ \text{quote}\ \text{swap}\ \text{compose} $$ <p>We can write this in Coq as follows:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/Dawn.v" data-first-line="90" data-last-line="94"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Dawn.v#L90-L94">Dawn.v</a>, lines 90 through 94</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">90 </span><span class="lnt">91 </span><span class="lnt">92 </span><span class="lnt">93 </span><span class="lnt">94 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Fixpoint</span> <span class="n">quote_n</span> <span class="o">(</span><span class="n">n</span> <span class="o">:</span> <span class="kt">nat</span><span class="o">)</span> <span class="o">:=</span> </span></span><span class="line"><span class="cl"> <span class="k">match</span> <span class="n">n</span> <span class="k">with</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">O</span> <span class="o">=&gt;</span> <span class="n">e_int</span> <span class="n">quote</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">S</span> <span class="n">n&#39;</span> <span class="o">=&gt;</span> <span class="n">e_compose</span> <span class="o">(</span><span class="n">quote_n</span> <span class="n">n&#39;</span><span class="o">)</span> <span class="o">(</span><span class="n">e_int</span> <span class="n">swap</span> <span class="o">::</span> <span class="n">e_int</span> <span class="n">quote</span> <span class="o">::</span> <span class="n">e_int</span> <span class="n">swap</span> <span class="o">::</span> <span class="n">e_int</span> <span class="n">compose</span> <span class="o">::</span> <span class="n">nil</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This definition diverges slightly from the one given in the UCC specification; particularly, UCC&rsquo;s spec mentions that \(\text{quote}_n\) is only defined for \(n \geq 1\).However, this means that in our code, we&rsquo;d have to somehow handle the error that would arise if the term \(\text{quote}_0\) is used. Instead, I defined <code>quote_n n</code> to simply mean \(\text{quote}_{n+1}\); thus, in Coq, no matter what <code>n</code> we use, we will have a valid expression, since <code>quote_n 0</code> will simply correspond to \(\text{quote}_1 = \text{quote}\).</p> <p>We can now attempt to prove that this definition is correct by ensuring that the examples given in the specification are valid. We may thus write,</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/Dawn.v" data-first-line="96" data-last-line="106"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Dawn.v#L96-L106">Dawn.v</a>, lines 96 through 106</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 96 </span><span class="lnt"> 97 </span><span class="lnt"> 98 </span><span class="lnt"> 99 </span><span class="lnt">100 </span><span class="lnt">101 </span><span class="lnt">102 </span><span class="lnt">103 </span><span class="lnt">104 </span><span class="lnt">105 </span><span class="lnt">106 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Theorem</span> <span class="n">quote_2_correct</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">v1</span> <span class="n">v2</span> <span class="o">:</span> <span class="n">value</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> </span></span><span class="line"><span class="cl"> <span class="n">Sem_expr</span> <span class="o">(</span><span class="n">v2</span> <span class="o">::</span> <span class="n">v1</span> <span class="o">::</span> <span class="n">vs</span><span class="o">)</span> <span class="o">(</span><span class="n">quote_n</span> <span class="n">1</span><span class="o">)</span> <span class="o">(</span><span class="n">v_quote</span> <span class="o">(</span><span class="n">e_comp</span> <span class="o">(</span><span class="n">projT1</span> <span class="n">v1</span><span class="o">)</span> <span class="o">(</span><span class="n">projT1</span> <span class="n">v2</span><span class="o">))</span> <span class="o">::</span> <span class="n">vs</span><span class="o">).</span> </span></span><span class="line"><span class="cl"><span class="kn">Proof</span> <span class="k">with</span> <span class="k">apply</span> <span class="n">Sem_e_int</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">intros</span> <span class="n">v1</span> <span class="n">v2</span> <span class="n">vs</span><span class="o">.</span> <span class="k">simpl</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="kr">repeat</span> <span class="o">(</span><span class="k">eapply</span> <span class="n">Sem_e_comp</span><span class="o">)...</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">apply</span> <span class="n">Sem_quote</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">apply</span> <span class="n">Sem_swap</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">apply</span> <span class="n">Sem_quote</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">apply</span> <span class="n">Sem_swap</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">apply</span> <span class="n">Sem_compose</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Qed</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We used a new tactic here, <code>repeat</code>, but overall, the structure of the proof is pretty straightforward: the definition of <code>quote_n</code> consists of many intrinsics, and we apply the corresponding rules one-by-one until we arrive at the final stack. Writing this proof was kind of boring, since I just had to see which intrinsic is being used in each step, and then write a line of <code>apply</code> code to handle that intrinsic. This gets worse for \(\text{quote}_3\):</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/Dawn.v" data-first-line="108" data-last-line="122"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Dawn.v#L108-L122">Dawn.v</a>, lines 108 through 122</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">108 </span><span class="lnt">109 </span><span class="lnt">110 </span><span class="lnt">111 </span><span class="lnt">112 </span><span class="lnt">113 </span><span class="lnt">114 </span><span class="lnt">115 </span><span class="lnt">116 </span><span class="lnt">117 </span><span class="lnt">118 </span><span class="lnt">119 </span><span class="lnt">120 </span><span class="lnt">121 </span><span class="lnt">122 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Theorem</span> <span class="n">quote_3_correct</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">v1</span> <span class="n">v2</span> <span class="n">v3</span> <span class="o">:</span> <span class="n">value</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> </span></span><span class="line"><span class="cl"> <span class="n">Sem_expr</span> <span class="o">(</span><span class="n">v3</span> <span class="o">::</span> <span class="n">v2</span> <span class="o">::</span> <span class="n">v1</span> <span class="o">::</span> <span class="n">vs</span><span class="o">)</span> <span class="o">(</span><span class="n">quote_n</span> <span class="n">2</span><span class="o">)</span> <span class="o">(</span><span class="n">v_quote</span> <span class="o">(</span><span class="n">e_comp</span> <span class="o">(</span><span class="n">projT1</span> <span class="n">v1</span><span class="o">)</span> <span class="o">(</span><span class="n">e_comp</span> <span class="o">(</span><span class="n">projT1</span> <span class="n">v2</span><span class="o">)</span> <span class="o">(</span><span class="n">projT1</span> <span class="n">v3</span><span class="o">)))</span> <span class="o">::</span> <span class="n">vs</span><span class="o">).</span> </span></span><span class="line"><span class="cl"><span class="kn">Proof</span> <span class="k">with</span> <span class="k">apply</span> <span class="n">Sem_e_int</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">intros</span> <span class="n">v1</span> <span class="n">v2</span> <span class="n">v3</span> <span class="n">vs</span><span class="o">.</span> <span class="k">simpl</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="kr">repeat</span> <span class="o">(</span><span class="k">eapply</span> <span class="n">Sem_e_comp</span><span class="o">)...</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">apply</span> <span class="n">Sem_quote</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">apply</span> <span class="n">Sem_swap</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">apply</span> <span class="n">Sem_quote</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">apply</span> <span class="n">Sem_swap</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">apply</span> <span class="n">Sem_compose</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">apply</span> <span class="n">Sem_swap</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">apply</span> <span class="n">Sem_quote</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">apply</span> <span class="n">Sem_swap</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">apply</span> <span class="n">Sem_compose</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Qed</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>It&rsquo;s so long! Instead, I decided to try out Coq&rsquo;s <code>Ltac2</code> mechanism to teach Coq how to write proofs like this itself. Here&rsquo;s what I came up with:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/Dawn.v" data-first-line="124" data-last-line="136"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Dawn.v#L124-L136">Dawn.v</a>, lines 124 through 136</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">124 </span><span class="lnt">125 </span><span class="lnt">126 </span><span class="lnt">127 </span><span class="lnt">128 </span><span class="lnt">129 </span><span class="lnt">130 </span><span class="lnt">131 </span><span class="lnt">132 </span><span class="lnt">133 </span><span class="lnt">134 </span><span class="lnt">135 </span><span class="lnt">136 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="n">Ltac2</span> <span class="n">rec</span> <span class="n">solve_basic</span> <span class="bp">()</span> <span class="o">:=</span> <span class="n">Control</span><span class="o">.</span><span class="n">enter</span> <span class="o">(</span><span class="k">fun</span> <span class="o">_</span> <span class="o">=&gt;</span> </span></span><span class="line"><span class="cl"> <span class="k">match</span><span class="o">!</span> <span class="n">goal</span> <span class="k">with</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="o">[|-</span> <span class="n">Sem_int</span> <span class="o">?</span><span class="n">vs1</span> <span class="n">swap</span> <span class="o">?</span><span class="n">vs2</span><span class="o">]</span> <span class="o">=&gt;</span> <span class="k">apply</span> <span class="n">Sem_swap</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="o">[|-</span> <span class="n">Sem_int</span> <span class="o">?</span><span class="n">vs1</span> <span class="n">clone</span> <span class="o">?</span><span class="n">vs2</span><span class="o">]</span> <span class="o">=&gt;</span> <span class="k">apply</span> <span class="n">Sem_clone</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="o">[|-</span> <span class="n">Sem_int</span> <span class="o">?</span><span class="n">vs1</span> <span class="n">drop</span> <span class="o">?</span><span class="n">vs2</span><span class="o">]</span> <span class="o">=&gt;</span> <span class="k">apply</span> <span class="n">Sem_drop</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="o">[|-</span> <span class="n">Sem_int</span> <span class="o">?</span><span class="n">vs1</span> <span class="n">quote</span> <span class="o">?</span><span class="n">vs2</span><span class="o">]</span> <span class="o">=&gt;</span> <span class="k">apply</span> <span class="n">Sem_quote</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="o">[|-</span> <span class="n">Sem_int</span> <span class="o">?</span><span class="n">vs1</span> <span class="n">compose</span> <span class="o">?</span><span class="n">vs2</span><span class="o">]</span> <span class="o">=&gt;</span> <span class="k">apply</span> <span class="n">Sem_compose</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="o">[|-</span> <span class="n">Sem_int</span> <span class="o">?</span><span class="n">vs1</span> <span class="k">apply</span> <span class="o">?</span><span class="n">vs2</span><span class="o">]</span> <span class="o">=&gt;</span> <span class="k">apply</span> <span class="n">Sem_apply</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="o">[|-</span> <span class="n">Sem_expr</span> <span class="o">?</span><span class="n">vs1</span> <span class="o">(</span><span class="n">e_comp</span> <span class="o">?</span><span class="n">e1</span> <span class="o">?</span><span class="n">e2</span><span class="o">)</span> <span class="o">?</span><span class="n">vs2</span><span class="o">]</span> <span class="o">=&gt;</span> <span class="k">eapply</span> <span class="n">Sem_e_comp</span><span class="o">;</span> <span class="n">solve_basic</span> <span class="bp">()</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="o">[|-</span> <span class="n">Sem_expr</span> <span class="o">?</span><span class="n">vs1</span> <span class="o">(</span><span class="n">e_int</span> <span class="o">?</span><span class="n">e</span><span class="o">)</span> <span class="o">?</span><span class="n">vs2</span><span class="o">]</span> <span class="o">=&gt;</span> <span class="k">apply</span> <span class="n">Sem_e_int</span><span class="o">;</span> <span class="n">solve_basic</span> <span class="bp">()</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="o">[|-</span> <span class="n">Sem_expr</span> <span class="o">?</span><span class="n">vs1</span> <span class="o">(</span><span class="n">e_quote</span> <span class="o">?</span><span class="n">e</span><span class="o">)</span> <span class="o">?</span><span class="n">vs2</span><span class="o">]</span> <span class="o">=&gt;</span> <span class="k">apply</span> <span class="n">Sem_e_quote</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="o">[_</span> <span class="o">:</span> <span class="o">_</span> <span class="o">|-</span> <span class="o">_]</span> <span class="o">=&gt;</span> <span class="bp">()</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span><span class="o">).</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>You don&rsquo;t have to understand the details, but in brief, this checks what kind of proof we&rsquo;re asking Coq to do (for instance, if we&rsquo;re trying to prove that a \(\text{swap}\) instruction has a particular effect), and tries to apply a corresponding semantic rule. Thus, it will try <code>Sem_swap</code> if the expression is \(\text{swap}\), <code>Sem_clone</code> if the expression is \(\text{clone}\), and so on. Then, the two proofs become:</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/Dawn.v" data-first-line="138" data-last-line="144"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Dawn.v#L138-L144">Dawn.v</a>, lines 138 through 144</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">138 </span><span class="lnt">139 </span><span class="lnt">140 </span><span class="lnt">141 </span><span class="lnt">142 </span><span class="lnt">143 </span><span class="lnt">144 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Theorem</span> <span class="n">quote_2_correct&#39;</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">v1</span> <span class="n">v2</span> <span class="o">:</span> <span class="n">value</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> </span></span><span class="line"><span class="cl"> <span class="n">Sem_expr</span> <span class="o">(</span><span class="n">v2</span> <span class="o">::</span> <span class="n">v1</span> <span class="o">::</span> <span class="n">vs</span><span class="o">)</span> <span class="o">(</span><span class="n">quote_n</span> <span class="n">1</span><span class="o">)</span> <span class="o">(</span><span class="n">v_quote</span> <span class="o">(</span><span class="n">e_comp</span> <span class="o">(</span><span class="n">projT1</span> <span class="n">v1</span><span class="o">)</span> <span class="o">(</span><span class="n">projT1</span> <span class="n">v2</span><span class="o">))</span> <span class="o">::</span> <span class="n">vs</span><span class="o">).</span> </span></span><span class="line"><span class="cl"><span class="kn">Proof</span><span class="o">.</span> <span class="k">intros</span><span class="o">.</span> <span class="k">simpl</span><span class="o">.</span> <span class="n">solve_basic</span> <span class="bp">()</span><span class="o">.</span> <span class="kn">Qed</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kn">Theorem</span> <span class="n">quote_3_correct&#39;</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">v1</span> <span class="n">v2</span> <span class="n">v3</span> <span class="o">:</span> <span class="n">value</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> </span></span><span class="line"><span class="cl"> <span class="n">Sem_expr</span> <span class="o">(</span><span class="n">v3</span> <span class="o">::</span> <span class="n">v2</span> <span class="o">::</span> <span class="n">v1</span> <span class="o">::</span> <span class="n">vs</span><span class="o">)</span> <span class="o">(</span><span class="n">quote_n</span> <span class="n">2</span><span class="o">)</span> <span class="o">(</span><span class="n">v_quote</span> <span class="o">(</span><span class="n">e_comp</span> <span class="o">(</span><span class="n">projT1</span> <span class="n">v1</span><span class="o">)</span> <span class="o">(</span><span class="n">e_comp</span> <span class="o">(</span><span class="n">projT1</span> <span class="n">v2</span><span class="o">)</span> <span class="o">(</span><span class="n">projT1</span> <span class="n">v3</span><span class="o">)))</span> <span class="o">::</span> <span class="n">vs</span><span class="o">).</span> </span></span><span class="line"><span class="cl"><span class="kn">Proof</span><span class="o">.</span> <span class="k">intros</span><span class="o">.</span> <span class="k">simpl</span><span class="o">.</span> <span class="n">solve_basic</span> <span class="bp">()</span><span class="o">.</span> <span class="kn">Qed</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#rotations"> <h4 id="rotations">Rotations</h4> </a> <p>There&rsquo;s a little trick to formalizing rotations. Values have an important property: when a value is run against a stack, all it does is place itself on a stack. We can state this as follows:</p> $$ \langle V \rangle\ v = \langle V\ v \rangle $$ <p>Or, in Coq,</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/Dawn.v" data-first-line="148" data-last-line="149"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Dawn.v#L148-L149">Dawn.v</a>, lines 148 through 149</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">148 </span><span class="lnt">149 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Lemma</span> <span class="n">eval_value</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">v</span> <span class="o">:</span> <span class="n">value</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> </span></span><span class="line"><span class="cl"> <span class="n">Sem_expr</span> <span class="n">vs</span> <span class="o">(</span><span class="n">projT1</span> <span class="n">v</span><span class="o">)</span> <span class="o">(</span><span class="n">v</span> <span class="o">::</span> <span class="n">vs</span><span class="o">).</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This is the trick to how \(\text{rotate}_n\) works: it creates a quote of \(n\) reordered and composed values on the stack, and then evaluates that quote. Since evaluating each value just places it on the stack, these values end up back on the stack, in the same order that they were in the quote. When writing the proof, <code>solve_basic ()</code> gets us almost all the way to the end (evaluating a list of values against a stack). Then, we simply apply the composition rule over and over, following it up with <code>eval_value</code> to prove that the each value is just being placed back on the stack.</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/Dawn.v" data-first-line="156" data-last-line="168"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Dawn.v#L156-L168">Dawn.v</a>, lines 156 through 168</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">156 </span><span class="lnt">157 </span><span class="lnt">158 </span><span class="lnt">159 </span><span class="lnt">160 </span><span class="lnt">161 </span><span class="lnt">162 </span><span class="lnt">163 </span><span class="lnt">164 </span><span class="lnt">165 </span><span class="lnt">166 </span><span class="lnt">167 </span><span class="lnt">168 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Theorem</span> <span class="n">rotate_3_correct</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">v1</span> <span class="n">v2</span> <span class="n">v3</span> <span class="o">:</span> <span class="n">value</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> </span></span><span class="line"><span class="cl"> <span class="n">Sem_expr</span> <span class="o">(</span><span class="n">v3</span> <span class="o">::</span> <span class="n">v2</span> <span class="o">::</span> <span class="n">v1</span> <span class="o">::</span> <span class="n">vs</span><span class="o">)</span> <span class="o">(</span><span class="n">rotate_n</span> <span class="n">1</span><span class="o">)</span> <span class="o">(</span><span class="n">v1</span> <span class="o">::</span> <span class="n">v3</span> <span class="o">::</span> <span class="n">v2</span> <span class="o">::</span> <span class="n">vs</span><span class="o">).</span> </span></span><span class="line"><span class="cl"><span class="kn">Proof</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">intros</span><span class="o">.</span> <span class="k">unfold</span> <span class="n">rotate_n</span><span class="o">.</span> <span class="k">simpl</span><span class="o">.</span> <span class="n">solve_basic</span> <span class="bp">()</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="kr">repeat</span> <span class="o">(</span><span class="k">eapply</span> <span class="n">Sem_e_comp</span><span class="o">);</span> <span class="k">apply</span> <span class="n">eval_value</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Qed</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kn">Theorem</span> <span class="n">rotate_4_correct</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">v1</span> <span class="n">v2</span> <span class="n">v3</span> <span class="n">v4</span> <span class="o">:</span> <span class="n">value</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> </span></span><span class="line"><span class="cl"> <span class="n">Sem_expr</span> <span class="o">(</span><span class="n">v4</span> <span class="o">::</span> <span class="n">v3</span> <span class="o">::</span> <span class="n">v2</span> <span class="o">::</span> <span class="n">v1</span> <span class="o">::</span> <span class="n">vs</span><span class="o">)</span> <span class="o">(</span><span class="n">rotate_n</span> <span class="n">2</span><span class="o">)</span> <span class="o">(</span><span class="n">v1</span> <span class="o">::</span> <span class="n">v4</span> <span class="o">::</span> <span class="n">v3</span> <span class="o">::</span> <span class="n">v2</span> <span class="o">::</span> <span class="n">vs</span><span class="o">).</span> </span></span><span class="line"><span class="cl"><span class="kn">Proof</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">intros</span><span class="o">.</span> <span class="k">unfold</span> <span class="n">rotate_n</span><span class="o">.</span> <span class="k">simpl</span><span class="o">.</span> <span class="n">solve_basic</span> <span class="bp">()</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="kr">repeat</span> <span class="o">(</span><span class="k">eapply</span> <span class="n">Sem_e_comp</span><span class="o">);</span> <span class="k">apply</span> <span class="n">eval_value</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Qed</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#e_comp-is-associative"> <h3 id="e_comp-is-associative"><code>e_comp</code> is Associative</h3> </a> <p>When composing three expressions, which way of inserting parentheses is correct? Is it \((e_1\ e_2)\ e_3\)? Or is it \(e_1\ (e_2\ e_3)\)? Well, both! Expression composition is associative, which means that the order of the parentheses doesn&rsquo;t matter. We state this in the following theorem, which says that the two ways of writing the composition, if they evaluate to anything, evaluate to the same thing.</p> <div class="highlight-group" data-base-path="" data-file-path="dawn/Dawn.v" data-first-line="170" data-last-line="171"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/dawn/Dawn.v#L170-L171">Dawn.v</a>, lines 170 through 171</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">170 </span><span class="lnt">171 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Theorem</span> <span class="n">e_comp_assoc</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">e1</span> <span class="n">e2</span> <span class="n">e3</span> <span class="o">:</span> <span class="n">expr</span><span class="o">)</span> <span class="o">(</span><span class="n">vs</span> <span class="n">vs&#39;</span> <span class="o">:</span> <span class="n">value_stack</span><span class="o">),</span> </span></span><span class="line"><span class="cl"> <span class="n">Sem_expr</span> <span class="n">vs</span> <span class="o">(</span><span class="n">e_comp</span> <span class="n">e1</span> <span class="o">(</span><span class="n">e_comp</span> <span class="n">e2</span> <span class="n">e3</span><span class="o">))</span> <span class="n">vs&#39;</span> <span class="o">&lt;-&gt;</span> <span class="n">Sem_expr</span> <span class="n">vs</span> <span class="o">(</span><span class="n">e_comp</span> <span class="o">(</span><span class="n">e_comp</span> <span class="n">e1</span> <span class="n">e2</span><span class="o">)</span> <span class="n">e3</span><span class="o">)</span> <span class="n">vs&#39;</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#conclusion"> <h3 id="conclusion">Conclusion</h3> </a> <p>That&rsquo;s all I&rsquo;ve got in me for today. However, we got pretty far! The UCC specification says:</p> <blockquote> <p>One of my long term goals for UCC is to democratize formal software verification in order to make it much more feasible and realistic to write perfect software.</p> </blockquote> <p>I think that UCC is definitely getting there: formally defining the semantics outlined on the page was quite straightforward. We can now have complete confidence in the behavior of \(\text{true}\), \(\text{false}\), \(\text{or}\), \(\text{quote}_n\) and \(\text{rotate}_n\). The proof of associativity is also enough to possibly argue for simplifying the core calculus&rsquo; syntax even more. All of this we got from an official source, with only a little bit of tweaking to get from the written description of the language to code! I&rsquo;m looking forward to reading the next post about the <em>multistack</em> concatenative calculus.</p> Type-Safe Event Emitter in TypeScript https://danilafe.com/blog/typescript_typesafe_events/ Sat, 04 Sep 2021 17:18:49 -0700 https://danilafe.com/blog/typescript_typesafe_events/ <p>I&rsquo;ve been playing around with TypeScript recently, and enjoying it too. Nearly all of my compile-time type safety desires have been accomodated by the language, and in a rather intuitive and clean way. Today, I&rsquo;m going to share a little trick I&rsquo;ve discovered which allows me to do something that I suspect would normally require <a href="https://en.wikipedia.org/wiki/Dependent_type"class="external-link">dependent types<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>.</p> <a href="#the-problem"> <h3 id="the-problem">The Problem</h3> </a> <p>Suppose you want to write a class that emits events. Clients can then install handlers, functions that are notified whenever an event is emitted. Easy enough; in JavaScript, this would look something like the following:</p> <div class="highlight-group" data-base-path="" data-file-path="typescript-emitter/js1.js" data-first-line="1" data-last-line="17"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typescript-emitter/js1.js#L1-L17">js1.js</a>, lines 1 through 17</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kr">class</span> <span class="nx">EventEmitter</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">constructor</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">handlers</span> <span class="o">=</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="nx">emit</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">handlers</span><span class="p">[</span><span class="nx">event</span><span class="p">]</span><span class="o">?</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">h</span> <span class="p">=&gt;</span> <span class="nx">h</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="nx">addHandler</span><span class="p">(</span><span class="nx">event</span><span class="p">,</span> <span class="nx">handler</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">handlers</span><span class="p">[</span><span class="nx">event</span><span class="p">])</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">handlers</span><span class="p">[</span><span class="nx">event</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="nx">handler</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">handlers</span><span class="p">[</span><span class="nx">event</span><span class="p">].</span><span class="nx">push</span><span class="p">(</span><span class="nx">handler</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We can even write some code to test that this works (just to ease my nerves):</p> <div class="highlight-group" data-base-path="" data-file-path="typescript-emitter/js1.js" data-first-line="19" data-last-line="23"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typescript-emitter/js1.js#L19-L23">js1.js</a>, lines 19 through 23</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">emitter</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">EventEmitter</span><span class="p">();</span> </span></span><span class="line"><span class="cl"><span class="nx">emitter</span><span class="p">.</span><span class="nx">addHandler</span><span class="p">(</span><span class="s2">&#34;start&#34;</span><span class="p">,</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Started!&#34;</span><span class="p">));</span> </span></span><span class="line"><span class="cl"><span class="nx">emitter</span><span class="p">.</span><span class="nx">addHandler</span><span class="p">(</span><span class="s2">&#34;end&#34;</span><span class="p">,</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Ended!&#34;</span><span class="p">));</span> </span></span><span class="line"><span class="cl"><span class="nx">emitter</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="s2">&#34;end&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="nx">emitter</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="s2">&#34;start&#34;</span><span class="p">);</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>As expected, we get:</p> <pre tabindex="0"><code>Ended! Started! </code></pre><p>As you probably guessed, we&rsquo;re going to build on this problem a little bit. In certain situations, you don&rsquo;t just care that an event occured; you also care about additional event data. For instance, when a number changes, you may want to know the number&rsquo;s new value. In JavaScript, this is a trivial change:</p> <div class="highlight-group" data-base-path="" data-file-path="typescript-emitter/js2.js" data-first-line="1" data-last-line="17"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typescript-emitter/js2.js#L1-L17">js2.js</a>, lines 1 through 17</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="hl"><span class="lnt"> 6 </span></span><span class="hl"><span class="lnt"> 7 </span></span><span class="hl"><span class="lnt"> 8 </span></span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kr">class</span> <span class="nx">EventEmitter</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">constructor</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">handlers</span> <span class="o">=</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line hl"><span class="cl"> <span class="nx">emit</span><span class="p">(</span><span class="nx">event</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line hl"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">handlers</span><span class="p">[</span><span class="nx">event</span><span class="p">]</span><span class="o">?</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">h</span> <span class="p">=&gt;</span> <span class="nx">h</span><span class="p">(</span><span class="nx">value</span><span class="p">));</span> </span></span><span class="line hl"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="nx">addHandler</span><span class="p">(</span><span class="nx">event</span><span class="p">,</span> <span class="nx">handler</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">handlers</span><span class="p">[</span><span class="nx">event</span><span class="p">])</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">handlers</span><span class="p">[</span><span class="nx">event</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="nx">handler</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">handlers</span><span class="p">[</span><span class="nx">event</span><span class="p">].</span><span class="nx">push</span><span class="p">(</span><span class="nx">handler</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>That&rsquo;s literally it. Once again, let&rsquo;s ensure that this works by sending two new events: <code>stringChange</code> and <code>numberChange</code>.</p> <div class="highlight-group" data-base-path="" data-file-path="typescript-emitter/js2.js" data-first-line="19" data-last-line="23"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typescript-emitter/js2.js#L19-L23">js2.js</a>, lines 19 through 23</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">emitter</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">EventEmitter</span><span class="p">();</span> </span></span><span class="line"><span class="cl"><span class="nx">emitter</span><span class="p">.</span><span class="nx">addHandler</span><span class="p">(</span><span class="s2">&#34;numberChange&#34;</span><span class="p">,</span> <span class="nx">n</span> <span class="p">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;New number value is: &#34;</span><span class="p">,</span> <span class="nx">n</span><span class="p">));</span> </span></span><span class="line"><span class="cl"><span class="nx">emitter</span><span class="p">.</span><span class="nx">addHandler</span><span class="p">(</span><span class="s2">&#34;stringChange&#34;</span><span class="p">,</span> <span class="nx">s</span> <span class="p">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;New string value is: &#34;</span><span class="p">,</span> <span class="nx">s</span><span class="p">));</span> </span></span><span class="line"><span class="cl"><span class="nx">emitter</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="s2">&#34;numberChange&#34;</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="nx">emitter</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="s2">&#34;stringChange&#34;</span><span class="p">,</span> <span class="s2">&#34;3&#34;</span><span class="p">);</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The result of this code is once again unsurprising:</p> <pre tabindex="0"><code>New number value is: 1 New string value is: 3 </code></pre><p>But now, how would one go about encoding this in TypeScript? In particular, what is the type of a handler? We could, of course, give each handler the type <code>(value: any) =&gt; void</code>. This, however, makes handlers unsafe. We could very easily write:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-TypeScript" data-lang="TypeScript"><span class="line"><span class="cl"><span class="nx">emitter</span><span class="p">.</span><span class="nx">addHandler</span><span class="p">(</span><span class="s2">&#34;numberChanged&#34;</span><span class="p">,</span> <span class="p">(</span><span class="nx">value</span>: <span class="kt">string</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;String length is&#34;</span><span class="p">,</span> <span class="nx">value</span><span class="p">.</span><span class="nx">length</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">});</span> </span></span><span class="line"><span class="cl"><span class="nx">emitted</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="s2">&#34;numberChanged&#34;</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span> </span></span></code></pre></div><p>Which would print out:</p> <pre tabindex="0"><code>String length is undefined </code></pre><p>No, I don&rsquo;t like this. TypeScript is supposed to be all about adding type safety to our code, and this is not at all type safe. We could do all sorts of weird things! There is a way, however, to make this use case work.</p> <a href="#the-solution"> <h3 id="the-solution">The Solution</h3> </a> <p>Let me show you what I came up with:</p> <div class="highlight-group" data-base-path="" data-file-path="typescript-emitter/ts.ts" data-first-line="1" data-last-line="19"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typescript-emitter/ts.ts#L1-L19">ts.ts</a>, lines 1 through 19</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="hl"><span class="lnt"> 1 </span></span><span class="hl"><span class="lnt"> 2 </span></span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="hl"><span class="lnt"> 8 </span></span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="hl"><span class="lnt">12 </span></span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-TypeScript" data-lang="TypeScript"><span class="line hl"><span class="cl"><span class="kr">class</span> <span class="nx">EventEmitter</span><span class="p">&lt;</span><span class="nt">T</span><span class="p">&gt;</span> <span class="p">{</span> </span></span><span class="line hl"><span class="cl"> <span class="kr">private</span> <span class="nx">handlers</span><span class="o">:</span> <span class="p">{</span> <span class="p">[</span><span class="nx">eventName</span> <span class="k">in</span> <span class="k">keyof</span> <span class="nx">T</span><span class="p">]</span><span class="o">?:</span> <span class="p">((</span><span class="nx">value</span>: <span class="kt">T</span><span class="p">[</span><span class="nx">eventName</span><span class="p">])</span> <span class="o">=&gt;</span> <span class="k">void</span><span class="p">)[]</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kr">constructor</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">handlers</span> <span class="o">=</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line hl"><span class="cl"> <span class="nx">emit</span><span class="p">&lt;</span><span class="nt">K</span> <span class="na">extends</span> <span class="na">keyof</span> <span class="na">T</span><span class="p">&gt;(</span><span class="nx">event</span>: <span class="kt">K</span><span class="p">,</span> <span class="nx">value</span>: <span class="kt">T</span><span class="p">[</span><span class="nx">K</span><span class="p">])</span><span class="o">:</span> <span class="k">void</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">handlers</span><span class="p">[</span><span class="nx">event</span><span class="p">]</span><span class="o">?</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">h</span> <span class="o">=&gt;</span> <span class="nx">h</span><span class="p">(</span><span class="nx">value</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line hl"><span class="cl"> <span class="nx">addHandler</span><span class="p">&lt;</span><span class="nt">K</span> <span class="na">extends</span> <span class="na">keyof</span> <span class="na">T</span><span class="p">&gt;(</span><span class="nx">event</span>: <span class="kt">K</span><span class="p">,</span> <span class="nx">handler</span><span class="o">:</span> <span class="p">(</span><span class="nx">value</span>: <span class="kt">T</span><span class="p">[</span><span class="nx">K</span><span class="p">])</span> <span class="o">=&gt;</span> <span class="k">void</span><span class="p">)</span><span class="o">:</span> <span class="k">void</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">handlers</span><span class="p">[</span><span class="nx">event</span><span class="p">])</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">handlers</span><span class="p">[</span><span class="nx">event</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="nx">handler</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="p">.</span><span class="nx">handlers</span><span class="p">[</span><span class="nx">event</span><span class="p">].</span><span class="nx">push</span><span class="p">(</span><span class="nx">handler</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The important changes are on lines 1, 2, 8, and 12 (highlighted in the above code block). Let&rsquo;s go through each one of them step-by-step.</p> <ul> <li><strong>Line 1</strong>: Parameterize the <code>EventEmitter</code> by some type <code>T</code>. We will use this type <code>T</code> to specify the exact kind of events that our <code>EventEmitter</code> will be able to emit and handle. Specifically, this type will be in the form <code>{ event: EventValueType }</code>. For example, for a <code>mouseClick</code> event, we may write <code>{ mouseClick: { x: number, y: number }}</code>.</li> <li><strong>Line 2</strong>: Add a proper type to <code>handlers</code>. This requires several ingredients of its own: <ul> <li>We use <a href="https://www.typescriptlang.org/docs/handbook/2/objects.html#index-signatures"class="external-link">index signatures<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> to limit the possible names to which handlers can be assigned. We limit these names to the keys of our type <code>T</code>; in the preceding example, <code>keyof T</code> would be <code>&quot;mouseClick&quot;</code>.</li> <li>We also limit the values: <code>T[eventName]</code> retrieves the type of the value associated with key <code>eventName</code>. In the mouse example, this type would be <code>{ x: number, y: number }</code>. We require that a key can only be associated with an array of functions to void, each of which accepts <code>T[K]</code> as first argument. This is precisely what we want; for example, <code>mouseClick</code> would map to an array of functions that accept the mouse click location.</li> </ul> </li> <li><strong>Line 8</strong>: We restrict the type of <code>emit</code> to only accept values that correspond to the keys of the type <code>T</code>. We can&rsquo;t simply write <code>event: keyof T</code>, because this would not give us enough information to retrieve the type of <code>value</code>: if <code>event</code> is just <code>keyof T</code>, then <code>value</code> can be any of the values of <code>T</code>. Instead, we use generics; this way, when the function is called with <code>&quot;mouseClick&quot;</code>, the type of <code>K</code> is inferred to also be <code>&quot;mouseClick&quot;</code>, which gives TypeScript enough information to narrow the type of <code>value</code>.</li> <li><strong>Line 12</strong>: We use the exact same trick here as we did on line 8.</li> </ul> <p>Let&rsquo;s give this a spin with our <code>numberChange</code>/<code>stringChange</code> example from earlier:</p> <div class="highlight-group" data-base-path="" data-file-path="typescript-emitter/ts.ts" data-first-line="21" data-last-line="27"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typescript-emitter/ts.ts#L21-L27">ts.ts</a>, lines 21 through 27</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-TypeScript" data-lang="TypeScript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">emitter</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">EventEmitter</span><span class="o">&lt;</span><span class="p">{</span> <span class="nx">numberChange</span>: <span class="kt">number</span><span class="p">,</span> <span class="nx">stringChange</span>: <span class="kt">string</span> <span class="p">}</span><span class="o">&gt;</span><span class="p">();</span> </span></span><span class="line"><span class="cl"><span class="nx">emitter</span><span class="p">.</span><span class="nx">addHandler</span><span class="p">(</span><span class="s2">&#34;numberChange&#34;</span><span class="p">,</span> <span class="nx">n</span> <span class="o">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;New number value is: &#34;</span><span class="p">,</span> <span class="nx">n</span><span class="p">));</span> </span></span><span class="line"><span class="cl"><span class="nx">emitter</span><span class="p">.</span><span class="nx">addHandler</span><span class="p">(</span><span class="s2">&#34;stringChange&#34;</span><span class="p">,</span> <span class="nx">s</span> <span class="o">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;New string value is: &#34;</span><span class="p">,</span> <span class="nx">s</span><span class="p">));</span> </span></span><span class="line"><span class="cl"><span class="nx">emitter</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="s2">&#34;numberChange&#34;</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="nx">emitter</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="s2">&#34;stringChange&#34;</span><span class="p">,</span> <span class="s2">&#34;3&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="nx">emitter</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="s2">&#34;numberChange&#34;</span><span class="p">,</span> <span class="s2">&#34;1&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="nx">emitter</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="s2">&#34;stringChange&#34;</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The function calls on lines 24 and 25 are correct, but the subsequent two (on lines 26 and 27) are not, as they attempt to emit the <em>opposite</em> type of the one they&rsquo;re supposed to. And indeed, TypeScript complains about only these two lines:</p> <pre tabindex="0"><code>code/typescript-emitter/ts.ts:26:30 - error TS2345: Argument of type &#39;string&#39; is not assignable to parameter of type &#39;number&#39;. 26 emitter.emit(&#34;numberChange&#34;, &#34;1&#34;); ~~~ code/typescript-emitter/ts.ts:27:30 - error TS2345: Argument of type &#39;number&#39; is not assignable to parameter of type &#39;string&#39;. 27 emitter.emit(&#34;stringChange&#34;, 3); ~ Found 2 errors. </code></pre><p>And there you have it! This approach is now also in use in <a href="https://github.com/vector-im/hydrogen-web"class="external-link">Hydrogen<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, a lightweight chat client for the <a href="https://matrix.org/"class="external-link">Matrix<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> protocol. In particular, check out <a href="https://github.com/vector-im/hydrogen-web/blob/master/src/utils/EventEmitter.ts"class="external-link"><code>EventEmitter.ts</code><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>.</p> Approximating Custom Functions in Hugo https://danilafe.com/blog/hugo_functions/ Sun, 17 Jan 2021 18:44:53 -0800 https://danilafe.com/blog/hugo_functions/ <p>This will be an uncharacteristically short post. Recently, I wrote about my experience with <a href="https://danilafe.com/blog/codelines/">including code from local files</a>. After I wrote that post, I decided to expand upon my setup. In particular, I wanted to display links to the files I&rsquo;m referring to, in three different cases: when I&rsquo;m referring to an entire code file, to an entire raw (non-highlighted) file, or to a portion of a code file.</p> <p>The problem was that in all three cases, I needed to determine the correct file URL to link to. The process for doing so is identical: it really only depends on the path to the file I&rsquo;m including. However, many other aspects of each case are different. In the &ldquo;entire code file&rdquo; case, I simply need to read in a file. In the &ldquo;portion of a code file&rdquo; case, I have to perform some processing to extract the specific lines I want to include. Whenever I include a code file &ndash; entirely or partially &ndash; I need to invoke the <code>highlight</code> function to perform syntax highlighting; however, I don&rsquo;t want to do that when including a raw file. It would be difficult to write a single shortcode or partial to handle all of these different cases.</p> <p>However, computing the target URL is a simple transformation of a path and a list of submodules into a link. More plainly, it is a function. Hugo doesn&rsquo;t really have support for custom functions, at least according to this <a href="https://discourse.gohugo.io/t/adding-custom-functions/14164"class="external-link">Discourse post<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. The only approach to add a <em>real</em> function to Hugo is to edit the Go-based source code, and recompile the thing. However, your own custom functions would then not be included in normal Hugo distributions, and any websites using these functions would not be portable. <em>Really</em> adding your own functions is not viable.</p> <p>However, we can approximate functions using Hugo&rsquo;s <a href="https://gohugo.io/functions/scratch/"class="external-link">scratchpad feature<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> By feeding a <span class="sidenote"> <label class="sidenote-label" for="mutable-note">scratchpad</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="mutable-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> In reality, any mutable container will work. The scratchpad just seems like the perfect tool for this purpose. <span class="sidenote-delimiter">]</span> </span> </span> to a partial, and expecting the partial to modify the scratchpad in some way, we can effectively recover data. For instance, in my <code>geturl</code> partial, I have something like the following:</p> <pre tabindex="0"><code>{{ .scratch.Set &#34;bestUrl&#34; theUrl }} </code></pre><p>Once this partial executes, and the rendering engine is back to its call site, the scratchpad will contain <code>bestUrl</code>. To call this partial while providing inputs (like the file path, for example), we can use Hugo&rsquo;s <code>dict</code> function. An (abridged) example:</p> <pre tabindex="0"><code>{{ partial &#34;geturl.html&#34; (dict &#34;scratch&#34; .Scratch &#34;path&#34; filePath) }} </code></pre><p>Now, from inside the partial, we&rsquo;ll be able to access the two variable using <code>.scratch</code> and <code>.path</code>. Once we&rsquo;ve called our partial, we simply extract the returned data from the scratchpad and use it:</p> <pre tabindex="0"><code>{{ partial &#34;geturl.html&#34; (dict &#34;scratch&#34; .Scratch &#34;path&#34; filePath) }} {{ $bestUrl := .Scratch.Get &#34;bestUrl&#34; }} {{ ... do stuff with $bestUrl ... }} </code></pre><p>Thus, although it&rsquo;s a little bit tedious, we&rsquo;re able to use <code>geturl</code> like a function, thereby refraining from duplicating its definition everywhere the same logic is needed. A few closing thoughts:</p> <ul> <li><strong>Why not just use a real language?</strong> It&rsquo;s true that I wrote a Ruby script to do some of the work involved with linking submodules. However, generating the same information for all calls to <code>codelines</code> would complicate the process of rendering the blog, and make live preview impossible. In general, by limiting the use of external scripts, it&rsquo;s easier to make <code>hugo</code> the only &ldquo;build tool&rdquo; for the site.</li> <li><strong>Is there an easier way?</strong> I <em>think</em> that calls to <code>partial</code> return a string. If you simply wanted to return a string, you could probably do without using a scratchpad. However, if you wanted to do something more complicated (say, return a map or list), you&rsquo;d probably want the scratchpad method after all.</li> </ul> Pleasant Code Includes with Hugo https://danilafe.com/blog/codelines/ Wed, 13 Jan 2021 21:31:29 -0800 https://danilafe.com/blog/codelines/ <p>Ever since I started <a href="https://danilafe.com/blog/00_compiler_intro/">the compiler series</a>, I began to include more and more fragments of code into my blog. I didn&rsquo;t want to be copy-pasting my code between my project and my Markdown files, so I quickly wrote up a Hugo <a href="https://gohugo.io/content-management/shortcodes/"class="external-link">shortcode<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> to pull in other files in the local directory. I&rsquo;ve since improved on this some more, so I thought I&rsquo;d share what I created with others.</p> <a href="#including-entire-files-and-lines"> <h3 id="including-entire-files-and-lines">Including Entire Files and Lines</h3> </a> <p>My needs for snippets were modest at first. For the most part, I had a single code file that I wanted to present, so it was acceptable to plop it in the middle of my post in one piece. The shortcode for that was quite simple:</p> <pre tabindex="0"><code>{{ highlight (readFile (printf &#34;code/%s&#34; (.Get 1))) (.Get 0) &#34;&#34; }} </code></pre><p>This leverages Hugo&rsquo;s built-in <a href="https://gohugo.io/functions/highlight/"class="external-link"><code>highlight</code><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> function to provide syntax highlighting to the included snippet. Hugo doesn&rsquo;t guess at the language of the code, so you have to manually provide it. Calling this shortcode looks as follows:</p> <pre tabindex="0"><code>{{&lt; codeblock &#34;C++&#34; &#34;compiler/03/type.hpp&#34; &gt;}} </code></pre><p>Note that this implicitly adds the <code>code/</code> prefix to all the files I include. This is a personal convention: I want all my code to be inside a dedicated directory.</p> <p>Of course, including entire files only takes you so far. What if you only need to discuss a small part of your code? Alternaitvely, what if you want to present code piece-by-piece, in the style of literate programming? I quickly ran into the need to do this, for which I wrote another shortcode:</p> <pre tabindex="0"><code>{{ $s := (readFile (printf &#34;code/%s&#34; (.Get 1))) }} {{ $t := split $s &#34;\n&#34; }} {{ if not (eq (int (.Get 2)) 1) }} {{ .Scratch.Set &#34;u&#34; (after (sub (int (.Get 2)) 1) $t) }} {{ else }} {{ .Scratch.Set &#34;u&#34; $t }} {{ end }} {{ $v := first (add (sub (int (.Get 3)) (int (.Get 2))) 1) (.Scratch.Get &#34;u&#34;) }} {{ if (.Get 4) }} {{ .Scratch.Set &#34;opts&#34; (printf &#34;,%s&#34; (.Get 4)) }} {{ else }} {{ .Scratch.Set &#34;opts&#34; &#34;&#34; }} {{ end }} {{ highlight (delimit $v &#34;\n&#34;) (.Get 0) (printf &#34;linenos=table,linenostart=%d%s&#34; (.Get 2) (.Scratch.Get &#34;opts&#34;)) }} </code></pre><p>This shortcode takes a language and a filename as before, but it also takes the numbers of the first and last lines indicating the part of the code that should be included. After splitting the contents of the file into lines, it throws away all lines before and after the window of code that you want to include. It seems to me (from my commit history) that Hugo&rsquo;s <a href="https://gohugo.io/functions/after/"class="external-link"><code>after</code><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> function (which should behave similarly to Haskell&rsquo;s <code>drop</code>) doesn&rsquo;t like to be given an argument of <code>0</code>. I had to add a special case for when this would occur, where I simply do not invoke <code>after</code> at all. The shortcode can be used as follows:</p> <pre tabindex="0"><code>{{&lt; codelines &#34;C++&#34; &#34;compiler/04/ast.cpp&#34; 19 22 &gt;}} </code></pre><p>To support a fuller range of Hugo&rsquo;s functionality, I also added an optional argument that accepts Hugo&rsquo;s Chroma settings. This way, I can do things like highlight certain lines in my code snippet, which is done as follows:</p> <pre tabindex="0"><code>{{&lt; codelines &#34;Idris&#34; &#34;typesafe-interpreter/TypesafeIntrV3.idr&#34; 31 39 &#34;hl_lines=7 8 9&#34; &gt;}} </code></pre><p>Note that the <code>hl_lines</code> field doesn&rsquo;t seem to work properly with <code>linenostart</code>, which means that the highlighted lines are counted from 1 no matter what. This is why in the above snippet, although I include lines 31 through 39, I feed lines 7, 8, and 9 to <code>hl_lines</code>. It&rsquo;s unusual, but hey, it works!</p> <a href="#linking-to-referenced-code"> <h3 id="linking-to-referenced-code">Linking to Referenced Code</h3> </a> <p>Some time after implementing my initial system for including lines of code, I got an email from a reader who pointed out that it was hard for them to find the exact file I was referencing, and to view the surrounding context of the presented lines. To address this, I decided that I&rsquo;d include the link to the file in question. After all, my website and all the associated code is on a <a href="https://dev.danilafe.com/Web-Projects/blog-static"class="external-link">Git server I host<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, so any local file I&rsquo;m referencing should &ndash; assuming it was properly committed &ndash; show up there, too. I hardcoded the URL of the <code>code</code> directory on the web interface, and appended the relative path of each included file to it. The shortcode came out as follows:</p> <pre tabindex="0"><code>{{ $s := (readFile (printf &#34;code/%s&#34; (.Get 1))) }} {{ $t := split $s &#34;\n&#34; }} {{ if not (eq (int (.Get 2)) 1) }} {{ .Scratch.Set &#34;u&#34; (after (sub (int (.Get 2)) 1) $t) }} {{ else }} {{ .Scratch.Set &#34;u&#34; $t }} {{ end }} {{ $v := first (add (sub (int (.Get 3)) (int (.Get 2))) 1) (.Scratch.Get &#34;u&#34;) }} {{ if (.Get 4) }} {{ .Scratch.Set &#34;opts&#34; (printf &#34;,%s&#34; (.Get 4)) }} {{ else }} {{ .Scratch.Set &#34;opts&#34; &#34;&#34; }} {{ end }} &lt;div class=&#34;highlight-group&#34;&gt; &lt;div class=&#34;highlight-label&#34;&gt;From &lt;a href=&#34;https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/{{ .Get 1 }}&#34;&gt;{{ path.Base (.Get 1) }}&lt;/a&gt;, {{ if eq (.Get 2) (.Get 3) }}line {{ .Get 2 }}{{ else }} lines {{ .Get 2 }} through {{ .Get 3 }}{{ end }}&lt;/div&gt; {{ highlight (delimit $v &#34;\n&#34;) (.Get 0) (printf &#34;linenos=table,linenostart=%d%s&#34; (.Get 2) (.Scratch.Get &#34;opts&#34;)) }} &lt;/div&gt; </code></pre><p>This results in code blocks like the one in the image below. The image is the result of the <code>codelines</code> call for the Idris language, presented above.</p> <figure class="medium"><img src="https://danilafe.com/blog/codelines/example.png" alt="An example of how the code looks."><figcaption> <p>An example of how the code looks.</p> </figcaption> </figure> <p>I got a lot of mileage out of this setup . . . until I wanted to include code from <em>other</em> git repositories. For instance, I wanted to talk about my <a href="https://adventofcode.com/"class="external-link">Advent of Code<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> submissions, without having to copy-paste the code into my blog repository!</p> <a href="#code-from-submodules"> <h3 id="code-from-submodules">Code from Submodules</h3> </a> <p>My first thought when including code from other repositories was to use submodules. This has the added advantage of &ldquo;pinning&rdquo; the version of the code I&rsquo;m talking about, which means that even if I push significant changes to the other repository, the code in my blog will remain the same. This, in turn, means that all of my <code>codelines</code> shortcodes will work as intended.</p> <p>The problem is, most Git web interfaces (my own included) don&rsquo;t display paths corresponding to submodules. Thus, even if all my code is checked out and Hugo correctly pulls the selected lines into its HTML output, the <em>links to the file</em> remain broken!</p> <p>There&rsquo;s no easy way to address this, particularly because <em>different submodules can be located on different hosts</em>! The Git URL used for a submodule is not known to Hugo (since, to the best of my knowledge, it can&rsquo;t run shell commands), and it could reside on <code>dev.danilafe.com</code>, or <code>github.com</code>, or elsewhere. Fortunately, it&rsquo;s fairly easy to tell when a file is part of a submodule, and which submodule that is. It&rsquo;s sufficient to find the longest submodule path that matches the selected file. If no submodule path matches, then the file is part of the blog repository, and no special action is needed.</p> <p>Of course, this means that Hugo needs to be made aware of the various submodules in my repository. It also needs to be aware of the submodules <em>inside</em> those submodules, and so on: it needs to be recursive. Git has a command to list all submodules recursively:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">git submodule status --recursive </span></span></code></pre></div><p>However, this only prints the commit, submodule path, and the upstream branch. I don&rsquo;t think there&rsquo;s a way to list the remotes&rsquo; URLs with this command; however, we do <em>need</em> the URLs, since that&rsquo;s how we create links to the Git web interfaces.</p> <p>There&rsquo;s another issue: how do we let Hugo know about the various submodules, even if we can find them? Hugo can read files, but doing any serious text processing is downright impractical. However, Hugo itself is not able to run commands, so it needs to be able to read in the output of another command that <em>can</em> find submodules.</p> <p>I settled on using Hugo&rsquo;s <code>params</code> configuration option. This allows users to communicate arbitrary properties to Hugo themes and templates. In my case, I want to communicate a collection of submodules. I didn&rsquo;t know about TOML&rsquo;s inline tables, so I decided to represent this collection as a map of (meaningless) submodule names to tables:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-TOML" data-lang="TOML"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">params</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="nx">params</span><span class="p">.</span><span class="nx">submoduleLinks</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="nx">params</span><span class="p">.</span><span class="nx">submoduleLinks</span><span class="p">.</span><span class="nx">aoc2020</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="nx">url</span> <span class="p">=</span> <span class="s2">&#34;https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82&#34;</span> </span></span><span class="line"><span class="cl"> <span class="nx">path</span> <span class="p">=</span> <span class="s2">&#34;aoc-2020&#34;</span> </span></span></code></pre></div><p>Since it was seemingly impossible to wrangle Git into outputting all of this information using one command, I decided to write a quick Ruby script to generate a list of submodules as follows. I had to use <code>cd</code> in one of my calls to Git because Git&rsquo;s <code>--git-dir</code> option doesn&rsquo;t seem to work with submodules, treating them like a &ldquo;bare&rdquo; checkout. I also chose to use an allowlist of remote URLs, since the URL format for linking to files in a particular repository differs from service to service. For now, I only use my own Git server, so only <code>dev.danilafe.com</code> is allowed; however, just by adding <code>elsif</code>s to my code, I can add other services in the future.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Ruby" data-lang="Ruby"><span class="line"><span class="cl"><span class="nb">puts</span> <span class="s2">&#34;[params]&#34;</span> </span></span><span class="line"><span class="cl"><span class="nb">puts</span> <span class="s2">&#34; [params.submoduleLinks]&#34;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">each_submodule</span><span class="p">(</span><span class="n">base_path</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="sb">`cd </span><span class="si">#{</span><span class="n">base_path</span><span class="si">}</span><span class="sb"> &amp;&amp; git submodule status`</span><span class="o">.</span><span class="n">lines</span> <span class="k">do</span> <span class="o">|</span><span class="n">line</span><span class="o">|</span> </span></span><span class="line"><span class="cl"> <span class="nb">hash</span><span class="p">,</span> <span class="n">path</span> <span class="o">=</span> <span class="n">line</span><span class="o">[</span><span class="mi">1</span><span class="o">..].</span><span class="n">split</span> <span class="s2">&#34; &#34;</span> </span></span><span class="line"><span class="cl"> <span class="n">full_path</span> <span class="o">=</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">base_path</span><span class="si">}</span><span class="s2">/</span><span class="si">#{</span><span class="n">path</span><span class="si">}</span><span class="s2">&#34;</span> </span></span><span class="line"><span class="cl"> <span class="n">url</span> <span class="o">=</span> <span class="sb">`git config --file </span><span class="si">#{</span><span class="n">base_path</span><span class="si">}</span><span class="sb">/.gitmodules --get &#39;submodule.</span><span class="si">#{</span><span class="n">path</span><span class="si">}</span><span class="sb">.url&#39;`</span><span class="o">.</span><span class="n">chomp</span><span class="o">.</span><span class="n">delete_suffix</span><span class="p">(</span><span class="s2">&#34;.git&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">safe_name</span> <span class="o">=</span> <span class="n">full_path</span><span class="o">.</span><span class="n">gsub</span><span class="p">(</span><span class="sr">/\/|-|_\./</span><span class="p">,</span> <span class="s2">&#34;&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">url</span> <span class="o">=~</span> <span class="sr">/dev.danilafe.com/</span> </span></span><span class="line"><span class="cl"> <span class="n">file_url</span> <span class="o">=</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">url</span><span class="si">}</span><span class="s2">/src/commit/</span><span class="si">#{</span><span class="nb">hash</span><span class="si">}</span><span class="s2">&#34;</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span> </span></span><span class="line"><span class="cl"> <span class="k">raise</span> <span class="s2">&#34;Submodule URL </span><span class="si">#{</span><span class="n">url</span><span class="o">.</span><span class="n">dump</span><span class="si">}</span><span class="s2"> not in a known format!&#34;</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">yield</span> <span class="p">({</span> <span class="ss">:path</span> <span class="o">=&gt;</span> <span class="n">full_path</span><span class="p">,</span> <span class="ss">:url</span> <span class="o">=&gt;</span> <span class="n">file_url</span><span class="p">,</span> <span class="ss">:name</span> <span class="o">=&gt;</span> <span class="n">safe_name</span> <span class="p">})</span> </span></span><span class="line"><span class="cl"> <span class="n">each_submodule</span><span class="p">(</span><span class="n">full_path</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">m</span><span class="o">|</span> <span class="k">yield</span> <span class="n">m</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"><span class="k">end</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">each_submodule</span><span class="p">(</span><span class="s2">&#34;.&#34;</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">m</span><span class="o">|</span> </span></span><span class="line"><span class="cl"> <span class="k">next</span> <span class="k">unless</span> <span class="n">m</span><span class="o">[</span><span class="ss">:path</span><span class="o">].</span><span class="n">start_with?</span> <span class="s2">&#34;./code/&#34;</span> </span></span><span class="line"><span class="cl"> <span class="nb">puts</span> <span class="s2">&#34; [params.submoduleLinks.</span><span class="si">#{</span><span class="n">m</span><span class="o">[</span><span class="ss">:name</span><span class="o">].</span><span class="n">delete_prefix</span><span class="p">(</span><span class="s2">&#34;.code&#34;</span><span class="p">)</span><span class="si">}</span><span class="s2">]&#34;</span> </span></span><span class="line"><span class="cl"> <span class="nb">puts</span> <span class="s2">&#34; url = </span><span class="si">#{</span><span class="n">m</span><span class="o">[</span><span class="ss">:url</span><span class="o">].</span><span class="n">dump</span><span class="si">}</span><span class="s2">&#34;</span> </span></span><span class="line"><span class="cl"> <span class="nb">puts</span> <span class="s2">&#34; path = </span><span class="si">#{</span><span class="n">m</span><span class="o">[</span><span class="ss">:path</span><span class="o">].</span><span class="n">delete_prefix</span><span class="p">(</span><span class="s2">&#34;./code/&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">dump</span><span class="si">}</span><span class="s2">&#34;</span> </span></span><span class="line"><span class="cl"><span class="k">end</span> </span></span></code></pre></div><p>I pipe the output of this script into a separate configuration file called <code>config-gen.toml</code>, and then run Hugo as follows:</p> <pre tabindex="0"><code>hugo --config config.toml,config-gen.toml </code></pre><p>Finally, I had to modify my shortcode to find and handle the longest submodule prefix. Here&rsquo;s the relevant portion, and you can <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/commit/bfeae89ab52d1696c4a56768b7f0c6682efaff82/themes/vanilla/layouts/shortcodes/codelines.html"class="external-link">view the entire file here<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>.</p> <pre tabindex="0"><code>{{ .Scratch.Set &#34;bestLength&#34; -1 }} {{ .Scratch.Set &#34;bestUrl&#34; (printf &#34;https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/%s&#34; (.Get 1)) }} {{ $filePath := (.Get 1) }} {{ $scratch := .Scratch }} {{ range $module, $props := .Site.Params.submoduleLinks }} {{ $path := index $props &#34;path&#34; }} {{ $bestLength := $scratch.Get &#34;bestLength&#34; }} {{ if and (le $bestLength (len $path)) (hasPrefix $filePath $path) }} {{ $scratch.Set &#34;bestLength&#34; (len $path) }} {{ $scratch.Set &#34;bestUrl&#34; (printf &#34;%s%s&#34; (index $props &#34;url&#34;) (strings.TrimPrefix $path $filePath)) }} {{ end }} {{ end }} </code></pre><p>And that&rsquo;s what I&rsquo;m using at the time of writing!</p> <a href="#conclusion"> <h3 id="conclusion">Conclusion</h3> </a> <p>My current system for code includes allows me to do the following things:</p> <ul> <li>Include entire files or sections of files into the page. This saves me from having to copy and paste code manually, which is error prone and can cause inconsistencies.</li> <li>Provide links to the files I reference on my Git interface. This allows users to easily view the entire file that I&rsquo;m talking about.</li> <li>Correctly link to files in repositories other than my blog repository, when they are included using submodules. This means I don&rsquo;t need to manually copy and update code from other projects.</li> </ul> <p>I hope some of these shortcodes and script come in handy for someone else. Thank you for reading!</p> Advent of Code in Coq - Day 8 https://danilafe.com/blog/01_aoc_coq/ Sun, 10 Jan 2021 22:48:39 -0800 https://danilafe.com/blog/01_aoc_coq/ <p>Huh? We&rsquo;re on day 8? What happened to days 2 through 7?</p> <p>Well, for the most part, I didn&rsquo;t think they were that interesting from the Coq point of view. Day 7 got close, but not close enough to inspire me to create a formalization. Day 8, on the other hand, is <span class="sidenote"> <label class="sidenote-label" for="pl-note">quite interesting,</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="pl-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Especially to someone like me who's interested in programming languages! <span class="sidenote-delimiter">]</span> </span> </span> and took quite some time to formalize.</p> <p>As before, here&rsquo;s an (abridged) description of the problem:</p> <blockquote> <p>Given a tiny assembly-like language, determine the state of its accumulator when the same instruction is executed twice.</p> </blockquote> <p>Before we start on the Coq formalization, let&rsquo;s talk about an idea from Programming Language Theory (PLT), <em>big step operational semantics</em>.</p> <a href="#big-step-operational-semantics"> <h3 id="big-step-operational-semantics">Big Step Operational Semantics</h3> </a> <p>What we have in Advent of Code&rsquo;s Day 8 is, undeniably, a small programming language. We are tasked with executing this language, or, in PLT lingo, defining its <em>semantics</em>. There are many ways of doing this - at university, I&rsquo;ve been taught of <a href="https://en.wikipedia.org/wiki/Denotational_semantics"class="external-link">denotational<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, <a href="https://en.wikipedia.org/wiki/Axiomatic_semantics"class="external-link">axiomatic<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, and <a href="https://en.wikipedia.org/wiki/Operational_semantics"class="external-link">operational<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> semantics. I believe that Coq&rsquo;s mechanism of inductive definitions lends itself very well to operational semantics, so we&rsquo;ll take that route. But even &ldquo;operational semantics&rdquo; doesn&rsquo;t refer to a concrete technique - we have a choice between small-step (structural) and big-step (natural) operational semantics. The former describe the minimal &ldquo;steps&rdquo; a program takes as it&rsquo;s being evaluated, while the latter define the final results of evaluating a program. I decided to go with big-step operational semantics, since they&rsquo;re more intutive (natural!).</p> <p>So, how does one go about &ldquo;[defining] the final results of evaluating a program?&rdquo; Most commonly, we go about using <em>inference rules</em>. Let&rsquo;s talk about those next.</p> <a href="#inference-rules"> <h4 id="inference-rules">Inference Rules</h4> </a> <p>Inference rules are a very general notion. The describe how we can determine (infer) a conclusion from a set of assumptions. It helps to look at an example. Here&rsquo;s a silly little inference rule:</p> $$ \frac {\text{I&amp;#39;m allergic to cats} \quad \text{My friend has a cat}} {\text{I will not visit my friend very much}} $$ <p>It reads, &ldquo;if I&rsquo;m allergic to cats, and if my friend has a cat, then I will not visit my friend very much&rdquo;. Here, &ldquo;I&rsquo;m allergic to cats&rdquo; and &ldquo;my friend has a cat&rdquo; are <em>premises</em>, and &ldquo;I will not visit my friend very much&rdquo; is a <em>conclusion</em>. An inference rule states that if all its premises are true, then its conclusion must be true. Here&rsquo;s another inference rule, this time with some mathematical notation instead of words:</p> $$ \frac {n &amp;lt; m} {n &#43; 1 &amp;lt; m &#43; 1} $$ <p>This one reads, &ldquo;if \(n\) is less than \(m\), then \(n+1\) is less than \(m+1\)&rdquo;. We can use inference rules to define various constructs. As an example, let&rsquo;s define what it means for a natural number to be even. It takes two rules:</p> $$ \frac {} {0 \; \text{is even}} \quad \frac {n \; \text{is even}} {n&#43;2 \; \text{is even}} $$ <p>First of all, zero is even. We take this as fact - there are no premises for the first rule, so they are all trivially true. Next, if a number is even, then adding 2 to that number results in another even number. Using the two of these rules together, we can correctly determine whether any number is or isn&rsquo;t even. We start knowing that 0 is even. Adding 2 we learn that 2 is even, and adding 2 again we see that 4 is even, as well. We can continue this to determine that 6, 8, 10, and so on are even too. Never in this process will we visit the numbers 1 or 3 or 5, and that&rsquo;s good - they&rsquo;re not even!</p> <p>Let&rsquo;s now extend this notion to programming languages, starting with a simple arithmetic language. This language is made up of natural numbers and the \(\square\) operation, which represents the addition of two numbers. Again, we need two rules:</p> $$ \frac {n \in \mathbb{N}} {n \; \text{evaluates to} \; n} \quad \frac {e_1 \; \text{evaluates to} \; n_1 \quad e_2 \; \text{evaluates to} \; n_2} {e_1 \square e_2 \; \text{evaluates to} \; n_1 &#43; n_2} $$ <p>First, let me explain myself. I used \(\square\) to demonstrate two important points. First, languages can be made of any kind of characters we want; it&rsquo;s the rules that we define that give these languages meaning. Second, while \(\square\) is the addition operation <em>in our language</em>, \(+\) is the <em>mathematical addition operator</em>. They are not the same - we use the latter to define how the former works.</p> <p>Finally, writing &ldquo;evaluates to&rdquo; gets quite tedious, especially for complex languages. Instead, PLT people use notation to make their semantics more concise. The symbol \(\Downarrow\) is commonly used to mean &ldquo;evaluates to&rdquo;; thus, \(e \Downarrow v\) reads &ldquo;the expression \(e\) evaluates to the value \(v\). Using this notation, our rules start to look like the following:</p> $$ \frac {n \in \mathbb{N}} {n \Downarrow n} \quad \frac {e_1 \Downarrow n_1 \quad e_2 \Downarrow n_2} {e_1 \square e_2 \Downarrow n_1 &#43; n_2} $$ <p>If nothing else, these are way more compact! Though these may look intimidating at first, it helps to simply read each symbol as its English meaning.</p> <a href="#encoding-inference-rules-in-coq"> <h4 id="encoding-inference-rules-in-coq">Encoding Inference Rules in Coq</h4> </a> <p>Now that we&rsquo;ve seen what inference rules are, we can take a look at how they can be represented in Coq. We can use Coq&rsquo;s <code>Inductive</code> mechanism to define the rules. Let&rsquo;s start with our &ldquo;is even&rdquo; property.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Inductive</span> <span class="n">is_even</span> <span class="o">:</span> <span class="kt">nat</span> <span class="o">-&gt;</span> <span class="kt">Prop</span> <span class="o">:=</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">zero_even</span> <span class="o">:</span> <span class="n">is_even</span> <span class="n">0</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">plustwo_even</span> <span class="o">:</span> <span class="n">is_even</span> <span class="n">n</span> <span class="o">-&gt;</span> <span class="n">is_even</span> <span class="o">(</span><span class="n">n</span><span class="o">+</span><span class="n">2</span><span class="o">).</span> </span></span></code></pre></div><p>The first line declares the property <code>is_even</code>, which, given a natural number, returns proposition. This means that <code>is_even</code> is not a proposition itself, but <code>is_even 0</code>, <code>is_even 1</code>, and <code>is_even 2</code> are all propositions.</p> <p>The following two lines each encode one of our aforementioned inference rules. The first rule, <code>zero_even</code>, is of type <code>is_even 0</code>. The <code>zero_even</code> rule doesn&rsquo;t require any arguments, and we can use it to create a proof that 0 is even. On the other hand, the <code>plustwo_even</code> rule <em>does</em> require an argument, <code>is_even n</code>. To construct a proof that a number <code>n+2</code> is even using <code>plustwo_even</code>, we need to provide a proof that <code>n</code> itself is even. From this definition we can see a general principle: we encode each inference rule as constructor of an inductive Coq type. Each rule encoded in this manner takes as arguments the proofs of its premises, and returns a proof of its conclusion.</p> <p>For another example, let&rsquo;s encode our simple addition language. First, we have to define the language itself:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Inductive</span> <span class="n">tinylang</span> <span class="o">:</span> <span class="kt">Type</span> <span class="o">:=</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">number</span> <span class="o">(</span><span class="n">n</span> <span class="o">:</span> <span class="kt">nat</span><span class="o">)</span> <span class="o">:</span> <span class="n">tinylang</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">box</span> <span class="o">(</span><span class="n">e1</span> <span class="n">e2</span> <span class="o">:</span> <span class="n">tinylang</span><span class="o">)</span> <span class="o">:</span> <span class="n">tinylang</span><span class="o">.</span> </span></span></code></pre></div><p>This defines the two elements of our example language: <code>number n</code> corresponds to \(n\), and <code>box e1 e2</code> corresponds to \(e_1 \square e_2\). Finally, we define the inference rules:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Inductive</span> <span class="n">tinylang_sem</span> <span class="o">:</span> <span class="n">tinylang</span> <span class="o">-&gt;</span> <span class="kt">nat</span> <span class="o">-&gt;</span> <span class="kt">Prop</span> <span class="o">:=</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">number_sem</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">n</span> <span class="o">:</span> <span class="kt">nat</span><span class="o">),</span> <span class="n">tinylang_sem</span> <span class="o">(</span><span class="n">number</span> <span class="n">n</span><span class="o">)</span> <span class="n">n</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">box_sem</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">e1</span> <span class="n">e2</span> <span class="o">:</span> <span class="n">tinylang</span><span class="o">)</span> <span class="o">(</span><span class="n">n1</span> <span class="n">n2</span> <span class="o">:</span> <span class="kt">nat</span><span class="o">),</span> </span></span><span class="line"><span class="cl"> <span class="n">tinylang_sem</span> <span class="n">e1</span> <span class="n">n1</span> <span class="o">-&gt;</span> <span class="n">tinylang_sem</span> <span class="n">e2</span> <span class="n">n2</span> <span class="o">-&gt;</span> </span></span><span class="line"><span class="cl"> <span class="n">tinylang_sem</span> <span class="o">(</span><span class="n">box</span> <span class="n">e1</span> <span class="n">e2</span><span class="o">)</span> <span class="o">(</span><span class="n">n1</span> <span class="o">+</span> <span class="n">n2</span><span class="o">).</span> </span></span></code></pre></td></tr></table> </div> </div><p>When we wrote our rules earlier, by using arbitrary variables like \(e_1\) and \(n_1\), we implicitly meant that our rules work for <em>any</em> number or expression. When writing Coq we have to make this assumption explicit by using <code>forall</code>. For instance, the rule on line 2 reads, &ldquo;for any number <code>n</code>, the expression <code>n</code> evaluates to <code>n</code>&rdquo;.</p> <a href="#semantics-of-our-language"> <h4 id="semantics-of-our-language">Semantics of Our Language</h4> </a> <p>We&rsquo;ve now written some example big-step operational semantics, both &ldquo;on paper&rdquo; and in Coq. Now, it&rsquo;s time to take a look at the specific semantics of the language from Day 8! Our language consists of a few parts.</p> <p>First, there are three opcodes: \(\texttt{jmp}\), \(\texttt{nop}\), and \(\texttt{add}\). Opcodes, combined with an integer, make up an instruction. For example, the instruction \(\texttt{add} \; 3\) will increase the content of the accumulator by three. Finally, a program consists of a sequence of instructions; They&rsquo;re separated by newlines in the puzzle input, but we&rsquo;ll instead separate them by semicolons. For example, here&rsquo;s a complete program.</p> $$ \texttt{add} \; 0; \; \texttt{nop} \; 2; \; \texttt{jmp} \; -2 $$ <p>Now, let&rsquo;s try evaluating this program. Starting at the beginning and with 0 in the accumulator, it will add 0 to the accumulator (keeping it the same), do nothing, and finally jump back to the beginning. At this point, it will try to run the addition instruction again, which is not allowed; thus, the program will terminate.</p> <p>Did you catch that? The semantics of this language will require more information than just our program itself (which we&rsquo;ll denote by \(p\)).</p> <ul> <li>First, to evaluate the program we will need a program counter, \(\textit{c}\). This program counter will tell us the position of the instruction to be executed next. It can also point past the last instruction, which means our program terminated successfully.</li> <li>Next, we&rsquo;ll need the accumulator \(a\). Addition instructions can change the accumulator, and we will be interested in the number that ends up in the accumulator when our program finishes executing.</li> <li>Finally, and more subtly, we&rsquo;ll need to keep track of the states we visited. For instance, in the course of evaluating our program above, we encounter the \((c, a)\) pair of \((0, 0)\) twice: once at the beginning, and once at the end. However, whereas at the beginning we have not yet encountered the addition instruction, at the end we have, so the evaluation behaves differently. To make the proofs work better in Coq, we&rsquo;ll use a set \(v\) of <span class="sidenote"> <label class="sidenote-label" for="allowed-note">allowed (valid) program counters (as opposed to visited program counters).</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="allowed-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Whereas the set of "visited" program counters keeps growing as our evaluation continues, the set of "allowed" program counters keeps shrinking. Because the "allowed" set never stops shrinking, assuming we're starting with a finite set, our execution will eventually terminate. <span class="sidenote-delimiter">]</span> </span> </span> </li> </ul> <p>Now we have all the elements of our evaluation. Let&rsquo;s define some notation. A program starts at some state, and terminates in another, possibly different state. In the course of a regular evaluation, the program never changes; only the state does. So I propose this (rather unorthodox) notation:</p> $$ (c, a, v) \Rightarrow_p (c&amp;#39;, a&amp;#39;, v&amp;#39;) $$ <p>This reads, &ldquo;after starting at program counter \(c\), accumulator \(a\), and set of valid addresses \(v\), the program \(p\) terminates with program counter \(c'\), accumulator \(a'\), and set of valid addresses \(v'\)&rdquo;. Before creating the inference rules for this evaluation relation, let&rsquo;s define the effect of evaluating a single instruction, using notation \((c, a) \rightarrow_i (c', a')\). An addition instruction changes the accumulator, and increases the program counter by 1.</p> $$ \frac{} {(c, a) \rightarrow_{\texttt{add} \; n} (c&#43;1, a&#43;n)} $$ <p>A no-op instruction does even less. All it does is increment the program counter.</p> $$ \frac{} {(c, a) \rightarrow_{\texttt{nop} \; n} (c&#43;1, a)} $$ <p>Finally, a jump instruction leaves the accumulator intact, but adds a number to the program counter itself!</p> $$ \frac{} {(c, a) \rightarrow_{\texttt{jmp} \; n} (c&#43;n, a)} $$ <p>None of these rules have any premises, and they really are quite simple. Now, let&rsquo;s define the rules for evaluating a program. First of all, a program starting in a state that is not considered &ldquo;valid&rdquo; is done evaluating, and is in a &ldquo;failed&rdquo; state.</p> $$ \frac{c \not \in v \quad c \not= \text{length}(p)} {(c, a, v) \Rightarrow_{p} (c, a, v)} $$ <p>We use \(\text{length}(p)\) to represent the number of instructions in \(p\). Note the second premise: even if our program counter \(c\) is not included in the valid set, if it&rsquo;s &ldquo;past the end of the program&rdquo;, the program terminates in an &ldquo;ok&rdquo; state. <span class="sidenote"> <label class="sidenote-label" for="avoid-c-note">Here&rsquo;s a rule for terminating in the &ldquo;ok&rdquo; state:</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="avoid-c-note"></input><span class="sidenote-content sidenote-left"><span class="sidenote-delimiter">[note:</span> In the presented rule, we don't use the variable <code>c</code> all that much, and we know its concrete value (from the equality premise). We could thus avoid introducing the name \(c\) by replacing it with said known value: $$ \frac{} {(\text{length}(p), a, v) \Rightarrow_{p} (\text{length}(p), a, v)} $$ This introduces some duplication, but that is really because all "base case" evaluation rules start and stop in the same state. To work around this, we could define a separate proposition to mean "program \(p\) is done in state \(s\)", then \(s\) will really only need to occur once, and so will \(\text{length}(p)\). This is, in fact, what we will do later on, since being able to talk abut "programs being done" will help us with components of our proof. <span class="sidenote-delimiter">]</span> </span> </span> </p> $$ \frac{c = \text{length}(p)} {(c, a, v) \Rightarrow_{p} (c, a, v)} $$ <p>When our program counter reaches the end of the program, we are also done evaluating it. Even though both rules <span class="sidenote"> <label class="sidenote-label" for="redundant-note">lead to the same conclusion,</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="redundant-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> In fact, if the end of the program is never included in the valid set, the second rule is completely redundant. <span class="sidenote-delimiter">]</span> </span> </span> it helps to distinguish the two possible outcomes. Finally, if neither of the termination conditions are met, our program can take a step, and continue evaluating from there.</p> $$ \frac{c \in v \quad p[c] = i \quad (c, a) \rightarrow_i (c&amp;#39;, a&amp;#39;) \quad (c&amp;#39;, a&amp;#39;, v - \{c\}) \Rightarrow_p (c&amp;#39;&amp;#39;, a&amp;#39;&amp;#39;, v&amp;#39;&amp;#39;)} {(c, a, v) \Rightarrow_{p} (c&amp;#39;&amp;#39;, a&amp;#39;&amp;#39;, v&amp;#39;&amp;#39;)} $$ <p>This is quite a rule. A lot of things need to work out for a program to evauate from a state that isn&rsquo;t currently the final state:</p> <ul> <li>The current program counter \(c\) must be valid. That is, it must be an element of \(v\).</li> <li>This program counter must correspond to an instruction \(i\) in \(p\), which we write as \(p[c] = i\).</li> <li>This instruction must be executed, changing our program counter from \(c\) to \(c'\) and our accumulator from \(a\) to \(a'\). The set of valid instructions will no longer include \(c\), and will become \(v - \{c\}\).</li> <li>Our program must then finish executing, starting at state \((c', a', v - \{c\})\), and ending in some (unknown) state \((c'', a'', v'')\).</li> </ul> <p>If all of these conditions are met, our program, starting at \((c, a, v)\), will terminate in the state \((c'', a'', v'')\). This third rule completes our semantics; a program being executed will keep running instructions using the third rule, until it finally hits an invalid program counter (terminating with the first rule) or gets to the end of the program (terminating with the second rule).</p> <a href="#aside-vectors-and-finite-mathbbn"> <h4 id="aside-vectors-and-finite-mathbbn">Aside: Vectors and Finite \(\mathbb{N}\)</h4> </a> <p>We&rsquo;ll be getting to the Coq implementation of our semantics soon, but before we do: what type should \(c\) be? It&rsquo;s entirely possible for an instruction like \(\texttt{jmp} \; -10000\) to throw our program counter way before the first instruction of our program, so at first, it seems as though we should use an integer. But the prompt doesn&rsquo;t even specify what should happen in this case - it only says an instruction shouldn&rsquo;t be run twice. The &ldquo;valid set&rdquo;, although it may help resolve this debate, is our invention, and isn&rsquo;t part of the original specification.</p> <p>There is, however, something we can infer from this problem. Since the problem of jumping &ldquo;too far behind&rdquo; or &ldquo;too far ahead&rdquo; is never mentioned, we can assume that <em>all jumps will lead either to an instruction, or right to the end of a program</em>. This means that \(c\) is a natural number, with</p> $$ 0 \leq c \leq \text{length}(p) $$ <p>In a language like Coq, it&rsquo;s possible to represent such a number. Since we&rsquo;ve gotten familliar with inference rules, let&rsquo;s present two rules that define such a number:</p> $$ \frac {n \in \mathbb{N}^&#43;} {Z : \text{Fin} \; n} \quad \frac {f : \text{Fin} \; n} {S f : \text{Fin} \; (n&#43;1)} $$ <p>This is a variation of the <a href="https://wiki.haskell.org/Peano_numbers"class="external-link">Peano encoding<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> of natural numbers. It reads as follows: zero (\(Z\)) is a finite natural number less than any positive natural number \(n\). Then, if a finite natural number \(f\) is less than \(n\), then adding one to that number (using the successor function \(S\)) will create a natural number less than \(n+1\). We encode this in Coq as follows (<a href="https://coq.inria.fr/library/Coq.Vectors.Fin.html#t"class="external-link">originally from here<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>):</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Inductive</span> <span class="n">t</span> <span class="o">:</span> <span class="kt">nat</span> <span class="o">-&gt;</span> <span class="kn">Set</span> <span class="o">:=</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">F1</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">{</span><span class="n">n</span><span class="o">},</span> <span class="n">t</span> <span class="o">(</span><span class="n">S</span> <span class="n">n</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">FS</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">{</span><span class="n">n</span><span class="o">},</span> <span class="n">t</span> <span class="n">n</span> <span class="o">-&gt;</span> <span class="n">t</span> <span class="o">(</span><span class="n">S</span> <span class="n">n</span><span class="o">).</span> </span></span></code></pre></div><p>The <code>F1</code> constructor here is equivalent to our \(Z\), and <code>FS</code> is equivalent to our \(S\). To represent positive natural numbers \(\mathbb{N}^+\), we simply take a regular natural number from \(\mathbb{N}\) and find its successor using <code>S</code> (simply adding 1). Again, we have to explicitly use <code>forall</code> in our type signatures.</p> <p>We can use a similar technique to represent a list with a known number of elements, known in the Idris and Coq world as a vector. Again, we only need two inference rules to define such a vector:</p> $$ \frac {t : \text{Type}} {[] : \text{Vec} \; t \; 0} \quad \frac {x : \text{t} \quad \textit{xs} : \text{Vec} \; t \; n} {(x::\textit{xs}) : \text{Vec} \; t \; (n&#43;1)} $$ <p>These rules read: the empty list \([]\) is zero-length vector of any type \(t\). Then, if we take an element \(x\) of type \(t\), and an \(n\)-long vector \(\textit{xs}\) of \(t\), then we can prepend \(x\) to \(\textit{xs}\) and get an \((n+1)\)-long vector of \(t\). In Coq, we write this as follows (<a href="https://coq.inria.fr/library/Coq.Vectors.VectorDef.html#t"class="external-link">originally from here<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>):</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Inductive</span> <span class="n">t</span> <span class="n">A</span> <span class="o">:</span> <span class="kt">nat</span> <span class="o">-&gt;</span> <span class="kt">Type</span> <span class="o">:=</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">nil</span> <span class="o">:</span> <span class="n">t</span> <span class="n">A</span> <span class="n">0</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">cons</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">h</span><span class="o">:</span><span class="n">A</span><span class="o">)</span> <span class="o">(</span><span class="n">n</span><span class="o">:</span><span class="kt">nat</span><span class="o">),</span> <span class="n">t</span> <span class="n">A</span> <span class="n">n</span> <span class="o">-&gt;</span> <span class="n">t</span> <span class="n">A</span> <span class="o">(</span><span class="n">S</span> <span class="n">n</span><span class="o">).</span> </span></span></code></pre></div><p>The <code>nil</code> constructor represents the empty list \([]\), and <code>cons</code> represents the operation of prepending an element (called <code>h</code> in the code and \(x\) in our inference rules) to another vector of length \(n\), which remains unnamed in the code but is called \(\textit{xs}\) in our rules.</p> <p>These two definitions work together quite well. For instance, suppose we have a vector of length \(n\). If we were to access its elements by indices starting at 0, we&rsquo;d be allowed to access indices 0 through \(n-1\). These are precisely the values of the finite natural numbers less than \(n\), \(\text{Fin} \; n \). Thus, given such an index \(\text{Fin} \; n\) and a vector \(\text{Vec} \; t \; n\), we are guaranteed to be able to retrieve the element at the given index! In our code, we will not have to worry about bounds checking.</p> <p>Of course, if our program has \(n\) elements, our program counter will be a finite number less than \(n+1\), since there&rsquo;s always the possibility of it pointing past the instructions, indicating that we&rsquo;ve finished running the program. This leads to some minor complications: we can&rsquo;t safely access the program instruction at index \(\text{Fin} \; (n+1)\). We can solve this problem by considering two cases: either our index points one past the end of the program (in which case its value is exactly the finite representation of \(n\)), or it&rsquo;s less than \(n\), in which case we can &ldquo;tighten&rdquo; the upper bound, and convert that index into a \(\text{Fin} \; n\). We formalize it in a lemma:</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day8.v" data-first-line="80" data-last-line="82"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day8.v#L80-L82">day8.v</a>, lines 80 through 82</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">80 </span><span class="lnt">81 </span><span class="lnt">82 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="kn">Lemma</span> <span class="n">fin_big_or_small</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">{</span><span class="n">n</span><span class="o">}</span> <span class="o">(</span><span class="n">f</span> <span class="o">:</span> <span class="n">fin</span> <span class="o">(</span><span class="n">S</span> <span class="n">n</span><span class="o">)),</span> </span></span><span class="line"><span class="cl"> <span class="o">(</span><span class="n">f</span> <span class="o">=</span> <span class="n">nat_to_fin</span> <span class="n">n</span><span class="o">)</span> <span class="o">\/</span> <span class="o">(</span><span class="k">exists</span> <span class="o">(</span><span class="n">f&#39;</span> <span class="o">:</span> <span class="n">fin</span> <span class="n">n</span><span class="o">),</span> <span class="n">f</span> <span class="o">=</span> <span class="n">weaken_one</span> <span class="n">f&#39;</span><span class="o">).</span> </span></span><span class="line"><span class="cl"> <span class="kn">Proof</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>There&rsquo;s a little bit of a gotcha here. Instead of translating our above statement literally, and returning a value that&rsquo;s the result of &ldquo;tightening&rdquo; our input <code>f</code>, we return a value <code>f'</code> that can be &ldquo;weakened&rdquo; to <code>f</code>. This is because &ldquo;tightening&rdquo; is not a total function - it&rsquo;s not always possible to convert a \(\text{Fin} \; (n+1)\) into a \(\text{Fin} \; n\). However, &ldquo;weakening&rdquo; \(\text{Fin} \; n\) <em>is</em> a total function, since a number less than \(n\) is, by the transitive property of a total order, also less than \(n+1\).</p> <p>The Coq proof for this claim is as follows:</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day8.v" data-first-line="88" data-last-line="97"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day8.v#L88-L97">day8.v</a>, lines 88 through 97</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">88 </span><span class="lnt">89 </span><span class="lnt">90 </span><span class="lnt">91 </span><span class="lnt">92 </span><span class="lnt">93 </span><span class="lnt">94 </span><span class="lnt">95 </span><span class="lnt">96 </span><span class="lnt">97 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="k">apply</span> <span class="n">Fin</span><span class="o">.</span><span class="n">rectS</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">intros</span> <span class="n">n</span><span class="o">.</span> <span class="k">destruct</span> <span class="n">n</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="k">left</span><span class="o">.</span> <span class="kp">reflexivity</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="k">right</span><span class="o">.</span> <span class="k">exists</span> <span class="n">F1</span><span class="o">.</span> <span class="k">auto</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">intros</span> <span class="n">n</span> <span class="n">p</span> <span class="n">IH</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">destruct</span> <span class="n">IH</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="k">left</span><span class="o">.</span> <span class="k">rewrite</span> <span class="n">H</span><span class="o">.</span> <span class="kp">reflexivity</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="k">right</span><span class="o">.</span> <span class="k">destruct</span> <span class="n">H</span> <span class="k">as</span> <span class="o">[</span><span class="n">f&#39;</span> <span class="n">Heq</span><span class="o">].</span> </span></span><span class="line"><span class="cl"> <span class="k">exists</span> <span class="o">(</span><span class="n">FS</span> <span class="n">f&#39;</span><span class="o">).</span> <span class="k">simpl</span><span class="o">.</span> <span class="k">rewrite</span> <span class="n">Heq</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="kp">reflexivity</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The <code>Fin.rectS</code> function is a convenient way to perform inductive proofs over our finite natural numbers. Informally, our proof proceeds as follows:</p> <ul> <li>If the current finite natural number is zero, take a look at the &ldquo;bound&rdquo; (which we assume is nonzero, since there isn&rsquo;t a natural number less than zero). <ul> <li>If this &ldquo;bounding number&rdquo; is one, our <code>f</code> can&rsquo;t be tightened any further, since doing so would create a number less than zero. Fortunately, in this case, <code>n</code> must be <code>0</code>, so <code>f</code> is the finite representation of <code>n</code>.</li> <li>Otherwise, <code>f</code> is most definitely a weakened version of another <code>f'</code>, since the tightest possible type for zero has a &ldquo;bounding number&rdquo; of one, and our &ldquo;bounding number&rdquo; is greater than that. We return a tighter version of our finite zero.</li> </ul> </li> <li>If our number is a successor of another finite number, we check if that other number can itself be tightened. <ul> <li>If it can&rsquo;t be tightened, then our smaller number is a finite representation of <code>n-1</code>. This, in turn, means that adding one to it will be the finite representation of <code>n</code> (if \(x\) is equal to \(n-1\), then \(x+1\) is equal to \(n\)).</li> <li>If it <em>can</em> be tightened, then so can the successor (if \(x\) is less than \(n-1\), then \(x+1\) is less than \(n\)).</li> </ul> </li> </ul> <p>Next, let&rsquo;s talk about addition, specifically the kind of addition done by the \(\texttt{jmp}\) instruction. We can always add an integer to a natural number, but we can at best guarantee that the result will be an integer. For instance, we can add <code>-1000</code> to <code>1</code>, and get <code>-999</code>, which is <em>not</em> a natural number. We implement this kind of addition in a function called <code>jump_t</code>:</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day8.v" data-first-line="56" data-last-line="56"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day8.v#L56-L56">day8.v</a>, line 56</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">56 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="kn">Definition</span> <span class="n">jump_t</span> <span class="o">{</span><span class="n">n</span><span class="o">}</span> <span class="o">(</span><span class="n">pc</span> <span class="o">:</span> <span class="n">fin</span> <span class="n">n</span><span class="o">)</span> <span class="o">(</span><span class="n">off</span> <span class="o">:</span> <span class="n">t</span><span class="o">)</span> <span class="o">:</span> <span class="n">t</span> <span class="o">:=</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>At the moment, its definition is not particularly important. What is important, though, is that it takes a bounded natural number <code>pc</code> (our program counter), an integer <code>off</code> (the offset provided by the jump instruction) and returns another integer representing the final offset. Why are integers of type <code>t</code>? Well, it so happens that Coq provides facilities for working with arbitrary implementations of integers, without relying on how they are implemented under the hood. This can be seen in its <a href="https://coq.inria.fr/library/Coq.ZArith.Int.html"class="external-link"><code>Coq.ZArith.Int</code><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> module, which describes what functions and types an implementation of integers should provide. Among those is <code>t</code>, the type of an integer in such an arbitrary implementation. We too will not make an assumption about how the integers are implemented, and simply use this generic <code>t</code> from now on.</p> <p>Now, suppose we wanted to write a function that <em>does</em> return a valid program counter after adding the offset to it. Since it&rsquo;s possible for this function to fail (for instance, if the offset is very negative), it has to return <code>option (fin (S n))</code>. That is, this function may either fail (returning <code>None</code>) or succeed, returning <code>Some f</code>, where <code>f</code> is of type <code>fin (S n)</code>, aka \(\text{Fin} \; (n + 1)\). Here&rsquo;s the function in Coq (again, don&rsquo;t worry too much about the definition):</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day8.v" data-first-line="61" data-last-line="61"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day8.v#L61-L61">day8.v</a>, line 61</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">61 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="kn">Definition</span> <span class="n">valid_jump_t</span> <span class="o">{</span><span class="n">n</span><span class="o">}</span> <span class="o">(</span><span class="n">pc</span> <span class="o">:</span> <span class="n">fin</span> <span class="n">n</span><span class="o">)</span> <span class="o">(</span><span class="n">off</span> <span class="o">:</span> <span class="n">t</span><span class="o">)</span> <span class="o">:</span> <span class="n">option</span> <span class="o">(</span><span class="n">fin</span> <span class="o">(</span><span class="n">S</span> <span class="n">n</span><span class="o">))</span> <span class="o">:=</span> <span class="o">@</span><span class="n">clamp</span> <span class="o">(</span><span class="n">S</span> <span class="n">n</span><span class="o">)</span> <span class="o">(</span><span class="n">jump_t</span> <span class="n">pc</span> <span class="n">off</span><span class="o">).</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We will make use of this function when we define and verify our semantics. Let&rsquo;s take a look at that next.</p> <a href="#semantics-in-coq"> <h4 id="semantics-in-coq">Semantics in Coq</h4> </a> <p>Now that we&rsquo;ve seen finite sets and vectors, it&rsquo;s time to use them to encode our semantics in Coq. Before we do anything else, we need to provide Coq definitions for the various components of our language, much like what we did with <code>tinylang</code>. We can start with opcodes:</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day8.v" data-first-line="20" data-last-line="23"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day8.v#L20-L23">day8.v</a>, lines 20 through 23</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="kn">Inductive</span> <span class="n">opcode</span> <span class="o">:</span> <span class="kt">Type</span> <span class="o">:=</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">add</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">nop</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">jmp</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Now we can define a few other parts of our language and semantics, namely states, instructions and programs (which I called &ldquo;inputs&rdquo; since, we&rsquo;ll, they&rsquo;re our puzzle input). A state is simply the 3-tuple of the program counter, the set of valid program counters, and the accumulator. We write it as follows:</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day8.v" data-first-line="33" data-last-line="33"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day8.v#L33-L33">day8.v</a>, line 33</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">33 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="kn">Definition</span> <span class="n">state</span> <span class="n">n</span> <span class="o">:</span> <span class="kt">Type</span> <span class="o">:=</span> <span class="o">(</span><span class="n">fin</span> <span class="o">(</span><span class="n">S</span> <span class="n">n</span><span class="o">)</span> <span class="o">*</span> <span class="k">set</span> <span class="o">(</span><span class="n">fin</span> <span class="n">n</span><span class="o">)</span> <span class="o">*</span> <span class="n">t</span><span class="o">).</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The star <code>*</code> is used here to represent a <a href="https://en.wikipedia.org/wiki/Product_type"class="external-link">product type<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> rather than arithmetic multiplication. Our state type accepts an argument, <code>n</code>, much like a finite natural number or a vector. In fact, this <code>n</code> is passed on to the state&rsquo;s program counter and set types. Rightly, a state for a program of length \(n\) will not be of the same type as a state for a program of length \(n+1\).</p> <p>An instruction is also a tuple, but this time containing only two elements: the opcode and the number. We write this as follows:</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day8.v" data-first-line="36" data-last-line="36"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day8.v#L36-L36">day8.v</a>, line 36</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">36 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="kn">Definition</span> <span class="n">inst</span> <span class="o">:</span> <span class="kt">Type</span> <span class="o">:=</span> <span class="o">(</span><span class="n">opcode</span> <span class="o">*</span> <span class="n">t</span><span class="o">).</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Finally, we have to define the type of a program. This type will also be indexed by <code>n</code>, the program&rsquo;s length. A program of length <code>n</code> is simply a vector of instructions <code>inst</code> of length <code>n</code>. This leads to the following definition:</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day8.v" data-first-line="38" data-last-line="38"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day8.v#L38-L38">day8.v</a>, line 38</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">38 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="kn">Definition</span> <span class="n">input</span> <span class="o">(</span><span class="n">n</span> <span class="o">:</span> <span class="kt">nat</span><span class="o">)</span> <span class="o">:=</span> <span class="n">VectorDef</span><span class="o">.</span><span class="n">t</span> <span class="n">inst</span> <span class="n">n</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>So far, so good! Finally, it&rsquo;s time to get started on the semantics themselves. We begin with the inductive definition of \((\rightarrow_i)\). I think this is fairly straightforward. However, we do use <code>t</code> instead of \(n\) from the rules, and we use <code>FS</code> instead of \(+1\). Also, we make the formerly implicit assumption that \(c+n\) is valid explicit, by providing a proof that <code>valid_jump_t pc t = Some pc'</code>.</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day8.v" data-first-line="103" data-last-line="110"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day8.v#L103-L110">day8.v</a>, lines 103 through 110</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">103 </span><span class="lnt">104 </span><span class="lnt">105 </span><span class="lnt">106 </span><span class="lnt">107 </span><span class="lnt">108 </span><span class="lnt">109 </span><span class="lnt">110 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="kn">Inductive</span> <span class="n">step_noswap</span> <span class="o">{</span><span class="n">n</span><span class="o">}</span> <span class="o">:</span> <span class="n">inst</span> <span class="o">-&gt;</span> <span class="o">(</span><span class="n">fin</span> <span class="n">n</span> <span class="o">*</span> <span class="n">t</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="o">(</span><span class="n">fin</span> <span class="o">(</span><span class="n">S</span> <span class="n">n</span><span class="o">)</span> <span class="o">*</span> <span class="n">t</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="kt">Prop</span> <span class="o">:=</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">step_noswap_add</span> <span class="o">:</span> <span class="k">forall</span> <span class="n">pc</span> <span class="n">acc</span> <span class="n">t</span><span class="o">,</span> </span></span><span class="line"><span class="cl"> <span class="n">step_noswap</span> <span class="o">(</span><span class="n">add</span><span class="o">,</span> <span class="n">t</span><span class="o">)</span> <span class="o">(</span><span class="n">pc</span><span class="o">,</span> <span class="n">acc</span><span class="o">)</span> <span class="o">(</span><span class="n">FS</span> <span class="n">pc</span><span class="o">,</span> <span class="n">M</span><span class="o">.</span><span class="n">add</span> <span class="n">acc</span> <span class="n">t</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">step_noswap_nop</span> <span class="o">:</span> <span class="k">forall</span> <span class="n">pc</span> <span class="n">acc</span> <span class="n">t</span><span class="o">,</span> </span></span><span class="line"><span class="cl"> <span class="n">step_noswap</span> <span class="o">(</span><span class="n">nop</span><span class="o">,</span> <span class="n">t</span><span class="o">)</span> <span class="o">(</span><span class="n">pc</span><span class="o">,</span> <span class="n">acc</span><span class="o">)</span> <span class="o">(</span><span class="n">FS</span> <span class="n">pc</span><span class="o">,</span> <span class="n">acc</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">step_noswap_jmp</span> <span class="o">:</span> <span class="k">forall</span> <span class="n">pc</span> <span class="n">pc&#39;</span> <span class="n">acc</span> <span class="n">t</span><span class="o">,</span> </span></span><span class="line"><span class="cl"> <span class="n">valid_jump_t</span> <span class="n">pc</span> <span class="n">t</span> <span class="o">=</span> <span class="n">Some</span> <span class="n">pc&#39;</span> <span class="o">-&gt;</span> </span></span><span class="line"><span class="cl"> <span class="n">step_noswap</span> <span class="o">(</span><span class="n">jmp</span><span class="o">,</span> <span class="n">t</span><span class="o">)</span> <span class="o">(</span><span class="n">pc</span><span class="o">,</span> <span class="n">acc</span><span class="o">)</span> <span class="o">(</span><span class="n">pc&#39;</span><span class="o">,</span> <span class="n">acc</span><span class="o">).</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Next, it will help us to combine the premises for &ldquo;failed&rdquo; and &ldquo;ok&rdquo; terminations into Coq data types. This will make it easier for us to formulate a lemma later on. Here are the definitions:</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day8.v" data-first-line="112" data-last-line="117"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day8.v#L112-L117">day8.v</a>, lines 112 through 117</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">112 </span><span class="lnt">113 </span><span class="lnt">114 </span><span class="lnt">115 </span><span class="lnt">116 </span><span class="lnt">117 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="kn">Inductive</span> <span class="kp">done</span> <span class="o">{</span><span class="n">n</span><span class="o">}</span> <span class="o">:</span> <span class="n">input</span> <span class="n">n</span> <span class="o">-&gt;</span> <span class="n">state</span> <span class="n">n</span> <span class="o">-&gt;</span> <span class="kt">Prop</span> <span class="o">:=</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">done_prog</span> <span class="o">:</span> <span class="k">forall</span> <span class="n">inp</span> <span class="n">v</span> <span class="n">acc</span><span class="o">,</span> <span class="kp">done</span> <span class="n">inp</span> <span class="o">(</span><span class="n">nat_to_fin</span> <span class="n">n</span><span class="o">,</span> <span class="n">v</span><span class="o">,</span> <span class="n">acc</span><span class="o">).</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kn">Inductive</span> <span class="n">stuck</span> <span class="o">{</span><span class="n">n</span><span class="o">}</span> <span class="o">:</span> <span class="n">input</span> <span class="n">n</span> <span class="o">-&gt;</span> <span class="n">state</span> <span class="n">n</span> <span class="o">-&gt;</span> <span class="kt">Prop</span> <span class="o">:=</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">stuck_prog</span> <span class="o">:</span> <span class="k">forall</span> <span class="n">inp</span> <span class="n">pc&#39;</span> <span class="n">v</span> <span class="n">acc</span><span class="o">,</span> </span></span><span class="line"><span class="cl"> <span class="o">~</span> <span class="n">set_In</span> <span class="n">pc&#39;</span> <span class="n">v</span> <span class="o">-&gt;</span> <span class="n">stuck</span> <span class="n">inp</span> <span class="o">(</span><span class="n">weaken_one</span> <span class="n">pc&#39;</span><span class="o">,</span> <span class="n">v</span><span class="o">,</span> <span class="n">acc</span><span class="o">).</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Since all of out &ldquo;termination&rdquo; rules start and end in the same state, there&rsquo;s no reason to write that state twice. Thus, both <code>done</code> and <code>stuck</code> only take the input <code>inp</code>, and the state, which includes the accumulator <code>acc</code>, the set of allowed program counters <code>v</code>, and the program counter at which the program came to an end. When the program terminates successfully, this program counter will be equal to the length of the program <code>n</code>, so we use <code>nat_to_fin n</code>. On the other hand, if the program terminates in as stuck state, it must be that it terminated at a program counter that points to an instruction. Thus, this program counter is actually a \(\text{Fin} \; n\), and not a \(\text{Fin} \ (n+1)\), and is not in the set of allowed program counters. We use the same &ldquo;weakening&rdquo; trick we saw earlier to represent this.</p> <p>Finally, we encode the three inference rules we came up with:</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day8.v" data-first-line="119" data-last-line="126"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day8.v#L119-L126">day8.v</a>, lines 119 through 126</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">119 </span><span class="lnt">120 </span><span class="lnt">121 </span><span class="lnt">122 </span><span class="lnt">123 </span><span class="lnt">124 </span><span class="lnt">125 </span><span class="lnt">126 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="kn">Inductive</span> <span class="n">run_noswap</span> <span class="o">{</span><span class="n">n</span><span class="o">}</span> <span class="o">:</span> <span class="n">input</span> <span class="n">n</span> <span class="o">-&gt;</span> <span class="n">state</span> <span class="n">n</span> <span class="o">-&gt;</span> <span class="n">state</span> <span class="n">n</span> <span class="o">-&gt;</span> <span class="kt">Prop</span> <span class="o">:=</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">run_noswap_ok</span> <span class="o">:</span> <span class="k">forall</span> <span class="n">inp</span> <span class="n">st</span><span class="o">,</span> <span class="kp">done</span> <span class="n">inp</span> <span class="n">st</span> <span class="o">-&gt;</span> <span class="n">run_noswap</span> <span class="n">inp</span> <span class="n">st</span> <span class="n">st</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">run_noswap_fail</span> <span class="o">:</span> <span class="k">forall</span> <span class="n">inp</span> <span class="n">st</span><span class="o">,</span> <span class="n">stuck</span> <span class="n">inp</span> <span class="n">st</span> <span class="o">-&gt;</span> <span class="n">run_noswap</span> <span class="n">inp</span> <span class="n">st</span> <span class="n">st</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">run_noswap_trans</span> <span class="o">:</span> <span class="k">forall</span> <span class="n">inp</span> <span class="n">pc</span> <span class="n">pc&#39;</span> <span class="n">v</span> <span class="n">acc</span> <span class="n">acc&#39;</span> <span class="n">st&#39;</span><span class="o">,</span> </span></span><span class="line"><span class="cl"> <span class="n">set_In</span> <span class="n">pc</span> <span class="n">v</span> <span class="o">-&gt;</span> </span></span><span class="line"><span class="cl"> <span class="n">step_noswap</span> <span class="o">(</span><span class="n">nth</span> <span class="n">inp</span> <span class="n">pc</span><span class="o">)</span> <span class="o">(</span><span class="n">pc</span><span class="o">,</span> <span class="n">acc</span><span class="o">)</span> <span class="o">(</span><span class="n">pc&#39;</span><span class="o">,</span> <span class="n">acc&#39;</span><span class="o">)</span> <span class="o">-&gt;</span> </span></span><span class="line"><span class="cl"> <span class="n">run_noswap</span> <span class="n">inp</span> <span class="o">(</span><span class="n">pc&#39;</span><span class="o">,</span> <span class="n">set_remove</span> <span class="n">Fin</span><span class="o">.</span><span class="n">eq_dec</span> <span class="n">pc</span> <span class="n">v</span><span class="o">,</span> <span class="n">acc&#39;</span><span class="o">)</span> <span class="n">st&#39;</span> <span class="o">-&gt;</span> </span></span><span class="line"><span class="cl"> <span class="n">run_noswap</span> <span class="n">inp</span> <span class="o">(</span><span class="n">weaken_one</span> <span class="n">pc</span><span class="o">,</span> <span class="n">v</span><span class="o">,</span> <span class="n">acc</span><span class="o">)</span> <span class="n">st&#39;</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Notice that we fused two of the premises in the last rule. Instead of naming the instruction at the current program counter (by writing \(p[c] = i\)) and using it in another premise, we simply use <code>nth inp pc</code>, which corresponds to \(p[c]\) in our &ldquo;paper&rdquo; semantics.</p> <p>Before we go on writing some actual proofs, we have one more thing we have to address. Earlier, we said:</p> <blockquote> <p>All jumps will lead either to an instruction, or right to the end of a program.</p> </blockquote> <p>To make Coq aware of this constraint, we&rsquo;ll have to formalize it. To start off, we&rsquo;ll define the notion of a &ldquo;valid instruction&rdquo;, which is guaranteed to keep the program counter in the correct range. There are a couple of ways to do this, but we&rsquo;ll use yet another definition based on inference rules. First, though, observe that the same instruction may be valid for one program, and invalid for another. For instance, \(\texttt{jmp} \; 100\) is perfectly valid for a program with thousands of instructions, but if it occurs in a program with only 3 instructions, it will certainly lead to disaster. Specifically, the validity of an instruction depends on the length of the program in which it resides, and the program counter at which it&rsquo;s encountered. Thus, we refine our idea of validity to &ldquo;being valid for a program of length \(n\) at program counter \(f\)&rdquo;. For this, we can use the following two inference rules:</p> $$ \frac {c : \text{Fin} \; n} {\texttt{add} \; t \; \text{valid for} \; n, c } \quad \frac {c : \text{Fin} \; n \quad o \in \{\texttt{nop}, \texttt{jmp}\} \quad J_v(c, t) = \text{Some} \; c&amp;#39; } {o \; t \; \text{valid for} \; n, c } $$ <p>The first rule states that if a program has length \(n\), then \(\texttt{add}\) is valid at any program counter whose value is less than \(n\). This is because running \(\texttt{add}\) will increment the program counter \(c\) by 1, and thus, create a new program counter that&rsquo;s less than \(n+1\), which, as we discussed above, is perfectly valid.</p> <p>The second rule works for the other two instructions. It has an extra premise: the result of <code>jump_valid_t</code> (written as \(J_v\)) has to be \(\text{Some} \; c'\), that is, <code>jump_valid_t</code> must succeed. Note that we require this even for no-ops, since it later turns out that one of the them may be a jump after all.</p> <p>We now have our validity rules. If an instruction satisfies them for a given program and at a given program counter, evaluating it will always result in a program counter that has a proper value. We encode the rules in Coq as follows:</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day8.v" data-first-line="152" data-last-line="157"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day8.v#L152-L157">day8.v</a>, lines 152 through 157</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">152 </span><span class="lnt">153 </span><span class="lnt">154 </span><span class="lnt">155 </span><span class="lnt">156 </span><span class="lnt">157 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="kn">Inductive</span> <span class="n">valid_inst</span> <span class="o">{</span><span class="n">n</span><span class="o">}</span> <span class="o">:</span> <span class="n">inst</span> <span class="o">-&gt;</span> <span class="n">fin</span> <span class="n">n</span> <span class="o">-&gt;</span> <span class="kt">Prop</span> <span class="o">:=</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">valid_inst_add</span> <span class="o">:</span> <span class="k">forall</span> <span class="n">t</span> <span class="n">f</span><span class="o">,</span> <span class="n">valid_inst</span> <span class="o">(</span><span class="n">add</span><span class="o">,</span> <span class="n">t</span><span class="o">)</span> <span class="n">f</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">valid_inst_nop</span> <span class="o">:</span> <span class="k">forall</span> <span class="n">t</span> <span class="n">f</span> <span class="n">f&#39;</span><span class="o">,</span> </span></span><span class="line"><span class="cl"> <span class="n">valid_jump_t</span> <span class="n">f</span> <span class="n">t</span> <span class="o">=</span> <span class="n">Some</span> <span class="n">f&#39;</span> <span class="o">-&gt;</span> <span class="n">valid_inst</span> <span class="o">(</span><span class="n">nop</span><span class="o">,</span> <span class="n">t</span><span class="o">)</span> <span class="n">f</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">valid_inst_jmp</span> <span class="o">:</span> <span class="k">forall</span> <span class="n">t</span> <span class="n">f</span> <span class="n">f&#39;</span><span class="o">,</span> </span></span><span class="line"><span class="cl"> <span class="n">valid_jump_t</span> <span class="n">f</span> <span class="n">t</span> <span class="o">=</span> <span class="n">Some</span> <span class="n">f&#39;</span> <span class="o">-&gt;</span> <span class="n">valid_inst</span> <span class="o">(</span><span class="n">jmp</span><span class="o">,</span> <span class="n">t</span><span class="o">)</span> <span class="n">f</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Note that we have three rules instead of two. This is because we &ldquo;unfolded&rdquo; \(o\) from our second rule: rather than using set notation (or &ldquo;or&rdquo;), we just generated two rules that vary in nothing but the operation involved.</p> <p>Of course, we must have that every instruction in a program is valid. We don&rsquo;t really need inference rules for this, as much as a &ldquo;forall&rdquo; quantifier. A program \(p\) of length \(n\) is valid if the following holds:</p> $$ \forall (c : \text{Fin} \; n). p[c] \; \text{valid for} \; n, c $$ <p>That is, for every possible in-bounds program counter \(c\), the instruction at the program counter is valid. We can now encode this in Coq, too:</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day8.v" data-first-line="160" data-last-line="161"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day8.v#L160-L161">day8.v</a>, lines 160 through 161</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">160 </span><span class="lnt">161 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="kn">Definition</span> <span class="n">valid_input</span> <span class="o">{</span><span class="n">n</span><span class="o">}</span> <span class="o">(</span><span class="n">inp</span> <span class="o">:</span> <span class="n">input</span> <span class="n">n</span><span class="o">)</span> <span class="o">:</span> <span class="kt">Prop</span> <span class="o">:=</span> <span class="k">forall</span> <span class="o">(</span><span class="n">pc</span> <span class="o">:</span> <span class="n">fin</span> <span class="n">n</span><span class="o">),</span> </span></span><span class="line"><span class="cl"> <span class="n">valid_inst</span> <span class="o">(</span><span class="n">nth</span> <span class="n">inp</span> <span class="n">pc</span><span class="o">)</span> <span class="n">pc</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>In the above, <code>n</code> is made implicit where possible. Since \(c\) (called <code>pc</code> in the code) is of type \(\text{Fin} \; n\), there&rsquo;s no need to write \(n\) <em>again</em>. The curly braces tell Coq to infer that argument where possible.</p> <a href="#proving-termination"> <h3 id="proving-termination">Proving Termination</h3> </a> <p>Here we go! It&rsquo;s finally time to make some claims about our definitions. Who knows - maybe we wrote down total garbage! We will be creating several related lemmas and theorems. All of them share two common assumptions:</p> <ul> <li>We have some valid program <code>inp</code> of length <code>n</code>.</li> <li>This program is a valid input, that is, <code>valid_input</code> holds for it. There&rsquo;s no sense in arguing based on an invalid input program.</li> </ul> <p>We represent these grouped assumptions by opening a Coq <code>Section</code>, which we call <code>ValidInput</code>, and listing our assumptions:</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day8.v" data-first-line="163" data-last-line="166"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day8.v#L163-L166">day8.v</a>, lines 163 through 166</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">163 </span><span class="lnt">164 </span><span class="lnt">165 </span><span class="lnt">166 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="kn">Section</span> <span class="n">ValidInput</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="kn">Variable</span> <span class="n">n</span> <span class="o">:</span> <span class="kt">nat</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="kn">Variable</span> <span class="n">inp</span> <span class="o">:</span> <span class="n">input</span> <span class="n">n</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="kn">Hypothesis</span> <span class="n">Hv</span> <span class="o">:</span> <span class="n">valid_input</span> <span class="n">inp</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We had to also explicitly mention the length <code>n</code> of our program. From now on, the variables <code>n</code>, <code>inp</code>, and <code>Hv</code> will be available to all of the proofs we write in this section. The first proof is rather simple. The claim is:</p> <blockquote> <p>For our valid program, at any program counter <code>pc</code> and accumulator <code>acc</code>, there must exist another program counter <code>pc'</code> and accumulator <code>acc'</code> such that the instruction evaluation relation \((\rightarrow_i)\) connects the two. That is, valid addresses aside, we can always make a step.</p> </blockquote> <p>Here is this claim encoded in Coq:</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day8.v" data-first-line="168" data-last-line="169"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day8.v#L168-L169">day8.v</a>, lines 168 through 169</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">168 </span><span class="lnt">169 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="kn">Theorem</span> <span class="n">valid_input_can_step</span> <span class="o">:</span> <span class="k">forall</span> <span class="n">pc</span> <span class="n">acc</span><span class="o">,</span> <span class="k">exists</span> <span class="n">pc&#39;</span> <span class="n">acc&#39;</span><span class="o">,</span> </span></span><span class="line"><span class="cl"> <span class="n">step_noswap</span> <span class="o">(</span><span class="n">nth</span> <span class="n">inp</span> <span class="n">pc</span><span class="o">)</span> <span class="o">(</span><span class="n">pc</span><span class="o">,</span> <span class="n">acc</span><span class="o">)</span> <span class="o">(</span><span class="n">pc&#39;</span><span class="o">,</span> <span class="n">acc&#39;</span><span class="o">).</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We start our proof by introducing all the relevant variables into the global context. I&rsquo;ve mentioned this when I wrote about day 1, but here&rsquo;s the gist: the <code>intros</code> keyword takes variables from a <code>forall</code>, and makes them concrete. In short, <code>intros x</code> is very much like saying &ldquo;suppose we have an <code>x</code>&rdquo;, and going on with the proof.</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day8.v" data-first-line="170" data-last-line="171"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day8.v#L170-L171">day8.v</a>, lines 170 through 171</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">170 </span><span class="lnt">171 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="kn">Proof</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">intros</span> <span class="n">pc</span> <span class="n">acc</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Here, we said &ldquo;take any program counter <code>pc</code> and any accumulator <code>acc</code>&rdquo;. Now what? Well, first of all, we want to take a look at the instruction at the current <code>pc</code>. We know that this instruction is a combination of an opcode and a number, so we use <code>destruct</code> to get access to both of these parts:</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day8.v" data-first-line="172" data-last-line="172"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day8.v#L172-L172">day8.v</a>, line 172</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">172 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="k">destruct</span> <span class="o">(</span><span class="n">nth</span> <span class="n">inp</span> <span class="n">pc</span><span class="o">)</span> <span class="n">eqn</span><span class="o">:</span><span class="n">Hop</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Now, Coq reports the following proof state:</p> <pre tabindex="0"><code>1 subgoal n : nat inp : input n Hv : valid_input inp pc : Fin.t n acc : t o : opcode t0 : t Hop : nth inp pc = (o, t0) ========================= (1 / 1) exists (pc&#39; : fin (S n)) (acc&#39; : t), step_noswap (o, t0) (pc, acc) (pc&#39;, acc&#39;) </code></pre><p>We have some opcode <code>o</code>, and some associated number <code>t0</code>, and we must show that there exist a <code>pc'</code> and <code>acc'</code> to which we can move on. To prove that something exists in Coq, we must provide an instance of that &ldquo;something&rdquo;. If we claim that there exists a dog that&rsquo;s not a good boy, we better have this elusive creature in hand. In other words, proofs in Coq are <a href="https://en.wikipedia.org/wiki/Constructive_proof"class="external-link">constructive<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. Without knowing the kind of operation we&rsquo;re dealing with, we can&rsquo;t say for sure how the step will proceed. Thus, we proceed by case analysis on <code>o</code>.</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day8.v" data-first-line="173" data-last-line="173"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day8.v#L173-L173">day8.v</a>, line 173</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">173 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="k">destruct</span> <span class="n">o</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>There are three possible cases we have to consider, one for each type of instruction.</p> <ul> <li>If the instruction is \(\texttt{add}\), we know that <code>pc' = pc + 1</code> and <code>acc' = acc + t0</code>. That is, the program counter is simply incremented, and the accumulator is modified with the number part of the instruction.</li> <li>If the instruction is \(\texttt{nop}\), the program coutner will again be incremented (<code>pc' = pc + 1</code>), but the accumulator will stay the same, so <code>acc' = acc</code>.</li> <li>If the instruction is \(\texttt{jmp}\), things are more complicated. We must rely on the assumption that our input is valid, which tells us that adding <code>t0</code> to our <code>pc</code> will result in <code>Some f</code>, and not <code>None</code>. Given this, we have <code>pc' = f</code>, and <code>acc' = acc</code>.</li> </ul> <p>This is how these three cases are translated to Coq:</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day8.v" data-first-line="174" data-last-line="177"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day8.v#L174-L177">day8.v</a>, lines 174 through 177</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">174 </span><span class="lnt">175 </span><span class="lnt">176 </span><span class="lnt">177 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">exists</span> <span class="o">(</span><span class="n">FS</span> <span class="n">pc</span><span class="o">).</span> <span class="k">exists</span> <span class="o">(</span><span class="n">M</span><span class="o">.</span><span class="n">add</span> <span class="n">acc</span> <span class="n">t0</span><span class="o">).</span> <span class="k">apply</span> <span class="n">step_noswap_add</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">exists</span> <span class="o">(</span><span class="n">FS</span> <span class="n">pc</span><span class="o">).</span> <span class="k">exists</span> <span class="n">acc</span><span class="o">.</span> <span class="k">eapply</span> <span class="n">step_noswap_nop</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="n">specialize</span> <span class="o">(</span><span class="n">Hv</span> <span class="n">pc</span><span class="o">).</span> <span class="k">rewrite</span> <span class="n">Hop</span> <span class="k">in</span> <span class="n">Hv</span><span class="o">.</span> <span class="k">inversion</span> <span class="n">Hv</span><span class="o">;</span> <span class="k">subst</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">exists</span> <span class="n">f&#39;</span><span class="o">.</span> <span class="k">exists</span> <span class="n">acc</span><span class="o">.</span> <span class="k">eapply</span> <span class="n">step_noswap_jmp</span><span class="o">.</span> <span class="k">apply</span> <span class="n">H0</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>For the first two cases, we simply provide the values we expect for <code>pc'</code> and <code>acc'</code>, and apply the corresponding inference rule that is satisfied by these values. For the third case, we have to invoke <code>Hv</code>, the hypothesis that our input is valid. In particular, we care about the instruction at <code>pc</code>, so we use <code>specialize</code> to plug <code>pc</code> into the more general hypothesis. We then replace <code>nth inp pc</code> with its known value, <code>(jmp, t0)</code>. This tells us the following, in Coq&rsquo;s words:</p> <pre tabindex="0"><code>Hv : valid_inst (jmp, t0) pc </code></pre><p>That is, <code>(jmp, t0)</code> is a valid instruction at <code>pc</code>. Then, using Coq&rsquo;s <code>inversion</code> tactic, we ask: how is this possible? There is only one inference rule that gives us such a conclusion, and it is named <code>valid_inst_jmp</code> in our Coq code. Since we have a proof that our <code>jmp</code> is valid, it must mean that this rule was used. Furthermore, since this rule requires that <code>valid_jump_t</code> evaluates to <code>Some f'</code>, we know that this must be the case here! Coq now has adds the following two lines to our proof state:</p> <pre tabindex="0"><code>f&#39; : fin (S n) H0 : valid_jump_t pc t0 = Some f&#39; </code></pre><p>Finally, we specify, as mentioned earlier, that <code>pc' = f'</code> and <code>acc' = acc</code>. As before, we apply the corresponding step rule for <code>jmp</code>. When it asks for a proof that <code>valid_jump_t</code> produces a valid program counter, we hand it <code>H0</code> using <code>apply H0</code>. And with that, Coq is happy!</p> <p>Next, we prove a claim that a valid program can always do <em>something</em>, and that something is one of three things:</p> <ul> <li>It can terminate in the &ldquo;ok&rdquo; state if the program counter reaches the programs&rsquo; end.</li> <li>It can terminate with an error if it&rsquo;s currently at a program counter that is not included in the valid set.</li> <li>Otherwise, it can run the current instruction and advance to a &ldquo;next&rdquo; state.</li> </ul> <p>Alternatively, we could say that one of the inference rules for \((\Rightarrow_p)\) must apply. This is not the case if the input is not valid, since, as I said before, an arbitrary input program can lead us to jump to a negative address (or to an address <em>way</em> past the end of the program). Here&rsquo;s the claim, translated to Coq:</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day8.v" data-first-line="181" data-last-line="186"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day8.v#L181-L186">day8.v</a>, lines 181 through 186</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">181 </span><span class="lnt">182 </span><span class="lnt">183 </span><span class="lnt">184 </span><span class="lnt">185 </span><span class="lnt">186 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="kn">Theorem</span> <span class="n">valid_input_progress</span> <span class="o">:</span> <span class="k">forall</span> <span class="n">pc</span> <span class="n">v</span> <span class="n">acc</span><span class="o">,</span> </span></span><span class="line"><span class="cl"> <span class="o">(</span><span class="n">pc</span> <span class="o">=</span> <span class="n">nat_to_fin</span> <span class="n">n</span> <span class="o">/\</span> <span class="kp">done</span> <span class="n">inp</span> <span class="o">(</span><span class="n">pc</span><span class="o">,</span> <span class="n">v</span><span class="o">,</span> <span class="n">acc</span><span class="o">))</span> <span class="o">\/</span> </span></span><span class="line"><span class="cl"> <span class="o">(</span><span class="k">exists</span> <span class="n">pcs</span><span class="o">,</span> <span class="n">pc</span> <span class="o">=</span> <span class="n">weaken_one</span> <span class="n">pcs</span> <span class="o">/\</span> </span></span><span class="line"><span class="cl"> <span class="o">((~</span> <span class="n">set_In</span> <span class="n">pcs</span> <span class="n">v</span> <span class="o">/\</span> <span class="n">stuck</span> <span class="n">inp</span> <span class="o">(</span><span class="n">pc</span><span class="o">,</span> <span class="n">v</span><span class="o">,</span> <span class="n">acc</span><span class="o">))</span> <span class="o">\/</span> </span></span><span class="line"><span class="cl"> <span class="o">(</span><span class="k">exists</span> <span class="n">pc&#39;</span> <span class="n">acc&#39;</span><span class="o">,</span> <span class="n">set_In</span> <span class="n">pcs</span> <span class="n">v</span> <span class="o">/\</span> </span></span><span class="line"><span class="cl"> <span class="n">step_noswap</span> <span class="o">(</span><span class="n">nth</span> <span class="n">inp</span> <span class="n">pcs</span><span class="o">)</span> <span class="o">(</span><span class="n">pcs</span><span class="o">,</span> <span class="n">acc</span><span class="o">)</span> <span class="o">(</span><span class="n">pc&#39;</span><span class="o">,</span> <span class="n">acc&#39;</span><span class="o">)))).</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Informally, we can prove this as follows:</p> <ul> <li>If the current program counter is equal to the length of the program, we&rsquo;ve reached the end. Thus, the program can terminate in the &ldquo;ok&rdquo; state.</li> <li>Otherwise, the current program counter must be less than the length of the program. <ul> <li>If we&rsquo;ve already encountered this program counter (that is, if it&rsquo;s gone from the set of valid program counters), then the program will terminate in the &ldquo;error&rdquo; state.</li> <li>Otherwise, the program counter is in the set of valid instructions. By our earlier theorem, in a valid program, the instruction at any program counter can be correctly executed, taking us to the next state. Now too our program can move to this next state.</li> </ul> </li> </ul> <p>Below is the Coq translation of the above.</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day8.v" data-first-line="187" data-last-line="203"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day8.v#L187-L203">day8.v</a>, lines 187 through 203</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">187 </span><span class="lnt">188 </span><span class="lnt">189 </span><span class="lnt">190 </span><span class="lnt">191 </span><span class="lnt">192 </span><span class="lnt">193 </span><span class="lnt">194 </span><span class="lnt">195 </span><span class="lnt">196 </span><span class="lnt">197 </span><span class="lnt">198 </span><span class="lnt">199 </span><span class="lnt">200 </span><span class="lnt">201 </span><span class="lnt">202 </span><span class="lnt">203 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="kn">Proof</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">intros</span> <span class="n">pc</span> <span class="n">v</span> <span class="n">acc</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="c">(* Have we reached the end? *)</span> </span></span><span class="line"><span class="cl"> <span class="k">destruct</span> <span class="o">(</span><span class="n">fin_big_or_small</span> <span class="n">pc</span><span class="o">).</span> </span></span><span class="line"><span class="cl"> <span class="c">(* We&#39;re at the end, so we&#39;re done. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">left</span><span class="o">.</span> <span class="k">rewrite</span> <span class="n">H</span><span class="o">.</span> <span class="k">split</span><span class="o">.</span> <span class="kp">reflexivity</span><span class="o">.</span> <span class="k">apply</span> <span class="n">done_prog</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="c">(* We&#39;re not at the end. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">right</span><span class="o">.</span> <span class="k">destruct</span> <span class="n">H</span> <span class="k">as</span> <span class="o">[</span><span class="n">pcs</span> <span class="n">H</span><span class="o">].</span> <span class="k">exists</span> <span class="n">pcs</span><span class="o">.</span> <span class="k">rewrite</span> <span class="n">H</span><span class="o">.</span> <span class="k">split</span><span class="o">.</span> <span class="kp">reflexivity</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="c">(* We&#39;re not at the end. Is the PC valid? *)</span> </span></span><span class="line"><span class="cl"> <span class="k">destruct</span> <span class="o">(</span><span class="n">set_In_dec</span> <span class="n">Fin</span><span class="o">.</span><span class="n">eq_dec</span> <span class="n">pcs</span> <span class="n">v</span><span class="o">).</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="c">(* It is. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">right</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">destruct</span> <span class="o">(</span><span class="n">valid_input_can_step</span> <span class="n">pcs</span> <span class="n">acc</span><span class="o">)</span> <span class="k">as</span> <span class="o">[</span><span class="n">pc&#39;</span> <span class="o">[</span><span class="n">acc&#39;</span> <span class="n">Hstep</span><span class="o">]].</span> </span></span><span class="line"><span class="cl"> <span class="k">exists</span> <span class="n">pc&#39;</span><span class="o">;</span> <span class="k">exists</span> <span class="n">acc&#39;</span><span class="o">;</span> <span class="k">auto</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="c">(* It is not. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">left</span><span class="o">.</span> <span class="k">split</span><span class="o">;</span> <span class="k">auto</span><span class="o">.</span> <span class="k">apply</span> <span class="n">stuck_prog</span><span class="o">;</span> <span class="k">auto</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="kn">Qed</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>It doesn&rsquo;t seem like we&rsquo;re that far from being done now. A program can always take a step, and each time it does, the set of valid program counters decreases in size. Eventually, this set will become empty, so if nothing else, our program will eventually terminate in an &ldquo;error&rdquo; state. Thus, it will stop running no matter what.</p> <p>This seems like a task for induction, in this case on the size of the valid set. In particular, strong mathematical induction <span class="sidenote"> <label class="sidenote-label" for="strong-induction-note">seem to work best.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="strong-induction-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Why strong induction? If we remove a single element from a set, its size should decrease strictly by 1. Thus, why would we need to care about sets of <em>all</em> sizes less than the current set's size?<br> <br> Unfortunately, we're not working with purely mathematical sets. Coq's default facility for sets is simply a layer on top of good old lists, and makes no effort to be "correct by construction". It is thus perfectly possible to have a "set" which inlcudes an element twice. Depending on the implementation of <code>set_remove</code>, we may end up removing the repeated element multiple times, thereby shrinking the length of our list by more than 1. I'd rather not worry about implementation details like that. <span class="sidenote-delimiter">]</span> </span> </span> Someone on StackOverflow <a href="https://stackoverflow.com/questions/45872719/how-to-do-induction-on-the-length-of-a-list-in-coq"class="external-link">implemented this<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, so I&rsquo;ll just use it. The Coq theorem corresonding to strong induction on the length of a list is as follows:</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day8.v" data-first-line="205" data-last-line="207"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day8.v#L205-L207">day8.v</a>, lines 205 through 207</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">205 </span><span class="lnt">206 </span><span class="lnt">207 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="kn">Theorem</span> <span class="n">list_length_induction</span> <span class="o">{</span><span class="n">X</span> <span class="o">:</span> <span class="kt">Type</span><span class="o">}</span> <span class="o">(</span><span class="n">P</span> <span class="o">:</span> <span class="kt">list</span> <span class="n">X</span> <span class="o">-&gt;</span> <span class="kt">Prop</span><span class="o">)</span> <span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="o">(</span><span class="k">forall</span> <span class="n">l</span><span class="o">,</span> <span class="o">(</span><span class="k">forall</span> <span class="n">l&#39;</span><span class="o">,</span> <span class="n">length</span> <span class="n">l&#39;</span> <span class="o">&lt;</span> <span class="n">length</span> <span class="n">l</span> <span class="o">-&gt;</span> <span class="n">P</span> <span class="n">l&#39;</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="n">P</span> <span class="n">l</span><span class="o">)</span> <span class="o">-&gt;</span> </span></span><span class="line"><span class="cl"> <span class="k">forall</span> <span class="n">l</span><span class="o">,</span> <span class="n">P</span> <span class="n">l</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>It reads,</p> <blockquote> <p>If for some list <code>l</code>, the property <code>P</code> holding for all lists shorter than <code>l</code> means that it also holds for <code>l</code> itself, then <code>P</code> holds for all lists.</p> </blockquote> <p>This is perhaps not particularly elucidating. We can alternatively think of this as trying to prove some property for all lists <code>l</code>. We start with all empty lists. Here, we have nothing else to rely on; there are no lists shorter than the empty list, and our property must hold for all empty lists. Then, we move on to proving the property for all lists of length 1, already knowing that it holds for all empty lists. Once we&rsquo;re done there, we move on to proving that <code>P</code> holds for all lists of length 2, now knowing that it holds for all empty lists <em>and</em> all lists of length 1. We continue doing this, eventually covering lists of any length.</p> <p>Before proving termination, there&rsquo;s one last thing we have to take care off. Coq&rsquo;s standard library does not come with a proof that removing an element from a set makes it smaller; we have to provide it ourselves. Here&rsquo;s the claim encoded in Coq:</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day8.v" data-first-line="217" data-last-line="219"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day8.v#L217-L219">day8.v</a>, lines 217 through 219</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">217 </span><span class="lnt">218 </span><span class="lnt">219 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="kn">Theorem</span> <span class="n">set_remove_length</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">f</span> <span class="o">:</span> <span class="n">fin</span> <span class="n">n</span><span class="o">)</span> <span class="o">(</span><span class="n">s</span> <span class="o">:</span> <span class="k">set</span> <span class="o">(</span><span class="n">fin</span> <span class="n">n</span><span class="o">)),</span> </span></span><span class="line"><span class="cl"> <span class="n">set_In</span> <span class="n">f</span> <span class="n">s</span> <span class="o">-&gt;</span> </span></span><span class="line"><span class="cl"> <span class="n">length</span> <span class="o">(</span><span class="n">set_remove</span> <span class="n">Fin</span><span class="o">.</span><span class="n">eq_dec</span> <span class="n">f</span> <span class="n">s</span><span class="o">)</span> <span class="o">&lt;</span> <span class="n">length</span> <span class="n">s</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This reads, &ldquo;if a set <code>s</code> contains a finite natural number <code>f</code>, removing <code>f</code> from <code>s</code> reduces the set&rsquo;s size&rdquo;. The details of the proof are not particularly interesting, and I hope that you understand intuitively why this is true. Finally, we make our termination claim.</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day8.v" data-first-line="230" data-last-line="231"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day8.v#L230-L231">day8.v</a>, lines 230 through 231</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">230 </span><span class="lnt">231 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="kn">Theorem</span> <span class="n">valid_input_terminates</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">pc</span> <span class="o">:</span> <span class="n">fin</span> <span class="o">(</span><span class="n">S</span> <span class="n">n</span><span class="o">))</span> <span class="o">(</span><span class="n">v</span> <span class="o">:</span> <span class="k">set</span> <span class="o">(</span><span class="n">fin</span> <span class="n">n</span><span class="o">))</span> <span class="o">(</span><span class="n">acc</span> <span class="o">:</span> <span class="n">t</span><span class="o">),</span> </span></span><span class="line"><span class="cl"> <span class="o">(</span><span class="k">exists</span> <span class="n">pc&#39;</span><span class="o">,</span> <span class="n">run_noswap</span> <span class="n">inp</span> <span class="o">(</span><span class="n">pc</span><span class="o">,</span> <span class="n">v</span><span class="o">,</span> <span class="n">acc</span><span class="o">)</span> <span class="n">pc&#39;</span><span class="o">).</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>It&rsquo;s quite a strong claim - given <em>any</em> program counter, set of valid addresses, and accumulator, a valid input program will terminate. Let&rsquo;s take a look at the proof.</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day8.v" data-first-line="232" data-last-line="234"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day8.v#L232-L234">day8.v</a>, lines 232 through 234</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">232 </span><span class="lnt">233 </span><span class="lnt">234 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="kn">Proof</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">intros</span> <span class="n">pc</span> <span class="n">v</span><span class="o">.</span> <span class="k">generalize</span> <span class="n">dependent</span> <span class="n">pc</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">induction</span> <span class="n">v</span> <span class="k">using</span> <span class="n">list_length_induction</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We use <code>intros</code> again. However, it brings in variables in order, and we really only care about the <em>second</em> variable. We thus <code>intros</code> the first two, and then &ldquo;put back&rdquo; the first one using <code>generalize dependent</code>. Then, we proceed by induction on length, as seen above.</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day8.v" data-first-line="235" data-last-line="236"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day8.v#L235-L236">day8.v</a>, lines 235 through 236</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">235 </span><span class="lnt">236 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="k">intros</span> <span class="n">pc</span> <span class="n">acc</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">destruct</span> <span class="o">(</span><span class="n">valid_input_progress</span> <span class="n">pc</span> <span class="n">l</span> <span class="n">acc</span><span class="o">)</span> <span class="k">as</span> <span class="o">[[_</span> <span class="n">Hd</span><span class="o">]|[</span><span class="n">pc&#39;</span> <span class="o">[</span><span class="n">Hw</span> <span class="o">[[_</span> <span class="n">Hst</span><span class="o">]|[</span><span class="n">pc&#39;&#39;</span> <span class="o">[</span><span class="n">acc&#39;&#39;</span> <span class="o">[</span><span class="n">Hin</span> <span class="n">Hst</span><span class="o">]]]]]]].</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Now we&rsquo;re in the &ldquo;inductive step&rdquo;. Our inductive hypothesis is that any set of valid addresses smaller than the current one will guarantee that the program will terminate. We must show that using our set, too, will guarantee termination. We already know that a valid input, given a state, can have one of three possible outcomes: &ldquo;ok&rdquo; termination, &ldquo;failed&rdquo; termination, or a &ldquo;step&rdquo;. We use <code>destruct</code> to take a look at each of these in turn. The first two cases (&ldquo;ok&rdquo; termination and &ldquo;failed&rdquo; termination) are fairly trivial:</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day8.v" data-first-line="237" data-last-line="240"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day8.v#L237-L240">day8.v</a>, lines 237 through 240</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">237 </span><span class="lnt">238 </span><span class="lnt">239 </span><span class="lnt">240 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="o">-</span> <span class="c">(* We&#39;re done. *)</span> </span></span><span class="line"><span class="cl"> <span class="n">eexists</span><span class="o">.</span> <span class="k">apply</span> <span class="n">run_noswap_ok</span><span class="o">.</span> <span class="kp">assumption</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="c">(* We&#39;re stuck. *)</span> </span></span><span class="line"><span class="cl"> <span class="n">eexists</span><span class="o">.</span> <span class="k">apply</span> <span class="n">run_noswap_fail</span><span class="o">.</span> <span class="kp">assumption</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We basically connect the dots between the premises (in a form like <code>done</code>) and the corresponding inference rule (<code>run_noswap_ok</code>). The more interesting case is when we can take a step.</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day8.v" data-first-line="241" data-last-line="253"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day8.v#L241-L253">day8.v</a>, lines 241 through 253</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">241 </span><span class="lnt">242 </span><span class="lnt">243 </span><span class="lnt">244 </span><span class="lnt">245 </span><span class="lnt">246 </span><span class="lnt">247 </span><span class="lnt">248 </span><span class="lnt">249 </span><span class="lnt">250 </span><span class="lnt">251 </span><span class="lnt">252 </span><span class="lnt">253 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="o">-</span> <span class="c">(* We can make a step. This will remove our current PC from the valid list, *)</span> </span></span><span class="line"><span class="cl"> <span class="n">edestruct</span> <span class="o">(</span><span class="n">H</span> <span class="o">(</span><span class="n">set_remove</span> <span class="n">Fin</span><span class="o">.</span><span class="n">eq_dec</span> <span class="n">pc&#39;</span> <span class="n">l</span><span class="o">)).</span> </span></span><span class="line"><span class="cl"> <span class="c">(* Since the PC must be in the list, removing it makes the list smaller. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">apply</span> <span class="o">(</span><span class="n">set_remove_length</span> <span class="o">_</span> <span class="o">_</span> <span class="n">Hin</span><span class="o">).</span> </span></span><span class="line"><span class="cl"> <span class="c">(* Without the current PC, our valid set shrinks. </span></span></span><span class="line"><span class="cl"><span class="c"> Since this is the inductive step, we have assumed </span></span></span><span class="line"><span class="cl"><span class="c"> that programs with smaller sets of valid PCs always </span></span></span><span class="line"><span class="cl"><span class="c"> terminate. Thus, after we make the step, we&#39;re done. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">exists</span> <span class="n">x</span><span class="o">.</span> <span class="k">subst</span><span class="o">.</span> <span class="k">eapply</span> <span class="n">run_noswap_trans</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="k">auto</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="k">apply</span> <span class="n">Hst</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="k">apply</span> <span class="n">H0</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="kn">Qed</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Since we know we can take a step, we know that we&rsquo;ll be removing the current program counter from the set of valid addresses. This set must currently contain the present program counter (since otherwise we&rsquo;d have &ldquo;failed&rdquo;), and thus will shrink when we remove it. This, in turn, lets us use the inductive hypothesis: it tells us that no matter the program counter or accumulator, if we start with this new &ldquo;shrunk&rdquo; set, we will terminate in some state. Coq&rsquo;s constructive nature helps us here: it doesn&rsquo;t just tells us that there is some state in which we terminate - it gives us that state! We use <code>edestruct</code> to get a handle on this final state, which Coq automatically names <code>x</code>. At this time Coq still isn&rsquo;t convinced that our new set is smaller, so we invoke our earlier <code>set_remove_length</code> theorem to placate it.</p> <p>We now have all the pieces: we know that we can take a step, removing the current program counter from our current set. We also know that with that newly shrunken set, we&rsquo;ll terminate in some final state <code>x</code>. Thus, all that&rsquo;s left to say is to apply our &ldquo;step&rdquo; rule. It asks us for three things:</p> <ol> <li>That the current program counter is in the set. We&rsquo;ve long since established this, and <code>auto</code> takes care of that.</li> <li>That a step is possible. We&rsquo;ve already established this, too, since we&rsquo;re in the &ldquo;can take a step&rdquo; case. We apply <code>Hst</code>, the hypothesis that confirms that we can, indeed, step.</li> <li>That we terminate after this. The <code>x</code> we got from our induction hypothesis came with a proof that running with the &ldquo;next&rdquo; program counter and accumulator will result in termination. We apply this proof, automatically named <code>H0</code> by Coq.</li> </ol> <p>And that&rsquo;s it! We&rsquo;ve proved that a program terminates no matter what. This has also (almost!) given us a solution to part 1. Consider the case in which we start with program counter 0, accumulator 0, and the &ldquo;full&rdquo; set of allowed program counters. Since our proof works for <em>all</em> configurations, it will also work for this one. Furthermore, since Coq proofs are constructive, this proof will <strong>return to us the final program counter and accumulator!</strong> This is precisely what we&rsquo;d need to solve part 1.</p> <p>But wait, almost? What&rsquo;s missing? We&rsquo;re missing a few implementation details:</p> <ul> <li>We&rsquo;ve not provided a concrete impelmentation of integers. The simplest thing to do here would be to use <a href="https://coq.inria.fr/library/Coq.ZArith.BinInt.html"class="external-link"><code>Coq.ZArith.BinInt</code><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, for which there is a module <a href="https://coq.inria.fr/library/Coq.ZArith.Int.html#Z_as_Int"class="external-link"><code>Z_as_Int</code><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> that provides <code>t</code> and friends.</li> <li>We assumed (reasonably, I would say) that it&rsquo;s possible to convert a natural number to an integer. If we&rsquo;re using the aforementioned <code>BinInt</code> module, we can use <a href="https://coq.inria.fr/library/Coq.ZArith.BinIntDef.html#Z.of_nat"class="external-link"><code>Z.of_nat</code><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>.</li> <li>We also assumed (still reasonably) that we can try convert an integer back to a finite natural number, failing if it&rsquo;s too small or too large. There&rsquo;s no built-in function for this, but <code>Z</code>, for one, distinguishes between the &ldquo;positive&rdquo;, &ldquo;zero&rdquo;, and &ldquo;negative&rdquo; cases, and we have <code>Pos.to_nat</code> for the positive case.</li> </ul> <p>Well, I seem to have covered all the implementation details. Why not just go ahead and solve the problem? I tried, and ran into two issues:</p> <ul> <li>Although this is &ldquo;given&rdquo;, we assumed that our input program will be valid. For us to use the result of our Coq proof, we need to provide it a constructive proof that our program is valid. Creating this proof is tedious in theory, and quite difficult in practice: I&rsquo;ve run into a strange issue trying to pattern match on finite naturals.</li> <li>Even supposing we <em>do</em> have a proof of validity, I&rsquo;m not certain if it&rsquo;s possible to actually extract an answer from it. It seems that Coq distinguishes between proofs (things of type <code>Prop</code>) and values (things of type <code>Set</code>). things of types <code>Prop</code> are supposed to be <em>erased</em>. This means that when you convert Coq code, to, say, Haskell, you will see no trace of any <code>Prop</code>s in that generated code. Unfortunately, this also means we <a href="https://stackoverflow.com/questions/27322979/why-coq-doesnt-allow-inversion-destruct-etc-when-the-goal-is-a-type"class="external-link">can&rsquo;t use our proofs to construct values<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, even though our proof objects do indeed contain them.</li> </ul> <p>So, we &ldquo;theoretically&rdquo; have a solution to part 1, down to the algorithm used to compute it and a proof that our algorithm works. In &ldquo;reality&rdquo;, though, we can&rsquo;t actually use this solution to procure an answer. Like we did with day 1, we&rsquo;ll have to settle for only a proof.</p> <p>Let&rsquo;s wrap up for this post. It would be more interesting to devise and formally verify an algorithm for part 2, but this post has already gotten quite long and contains a lot of information. Perhaps I will revisit this at a later time. Thanks for reading!</p> Advent of Code in Coq - Day 1 https://danilafe.com/blog/00_aoc_coq/ Wed, 02 Dec 2020 18:44:56 -0800 https://danilafe.com/blog/00_aoc_coq/ <p>The first puzzle of this year&rsquo;s <a href="https://adventofcode.com"class="external-link">Advent of Code<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> was quite simple, which gave me a thought: &ldquo;Hey, this feels within reach for me to formally verify!&rdquo; At first, I wanted to formalize and prove the correctness of the <a href="https://www.geeksforgeeks.org/two-pointers-technique/"class="external-link">two-pointer solution<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. However, I didn&rsquo;t have the time to mess around with the various properties of sorted lists and their traversals. So, I settled for the brute force solution. Despite the simplicity of its implementation, there is plenty to talk about when proving its correctness using Coq. Let&rsquo;s get right into it!</p> <p>Before we start, in the interest of keeping the post self-contained, here&rsquo;s the (paraphrased) problem statement:</p> <blockquote> <p>Given an unsorted list of numbers, find two distinct numbers that add up to 2020.</p> </blockquote> <p>With this in mind, we can move on to writing some Coq!</p> <a href="#defining-the-functions"> <h3 id="defining-the-functions">Defining the Functions</h3> </a> <p>The first step to proving our code correct is to actually write the code! To start with, let&rsquo;s write a helper function that, given a number <code>x</code>, tries to find another number <code>y</code> such that <code>x + y = 2020</code>. In fact, rather than hardcoding the desired sum to <code>2020</code>, let&rsquo;s just use another argument called <code>total</code>. The code is quite simple:</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day1.v" data-first-line="11" data-last-line="18"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day1.v#L11-L18">day1.v</a>, lines 11 through 18</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Fixpoint</span> <span class="n">find_matching</span> <span class="o">(</span><span class="k">is</span> <span class="o">:</span> <span class="kt">list</span> <span class="kt">nat</span><span class="o">)</span> <span class="o">(</span><span class="n">total</span> <span class="o">:</span> <span class="kt">nat</span><span class="o">)</span> <span class="o">(</span><span class="n">x</span> <span class="o">:</span> <span class="kt">nat</span><span class="o">)</span> <span class="o">:</span> <span class="n">option</span> <span class="kt">nat</span> <span class="o">:=</span> </span></span><span class="line"><span class="cl"> <span class="k">match</span> <span class="k">is</span> <span class="k">with</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">nil</span> <span class="o">=&gt;</span> <span class="n">None</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">cons</span> <span class="n">y</span> <span class="n">ys</span> <span class="o">=&gt;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">Nat</span><span class="o">.</span><span class="n">eqb</span> <span class="o">(</span><span class="n">x</span> <span class="o">+</span> <span class="n">y</span><span class="o">)</span> <span class="n">total</span> </span></span><span class="line"><span class="cl"> <span class="k">then</span> <span class="n">Some</span> <span class="n">y</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span> <span class="n">find_matching</span> <span class="n">ys</span> <span class="n">total</span> <span class="n">x</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Here, <code>is</code> is the list of numbers that we want to search. We proceed by case analysis: if the list is empty, we can&rsquo;t find a match, so we return <code>None</code> (the Coq equivalent of Haskell&rsquo;s <code>Nothing</code>). On the other hand, if the list has at least one element <code>y</code>, we see if it adds up to <code>total</code>, and return <code>Some y</code> (equivalent to <code>Just y</code> in Haskell) if it does. If it doesn&rsquo;t, we continue our search into the rest of the list.</p> <p>It&rsquo;s somewhat unusual, in my experience, to put the list argument first when writing functions in a language with <a href="https://wiki.haskell.org/Currying"class="external-link">currying<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. However, it seems as though Coq&rsquo;s <code>simpl</code> tactic, which we will use later, works better for our purposes when the argument being case analyzed is given first.</p> <p>We can now use <code>find_matching</code> to define our <code>find_sum</code> function, which solves part 1. Here&rsquo;s the code:</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day1.v" data-first-line="20" data-last-line="28"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day1.v#L20-L28">day1.v</a>, lines 20 through 28</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Fixpoint</span> <span class="n">find_sum</span> <span class="o">(</span><span class="k">is</span> <span class="o">:</span> <span class="kt">list</span> <span class="kt">nat</span><span class="o">)</span> <span class="o">(</span><span class="n">total</span> <span class="o">:</span> <span class="kt">nat</span><span class="o">)</span> <span class="o">:</span> <span class="n">option</span> <span class="o">(</span><span class="kt">nat</span> <span class="o">*</span> <span class="kt">nat</span><span class="o">)</span> <span class="o">:=</span> </span></span><span class="line"><span class="cl"> <span class="k">match</span> <span class="k">is</span> <span class="k">with</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">nil</span> <span class="o">=&gt;</span> <span class="n">None</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">cons</span> <span class="n">x</span> <span class="n">xs</span> <span class="o">=&gt;</span> </span></span><span class="line"><span class="cl"> <span class="k">match</span> <span class="n">find_matching</span> <span class="n">xs</span> <span class="n">total</span> <span class="n">x</span> <span class="k">with</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">None</span> <span class="o">=&gt;</span> <span class="n">find_sum</span> <span class="n">xs</span> <span class="n">total</span> <span class="c">(* Was buggy! *)</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">Some</span> <span class="n">y</span> <span class="o">=&gt;</span> <span class="n">Some</span> <span class="o">(</span><span class="n">x</span><span class="o">,</span> <span class="n">y</span><span class="o">)</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>For every <code>x</code> that we encounter in our input list <code>is</code>, we want to check if there&rsquo;s a matching number in the rest of the list. We only search the remainder of the list because we can&rsquo;t use <code>x</code> twice: the <code>x</code> and <code>y</code> we return that add up to <code>total</code> must be different elements. We use <code>find_matching</code> to try find a complementary number for <code>x</code>. If we don&rsquo;t find it, this <code>x</code> isn&rsquo;t it, so we recursively move on to <code>xs</code>. On the other hand, if we <em>do</em> find a matching <code>y</code>, we&rsquo;re done! We return <code>(x,y)</code>, wrapped in <code>Some</code> to indicate that we found something useful.</p> <p>What about that <code>(* Was buggy! *)</code> line? Well, it so happens that my initial implementation had a bug on this line, one that came up as I was proving the correctness of my function. When I wasn&rsquo;t able to prove a particular behavior in one of the cases, I realized something was wrong. In short, my proof actually helped me find and fix a bug!</p> <p>This is all the code we&rsquo;ll need to get our solution. Next, let&rsquo;s talk about some properties of our two functions.</p> <a href="#our-first-lemma"> <h3 id="our-first-lemma">Our First Lemma</h3> </a> <p>When we call <code>find_matching</code>, we want to be sure that if we get a number, it does indeed add up to our expected total. We can state it a little bit more formally as follows:</p> <blockquote> <p>For any numbers <code>k</code> and <code>x</code>, and for any list of number <code>is</code>, if <code>find_matching is k x</code> returns a number <code>y</code>, then <code>x + y = k</code>.</p> </blockquote> <p>And this is how we write it in Coq:</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day1.v" data-first-line="30" data-last-line="31"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day1.v#L30-L31">day1.v</a>, lines 30 through 31</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">30 </span><span class="lnt">31 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Lemma</span> <span class="n">find_matching_correct</span> <span class="o">:</span> <span class="k">forall</span> <span class="k">is</span> <span class="n">k</span> <span class="n">x</span> <span class="n">y</span><span class="o">,</span> </span></span><span class="line"><span class="cl"> <span class="n">find_matching</span> <span class="k">is</span> <span class="n">k</span> <span class="n">x</span> <span class="o">=</span> <span class="n">Some</span> <span class="n">y</span> <span class="o">-&gt;</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span> <span class="o">=</span> <span class="n">k</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The arrow, <code>-&gt;</code>, reads &ldquo;implies&rdquo;. Other than that, I think this property reads pretty well. The proof, unfortunately, is a little bit more involved. Here are the first few lines:</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day1.v" data-first-line="32" data-last-line="35"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day1.v#L32-L35">day1.v</a>, lines 32 through 35</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Proof</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">intros</span> <span class="k">is</span><span class="o">.</span> <span class="k">induction</span> <span class="k">is</span><span class="o">;</span> </span></span><span class="line"><span class="cl"> <span class="k">intros</span> <span class="n">k</span> <span class="n">x</span> <span class="n">y</span> <span class="n">Hev</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">simpl</span> <span class="k">in</span> <span class="n">Hev</span><span class="o">.</span> <span class="k">inversion</span> <span class="n">Hev</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We start with the <code>intros is</code> tactic, which is akin to saying &ldquo;consider a particular list of integers <code>is</code>&rdquo;. We do this without losing generality: by simply examining a concrete list, we&rsquo;ve said nothing about what that list is like. We then proceed by induction on <code>is</code>.</p> <p>To prove something by induction for a list, we need to prove two things:</p> <ul> <li>The <strong>base case</strong>. Whatever property we want to hold, it must hold for the empty list, which is the simplest possible list. In our case, this means <code>find_matching</code> searching an empty list.</li> <li>The <strong>inductive case</strong>. Assuming that a property holds for any list <code>[b, c, ...]</code>, we want to show that the property also holds for the list <code>[a, b, c, ...]</code>. That is, the property must remain true if we prepend an element to a list for which this property holds.</li> </ul> <p>These two things combined give us a proof for <em>all</em> lists, which is exactly what we want! If you don&rsquo;t belive me, here&rsquo;s how it works. Suppose you want to prove that some property <code>P</code> holds for <code>[1,2,3,4]</code>. Given the base case, we know that <code>P []</code> holds. Next, by the inductive case, since <code>P []</code> holds, we can prepend <code>4</code> to the list, and the property will still hold. Thus, <code>P [4]</code>. Now that <code>P [4]</code> holds, we can again prepend an element to the list, this time a <code>3</code>, and conclude that <code>P [3,4]</code>. Repeating this twice more, we arrive at our desired fact: <code>P [1,2,3,4]</code>.</p> <p>When we write <code>induction is</code>, Coq will generate two proof goals for us, one for the base case, and one for the inductive case. We will have to prove each of them separately. Since we have not yet introduced the variables <code>k</code>, <code>x</code>, and <code>y</code>, they remain inside a <code>forall</code> quantifier at that time. To be able to refer to them, we want to use <code>intros</code>. We want to do this in both the base and the inductive case. To quickly do this, we use Coq&rsquo;s <code>;</code> operator. When we write <code>a; b</code>, Coq runs the tactic <code>a</code>, and then runs the tactic <code>b</code> in every proof goal generated by <code>a</code>. This is exactly what we want.</p> <p>There&rsquo;s one more variable inside our second <code>intros</code>: <code>Hev</code>. This variable refers to the hypothesis of our statement: that is, the part on the left of the <code>-&gt;</code>. To prove that <code>A</code> implies <code>B</code>, we assume that <code>A</code> holds, and try to argue <code>B</code> from there. Here is no different: when we use <code>intros Hev</code>, we say, &ldquo;suppose that you have a proof that <code>find_matching</code> evaluates to <code>Some y</code>, called <code>Hev</code>&rdquo;. The thing on the right of <code>-&gt;</code> becomes our proof goal.</p> <p>Now, it&rsquo;s time to look at the cases. To focus on one case at a time, we use <code>-</code>. The first case is our base case. Here&rsquo;s what Coq prints out at this time:</p> <pre tabindex="0"><code>k, x, y : nat Hev : find_matching nil k x = Some y ========================= (1 / 1) x + y = k </code></pre><p>All the stuff above the <code>===</code> line are our hypotheses. We know that we have some <code>k</code>, <code>x</code>, and <code>y</code>, all of which are numbers. We also have the assumption that <code>find_matching</code> returns <code>Some y</code>. In the base case, <code>is</code> is just <code>[]</code>, and this is reflected in the type for <code>Hev</code>. To make this more clear, we&rsquo;ll simplify the call to <code>find_matching</code> in <code>Hev</code>, using <code>simpl in Hev</code>. Now, here&rsquo;s what Coq has to say about <code>Hev</code>:</p> <pre tabindex="0"><code>Hev : None = Some y </code></pre><p>Well, this doesn&rsquo;t make any sense. How can something be equal to nothing? We ask Coq this question using <code>inversion Hev</code>. Effectively, the question that <code>inversion</code> asks is: what are the possible ways we could have acquired <code>Hev</code>? Coq generates a proof goal for each of these possible ways. Alas, there are no ways to arrive at this contradictory assumption: the number of proof sub-goals is zero. This means we&rsquo;re done with the base case!</p> <p>The inductive case is the meat of this proof. Here&rsquo;s the corresponding part of the Coq source file:</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day1.v" data-first-line="36" data-last-line="40"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day1.v#L36-L40">day1.v</a>, lines 36 through 40</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">simpl</span> <span class="k">in</span> <span class="n">Hev</span><span class="o">.</span> <span class="k">destruct</span> <span class="o">(</span><span class="n">Nat</span><span class="o">.</span><span class="n">eqb</span> <span class="o">(</span><span class="n">x</span><span class="o">+</span><span class="n">a</span><span class="o">)</span> <span class="n">k</span><span class="o">)</span> <span class="n">eqn</span><span class="o">:</span><span class="n">Heq</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="k">injection</span> <span class="n">Hev</span> <span class="k">as</span> <span class="n">H</span><span class="o">;</span> <span class="k">subst</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">apply</span> <span class="n">EqNat</span><span class="o">.</span><span class="n">beq_nat_eq</span><span class="o">.</span> <span class="k">auto</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="k">apply</span> <span class="n">IHis</span><span class="o">.</span> <span class="kp">assumption</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Qed</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This time, the proof state is more complicated:</p> <pre tabindex="0"><code>a : nat is : list nat IHis : forall k x y : nat, find_matching is k x = Some y -&gt; x + y = k k, x, y : nat Hev : find_matching (a :: is) k x = Some y ========================= (1 / 1) x + y = k </code></pre><p>Following the footsteps of our informal description of the inductive case, Coq has us prove our property for <code>(a :: is)</code>, or the list <code>is</code> to which <code>a</code> is being prepended. Like before, we assume that our property holds for <code>is</code>. This is represented in the <strong>induction hypothesis</strong> <code>IHis</code>. It states that if <code>find_matching</code> finds a <code>y</code> in <code>is</code>, it must add up to <code>k</code>. However, <code>IHis</code> doesn&rsquo;t tell us anything about <code>a :: is</code>: that&rsquo;s our job. We also still have <code>Hev</code>, which is our assumption that <code>find_matching</code> finds a <code>y</code> in <code>(a :: is)</code>. Running <code>simpl in Hev</code> gives us the following:</p> <pre tabindex="0"><code>Hev : (if x + a =? k then Some a else find_matching is k x) = Some y </code></pre><p>The result of <code>find_matching</code> now depends on whether or not the new element <code>a</code> adds up to <code>k</code>. If it does, then <code>find_matching</code> will return <code>a</code>, which means that <code>y</code> is the same as <code>a</code>. If not, it must be that <code>find_matching</code> finds the <code>y</code> in the rest of the list, <code>is</code>. We&rsquo;re not sure which of the possibilities is the case. Fortunately, we don&rsquo;t need to be! If we can prove that the <code>y</code> that <code>find_matching</code> finds is correct regardless of whether <code>a</code> adds up to <code>k</code> or not, we&rsquo;re good to go! To do this, we perform case analysis using <code>destruct</code>.</p> <p>Our particular use of <code>destruct</code> says: check any possible value for <code>x + a ?= k</code>, and create an equation <code>Heq</code> that tells us what that value is. <code>?=</code> returns a boolean value, and so <code>destruct</code> generates two new goals: one where the function returns <code>true</code>, and one where it returns <code>false</code>. We start with the former. Here&rsquo;s the proof state:</p> <pre tabindex="0"><code>a : nat is : list nat IHis : forall k x y : nat, find_matching is k x = Some y -&gt; x + y = k k, x, y : nat Heq : (x + a =? k) = true Hev : Some a = Some y ========================= (1 / 1) x + y = k </code></pre><p>There is a new hypothesis: <code>Heq</code>. It tells us that we&rsquo;re currently considering the case where <code>?=</code> evaluates to <code>true</code>. Also, <code>Hev</code> has been considerably simplified: now that we know the condition of the <code>if</code> expression, we can just replace it with the <code>then</code> branch.</p> <p>Looking at <code>Hev</code>, we can see that our prediction was right: <code>a</code> is equal to <code>y</code>. After all, if they weren&rsquo;t, <code>Some a</code> wouldn&rsquo;t equal to <code>Some y</code>. To make Coq take this information into account, we use <code>injection</code>. This will create a new hypothesis, <code>a = y</code>. But if one is equal to the other, why don&rsquo;t we just use only one of these variables everywhere? We do exactly that by using <code>subst</code>, which replaces <code>a</code> with <code>y</code> everywhere in our proof.</p> <p>The proof state is now:</p> <pre tabindex="0"><code>is : list nat IHis : forall k x y : nat, find_matching is k x = Some y -&gt; x + y = k k, x, y : nat Heq : (x + y =? k) = true ========================= (1 / 1) x + y = k </code></pre><p>We&rsquo;re close, but there&rsquo;s one more detail to keep in mind. Our goal, <code>x + y = k</code>, is the <strong>proposition</strong> that <code>x + y</code> is equal to <code>k</code>. However, <code>Heq</code> tells us that the <strong>function</strong> <code>?=</code> evaluates to <code>true</code>. These are fundamentally different. One talks about mathematical equality, while the other about some function <code>?=</code> defined somewhere in Coq&rsquo;s standard library. Who knows - maybe there&rsquo;s a bug in Coq&rsquo;s implementation! Fortunately, Coq comes with a proof that if two numbers are equal according to <code>?=</code>, they are mathematically equal. This proof is called <code>eqb_nat_eq</code>. We tell Coq to use this with <code>apply</code>. Our proof goal changes to:</p> <pre tabindex="0"><code>true = (x + y =? k) </code></pre><p>This is <em>almost</em> like <code>Heq</code>, but flipped. Instead of manually flipping it and using <code>apply</code> with <code>Heq</code>, I let Coq do the rest of the work using <code>auto</code>.</p> <p>Phew! All this for the <code>true</code> case of <code>?=</code>. Next, what happens if <code>x + a</code> does not equal <code>k</code>? Here&rsquo;s the proof state at this time:</p> <pre tabindex="0"><code>a : nat is : list nat IHis : forall k x y : nat, find_matching is k x = Some y -&gt; x + y = k k, x, y : nat Heq : (x + a =? k) = false Hev : find_matching is k x = Some y ========================= (1 / 1) x + y = k </code></pre><p>Since <code>a</code> was not what it was looking for, <code>find_matching</code> moved on to <code>is</code>. But hey, we&rsquo;re in the inductive case! We are assuming that <code>find_matching</code> will work properly with the list <code>is</code>. Since <code>find_matching</code> found its <code>y</code> in <code>is</code>, this should be all we need! We use our induction hypothesis <code>IHis</code> with <code>apply</code>. <code>IHis</code> itself does not know that <code>find_matching</code> moved on to <code>is</code>, so it asks us to prove it. Fortunately, <code>Hev</code> tells us exactly that, so we use <code>assumption</code>, and the proof is complete! Quod erat demonstrandum, QED!</p> <a href="#the-rest-of-the-owl"> <h3 id="the-rest-of-the-owl">The Rest of the Owl</h3> </a> <p>Here are a couple of other properties of <code>find_matching</code>. For brevity&rsquo;s sake, I will not go through their proofs step-by-step. I find that the best way to understand Coq proofs is to actually step through them in the IDE!</p> <p>First on the list is <code>find_matching_skip</code>. Here&rsquo;s the type:</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day1.v" data-first-line="42" data-last-line="43"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day1.v#L42-L43">day1.v</a>, lines 42 through 43</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">42 </span><span class="lnt">43 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Lemma</span> <span class="n">find_matching_skip</span> <span class="o">:</span> <span class="k">forall</span> <span class="n">k</span> <span class="n">x</span> <span class="n">y</span> <span class="n">i</span> <span class="k">is</span><span class="o">,</span> </span></span><span class="line"><span class="cl"> <span class="n">find_matching</span> <span class="k">is</span> <span class="n">k</span> <span class="n">x</span> <span class="o">=</span> <span class="n">Some</span> <span class="n">y</span> <span class="o">-&gt;</span> <span class="n">find_matching</span> <span class="o">(</span><span class="n">cons</span> <span class="n">i</span> <span class="k">is</span><span class="o">)</span> <span class="n">k</span> <span class="n">x</span> <span class="o">=</span> <span class="n">Some</span> <span class="n">y</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>It reads: if we correctly find a number in a small list <code>is</code>, we can find that same number even if another number is prepended to <code>is</code>. That makes sense: <em>adding</em> a number to a list doesn&rsquo;t remove whatever we found in it! I used this lemma to prove another, <code>find_matching_works</code>:</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day1.v" data-first-line="53" data-last-line="54"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day1.v#L53-L54">day1.v</a>, lines 53 through 54</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">53 </span><span class="lnt">54 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Lemma</span> <span class="n">find_matching_works</span> <span class="o">:</span> <span class="k">forall</span> <span class="k">is</span> <span class="n">k</span> <span class="n">x</span> <span class="n">y</span><span class="o">,</span> <span class="n">In</span> <span class="n">y</span> <span class="k">is</span> <span class="o">/\</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span> <span class="o">=</span> <span class="n">k</span> <span class="o">-&gt;</span> </span></span><span class="line"><span class="cl"> <span class="n">find_matching</span> <span class="k">is</span> <span class="n">k</span> <span class="n">x</span> <span class="o">=</span> <span class="n">Some</span> <span class="n">y</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This reads, if there <em>is</em> an element <code>y</code> in <code>is</code> that adds up to <code>k</code> with <code>x</code>, then <code>find_matching</code> will find it. This is an important property. After all, if it didn&rsquo;t hold, it would mean that <code>find_matching</code> would occasionally fail to find a matching number, even though it&rsquo;s there! We can&rsquo;t have that.</p> <p>Finally, we want to specify what it means for <code>find_sum</code>, our solution function, to actually work. The naive definition would be:</p> <blockquote> <p>Given a list of integers, <code>find_sum</code> always finds a pair of numbers that add up to <code>k</code>.</p> </blockquote> <p>Unfortunately, this is not true. What if, for instance, we give <code>find_sum</code> an empty list? There are no numbers from that list to find and add together. Even a non-empty list may not include such a pair! We need a way to characterize valid input lists. I claim that all lists from this Advent of Code puzzle are guaranteed to have two numbers that add up to our goal, and that these numbers are not equal to each other. In Coq, we state this as follows:</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day1.v" data-first-line="8" data-last-line="9"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day1.v#L8-L9">day1.v</a>, lines 8 through 9</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">8 </span><span class="lnt">9 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Definition</span> <span class="n">has_pair</span> <span class="o">(</span><span class="n">t</span> <span class="o">:</span> <span class="kt">nat</span><span class="o">)</span> <span class="o">(</span><span class="k">is</span> <span class="o">:</span> <span class="kt">list</span> <span class="kt">nat</span><span class="o">)</span> <span class="o">:</span> <span class="kt">Prop</span> <span class="o">:=</span> </span></span><span class="line"><span class="cl"> <span class="k">exists</span> <span class="n">n1</span> <span class="n">n2</span> <span class="o">:</span> <span class="kt">nat</span><span class="o">,</span> <span class="n">n1</span> <span class="o">&lt;&gt;</span> <span class="n">n2</span> <span class="o">/\</span> <span class="n">In</span> <span class="n">n1</span> <span class="k">is</span> <span class="o">/\</span> <span class="n">In</span> <span class="n">n2</span> <span class="k">is</span> <span class="o">/\</span> <span class="n">n1</span> <span class="o">+</span> <span class="n">n2</span> <span class="o">=</span> <span class="n">t</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This defines a new property, <code>has_pair t is</code> (read &ldquo;<code>is</code> has a pair of numbers that add to <code>t</code>&rdquo;), which means:</p> <blockquote> <p>There are two numbers <code>n1</code> and <code>n2</code> such that, they are not equal to each other (<code>n1&lt;&gt;n2</code>) <strong>and</strong> the number <code>n1</code> is an element of <code>is</code> (<code>In n1 is</code>) <strong>and</strong> the number <code>n2</code> is an element of <code>is</code> (<code>In n2 is</code>) <strong>and</strong> the two numbers add up to <code>t</code> (<code>n1 + n2 = t</code>).</p> </blockquote> <p>When making claims about the correctness of our algorithm, we will assume that this property holds. Finally, here&rsquo;s the theorem we want to prove:</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day1.v" data-first-line="68" data-last-line="70"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day1.v#L68-L70">day1.v</a>, lines 68 through 70</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">68 </span><span class="lnt">69 </span><span class="lnt">70 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Theorem</span> <span class="n">find_sum_works</span> <span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="k">forall</span> <span class="n">k</span> <span class="k">is</span><span class="o">,</span> <span class="n">has_pair</span> <span class="n">k</span> <span class="k">is</span> <span class="o">-&gt;</span> </span></span><span class="line"><span class="cl"> <span class="k">exists</span> <span class="n">x</span> <span class="n">y</span><span class="o">,</span> <span class="o">(</span><span class="n">find_sum</span> <span class="k">is</span> <span class="n">k</span> <span class="o">=</span> <span class="n">Some</span> <span class="o">(</span><span class="n">x</span><span class="o">,</span> <span class="n">y</span><span class="o">)</span> <span class="o">/\</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span> <span class="o">=</span> <span class="n">k</span><span class="o">).</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>It reads, &ldquo;for any total <code>k</code> and list <code>is</code>, if <code>is</code> has a pair of numbers that add to <code>k</code>, then <code>find_sum</code> will return a pair of numbers <code>x</code> and <code>y</code> that add to <code>k</code>&rdquo;. There&rsquo;s some nuance here. We hardly reference the <code>has_pair</code> property in this definition, and for good reason. Our <code>has_pair</code> hypothesis only says that there is <em>at least one</em> pair of numbers in <code>is</code> that meets our criteria. However, this pair need not be the only one, nor does it need to be the one returned by <code>find_sum</code>! However, if we have many pairs, we want to confirm that <code>find_sum</code> will find one of them. Finally, here is the proof. I will not be able to go through it in detail in this post, but I did comment it to make it easier to read:</p> <div class="highlight-group" data-base-path="aoc-2020" data-file-path="aoc-2020/day1.v" data-first-line="71" data-last-line="106"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Advent-of-Code/AdventOfCode-2020/src/commit/7a8503c3fe1aa7e624e4d8672aa9b56d24b4ba82/day1.v#L71-L106">day1.v</a>, lines 71 through 106</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 71 </span><span class="lnt"> 72 </span><span class="lnt"> 73 </span><span class="lnt"> 74 </span><span class="lnt"> 75 </span><span class="lnt"> 76 </span><span class="lnt"> 77 </span><span class="lnt"> 78 </span><span class="lnt"> 79 </span><span class="lnt"> 80 </span><span class="lnt"> 81 </span><span class="lnt"> 82 </span><span class="lnt"> 83 </span><span class="lnt"> 84 </span><span class="lnt"> 85 </span><span class="lnt"> 86 </span><span class="lnt"> 87 </span><span class="lnt"> 88 </span><span class="lnt"> 89 </span><span class="lnt"> 90 </span><span class="lnt"> 91 </span><span class="lnt"> 92 </span><span class="lnt"> 93 </span><span class="lnt"> 94 </span><span class="lnt"> 95 </span><span class="lnt"> 96 </span><span class="lnt"> 97 </span><span class="lnt"> 98 </span><span class="lnt"> 99 </span><span class="lnt">100 </span><span class="lnt">101 </span><span class="lnt">102 </span><span class="lnt">103 </span><span class="lnt">104 </span><span class="lnt">105 </span><span class="lnt">106 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Proof</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">intros</span> <span class="n">k</span> <span class="k">is</span><span class="o">.</span> <span class="k">generalize</span> <span class="n">dependent</span> <span class="n">k</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">induction</span> <span class="k">is</span><span class="o">;</span> <span class="k">intros</span> <span class="n">k</span> <span class="o">[</span><span class="n">x&#39;</span> <span class="o">[</span><span class="n">y&#39;</span> <span class="o">[</span><span class="n">Hneq</span> <span class="o">[</span><span class="n">Hinx</span> <span class="o">[</span><span class="n">Hiny</span> <span class="n">Hsum</span><span class="o">]]]]].</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="c">(* is is empty. But x is in is! *)</span> </span></span><span class="line"><span class="cl"> <span class="k">inversion</span> <span class="n">Hinx</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="c">(* is is not empty. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">inversion</span> <span class="n">Hinx</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="c">(* x is the first element. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">subst</span> <span class="n">a</span><span class="o">.</span> <span class="k">inversion</span> <span class="n">Hiny</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">*</span> <span class="c">(* y is also the first element; but this is impossible! *)</span> </span></span><span class="line"><span class="cl"> <span class="n">exfalso</span><span class="o">.</span> <span class="k">apply</span> <span class="n">Hneq</span><span class="o">.</span> <span class="k">apply</span> <span class="n">H</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">*</span> <span class="c">(* y is somewhere in the rest of the list. </span></span></span><span class="line"><span class="cl"><span class="c"> We&#39;ve proven that we will find it! *)</span> </span></span><span class="line"><span class="cl"> <span class="k">exists</span> <span class="n">x&#39;</span><span class="o">.</span> <span class="k">simpl</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">erewrite</span> <span class="n">find_matching_works</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">{</span> <span class="k">exists</span> <span class="n">y&#39;</span><span class="o">.</span> <span class="k">split</span><span class="o">.</span> <span class="kp">reflexivity</span><span class="o">.</span> <span class="kp">assumption</span><span class="o">.</span> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="o">{</span> <span class="k">split</span><span class="o">;</span> <span class="kp">assumption</span><span class="o">.</span> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="c">(* x is not the first element. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">inversion</span> <span class="n">Hiny</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">*</span> <span class="c">(* y is the first element, </span></span></span><span class="line"><span class="cl"><span class="c"> so x is somewhere in the rest of the list. </span></span></span><span class="line"><span class="cl"><span class="c"> Again, we&#39;ve proven that we can find it. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">subst</span> <span class="n">a</span><span class="o">.</span> <span class="k">exists</span> <span class="n">y&#39;</span><span class="o">.</span> <span class="k">simpl</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">erewrite</span> <span class="n">find_matching_works</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">{</span> <span class="k">exists</span> <span class="n">x&#39;</span><span class="o">.</span> <span class="k">split</span><span class="o">.</span> <span class="kp">reflexivity</span><span class="o">.</span> <span class="k">rewrite</span> <span class="n">plus_comm</span><span class="o">.</span> <span class="kp">assumption</span><span class="o">.</span> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="o">{</span> <span class="k">split</span><span class="o">.</span> <span class="kp">assumption</span><span class="o">.</span> <span class="k">rewrite</span> <span class="n">plus_comm</span><span class="o">.</span> <span class="kp">assumption</span><span class="o">.</span> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="o">*</span> <span class="c">(* y is not the first element, either. </span></span></span><span class="line"><span class="cl"><span class="c"> Of course, there could be another matching pair </span></span></span><span class="line"><span class="cl"><span class="c"> starting with a. Otherwise, the inductive hypothesis applies. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">simpl</span><span class="o">.</span> <span class="k">destruct</span> <span class="o">(</span><span class="n">find_matching</span> <span class="k">is</span> <span class="n">k</span> <span class="n">a</span><span class="o">)</span> <span class="n">eqn</span><span class="o">:</span><span class="n">Hf</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">{</span> <span class="k">exists</span> <span class="n">a</span><span class="o">.</span> <span class="k">exists</span> <span class="n">n</span><span class="o">.</span> <span class="k">split</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="kp">reflexivity</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">apply</span> <span class="n">find_matching_correct</span> <span class="k">with</span> <span class="k">is</span><span class="o">.</span> <span class="kp">assumption</span><span class="o">.</span> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="o">{</span> <span class="k">apply</span> <span class="n">IHis</span><span class="o">.</span> <span class="k">unfold</span> <span class="n">has_pair</span><span class="o">.</span> <span class="k">exists</span> <span class="n">x&#39;</span><span class="o">.</span> <span class="k">exists</span> <span class="n">y&#39;</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="kr">repeat</span> <span class="k">split</span><span class="o">;</span> <span class="kp">assumption</span><span class="o">.</span> <span class="o">}</span> </span></span><span class="line"><span class="cl"><span class="kn">Qed</span><span class="o">.</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Coq seems happy with it, and so am I! The bug I mentioned earlier popped up on line 96. I had accidentally made <code>find_sum</code> return <code>None</code> if it couldn&rsquo;t find a complement for the <code>x</code> it encountered. This meant that it never recursed into the remaining list <code>xs</code>, and thus, the pair was never found at all! It this became impossible to prove that <code>find_some</code> will return <code>Some y</code>, and I had to double back and check my definitions.</p> <p>I hope you enjoyed this post! If you&rsquo;re interested to learn more about Coq, I strongly recommend checking out <a href="https://softwarefoundations.cis.upenn.edu/"class="external-link">Software Foundations<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, a series of books on Coq written as comments in a Coq source file! In particular, check out <a href="https://softwarefoundations.cis.upenn.edu/lf-current/index.html"class="external-link">Logical Foundations<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> for an introduction to using Coq. Thanks for reading!</p> A Typesafe Representation of an Imperative Language https://danilafe.com/blog/typesafe_imperative_lang/ Mon, 02 Nov 2020 01:07:21 -0800 https://danilafe.com/blog/typesafe_imperative_lang/ <p>A recent homework assignment for my university&rsquo;s programming languages course was to encode the abstract syntax for a small imperative language into Haskell data types. The language consisted of very few constructs, and was very much a &ldquo;toy&rdquo;. On the expression side of things, it had three registers (<code>A</code>, <code>B</code>, and <code>R</code>), numbers, addition, comparison using &ldquo;less than&rdquo;, and logical negation. It also included a statement for storing the result of an expression into a register, <code>if/else</code>, and an infinite loop construct with an associated <code>break</code> operation. A sample program in the language which computes the product of two numbers is as follows:</p> <pre tabindex="0"><code>A := 7 B := 9 R := 0 do if A &lt;= 0 then break else R := R + B; A := A + -1; end end </code></pre><p>The homework notes that type errors may arise in the little imperative language. We could, for instance, try to add a boolean to a number: <code>3 + (1 &lt; 2)</code>. Alternatively, we could try use a number in the condition of an <code>if/else</code> expression. A &ldquo;naive&rdquo; encoding of the abstract syntax would allow for such errors.</p> <p>However, assuming that registers could only store integers and not booleans, it is fairly easy to separate the expression grammar into two nonterminals, yielding boolean and integer expressions respectively. Since registers can only store integers, the <code>(:=)</code> operation will always require an integer expression, and an <code>if/else</code> statement will always require a boolean expression. A matching Haskell encoding would not allow &ldquo;invalid&rdquo; programs to compile. That is, the programs would be type-correct by construction.</p> <p>Then, a question arose in the ensuing discussion: what if registers <em>could</em> contain booleans? It would be impossible to create such a &ldquo;correct-by-construction&rdquo; representation then, wouldn&rsquo;t it? <span class="sidenote"> <label class="sidenote-label" for="haskell-note">Although I don&rsquo;t know about Haskell,</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="haskell-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> I am pretty certain that a similar encoding in Haskell is possible. However, Haskell wasn't originally created for that kind of abuse of its type system, so it would probably not look very good. <span class="sidenote-delimiter">]</span> </span> </span> I am sure that it <em>is</em> possible to do this in Idris, a dependently typed programming language. In this post I will talk about how to do that.</p> <a href="#registers-and-expressions"> <h3 id="registers-and-expressions">Registers and Expressions</h3> </a> <p>Let&rsquo;s start by encoding registers. Since we only have three registers, we can encode them using a simple data type declaration, much the same as we would in Haskell:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-imperative/TypesafeImp.idr" data-first-line="1" data-last-line="1"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-imperative/TypesafeImp.idr#L1-L1">TypesafeImp.idr</a>, line 1</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="kr">data</span> <span class="kt">Reg</span> <span class="ow">=</span> <span class="kt">A</span> <span class="ow">|</span> <span class="kt">B</span> <span class="ow">|</span> <span class="kt">R</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Now that registers can store either integers or booleans (and only those two), we need to know which one is which. For this purpose, we can declare another data type:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-imperative/TypesafeImp.idr" data-first-line="3" data-last-line="3"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-imperative/TypesafeImp.idr#L3-L3">TypesafeImp.idr</a>, line 3</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">3 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="kr">data</span> <span class="kt">Ty</span> <span class="ow">=</span> <span class="kt">IntTy</span> <span class="ow">|</span> <span class="kt">BoolTy</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>At any point in the (hypothetical) execution of our program, each of the registers will have a type, either boolean or integer. The combined state of the three registers would then be the combination of these three states; we can represent this using a 3-tuple:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-imperative/TypesafeImp.idr" data-first-line="5" data-last-line="6"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-imperative/TypesafeImp.idr#L5-L6">TypesafeImp.idr</a>, lines 5 through 6</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">5 </span><span class="lnt">6 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">TypeState</span> <span class="ow">:</span> <span class="kt">Type</span> </span></span><span class="line"><span class="cl"><span class="kt">TypeState</span> <span class="ow">=</span> <span class="ow">(</span><span class="kt">Ty</span>, <span class="kt">Ty</span>, <span class="kt">Ty</span><span class="ow">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Let&rsquo;s say that the first element of the tuple will be the type of the register <code>A</code>, the second the type of <code>B</code>, and the third the type of <code>R</code>. Then, we can define two helper functions, one for retrieving the type of a register, and one for changing it:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-imperative/TypesafeImp.idr" data-first-line="8" data-last-line="16"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-imperative/TypesafeImp.idr#L8-L16">TypesafeImp.idr</a>, lines 8 through 16</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">getRegTy</span> <span class="ow">:</span> <span class="kt">Reg</span> <span class="ow">-&gt;</span> <span class="kt">TypeState</span> <span class="ow">-&gt;</span> <span class="kt">Ty</span> </span></span><span class="line"><span class="cl">getRegTy <span class="kt">A</span> <span class="ow">(</span>a, <span class="kr">_</span>, <span class="kr">_</span><span class="ow">)</span> <span class="ow">=</span> a </span></span><span class="line"><span class="cl">getRegTy <span class="kt">B</span> <span class="ow">(</span><span class="kr">_</span>, b, <span class="kr">_</span><span class="ow">)</span> <span class="ow">=</span> b </span></span><span class="line"><span class="cl">getRegTy <span class="kt">R</span> <span class="ow">(</span><span class="kr">_</span>, <span class="kr">_</span>, r<span class="ow">)</span> <span class="ow">=</span> r </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="nf">setRegTy</span> <span class="ow">:</span> <span class="kt">Reg</span> <span class="ow">-&gt;</span> <span class="kt">Ty</span> <span class="ow">-&gt;</span> <span class="kt">TypeState</span> <span class="ow">-&gt;</span> <span class="kt">TypeState</span> </span></span><span class="line"><span class="cl">setRegTy <span class="kt">A</span> a <span class="ow">(</span><span class="kr">_</span>, b, r<span class="ow">)</span> <span class="ow">=</span> <span class="ow">(</span>a, b, r<span class="ow">)</span> </span></span><span class="line"><span class="cl">setRegTy <span class="kt">B</span> b <span class="ow">(</span>a, <span class="kr">_</span>, r<span class="ow">)</span> <span class="ow">=</span> <span class="ow">(</span>a, b, r<span class="ow">)</span> </span></span><span class="line"><span class="cl">setRegTy <span class="kt">R</span> r <span class="ow">(</span>a, b, <span class="kr">_</span><span class="ow">)</span> <span class="ow">=</span> <span class="ow">(</span>a, b, r<span class="ow">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Now, it&rsquo;s time to talk about expressions. We know now that an expression can evaluate to either a boolean or an integer value (because a register can contain either of those types of values). Perhaps we can specify the type that an expression evaluates to in the expression&rsquo;s own type: <code>Expr IntTy</code> would evaluate to integers, and <code>Expr BoolTy</code> would evaluate to booleans. Then, we could have constructors as follows:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">Lit</span> <span class="ow">:</span> <span class="kt">Int</span> <span class="ow">-&gt;</span> <span class="kt">Expr</span> <span class="kt">IntTy</span> </span></span><span class="line"><span class="cl"><span class="nf">Not</span> <span class="ow">:</span> <span class="kt">Expr</span> <span class="kt">BoolTy</span> <span class="ow">-&gt;</span> <span class="kt">Expr</span> <span class="kt">BoolTy</span> </span></span></code></pre></div><p>Sounds good! But what about loading a register?</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">Load</span> <span class="ow">:</span> <span class="kt">Reg</span> <span class="ow">-&gt;</span> <span class="kt">Expr</span> <span class="kt">IntTy</span> <span class="c1">-- no; what if the register is a boolean?</span> </span></span><span class="line"><span class="cl"><span class="nf">Load</span> <span class="ow">:</span> <span class="kt">Reg</span> <span class="ow">-&gt;</span> <span class="kt">Expr</span> <span class="kt">BoolTy</span> <span class="c1">-- no; what if the register is an integer?</span> </span></span><span class="line"><span class="cl"><span class="nf">Load</span> <span class="ow">:</span> <span class="kt">Reg</span> <span class="ow">-&gt;</span> <span class="kt">Expr</span> a <span class="c1">-- no; a register access can&#39;t be either!</span> </span></span></code></pre></div><p>The type of an expression that loads a register depends on the current state of the program! If we last stored an integer into a register, then loading from that register would give us an integer. But if we last stored a boolean into a register, then reading from it would give us a boolean. Our expressions need to be aware of the current types of each register. To do so, we add the state as a parameter to our <code>Expr</code> type. This would lead to types like the following:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="c1">-- An expression that produces a boolean when all the registers</span> </span></span><span class="line"><span class="cl"><span class="c1">-- are integers.</span> </span></span><span class="line"><span class="cl"><span class="kt">Expr</span> <span class="ow">(</span><span class="kt">IntTy</span>, <span class="kt">IntTy</span>, <span class="kt">IntTy</span><span class="ow">)</span> <span class="kt">BoolTy</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1">-- An expression that produces an integer when A and B are integers,</span> </span></span><span class="line"><span class="cl"><span class="c1">-- and R is a boolean.</span> </span></span><span class="line"><span class="cl"><span class="kt">Expr</span> <span class="ow">(</span><span class="kt">IntTy</span>, <span class="kt">IntTy</span>, <span class="kt">BoolTy</span><span class="ow">)</span> <span class="kt">IntTy</span> </span></span></code></pre></div><p>In Idris, the whole definition becomes:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-imperative/TypesafeImp.idr" data-first-line="18" data-last-line="23"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-imperative/TypesafeImp.idr#L18-L23">TypesafeImp.idr</a>, lines 18 through 23</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="kr">data</span> <span class="kt">Expr</span> <span class="ow">:</span> <span class="kt">TypeState</span> <span class="ow">-&gt;</span> <span class="kt">Ty</span> <span class="ow">-&gt;</span> <span class="kt">Type</span> <span class="kr">where</span> </span></span><span class="line"><span class="cl"> <span class="nf">Lit</span> <span class="ow">:</span> <span class="kt">Int</span> <span class="ow">-&gt;</span> <span class="kt">Expr</span> s <span class="kt">IntTy</span> </span></span><span class="line"><span class="cl"> <span class="nf">Load</span> <span class="ow">:</span> <span class="ow">(</span>r <span class="ow">:</span> <span class="kt">Reg</span><span class="ow">)</span> <span class="ow">-&gt;</span> <span class="kt">Expr</span> s <span class="ow">(</span>getRegTy r s<span class="ow">)</span> </span></span><span class="line"><span class="cl"> <span class="nf">Add</span> <span class="ow">:</span> <span class="kt">Expr</span> s <span class="kt">IntTy</span> <span class="ow">-&gt;</span> <span class="kt">Expr</span> s <span class="kt">IntTy</span> <span class="ow">-&gt;</span> <span class="kt">Expr</span> s <span class="kt">IntTy</span> </span></span><span class="line"><span class="cl"> <span class="nf">Leq</span> <span class="ow">:</span> <span class="kt">Expr</span> s <span class="kt">IntTy</span> <span class="ow">-&gt;</span> <span class="kt">Expr</span> s <span class="kt">IntTy</span> <span class="ow">-&gt;</span> <span class="kt">Expr</span> s <span class="kt">BoolTy</span> </span></span><span class="line"><span class="cl"> <span class="nf">Not</span> <span class="ow">:</span> <span class="kt">Expr</span> s <span class="kt">BoolTy</span> <span class="ow">-&gt;</span> <span class="kt">Expr</span> s <span class="kt">BoolTy</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The only &ldquo;interesting&rdquo; constructor is <code>Load</code>, which, given a register <code>r</code>, creates an expression that has <code>r</code>&rsquo;s type in the current state <code>s</code>.</p> <a href="#statements"> <h3 id="statements">Statements</h3> </a> <p>Statements are a bit different. Unlike expressions, they don&rsquo;t evaluate to anything; rather, they do something. That &ldquo;something&rdquo; may very well be changing the current state. We could, for instance, set <code>A</code> to be a boolean, while it was previously an integer. This suggests equipping our <code>Stmt</code> type with two arguments: the initial state (before the statement&rsquo;s execution), and the final state (after the statement&rsquo;s execution). This would lead to types like this:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="c1">-- Statement that, when run while all registers contain integers,</span> </span></span><span class="line"><span class="cl"><span class="c1">-- terminates with registers B and R having been assigned boolean values.</span> </span></span><span class="line"><span class="cl"><span class="kt">Stmt</span> <span class="ow">(</span><span class="kt">IntTy</span>, <span class="kt">IntTy</span>, <span class="kt">IntTy</span><span class="ow">)</span> <span class="ow">(</span><span class="kt">IntTy</span>, <span class="kt">BoolTy</span>, <span class="kt">BoolTy</span><span class="ow">)</span> </span></span></code></pre></div><p>However, there&rsquo;s a problem with <code>loop</code> and <code>break</code>. When we run a loop, we will require that the state at the end of one iteration is the same as the state at its beginning. Otherwise, it would be possible for a loop to keep changing the types of registers every iteration, and it would become impossible for us to infer the final state without actually running the program. In itself, this restriction isn&rsquo;t a problem; most static type systems require both branches of an <code>if/else</code> expression to be of the same type for a similar reason. The problem comes from the interaction with <code>break</code>.</p> <p>By itself, the would-be type of <code>break</code> seems innocent enough. It doesn&rsquo;t change any registers, so we could call it <code>Stmt s s</code>. But consider the following program:</p> <pre tabindex="0"><code>A := 0 B := 0 R := 0 do if 5 &lt;= A then B := 1 &lt;= 1 break B := 0 else A := A + 1 end end </code></pre><p>The loop starts with all registers having integer values. As per our aforementioned loop requirement, the body of the loop must terminate with all registers <em>still</em> having integer values. For the first five iterations that&rsquo;s exactly what will happen. However, after we increment <code>A</code> the fifth time, we will set <code>B</code> to a boolean value &ndash; using a valid statement &ndash; and then <code>break</code>. The <code>break</code> statement will be accepted by the typechecker, and so will the whole <code>then</code> branch. After all, it seems as though we reset <code>B</code> back to an integer value. But that won&rsquo;t be the case. We will have jumped to the end of the loop, where we are expected to have an all-integer type, which we will not have.</p> <p>The solution I came up with to address this issue was to add a <em>third</em> argument to <code>Stmt</code>, which contains the &ldquo;context&rdquo; type. That is, it contains the type of the innermost loop surrounding the statement. A <code>break</code> statement would only be permissible if the current type matches the loop type. With this, we finally write down a definition of <code>Stmt</code>:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-imperative/TypesafeImp.idr" data-first-line="26" data-last-line="30"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-imperative/TypesafeImp.idr#L26-L30">TypesafeImp.idr</a>, lines 26 through 30</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"> <span class="kr">data</span> <span class="kt">Stmt</span> <span class="ow">:</span> <span class="kt">TypeState</span> <span class="ow">-&gt;</span> <span class="kt">TypeState</span> <span class="ow">-&gt;</span> <span class="kt">TypeState</span> <span class="ow">-&gt;</span> <span class="kt">Type</span> <span class="kr">where</span> </span></span><span class="line"><span class="cl"> <span class="nf">Store</span> <span class="ow">:</span> <span class="ow">(</span>r <span class="ow">:</span> <span class="kt">Reg</span><span class="ow">)</span> <span class="ow">-&gt;</span> <span class="kt">Expr</span> s t <span class="ow">-&gt;</span> <span class="kt">Stmt</span> l s <span class="ow">(</span>setRegTy r t s<span class="ow">)</span> </span></span><span class="line"><span class="cl"> <span class="nf">If</span> <span class="ow">:</span> <span class="kt">Expr</span> s <span class="kt">BoolTy</span> <span class="ow">-&gt;</span> <span class="kt">Prog</span> l s n <span class="ow">-&gt;</span> <span class="kt">Prog</span> l s n <span class="ow">-&gt;</span> <span class="kt">Stmt</span> l s n </span></span><span class="line"><span class="cl"> <span class="nf">Loop</span> <span class="ow">:</span> <span class="kt">Prog</span> s s s <span class="ow">-&gt;</span> <span class="kt">Stmt</span> l s s </span></span><span class="line"><span class="cl"> <span class="nf">Break</span> <span class="ow">:</span> <span class="kt">Stmt</span> s s s</span></span></code></pre></td></tr></table> </div> </div> </div> <p>The <code>Store</code> constructor takes a register <code>r</code> and an expression producing some type <code>t</code> in state <code>s</code>. From these, it creates a statement that starts in <code>s</code>, and finishes in a state similar to <code>s</code>, but with <code>r</code> now having type <code>t</code>. The loop type <code>l</code> remains unaffected and unused; we are free to assign any register any value.</p> <p>The <code>If</code> constructor takes a condition <code>Expr</code>, which starts in state <code>s</code> and <em>must</em> produce a boolean. It also takes two programs (sequences of statements), each of which starts in <code>s</code> and finishes in another state <code>n</code>. This results in a statement that starts in state <code>s</code>, and finishes in state <code>n</code>. Conceptually, each branch of the <code>if/else</code> statement must result in the same final state (in terms of types); otherwise, we wouldn&rsquo;t know which of the states to pick when deciding the final state of the <code>If</code> itself. As with <code>Store</code>, the loop type <code>l</code> is untouched here. Individual statements are free to modify the state however they wish.</p> <p>The <code>Loop</code> constructor is very restrictive. It takes a single program (the sequence of instructions that it will be repeating). As we discussed above, this program must start <em>and</em> end in the same state <code>s</code>. Furthermore, this program&rsquo;s loop type must also be <code>s</code>, since the loop we&rsquo;re constructing will be surrounding the program. The resulting loop itself still has an arbitrary loop type <code>l</code>, since it doesn&rsquo;t surround itself.</p> <p>Finally, <code>Break</code> can only be constructed when the loop state matches the current state. Since we&rsquo;ll be jumping to the end of the innermost loop, the final state is also the same as the loop state.</p> <p>These are all the constructors we&rsquo;ll be needing. It&rsquo;s time to move on to whole programs!</p> <a href="#programs"> <h3 id="programs">Programs</h3> </a> <p>A program is simply a list of statements. However, we can&rsquo;t use a regular Idris list, because a regular list wouldn&rsquo;t be able to represent the relationship between each successive statement. In our program, we want the final state of one statement to be the initial state of the following one, since they&rsquo;ll be executed in sequence. To represent this, we have to define our own list-like GADT. The definition of the type turns out fairly straightforward:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-imperative/TypesafeImp.idr" data-first-line="32" data-last-line="34"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-imperative/TypesafeImp.idr#L32-L34">TypesafeImp.idr</a>, lines 32 through 34</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"> <span class="kr">data</span> <span class="kt">Prog</span> <span class="ow">:</span> <span class="kt">TypeState</span> <span class="ow">-&gt;</span> <span class="kt">TypeState</span> <span class="ow">-&gt;</span> <span class="kt">TypeState</span> <span class="ow">-&gt;</span> <span class="kt">Type</span> <span class="kr">where</span> </span></span><span class="line"><span class="cl"> <span class="nf">Nil</span> <span class="ow">:</span> <span class="kt">Prog</span> l s s </span></span><span class="line"><span class="cl"> <span class="ow">(::)</span> <span class="ow">:</span> <span class="kt">Stmt</span> l s n <span class="ow">-&gt;</span> <span class="kt">Prog</span> l n m <span class="ow">-&gt;</span> <span class="kt">Prog</span> l s m</span></span></code></pre></td></tr></table> </div> </div> </div> <p>The <code>Nil</code> constructor represents an empty program (much like the built-in <code>Nil</code> represents an empty list). Since no actions are done, it creates a <code>Prog</code> that starts and ends in the same state: <code>s</code>. The <code>(::)</code> constructor, much like the built-in <code>(::)</code> constructor, takes a statement and another program. The statement begins in state <code>s</code> and ends in state <code>n</code>; the program after that statement must then start in state <code>n</code>, and end in some other state <code>m</code>. The combination of the statement and the program starts in state <code>s</code>, and finishes in state <code>m</code>. Thus, <code>(::)</code> yields <code>Prog s m</code>. None of the constructors affect the loop type <code>l</code>: we are free to sequence any statements that we want, and it is impossible for us to construct statements using <code>l</code> that cause runtime errors.</p> <p>This should be all! Let&rsquo;s try out some programs.</p> <a href="#trying-it-out"> <h3 id="trying-it-out">Trying it Out</h3> </a> <p>The following (type-correct) program compiles just fine:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-imperative/TypesafeImp.idr" data-first-line="36" data-last-line="47"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-imperative/TypesafeImp.idr#L36-L47">TypesafeImp.idr</a>, lines 36 through 47</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">initialState</span> <span class="ow">:</span> <span class="kt">TypeState</span> </span></span><span class="line"><span class="cl">initialState <span class="ow">=</span> <span class="ow">(</span><span class="kt">IntTy</span>, <span class="kt">IntTy</span>, <span class="kt">IntTy</span><span class="ow">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="nf">testProg</span> <span class="ow">:</span> <span class="kt">Prog</span> <span class="kt">Main</span><span class="ow">.</span>initialState <span class="kt">Main</span><span class="ow">.</span>initialState <span class="kt">Main</span><span class="ow">.</span>initialState </span></span><span class="line"><span class="cl">testProg <span class="ow">=</span> </span></span><span class="line"><span class="cl"> <span class="ow">[</span> <span class="kt">Store</span> <span class="kt">A</span> <span class="ow">(</span><span class="kt">Lit</span> <span class="mi">1</span> `Leq` <span class="kt">Lit</span> <span class="mi">2</span><span class="ow">)</span> </span></span><span class="line"><span class="cl"> , <span class="kt">If</span> <span class="ow">(</span><span class="kt">Load</span> <span class="kt">A</span><span class="ow">)</span> </span></span><span class="line"><span class="cl"> <span class="ow">[</span> <span class="kt">Store</span> <span class="kt">A</span> <span class="ow">(</span><span class="kt">Lit</span> <span class="mi">1</span><span class="ow">)</span> <span class="ow">]</span> </span></span><span class="line"><span class="cl"> <span class="ow">[</span> <span class="kt">Store</span> <span class="kt">A</span> <span class="ow">(</span><span class="kt">Lit</span> <span class="mi">2</span><span class="ow">)</span> <span class="ow">]</span> </span></span><span class="line"><span class="cl"> , <span class="kt">Store</span> <span class="kt">B</span> <span class="ow">(</span><span class="kt">Lit</span> <span class="mi">2</span><span class="ow">)</span> </span></span><span class="line"><span class="cl"> , <span class="kt">Store</span> <span class="kt">R</span> <span class="ow">(</span><span class="kt">Add</span> <span class="ow">(</span><span class="kt">Load</span> <span class="kt">A</span><span class="ow">)</span> <span class="ow">(</span><span class="kt">Load</span> <span class="kt">B</span><span class="ow">))</span> </span></span><span class="line"><span class="cl"> <span class="ow">]</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>First, it loads a boolean into register <code>A</code>; then, inside the <code>if/else</code> statement, it stores an integer into <code>A</code>. Finally, it stores another integer into <code>B</code>, and adds them into <code>R</code>. Even though <code>A</code> was a boolean at first, the type checker can deduce that it was reset back to an integer after the <code>if/else</code>, and the program is accepted. On the other hand, had we forgotten to set <code>A</code> to a boolean first:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"> <span class="ow">[</span> <span class="kt">If</span> <span class="ow">(</span><span class="kt">Load</span> <span class="kt">A</span><span class="ow">)</span> </span></span><span class="line"><span class="cl"> <span class="ow">[</span> <span class="kt">Store</span> <span class="kt">A</span> <span class="ow">(</span><span class="kt">Lit</span> <span class="mi">1</span><span class="ow">)</span> <span class="ow">]</span> </span></span><span class="line"><span class="cl"> <span class="ow">[</span> <span class="kt">Store</span> <span class="kt">A</span> <span class="ow">(</span><span class="kt">Lit</span> <span class="mi">2</span><span class="ow">)</span> <span class="ow">]</span> </span></span><span class="line"><span class="cl"> , <span class="kt">Store</span> <span class="kt">B</span> <span class="ow">(</span><span class="kt">Lit</span> <span class="mi">2</span><span class="ow">)</span> </span></span><span class="line"><span class="cl"> , <span class="kt">Store</span> <span class="kt">R</span> <span class="ow">(</span><span class="kt">Add</span> <span class="ow">(</span><span class="kt">Load</span> <span class="kt">A</span><span class="ow">)</span> <span class="ow">(</span><span class="kt">Load</span> <span class="kt">B</span><span class="ow">))</span> </span></span><span class="line"><span class="cl"> <span class="ow">]</span> </span></span></code></pre></div><p>We would get a type error:</p> <pre tabindex="0"><code>Type mismatch between getRegTy A (IntTy, IntTy, IntTy) and BoolTy </code></pre><p>The type of register <code>A</code> (that is, <code>IntTy</code>) is incompatible with <code>BoolTy</code>. Our <code>initialState</code> says that <code>A</code> starts out as an integer, so it can&rsquo;t be used in an <code>if/else</code> right away! Similar errors occur if we make one of the branches of the <code>if/else</code> empty, or if we set <code>B</code> to a boolean.</p> <p>We can also encode the example program from the beginning of this post:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-imperative/TypesafeImp.idr" data-first-line="49" data-last-line="61"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-imperative/TypesafeImp.idr#L49-L61">TypesafeImp.idr</a>, lines 49 through 61</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">prodProg</span> <span class="ow">:</span> <span class="kt">Prog</span> <span class="kt">Main</span><span class="ow">.</span>initialState <span class="kt">Main</span><span class="ow">.</span>initialState <span class="kt">Main</span><span class="ow">.</span>initialState </span></span><span class="line"><span class="cl">prodProg <span class="ow">=</span> </span></span><span class="line"><span class="cl"> <span class="ow">[</span> <span class="kt">Store</span> <span class="kt">A</span> <span class="ow">(</span><span class="kt">Lit</span> <span class="mi">7</span><span class="ow">)</span> </span></span><span class="line"><span class="cl"> , <span class="kt">Store</span> <span class="kt">B</span> <span class="ow">(</span><span class="kt">Lit</span> <span class="mi">9</span><span class="ow">)</span> </span></span><span class="line"><span class="cl"> , <span class="kt">Store</span> <span class="kt">R</span> <span class="ow">(</span><span class="kt">Lit</span> <span class="mi">0</span><span class="ow">)</span> </span></span><span class="line"><span class="cl"> , <span class="kt">Loop</span> </span></span><span class="line"><span class="cl"> <span class="ow">[</span> <span class="kt">If</span> <span class="ow">(</span><span class="kt">Load</span> <span class="kt">A</span> `Leq` <span class="kt">Lit</span> <span class="mi">0</span><span class="ow">)</span> </span></span><span class="line"><span class="cl"> <span class="ow">[</span> <span class="kt">Break</span> <span class="ow">]</span> </span></span><span class="line"><span class="cl"> <span class="ow">[</span> <span class="kt">Store</span> <span class="kt">R</span> <span class="ow">(</span><span class="kt">Load</span> <span class="kt">R</span> `Add` <span class="kt">Load</span> <span class="kt">B</span><span class="ow">)</span> </span></span><span class="line"><span class="cl"> , <span class="kt">Store</span> <span class="kt">A</span> <span class="ow">(</span><span class="kt">Load</span> <span class="kt">A</span> `Add` <span class="kt">Lit</span> <span class="ow">(-</span><span class="mi">1</span><span class="ow">))</span> </span></span><span class="line"><span class="cl"> <span class="ow">]</span> </span></span><span class="line"><span class="cl"> <span class="ow">]</span> </span></span><span class="line"><span class="cl"> <span class="ow">]</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This program compiles just fine, too! It is a little reminiscent of the program we used to demonstrate how <code>break</code> could break things if we weren&rsquo;t careful. So, let&rsquo;s go ahead and try <code>break</code> in an invalid state:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"> <span class="ow">[</span> <span class="kt">Store</span> <span class="kt">A</span> <span class="ow">(</span><span class="kt">Lit</span> <span class="mi">7</span><span class="ow">)</span> </span></span><span class="line"><span class="cl"> , <span class="kt">Store</span> <span class="kt">B</span> <span class="ow">(</span><span class="kt">Lit</span> <span class="mi">9</span><span class="ow">)</span> </span></span><span class="line"><span class="cl"> , <span class="kt">Store</span> <span class="kt">R</span> <span class="ow">(</span><span class="kt">Lit</span> <span class="mi">0</span><span class="ow">)</span> </span></span><span class="line"><span class="cl"> , <span class="kt">Loop</span> </span></span><span class="line"><span class="cl"> <span class="ow">[</span> <span class="kt">If</span> <span class="ow">(</span><span class="kt">Load</span> <span class="kt">A</span> `Leq` <span class="kt">Lit</span> <span class="mi">0</span><span class="ow">)</span> </span></span><span class="line"><span class="cl"> <span class="ow">[</span> <span class="kt">Store</span> <span class="kt">B</span> <span class="ow">(</span><span class="kt">Lit</span> <span class="mi">1</span> `Leq` <span class="kt">Lit</span> <span class="mi">1</span><span class="ow">)</span>, <span class="kt">Break</span>, <span class="kt">Store</span> <span class="kt">B</span> <span class="ow">(</span><span class="kt">Lit</span> <span class="mi">0</span><span class="ow">)</span> <span class="ow">]</span> </span></span><span class="line"><span class="cl"> <span class="ow">[</span> <span class="kt">Store</span> <span class="kt">R</span> <span class="ow">(</span><span class="kt">Load</span> <span class="kt">R</span> `Add` <span class="kt">Load</span> <span class="kt">B</span><span class="ow">)</span> </span></span><span class="line"><span class="cl"> , <span class="kt">Store</span> <span class="kt">A</span> <span class="ow">(</span><span class="kt">Load</span> <span class="kt">A</span> `Add` <span class="kt">Lit</span> <span class="ow">(-</span><span class="mi">1</span><span class="ow">))</span> </span></span><span class="line"><span class="cl"> <span class="ow">]</span> </span></span><span class="line"><span class="cl"> <span class="ow">]</span> </span></span><span class="line"><span class="cl"> <span class="ow">]</span> </span></span></code></pre></div><p>Again, the type checker complains:</p> <pre tabindex="0"><code>Type mismatch between IntTy and BoolTy </code></pre><p>And so, we have an encoding of our language that allows registers to be either integers or booleans, while still preventing type-incorrect programs!</p> <a href="#building-an-interpreter"> <h3 id="building-an-interpreter">Building an Interpreter</h3> </a> <p>A good test of such an encoding is the implementation of an interpreter. It should be possible to convince the typechecker that our interpreter doesn&rsquo;t need to handle type errors in the toy language, since they cannot occur.</p> <p>Let&rsquo;s start with something simple. First of all, suppose we have an expression of type <code>Expr InTy</code>. In our toy language, it produces an integer. Our interpreter, then, will probably want to use Idris&rsquo; type <code>Int</code>. Similarly, an expression of type <code>Expr BoolTy</code> will produce a boolean in our toy language, which in Idris we can represent using the built-in <code>Bool</code> type. Despite the similar naming, though, there&rsquo;s no connection between Idris&rsquo; <code>Bool</code> and our own <code>BoolTy</code>. We need to define a conversion from our own types &ndash; which are values of type <code>Ty</code> &ndash; into Idris types that result from evaluating expressions. We do so as follows:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-imperative/TypesafeImp.idr" data-first-line="63" data-last-line="65"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-imperative/TypesafeImp.idr#L63-L65">TypesafeImp.idr</a>, lines 63 through 65</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">63 </span><span class="lnt">64 </span><span class="lnt">65 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">repr</span> <span class="ow">:</span> <span class="kt">Ty</span> <span class="ow">-&gt;</span> <span class="kt">Type</span> </span></span><span class="line"><span class="cl">repr <span class="kt">IntTy</span> <span class="ow">=</span> <span class="kt">Int</span> </span></span><span class="line"><span class="cl">repr <span class="kt">BoolTy</span> <span class="ow">=</span> <span class="kt">Bool</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Similarly, we want to convert our <code>TypeState</code> (a tuple describing the <em>types</em> of our registers) into a tuple that actually holds the values of each register, which we will call <code>State</code>. The value of each register at any point depends on its type. My first thought was to define <code>State</code> as a function from <code>TypeState</code> to an Idris <code>Type</code>:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">State</span> <span class="ow">:</span> <span class="kt">TypeState</span> <span class="ow">-&gt;</span> <span class="kt">Type</span> </span></span><span class="line"><span class="cl"><span class="kt">State</span> <span class="ow">(</span>a, b, c<span class="ow">)</span> <span class="ow">=</span> <span class="ow">(</span>repr a, repr b, repr c<span class="ow">)</span> </span></span></code></pre></div><p>Unfortunately, this doesn&rsquo;t quite cut it. The problem is that this function technically doesn&rsquo;t give Idris any guarantees that <code>State</code> will be a tuple. The most Idris knows is that <code>State</code> will be some <code>Type</code>, which could be <code>Int</code>, <code>Bool</code>, or anything else! This becomes a problem when we try to pattern match on states to get the contents of a particular register. Instead, we have to define a new data type:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-imperative/TypesafeImp.idr" data-first-line="67" data-last-line="68"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-imperative/TypesafeImp.idr#L67-L68">TypesafeImp.idr</a>, lines 67 through 68</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">67 </span><span class="lnt">68 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="kr">data</span> <span class="kt">State</span> <span class="ow">:</span> <span class="kt">TypeState</span> <span class="ow">-&gt;</span> <span class="kt">Type</span> <span class="kr">where</span> </span></span><span class="line"><span class="cl"> <span class="nf">MkState</span> <span class="ow">:</span> <span class="ow">(</span>repr a, repr b, repr c<span class="ow">)</span> <span class="ow">-&gt;</span> <span class="kt">State</span> <span class="ow">(</span>a, b, c<span class="ow">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>In this snippet, <code>State</code> is still a (type level) function from <code>TypeState</code> to <code>Type</code>. However, by using a GADT, we guarantee that there&rsquo;s only one way to construct a <code>State (a,b,c)</code>: using a corresponding tuple. Now, Idris will accept our pattern matching:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-imperative/TypesafeImp.idr" data-first-line="70" data-last-line="78"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-imperative/TypesafeImp.idr#L70-L78">TypesafeImp.idr</a>, lines 70 through 78</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">70 </span><span class="lnt">71 </span><span class="lnt">72 </span><span class="lnt">73 </span><span class="lnt">74 </span><span class="lnt">75 </span><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">getReg</span> <span class="ow">:</span> <span class="ow">(</span>r <span class="ow">:</span> <span class="kt">Reg</span><span class="ow">)</span> <span class="ow">-&gt;</span> <span class="kt">State</span> s <span class="ow">-&gt;</span> repr <span class="ow">(</span>getRegTy r s<span class="ow">)</span> </span></span><span class="line"><span class="cl">getReg <span class="kt">A</span> <span class="ow">(</span><span class="kt">MkState</span> <span class="ow">(</span>a, <span class="kr">_</span>, <span class="kr">_</span><span class="ow">))</span> <span class="ow">=</span> a </span></span><span class="line"><span class="cl">getReg <span class="kt">B</span> <span class="ow">(</span><span class="kt">MkState</span> <span class="ow">(</span><span class="kr">_</span>, b, <span class="kr">_</span><span class="ow">))</span> <span class="ow">=</span> b </span></span><span class="line"><span class="cl">getReg <span class="kt">R</span> <span class="ow">(</span><span class="kt">MkState</span> <span class="ow">(</span><span class="kr">_</span>, <span class="kr">_</span>, r<span class="ow">))</span> <span class="ow">=</span> r </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="nf">setReg</span> <span class="ow">:</span> <span class="ow">(</span>r <span class="ow">:</span> <span class="kt">Reg</span><span class="ow">)</span> <span class="ow">-&gt;</span> repr t <span class="ow">-&gt;</span> <span class="kt">State</span> s <span class="ow">-&gt;</span> <span class="kt">State</span> <span class="ow">(</span>setRegTy r t s<span class="ow">)</span> </span></span><span class="line"><span class="cl">setReg <span class="kt">A</span> a <span class="ow">(</span><span class="kt">MkState</span> <span class="ow">(</span><span class="kr">_</span>, b, r<span class="ow">))</span> <span class="ow">=</span> <span class="kt">MkState</span> <span class="ow">(</span>a, b, r<span class="ow">)</span> </span></span><span class="line"><span class="cl">setReg <span class="kt">B</span> b <span class="ow">(</span><span class="kt">MkState</span> <span class="ow">(</span>a, <span class="kr">_</span>, r<span class="ow">))</span> <span class="ow">=</span> <span class="kt">MkState</span> <span class="ow">(</span>a, b, r<span class="ow">)</span> </span></span><span class="line"><span class="cl">setReg <span class="kt">R</span> r <span class="ow">(</span><span class="kt">MkState</span> <span class="ow">(</span>a, b, <span class="kr">_</span><span class="ow">))</span> <span class="ow">=</span> <span class="kt">MkState</span> <span class="ow">(</span>a, b, r<span class="ow">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The <code>getReg</code> function will take out the value of the corresponding register, returning <code>Int</code> or <code>Bool</code> depending on the <code>TypeState</code>. What&rsquo;s important is that if the <code>TypeState</code> is known, then so is the type of <code>getReg</code>: no <code>Either</code> is involved here, and we can directly use the integer or boolean stored in the register. This is exactly what we do:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-imperative/TypesafeImp.idr" data-first-line="80" data-last-line="85"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-imperative/TypesafeImp.idr#L80-L85">TypesafeImp.idr</a>, lines 80 through 85</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">80 </span><span class="lnt">81 </span><span class="lnt">82 </span><span class="lnt">83 </span><span class="lnt">84 </span><span class="lnt">85 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">expr</span> <span class="ow">:</span> <span class="kt">Expr</span> s t <span class="ow">-&gt;</span> <span class="kt">State</span> s <span class="ow">-&gt;</span> repr t </span></span><span class="line"><span class="cl">expr <span class="ow">(</span><span class="kt">Lit</span> i<span class="ow">)</span> <span class="kr">_</span> <span class="ow">=</span> i </span></span><span class="line"><span class="cl">expr <span class="ow">(</span><span class="kt">Load</span> r<span class="ow">)</span> s <span class="ow">=</span> getReg r s </span></span><span class="line"><span class="cl">expr <span class="ow">(</span><span class="kt">Add</span> l r<span class="ow">)</span> s <span class="ow">=</span> expr l s <span class="ow">+</span> expr r s </span></span><span class="line"><span class="cl">expr <span class="ow">(</span><span class="kt">Leq</span> l r<span class="ow">)</span> s <span class="ow">=</span> expr l s <span class="ow">&lt;=</span> expr r s </span></span><span class="line"><span class="cl">expr <span class="ow">(</span><span class="kt">Not</span> e<span class="ow">)</span> s <span class="ow">=</span> not <span class="ow">$</span> expr e s</span></span></code></pre></td></tr></table> </div> </div> </div> <p>This is pretty concise. Idris knows that <code>Lit i</code> is of type <code>Expr IntTy</code>, and it knows that <code>repr IntTy = Int</code>, so it also knows that <code>eval (Lit i)</code> produces an <code>Int</code>. Similarly, we wrote <code>Reg r</code> to have type <code>Expr s (getRegTy r s)</code>. Since <code>getReg</code> returns <code>repr (getRegTy r s)</code>, things check out here, too. A similar logic applies to the rest of the cases.</p> <p>The situation with statements is somewhat different. As we said, a statement doesn&rsquo;t return a value; it changes state. A good initial guess would be that to evaluate a statement that starts in state <code>s</code> and terminates in state <code>n</code>, we would take as input <code>State s</code> and return <code>State n</code>. However, things are not quite as simple, thanks to <code>Break</code>. Not only does <code>Break</code> require special case logic to return control to the end of the <code>Loop</code>, but it also requires some additional consideration: in a statement of type <code>Stmt l s n</code>, evaluating <code>Break</code> can return <code>State l</code>.</p> <p>To implement this, we&rsquo;ll use the <code>Either</code> type. The <code>Left</code> constructor will be contain the state at the time of evaluating a <code>Break</code>, and will indicate to the interpreter that we&rsquo;re breaking out of a loop. On the other hand, the <code>Right</code> constructor will contain the state as produced by all other statements, which would be considered <span class="sidenote"> <label class="sidenote-label" for="left-right-note">the &ldquo;normal&rdquo; case.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="left-right-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> We use <code>Left</code> for the "abnormal" case because of Idris' (and Haskell's) convention to use it as such. For instance, the two languages define a <code>Monad</code> instance for <code>Either a</code> where <code>(>>=)</code> behaves very much like it does for <code>Maybe</code>, with <code>Left</code> being treated as <code>Nothing</code>, and <code>Right</code> as <code>Just</code>. We will use this instance to clean up some of our computations. <span class="sidenote-delimiter">]</span> </span> </span> Note that this doesn&rsquo;t indicate an error: we need to represent the two states (breaking out of a loop and normal execution) to define our language&rsquo;s semantics.</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-imperative/TypesafeImp.idr" data-first-line="88" data-last-line="95"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-imperative/TypesafeImp.idr#L88-L95">TypesafeImp.idr</a>, lines 88 through 95</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">88 </span><span class="lnt">89 </span><span class="lnt">90 </span><span class="lnt">91 </span><span class="lnt">92 </span><span class="lnt">93 </span><span class="lnt">94 </span><span class="lnt">95 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"> <span class="nf">stmt</span> <span class="ow">:</span> <span class="kt">Stmt</span> l s n <span class="ow">-&gt;</span> <span class="kt">State</span> s <span class="ow">-&gt;</span> <span class="kt">Either</span> <span class="ow">(</span><span class="kt">State</span> l<span class="ow">)</span> <span class="ow">(</span><span class="kt">State</span> n<span class="ow">)</span> </span></span><span class="line"><span class="cl"> stmt <span class="ow">(</span><span class="kt">Store</span> r e<span class="ow">)</span> s <span class="ow">=</span> <span class="kt">Right</span> <span class="ow">$</span> setReg r <span class="ow">(</span>expr e s<span class="ow">)</span> s </span></span><span class="line"><span class="cl"> stmt <span class="ow">(</span><span class="kt">If</span> c t e<span class="ow">)</span> s <span class="ow">=</span> <span class="kr">if</span> expr c s <span class="kr">then</span> prog t s <span class="kr">else</span> prog e s </span></span><span class="line"><span class="cl"> stmt <span class="ow">(</span><span class="kt">Loop</span> p<span class="ow">)</span> s <span class="ow">=</span> </span></span><span class="line"><span class="cl"> <span class="kr">case</span> prog p s <span class="ow">&gt;&gt;=</span> stmt <span class="ow">(</span><span class="kt">Loop</span> p<span class="ow">)</span> <span class="kr">of</span> </span></span><span class="line"><span class="cl"> <span class="kt">Right</span> s <span class="ow">=&gt;</span> <span class="kt">Right</span> s </span></span><span class="line"><span class="cl"> <span class="kt">Left</span> s <span class="ow">=&gt;</span> <span class="kt">Right</span> s </span></span><span class="line"><span class="cl"> stmt <span class="kt">Break</span> s <span class="ow">=</span> <span class="kt">Left</span> s</span></span></code></pre></td></tr></table> </div> </div> </div> <p>First, note the type. We return an <code>Either</code> value, which will contain <code>State l</code> (in the <code>Left</code> constructor) if a <code>Break</code> was evaluated, and <code>State n</code> (in the <code>Right</code> constructor) if execution went on without breaking.</p> <p>The <code>Store</code> case is rather simple. We use <code>setReg</code> to update the result of the register <code>r</code> with the result of evaluating <code>e</code>. Because a store doesn&rsquo;t cause us to start breaking out of a loop, we use <code>Right</code> to wrap the new state.</p> <p>The <code>If</code> case is also rather simple. Its condition is guaranteed to evaluate to a boolean, so it&rsquo;s sufficient for us to use Idris&rsquo; <code>if</code> expression. We use the <code>prog</code> function here, which implements the evaluation of a whole program. We&rsquo;ll get to it momentarily.</p> <p><code>Loop</code> is the most interesting case. We start by evaluating the program <code>p</code> serving as the loop body. The result of this computation will be either a state after a break, held in <code>Left</code>, or as the normal execution state, held in <code>Right</code>. The <code>(&gt;&gt;=)</code> operation will do nothing in the first case, and feed the resulting (normal) state to <code>stmt (Loop p)</code> in the second case. This is exactly what we want: if we broke out of the current iteration of the loop, we shouldn&rsquo;t continue on to the next iteration. At the end of evaluating both <code>p</code> and the recursive call to <code>stmt</code>, we&rsquo;ll either have exited normally, or via breaking out. We don&rsquo;t want to continue breaking out further, so we return the final state wrapped in <code>Right</code> in both cases. Finally, <code>Break</code> returns the current state wrapped in <code>Left</code>, beginning the process of breaking out.</p> <p>The task of <code>prog</code> is simply to sequence several statements together. The monadic bind operator, <code>(&gt;&gt;=)</code>, is again perfect for this task, since it &ldquo;stops&rdquo; when it hits a <code>Left</code>, but continues otherwise. This is the implementation:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-imperative/TypesafeImp.idr" data-first-line="97" data-last-line="99"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-imperative/TypesafeImp.idr#L97-L99">TypesafeImp.idr</a>, lines 97 through 99</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">97 </span><span class="lnt">98 </span><span class="lnt">99 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"> <span class="nf">prog</span> <span class="ow">:</span> <span class="kt">Prog</span> l s n <span class="ow">-&gt;</span> <span class="kt">State</span> s <span class="ow">-&gt;</span> <span class="kt">Either</span> <span class="ow">(</span><span class="kt">State</span> l<span class="ow">)</span> <span class="ow">(</span><span class="kt">State</span> n<span class="ow">)</span> </span></span><span class="line"><span class="cl"> prog <span class="kt">Nil</span> s <span class="ow">=</span> <span class="kt">Right</span> s </span></span><span class="line"><span class="cl"> prog <span class="ow">(</span>st<span class="ow">::</span>p<span class="ow">)</span> s <span class="ow">=</span> stmt st s <span class="ow">&gt;&gt;=</span> prog p</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Awesome! Let&rsquo;s try it out, shall we? I defined a quick <code>run</code> function as follows:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-imperative/TypesafeImp.idr" data-first-line="101" data-last-line="102"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-imperative/TypesafeImp.idr#L101-L102">TypesafeImp.idr</a>, lines 101 through 102</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">101 </span><span class="lnt">102 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">run</span> <span class="ow">:</span> <span class="kt">Prog</span> l s l <span class="ow">-&gt;</span> <span class="kt">State</span> s <span class="ow">-&gt;</span> <span class="kt">State</span> l </span></span><span class="line"><span class="cl">run p s <span class="ow">=</span> either id id <span class="ow">$</span> prog p s</span></span></code></pre></td></tr></table> </div> </div> </div> <p>We then have:</p> <pre tabindex="0"><code>*TypesafeImp&gt; run prodProg (MkState (0,0,0)) MkState (0, 9, 63) : State (IntTy, IntTy, IntTy) </code></pre><p>This seems correct! The program multiplies seven by nine, and stops when register <code>A</code> reaches zero. Our test program runs, too:</p> <pre tabindex="0"><code>*TypesafeImp&gt; run testProg (MkState (0,0,0)) MkState (1, 2, 3) : State (IntTy, IntTy, IntTy) </code></pre><p>This is the correct answer: <code>A</code> ends up being set to <code>1</code> in the <code>then</code> branch of the conditional statement, <code>B</code> is set to 2 right after, and <code>R</code>, the sum of <code>A</code> and <code>B</code>, is rightly <code>3</code>.</p> <p>As you can see, we didn&rsquo;t have to write any error handling code! This is because the typechecker <em>knows</em> that type errors aren&rsquo;t possible: our programs are guaranteed to be <span class="sidenote"> <label class="sidenote-label" for="termination-note">type correct.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="termination-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Our programs <em>aren't</em> guaranteed to terminate: we're lucky that Idris' totality checker is turned off by default. <span class="sidenote-delimiter">]</span> </span> </span> This was a fun exercise, and I hope you enjoyed reading along! I hope to see you in my future posts.</p> Compiling a Functional Language Using C++, Part 13 - Cleanup https://danilafe.com/blog/13_compiler_cleanup/ Sat, 19 Sep 2020 16:14:13 -0700 https://danilafe.com/blog/13_compiler_cleanup/ <p>In <a href="https://danilafe.com/blog/12_compiler_let_in_lambda/">part 12</a>, we added <code>let/in</code> and lambda expressions to our compiler. At the end of that post, I mentioned that before we move on to bigger and better things, I wanted to take a step back and clean up the compiler. Now is the time to do that.</p> <p>In particular, I identified four things that could be improved or cleaned up:</p> <ul> <li><strong>Error handling</strong>. We need to stop using <code>throw 0</code> and start using <code>assert</code>. We can also make our errors much more descriptive by including source locations in the output.</li> <li><strong>Name mangling</strong>. I don&rsquo;t think I got it quite right last time. Now is the time to clean it up.</li> <li><strong>Code organization</strong>. I think we can benefit from a top-level class, and a more clear &ldquo;dependency order&rdquo; between the various classes and structures we&rsquo;ve defined.</li> <li><strong>Code style</strong>. In particular, I&rsquo;ve been lazily using <code>struct</code> in a lot of places. That&rsquo;s not a good idea; it&rsquo;s better to use <code>class</code>, and only expose <em>some</em> fields and methods to the rest of the code.</li> </ul> <a href="#error-reporting-and-handling"> <h3 id="error-reporting-and-handling">Error Reporting and Handling</h3> </a> <p>The previous post was rather long, which led me to omit a rather important aspect of the compiler: proper error reporting. Once again our compiler has instances of <code>throw 0</code>, which is a cheap way of avoiding properly handling a runtime error. Before we move on, it&rsquo;s best to get rid of such blatantly lazy code.</p> <p>Our existing exceptions (mostly type errors) can use some work, too. Even the most descriptive issues our compiler reports &ndash; unification errors &ndash; don&rsquo;t include the crucial information of <em>where</em> the error is. For large programs, this means having to painstakingly read through the entire file to try figure out which subexpression could possibly have an incorrect type. This is far from the ideal debugging experience.</p> <p>Addressing all this is a multi-step change in itself. We want to:</p> <ul> <li>Replace all <code>throw 0</code> code with actual exceptions.</li> <li>Replace some exceptions that shouldn&rsquo;t be possible for a user to trigger with assertions.</li> <li>Keep track of source locations of each subexpression, so that we may be able to print it if it causes an error.</li> <li>Be able to print out said source locations at will. This isn&rsquo;t a <em>necessity</em>, but virtually all &ldquo;big&rdquo; compilers do this. Instead of reporting that an error occurs on a particular line, we will actually print the line.</li> </ul> <p>Let&rsquo;s start with gathering the actual location data.</p> <a href="#bisons-locations"> <h4 id="bisons-locations">Bison&rsquo;s Locations</h4> </a> <p>Bison actually has some rather nice support for location tracking. It can automatically assemble the &ldquo;from&rdquo; and &ldquo;to&rdquo; locations of a nonterminal from the locations of children, which would be very tedious to write by hand. We enable this feature using the following option:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/parser.y" data-first-line="46" data-last-line="46"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/parser.y#L46-L46">parser.y</a>, line 46</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">46 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="o">%</span><span class="n">locations</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>There&rsquo;s just one hitch, though. Sure, Bison can compute bigger locations from smaller ones, but it must get the smaller ones from somewhere. Since Bison operates on <em>tokens</em>, rather than <em>characters</em>, it effectively doesn&rsquo;t interact with the source text at all, and can&rsquo;t determine from which line or column a token originated. The task of determining the locations of input tokens is delegated to the tokenizer &ndash; Flex, in our case. Flex, on the other hand, doesn&rsquo;t have a built-in mechanism for tracking locations. Fortunately, Bison provides a <code>yy::location</code> class that includes most of the needed functionality.</p> <p>A <code>yy::location</code> consists of two source positions, <code>begin</code> and <code>end</code>, which themselves are represented using lines and columns. It also has the following methods:</p> <ul> <li><code>yy::location::columns(int)</code> advances the <code>end</code> position by the given number of columns, while <code>begin</code> stays the same. If <code>begin</code> and <code>end</code> both point to the beginning of a token, then <code>columns(token_length)</code> will move <code>end</code> to the token&rsquo;s end, and thus make the whole <code>location</code> contain the token.</li> <li><code>yy::location::lines(int)</code> behaves similarly to <code>columns</code>, except that it advances <code>end</code> by the given number of lines, rather than columns. It also resets the columns counter to <code>1</code>.</li> <li><code>yy::location::step()</code> moves <code>begin</code> to where <code>end</code> is. This is useful for when we&rsquo;ve finished processing a token, and want to move on to the next one.</li> </ul> <p>For Flex specifically, <code>yyleng</code> has the length of the token currently being processed. Rather than adding the calls to <code>columns</code> and <code>step</code> to every rule, we can define the <code>YY_USER_ACTION</code> macro, which is run before each token is processed.</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/scanner.l" data-first-line="12" data-last-line="14"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/scanner.l#L12-L14">scanner.l</a>, lines 12 through 14</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="cp">#define YY_USER_ACTION \ </span></span></span><span class="line"><span class="cl"><span class="cp"> drv.get_file_manager().write(yytext, yyleng); \ </span></span></span><span class="line"><span class="cl"><span class="cp"> LOC.step(); LOC.columns(yyleng); </span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We&rsquo;ll see why we are using <code>LOC</code> instead of something like <code>location</code> soon; for now, you can treat <code>LOC</code> as if it were a global variable declared in the tokenizer. Before processing each token, we ensure that the <code>yy::location</code> has its <code>begin</code> and <code>end</code> at the same position, and then advance <code>end</code> by <code>yyleng</code> columns. This is <span class="sidenote"> <label class="sidenote-label" for="sufficient-note">sufficient</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="sufficient-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> This doesn't hold for all languages. It may be possible for a language to have tokens that contain <code>\n</code>, in which case, rather than just using <code>yyleng</code>, we'd need to add special logic to iterate over the token and detect the line breaks.<br> <br> Also, this requires that the <code>end</code> of the previous token was correctly computed. <span class="sidenote-delimiter">]</span> </span> </span> to make <code>LOC</code> represent our token&rsquo;s source position. For the moment, don&rsquo;t worry too much about <code>drv</code>; this is the parsing driver, and we will talk about it shortly.</p> <p>So now we have a &ldquo;global&rdquo; variable <code>LOC</code> that gives us the source position of the current token. To get it to Bison, we have to pass it as an argument to each of the <code>make_TOKEN</code> calls. Here are a few sample lines that should give you the general idea:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/scanner.l" data-first-line="40" data-last-line="43"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/scanner.l#L40-L43">scanner.l</a>, lines 40 through 43</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="o">=</span> <span class="p">{</span> <span class="k">return</span> <span class="n">yy</span><span class="o">::</span><span class="n">parser</span><span class="o">::</span><span class="n">make_EQUAL</span><span class="p">(</span><span class="n">LOC</span><span class="p">);</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">[</span><span class="n">a</span><span class="o">-</span><span class="n">z</span><span class="p">][</span><span class="n">a</span><span class="o">-</span><span class="n">zA</span><span class="o">-</span><span class="n">Z</span><span class="p">]</span><span class="o">*</span> <span class="p">{</span> <span class="k">return</span> <span class="n">yy</span><span class="o">::</span><span class="n">parser</span><span class="o">::</span><span class="n">make_LID</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">(</span><span class="n">yytext</span><span class="p">),</span> <span class="n">LOC</span><span class="p">);</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">[</span><span class="n">A</span><span class="o">-</span><span class="n">Z</span><span class="p">][</span><span class="n">a</span><span class="o">-</span><span class="n">zA</span><span class="o">-</span><span class="n">Z</span><span class="p">]</span><span class="o">*</span> <span class="p">{</span> <span class="k">return</span> <span class="n">yy</span><span class="o">::</span><span class="n">parser</span><span class="o">::</span><span class="n">make_UID</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">(</span><span class="n">yytext</span><span class="p">),</span> <span class="n">LOC</span><span class="p">);</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="o">&lt;&lt;</span><span class="n">EOF</span><span class="o">&gt;&gt;</span> <span class="p">{</span> <span class="k">return</span> <span class="n">yy</span><span class="o">::</span><span class="n">parser</span><span class="o">::</span><span class="n">make_YYEOF</span><span class="p">(</span><span class="n">LOC</span><span class="p">);</span> <span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>That last line is actually new. Previously, we somehow got away without explicitly sending the end-of-file token to Bison. I suspect that this was due to some kind of implicit conversion of the Flex macro <code>YY_NULL</code> into a token; now that we have to pass a position to every token constructor, such an implicit conversion is probably impossible.</p> <p>Now we have Bison computing source locations for each nonterminal. However, at the moment, we still aren&rsquo;t using them. To change that, we need to add a <code>yy::location</code> argument to each of our <code>ast</code> nodes, as well as to the <code>pattern</code> subclasses, <code>definition_defn</code> and <code>definition_data</code>. To avoid breaking all the code that creates AST nodes and definitions outside of the parser, we&rsquo;ll make this argument optional. Inside of <code>ast.hpp</code>, we define a new field as follows:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/ast.hpp" data-first-line="16" data-last-line="16"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/ast.hpp#L16-L16">ast.hpp</a>, line 16</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">16 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="n">yy</span><span class="o">::</span><span class="n">location</span> <span class="n">loc</span><span class="p">;</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Then, we add a constructor to <code>ast</code> as follows:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/ast.hpp" data-first-line="18" data-last-line="18"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/ast.hpp#L18-L18">ast.hpp</a>, line 18</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">18 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="n">ast</span><span class="p">(</span><span class="n">yy</span><span class="o">::</span><span class="n">location</span> <span class="n">l</span><span class="p">)</span> <span class="o">:</span> <span class="n">env</span><span class="p">(</span><span class="k">nullptr</span><span class="p">),</span> <span class="n">loc</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">l</span><span class="p">))</span> <span class="p">{}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Note that it&rsquo;s not optional here, since <code>ast</code> itself is an abstract class, and thus will never be constructed directly. It is in the subclasses of <code>ast</code> that we provide a default value. The change is rather mechanical, but here&rsquo;s an example from <code>ast_binop</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/ast.hpp" data-first-line="98" data-last-line="99"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/ast.hpp#L98-L99">ast.hpp</a>, lines 98 through 99</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">98 </span><span class="lnt">99 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="n">ast_binop</span><span class="p">(</span><span class="n">binop</span> <span class="n">o</span><span class="p">,</span> <span class="n">ast_ptr</span> <span class="n">l</span><span class="p">,</span> <span class="n">ast_ptr</span> <span class="n">r</span><span class="p">,</span> <span class="n">yy</span><span class="o">::</span><span class="n">location</span> <span class="n">lc</span> <span class="o">=</span> <span class="n">yy</span><span class="o">::</span><span class="n">location</span><span class="p">())</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">ast</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">lc</span><span class="p">)),</span> <span class="n">op</span><span class="p">(</span><span class="n">o</span><span class="p">),</span> <span class="n">left</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">l</span><span class="p">)),</span> <span class="n">right</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">r</span><span class="p">))</span> <span class="p">{}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Finally, we tell Bison to pass the computed location data as an argument when constructing our data structures. This too is a mechanical change, and I think the following few lines demonstrate the general idea in sufficient detail:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/parser.y" data-first-line="92" data-last-line="96"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/parser.y#L92-L96">parser.y</a>, lines 92 through 96</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">92 </span><span class="lnt">93 </span><span class="lnt">94 </span><span class="lnt">95 </span><span class="lnt">96 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="nl">aMul</span> </span></span><span class="line"><span class="cl"> <span class="p">:</span> <span class="n">aMul</span> <span class="n">TIMES</span> <span class="n">app</span> <span class="p">{</span> <span class="err">$$</span> <span class="o">=</span> <span class="n">ast_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">ast_binop</span><span class="p">(</span><span class="n">TIMES</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="err">$</span><span class="mi">1</span><span class="p">),</span> <span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="err">$</span><span class="mi">3</span><span class="p">),</span> <span class="err">@$</span><span class="p">));</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">aMul</span> <span class="n">DIVIDE</span> <span class="n">app</span> <span class="p">{</span> <span class="err">$$</span> <span class="o">=</span> <span class="n">ast_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">ast_binop</span><span class="p">(</span><span class="n">DIVIDE</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="err">$</span><span class="mi">1</span><span class="p">),</span> <span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="err">$</span><span class="mi">3</span><span class="p">),</span> <span class="err">@$</span><span class="p">));</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">app</span> <span class="p">{</span> <span class="err">$$</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="err">$</span><span class="mi">1</span><span class="p">);</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">;</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Here, the <code>@$</code> character is used to reference the current nonterminal&rsquo;s location data.</p> <a href="#line-offsets-file-input-and-the-parsing-driver"> <h4 id="line-offsets-file-input-and-the-parsing-driver">Line Offsets, File Input, and the Parsing Driver</h4> </a> <p>There are three more challenges with printing out the line of code where an error occurred. First of all, to print out a line of code, we need to have that line of code available to us. We do not currently meet this requirement: our compiler reads code form <code>stdin</code> (as is default for Flex), and <code>stdin</code> doesn&rsquo;t always support rewinding. This, in turn, means that once Flex has read a character from the input, it may not be possible to go back and retrieve that character again.</p> <p>Second, even if we do have have the entire stream or buffer available to us, to retrieve an offset and length within that buffer from just a line and column number would be a lot of work. A naive approach would be to iterate through the input again, once more keeping track of lines and columns, and print the desired line once we reach it. However, this would lead us to redo a lot of work that our tokenizer is already doing.</p> <p>Third, Flex&rsquo;s input mechanism, even if it it&rsquo;s configured not to read from <code>stdin</code>, uses a global file descriptor called <code>yyin</code>. However, we&rsquo;re better off minimizing global state (especially if we want to read, parse, and compile multiple files in the future). While we&rsquo;re configuring Flex&rsquo;s input mechanism, we may as well fix this, too.</p> <p>There are several approaches to fixing the first issue. One possible way is to store the content of <code>stdin</code> into a temporary file. Then, it&rsquo;s possible to read from the file multiple times by using the C functions <code>fseek</code> and <code>rewind</code>. However, since we&rsquo;re working with files, why not just work directly with the files created by the user? Instead of reading from <code>stdin</code>, we may as well take in a path to a file via <code>argv</code>, and read from there. Also, instead of <code>fseek</code> and <code>rewind</code>, we can just read the file into memory, and access it like a normal character buffer. This does mean that we can stick with <code>stdin</code>, but it&rsquo;s more conventional to read source code from files, anyway.</p> <p>To address the second issue, we can keep a mapping of line numbers to their locations in the source buffer. This is rather easy to maintain using an array: the first element of the array is 0, which is the beginning of the first line in any source file. From there, every time we encounter the character <code>\n</code>, we can push the current source location to the top, marking it as the beginning of another line. Where exactly we store this array is as yet unclear, since we&rsquo;re trying to avoid global variables.</p> <p>Finally, to begin addressing the third issue, we can use Flex&rsquo;s <code>reentrant</code> option, which makes it so that all of the tokenizer&rsquo;s state is stored in an opaque <code>yyscan_t</code> structure, rather than in global variables. This way, we can configure <code>yyin</code> without setting a global variable, which is a step in the right direction. We&rsquo;ll work on this momentarily.</p> <p>Our tokenizing and parsing stack has more global variables than just those specific to Flex. Among these variables is <code>global_defs</code>, which receives all the top-level function and data type definitions. We will also need some way of accessing the <code>yy::location</code> instance, and a way of storing our file input in memory. Fortunately, we&rsquo;re not the only ones to have ever come across the issue of creating non-global state: the Bison documentation has a <a href="https://www.gnu.org/software/bison/manual/html_node/Calc_002b_002b-Parsing-Driver.html"class="external-link">section in its C++ guide<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> that describes a technique for manipulating state &ndash; &ldquo;parsing context&rdquo;, in their words. This technique involves the creation of a <em>parsing driver</em>.</p> <p>The parsing driver is a class (or struct) that holds all the parse-related state. We can arrange for this class to be available to our tokenizing and parsing functions, which will allow us to use it pretty much like we&rsquo;d use a global variable. This is the <code>drv</code> that we saw in <code>YY_USER_ACTION</code>. We can define it as follows:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/parse_driver.hpp" data-first-line="36" data-last-line="54"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/parse_driver.hpp#L36-L54">parse_driver.hpp</a>, lines 36 through 54</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">parse_driver</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">private</span><span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">file_name</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">yy</span><span class="o">::</span><span class="n">location</span> <span class="n">location</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">definition_group</span><span class="o">*</span> <span class="n">global_defs</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">file_mgr</span><span class="o">*</span> <span class="n">file_m</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">public</span><span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="n">parse_driver</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="n">file_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">definition_group</span><span class="o">&amp;</span> <span class="n">defs</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">file</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">file_name</span><span class="p">(</span><span class="n">file</span><span class="p">),</span> <span class="n">file_m</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mgr</span><span class="p">),</span> <span class="n">global_defs</span><span class="p">(</span><span class="o">&amp;</span><span class="n">defs</span><span class="p">)</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">bool</span> <span class="nf">operator</span><span class="p">()();</span> </span></span><span class="line"><span class="cl"> <span class="n">yy</span><span class="o">::</span><span class="n">location</span><span class="o">&amp;</span> <span class="n">get_current_location</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">file_mgr</span><span class="o">&amp;</span> <span class="n">get_file_manager</span><span class="p">()</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">definition_group</span><span class="o">&amp;</span> <span class="n">get_global_defs</span><span class="p">()</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>There aren&rsquo;t many fields here. The <code>file_name</code> string represents the file that we&rsquo;ll be reading code from. The <code>location</code> field will be accessed by Flex via <code>get_current_location</code>. Bison will store the function and data type definitions it reads into <code>global_defs</code> via <code>get_global_defs</code>. Finally, <code>file_m</code> will be used to keep track of the content of the file we&rsquo;re reading, as well as the line offsets within that file. Notice that a couple of these fields are pointers that we take by reference in the constructor. The <code>parse_driver</code> doesn&rsquo;t <em>own</em> the global definitions, nor the file manager. They exist outside of it, and will continue to be used in other ways the <code>parse_driver</code> does not need to know about. Also, the <code>LOC</code> variable in Flex is actually a call to <code>get_current_location</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/scanner.l" data-first-line="15" data-last-line="15"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/scanner.l#L15-L15">scanner.l</a>, line 15</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">15 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="cp">#define LOC drv.get_current_location()</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The methods of <code>parse_driver</code> are rather simple. The majority of them deals with giving access to the parser&rsquo;s members: the <code>yy::location</code>, the <code>definition_group</code>, and the <code>file_mgr</code>. The only exception to this is <code>operator()</code>, which we use to actually trigger the parsing process. We&rsquo;ll make this method return <code>true</code> if parsing succeeded, and <code>false</code> otherwise (if, say, the file we tried to read doesn&rsquo;t exist). Here&rsquo;s its implementation:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/parse_driver.cpp" data-first-line="48" data-last-line="60"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/parse_driver.cpp#L48-L60">parse_driver.cpp</a>, lines 48 through 60</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">bool</span> <span class="n">parse_driver</span><span class="o">::</span><span class="k">operator</span><span class="p">()()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">FILE</span><span class="o">*</span> <span class="n">stream</span> <span class="o">=</span> <span class="n">fopen</span><span class="p">(</span><span class="n">file_name</span><span class="p">.</span><span class="n">c_str</span><span class="p">(),</span> <span class="s">&#34;r&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="n">stream</span><span class="p">)</span> <span class="k">return</span> <span class="nb">false</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">yyscan_t</span> <span class="n">scanner</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">yylex_init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">scanner</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">yyset_in</span><span class="p">(</span><span class="n">stream</span><span class="p">,</span> <span class="n">scanner</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">yy</span><span class="o">::</span><span class="n">parser</span> <span class="n">parser</span><span class="p">(</span><span class="n">scanner</span><span class="p">,</span> <span class="o">*</span><span class="k">this</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">parser</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">yylex_destroy</span><span class="p">(</span><span class="n">scanner</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">fclose</span><span class="p">(</span><span class="n">stream</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">file_m</span><span class="o">-&gt;</span><span class="n">finalize</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We try open the user-specified file, and return <code>false</code> if we can&rsquo;t. After this, we start doing the setup specific to a reentrant Flex scanner. We declare a <code>yyscan_t</code> variable, which will contain all of Flex&rsquo;s state. Then, we initialize it using <code>yylex_init</code>. Finally, since we can no longer touch the <code>yyin</code> global variable (it doesn&rsquo;t exist), we have to resort to using a setter function provided by Flex to configure the tokenizer&rsquo;s input stream.</p> <p>Next, we construct our Bison-generated parser. Note that unlike before, we have to pass in two arguments: <code>scanner</code> and <code>*this</code>, the latter being of type <code>parse_driver&amp;</code>. We&rsquo;ll come back to how this works in a moment. With the scanner and parser initialized, we invoke <code>parser::operator()</code>, which actually runs the Flex- and Bison-generated code. To clean up, we run <code>yylex_destroy</code> and <code>fclose</code>. Finally, we call <code>file_mgr::finalize</code>, and return. But what <em>is</em> <code>file_mgr</code>?</p> <p>The <code>file_mgr</code> class does two things: it stores the part of the file that has already been read by Flex in memory, and it keeps track of where each line in our source file begins within the text. Here is its definition:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/parse_driver.hpp" data-first-line="14" data-last-line="34"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/parse_driver.hpp#L14-L34">parse_driver.hpp</a>, lines 14 through 34</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">file_mgr</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">private</span><span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">ostringstream</span> <span class="n">string_stream</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">file_contents</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">size_t</span> <span class="n">file_offset</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">size_t</span><span class="o">&gt;</span> <span class="n">line_offsets</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">public</span><span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="n">file_mgr</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">write</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">buffer</span><span class="p">,</span> <span class="n">size_t</span> <span class="n">len</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">mark_line</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">finalize</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">size_t</span> <span class="nf">get_index</span><span class="p">(</span><span class="kt">int</span> <span class="n">line</span><span class="p">,</span> <span class="kt">int</span> <span class="n">column</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">size_t</span> <span class="nf">get_line_end</span><span class="p">(</span><span class="kt">int</span> <span class="n">line</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">print_location</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">stream</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">yy</span><span class="o">::</span><span class="n">location</span><span class="o">&amp;</span> <span class="n">loc</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="kt">bool</span> <span class="n">highlight</span> <span class="o">=</span> <span class="nb">true</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>In this class, the <code>string_stream</code> member is used to construct an <code>std::string</code> from the bits of text that Flex reads, processes, and feeds to the <code>file_mgr</code> using the <code>write</code> method. It&rsquo;s more efficient to use a string stream than to concatenate strings repeatedly. Once Flex is finished processing the file, the final contents of the <code>string_stream</code> are transferred into the <code>file_contents</code> string using the <code>finalize</code> method. The <code>offset</code> and <code>line_offsets</code> fields will be used as we described earlier: each time Flex encounters the <code>\n</code> character, the <code>offset</code> variable will pushed in top of the <code>line_offsets</code> vector, marking the beginning of the corresponding line. The methods of the class are as follows:</p> <ul> <li><code>write</code> will be called from Flex, and will allow us to record the content of the file we&rsquo;re processing to the <code>string_stream</code>. We&rsquo;ve already seen it used in the <code>YY_USER_ACTION</code> macro.</li> <li><code>mark_line</code> will also be called from Flex, and will mark the current <code>file_offset</code> as the beginning of a line by pushing it into <code>line_offsets</code>.</li> <li><code>finalize</code> will be called by the <code>parse_driver</code> when the parsing finishes. At this time, the <code>string_stream</code> should contain all of the input file, and this data is transferred to <code>file_contents</code>, as we mentioned above.</li> <li><code>get_index</code> and <code>get_line_end</code> will be used for converting <code>yy::location</code> instances to offsets within the source code buffer.</li> <li><code>print_location</code> will be used for printing errors. It will print the lines spanned by the given location, with the location itself colored and underlined if the last argument is <code>true</code>. This will make our errors easier on the eyes.</li> </ul> <p>Let&rsquo;s take a look at their implementations. First, <code>write</code>. For the most part, this method is a proxy for the <code>write</code> method of our <code>string_stream</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/parse_driver.cpp" data-first-line="9" data-last-line="12"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/parse_driver.cpp#L9-L12">parse_driver.cpp</a>, lines 9 through 12</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">file_mgr</span><span class="o">::</span><span class="n">write</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">buf</span><span class="p">,</span> <span class="n">size_t</span> <span class="n">len</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">string_stream</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="n">len</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">file_offset</span> <span class="o">+=</span> <span class="n">len</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We do, however, also keep track of the <code>file_offset</code> variable here, which ensures we have up-to-date information regarding our position in the source file. The implementation of <code>mark_line</code> uses this information:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/parse_driver.cpp" data-first-line="14" data-last-line="16"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/parse_driver.cpp#L14-L16">parse_driver.cpp</a>, lines 14 through 16</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">file_mgr</span><span class="o">::</span><span class="n">mark_line</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">line_offsets</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">file_offset</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The <code>finalize</code> method is trivial, and requires little additional discussion:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/parse_driver.cpp" data-first-line="18" data-last-line="20"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/parse_driver.cpp#L18-L20">parse_driver.cpp</a>, lines 18 through 20</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">file_mgr</span><span class="o">::</span><span class="n">finalize</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">file_contents</span> <span class="o">=</span> <span class="n">string_stream</span><span class="p">.</span><span class="n">str</span><span class="p">();</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Once we have the line offsets, <code>get_index</code> becomes very simple:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/parse_driver.cpp" data-first-line="22" data-last-line="25"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/parse_driver.cpp#L22-L25">parse_driver.cpp</a>, lines 22 through 25</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">size_t</span> <span class="n">file_mgr</span><span class="o">::</span><span class="n">get_index</span><span class="p">(</span><span class="kt">int</span> <span class="n">line</span><span class="p">,</span> <span class="kt">int</span> <span class="n">column</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">assert</span><span class="p">(</span><span class="n">line</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">line</span> <span class="o">&lt;=</span> <span class="n">line_offsets</span><span class="p">.</span><span class="n">size</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">line_offsets</span><span class="p">.</span><span class="n">at</span><span class="p">(</span><span class="n">line</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="n">column</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Here, we use an assertion for the first time. Calling <code>get_index</code> with a negative or zero line doesn&rsquo;t make any sense, since Bison starts tracking line numbers at 1. Similarly, asking for a line for which we don&rsquo;t have a recorded offset is invalid. Both of these nonsensical calls to <code>get_index</code> cannot be caused by the user under normal circumstances, and indicate the method&rsquo;s misuse by the author of the compiler (us!). Thus, we terminate the program.</p> <p>Finally, the implementation of <code>line_end</code> just finds the beginning of the next line. We stick to the C convention of marking &rsquo;end&rsquo; indices exclusive (pointing just past the end of the array):</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/parse_driver.cpp" data-first-line="27" data-last-line="30"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/parse_driver.cpp#L27-L30">parse_driver.cpp</a>, lines 27 through 30</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">size_t</span> <span class="n">file_mgr</span><span class="o">::</span><span class="n">get_line_end</span><span class="p">(</span><span class="kt">int</span> <span class="n">line</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">line</span> <span class="o">==</span> <span class="n">line_offsets</span><span class="p">.</span><span class="n">size</span><span class="p">())</span> <span class="k">return</span> <span class="n">file_contents</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nf">get_index</span><span class="p">(</span><span class="n">line</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Since <code>line_offsets</code> has as many elements as there are lines, the last line number would be equal to the vector&rsquo;s size. When looking up the end of the last line, we can&rsquo;t look for the beginning of the next line, so instead we return the end of the file.</p> <p>Next, the <code>print_location</code> method prints three sections of the source file. These are the text &ldquo;before&rdquo; the error, the error itself, and, finally, the text &ldquo;after&rdquo; the error. For example, if an error began on the fifth column of the third line, and ended on the eighth column of the fourth line, the &ldquo;before&rdquo; section would include the first four columns of the third line, and the &ldquo;after&rdquo; section would be the ninth column onward on the fourth line. Before and after the error itself, if the <code>highlight</code> argument is true, we sprinkle the ANSI escape codes to enable and disable special formatting, respectively. For now, the special formatting involves underlining the text and making it red.</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/parse_driver.cpp" data-first-line="32" data-last-line="46"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/parse_driver.cpp#L32-L46">parse_driver.cpp</a>, lines 32 through 46</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">file_mgr</span><span class="o">::</span><span class="n">print_location</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">stream</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">yy</span><span class="o">::</span><span class="n">location</span><span class="o">&amp;</span> <span class="n">loc</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="kt">bool</span> <span class="n">highlight</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">size_t</span> <span class="n">print_start</span> <span class="o">=</span> <span class="n">get_index</span><span class="p">(</span><span class="n">loc</span><span class="p">.</span><span class="n">begin</span><span class="p">.</span><span class="n">line</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">size_t</span> <span class="n">highlight_start</span> <span class="o">=</span> <span class="n">get_index</span><span class="p">(</span><span class="n">loc</span><span class="p">.</span><span class="n">begin</span><span class="p">.</span><span class="n">line</span><span class="p">,</span> <span class="n">loc</span><span class="p">.</span><span class="n">begin</span><span class="p">.</span><span class="n">column</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">size_t</span> <span class="n">highlight_end</span> <span class="o">=</span> <span class="n">get_index</span><span class="p">(</span><span class="n">loc</span><span class="p">.</span><span class="n">end</span><span class="p">.</span><span class="n">line</span><span class="p">,</span> <span class="n">loc</span><span class="p">.</span><span class="n">end</span><span class="p">.</span><span class="n">column</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">size_t</span> <span class="n">print_end</span> <span class="o">=</span> <span class="n">get_line_end</span><span class="p">(</span><span class="n">loc</span><span class="p">.</span><span class="n">end</span><span class="p">.</span><span class="n">line</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">content</span> <span class="o">=</span> <span class="n">file_contents</span><span class="p">.</span><span class="n">c_str</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">stream</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">content</span> <span class="o">+</span> <span class="n">print_start</span><span class="p">,</span> <span class="n">highlight_start</span> <span class="o">-</span> <span class="n">print_start</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">highlight</span><span class="p">)</span> <span class="n">stream</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;</span><span class="se">\033</span><span class="s">[4;31m&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">stream</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">content</span> <span class="o">+</span> <span class="n">highlight_start</span><span class="p">,</span> <span class="n">highlight_end</span> <span class="o">-</span> <span class="n">highlight_start</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">highlight</span><span class="p">)</span> <span class="n">stream</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;</span><span class="se">\033</span><span class="s">[0m&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">stream</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">content</span> <span class="o">+</span> <span class="n">highlight_end</span><span class="p">,</span> <span class="n">print_end</span> <span class="o">-</span> <span class="n">highlight_end</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Finally, to get the forward declarations for the <code>yy*</code> functions and types, we set the <code>header-file</code> option in Flex:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/scanner.l" data-first-line="3" data-last-line="3"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/scanner.l#L3-L3">scanner.l</a>, line 3</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">3 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="o">%</span><span class="n">option</span> <span class="n">header</span><span class="o">-</span><span class="n">file</span><span class="o">=</span><span class="s">&#34;scanner.hpp&#34;</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We also include this <code>scanner.hpp</code> file in our <code>parse_driver.cpp</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/parse_driver.cpp" data-first-line="2" data-last-line="2"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/parse_driver.cpp#L2-L2">parse_driver.cpp</a>, line 2</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">2 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;scanner.hpp&#34;</span></span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#adding-the-driver-to-flex-and-bison"> <h4 id="adding-the-driver-to-flex-and-bison">Adding the Driver to Flex and Bison</h4> </a> <p>Bison&rsquo;s C++ language template generates a class called <code>yy::parser</code>. We don&rsquo;t really want to modify this class in any way: not only is it generated code, but it&rsquo;s also rather complex. Instead, Bison provides us with a mechanism to pass more data in to the parser. This data is made available to all the actions that the parser runs. Better yet, Bison also attempts to pass this data on to the tokenizer, which in our case would mean that whatever data we provide Bison will also be available to Flex. This is how we&rsquo;ll allow the two components to access our new <code>parse_driver</code> class. This is also how we&rsquo;ll pass in the <code>yyscan_t</code> that Flex now needs to run its tokenizing code. To do all this, we use Bison&rsquo;s <code>%param</code> option. I&rsquo;m going to include a few more lines from <code>parser.y</code>, since they contain the necessary <code>#include</code> directives and a required type definition:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/parser.y" data-first-line="1" data-last-line="18"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/parser.y#L1-L18">parser.y</a>, lines 1 through 18</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="o">%</span><span class="n">code</span> <span class="k">requires</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;string&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;vector&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;ast.hpp&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;definition.hpp&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;parser.hpp&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;parsed_type.hpp&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">parse_driver</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="n">yyscan_t</span> <span class="o">=</span> <span class="kt">void</span><span class="o">*</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="o">%</span><span class="n">param</span> <span class="p">{</span> <span class="n">yyscan_t</span> <span class="n">scanner</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="o">%</span><span class="n">param</span> <span class="p">{</span> <span class="n">parse_driver</span><span class="o">&amp;</span> <span class="n">drv</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="o">%</span><span class="n">code</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;parse_driver.hpp&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The <code>%param</code> option effectively adds the parameter listed between the curly braces to the constructor of the generated <code>yy::parser</code>. We&rsquo;ve already seen this in the implementation of our driver, where we passed <code>scanner</code> and <code>*this</code> as arguments when creating the parser. The parameters we declare are also passed to the <code>yylex</code> function, which is expected to accept them in the same order.</p> <p>Since we&rsquo;re adding <code>parse_driver</code> as an argument we have to declare it. However, we can&rsquo;t include the <code>parse_driver</code> header right away because <code>parse_driver</code> itself includes the <code>parser</code> header: we&rsquo;d end up with a circular dependency. Instead, we resort to forward-declaring the driver class, as well as the <code>yyscan_t</code> structure containing Flex&rsquo;s state.</p> <p>Adding a parameter to Bison doesn&rsquo;t automatically affect Flex. To let Flex know that its <code>yylex</code> function must now accept the state and the parsing driver, we have to define the <code>YY_DECL</code> macro. We do this in <code>parse_driver.hpp</code>, since this forward declaration will be used by both Flex and Bison:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/parse_driver.hpp" data-first-line="56" data-last-line="58"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/parse_driver.hpp#L56-L58">parse_driver.hpp</a>, lines 56 through 58</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="cp">#define YY_DECL yy::parser::symbol_type yylex(yyscan_t yyscanner, parse_driver&amp; drv) </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="n">YY_DECL</span><span class="p">;</span></span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#improving-exceptions"> <h4 id="improving-exceptions">Improving Exceptions</h4> </a> <p>Now, it&rsquo;s time to add location data (and a little bit more) to our exceptions. We want to make it possible for exceptions to include data about where the error occurred, and to print this data to the user. However, it&rsquo;s also possible for us to have exceptions that simply do not have that location data. Furthermore, we want to know whether or not an exception has an associated location; we&rsquo;d rather not print an invalid or &ldquo;default&rdquo; location when an error occurs.</p> <p>In the old days of programming, we could represent the absence of location data with a <code>nullptr</code>, or <code>NULL</code>. But not only does this approach expose us to all kind of <code>NULl</code>-safety bugs, but it also requires heap allocation! This doesn&rsquo;t make it sound all that appealing; instead, I think we should opt for using <code>std::optional</code>.</p> <p>Though <code>std::optional</code> is standard (as may be obvious from its namespace), it&rsquo;s a rather recent addition to the C++ STL. In order to gain access to it, we need to ensure that our project is compiled using C++17. To this end, we add the following two lines to our CMakeLists.txt:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/CMakeLists.txt" data-first-line="5" data-last-line="6"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/CMakeLists.txt#L5-L6">CMakeLists.txt</a>, lines 5 through 6</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">5 </span><span class="lnt">6 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-CMake" data-lang="CMake"><span class="line"><span class="cl"><span class="nb">set</span><span class="p">(</span><span class="s">CMAKE_CXX_STANDARD</span> <span class="s">17</span><span class="p">)</span><span class="err"> </span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="nb">set</span><span class="p">(</span><span class="s">CMAKE_CXX_STANDARD_REQUIRED</span> <span class="s">ON</span><span class="p">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Now, let&rsquo;s add a new base class for all of our compiler errors, unsurprisingly called <code>compiler_error</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/error.hpp" data-first-line="10" data-last-line="26"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/error.hpp#L10-L26">error.hpp</a>, lines 10 through 26</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">compiler_error</span> <span class="o">:</span> <span class="n">std</span><span class="o">::</span><span class="n">exception</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">private</span><span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">description</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">maybe_location</span> <span class="n">loc</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">public</span><span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="n">compiler_error</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">d</span><span class="p">,</span> <span class="n">maybe_location</span> <span class="n">l</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">nullopt</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">description</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">d</span><span class="p">)),</span> <span class="n">loc</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">l</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="nf">what</span><span class="p">()</span> <span class="k">const</span> <span class="k">noexcept</span> <span class="k">override</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">print_about</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">print_location</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">,</span> <span class="n">file_mgr</span><span class="o">&amp;</span> <span class="n">fm</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">highlight</span> <span class="o">=</span> <span class="nb">false</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">pretty_print</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">,</span> <span class="n">file_mgr</span><span class="o">&amp;</span> <span class="n">fm</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span></code></pre></td></tr></table> </div> </div> </div> <p>We&rsquo;ll put some &lsquo;common&rsquo; exception functionality into the <code>print_location</code> and <code>print_about</code> methods. If the error has an associated location, the former method will print that location to the screen. We don&rsquo;t always want to highlight the part of the code that caused the error: for instance, an invalid data type definition may span several lines, and coloring that whole section of text red would be too much. To address this, we add the <code>highlight</code> boolean argument, which can be used to switch the colors on and off. The <code>print_about</code> method will simply print the <code>what()</code> message of the exception, in addition to the &ldquo;specific&rdquo; error that occurred (stored in <code>description</code>). Here are the implementations of the functions:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/error.cpp" data-first-line="3" data-last-line="16"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/error.cpp#L3-L16">error.cpp</a>, lines 3 through 16</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">compiler_error</span><span class="o">::</span><span class="n">what</span><span class="p">()</span> <span class="k">const</span> <span class="k">noexcept</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="s">&#34;an error occured while compiling the program&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">compiler_error</span><span class="o">::</span><span class="n">print_about</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">to</span> <span class="o">&lt;&lt;</span> <span class="n">what</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;: &#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">to</span> <span class="o">&lt;&lt;</span> <span class="n">description</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">compiler_error</span><span class="o">::</span><span class="n">print_location</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">,</span> <span class="n">file_mgr</span><span class="o">&amp;</span> <span class="n">fm</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">highlight</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="n">loc</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">to</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;occuring on line &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">loc</span><span class="o">-&gt;</span><span class="n">begin</span><span class="p">.</span><span class="n">line</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;:&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">fm</span><span class="p">.</span><span class="n">print_location</span><span class="p">(</span><span class="n">to</span><span class="p">,</span> <span class="o">*</span><span class="n">loc</span><span class="p">,</span> <span class="n">highlight</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We will also add a <code>pretty_print</code> method to all of our exceptions. This method will handle all the exception-specific printing logic. For the generic compiler error, this means simply printing out the error text and the location:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/error.cpp" data-first-line="18" data-last-line="21"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/error.cpp#L18-L21">error.cpp</a>, lines 18 through 21</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">compiler_error</span><span class="o">::</span><span class="n">pretty_print</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">,</span> <span class="n">file_mgr</span><span class="o">&amp;</span> <span class="n">fm</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">print_about</span><span class="p">(</span><span class="n">to</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">print_location</span><span class="p">(</span><span class="n">to</span><span class="p">,</span> <span class="n">fm</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>For <code>type_error</code>, this logic slightly changes, enabling colors when printing the location:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/error.cpp" data-first-line="27" data-last-line="30"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/error.cpp#L27-L30">error.cpp</a>, lines 27 through 30</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">type_error</span><span class="o">::</span><span class="n">pretty_print</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">,</span> <span class="n">file_mgr</span><span class="o">&amp;</span> <span class="n">fm</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">print_about</span><span class="p">(</span><span class="n">to</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">print_location</span><span class="p">(</span><span class="n">to</span><span class="p">,</span> <span class="n">fm</span><span class="p">,</span> <span class="nb">true</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Finally, for <code>unification_error</code>, we also include the code to print out the two types that our compiler could not unify:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/error.cpp" data-first-line="32" data-last-line="41"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/error.cpp#L32-L41">error.cpp</a>, lines 32 through 41</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">unification_error</span><span class="o">::</span><span class="n">pretty_print</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">,</span> <span class="n">file_mgr</span><span class="o">&amp;</span> <span class="n">fm</span><span class="p">,</span> <span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_error</span><span class="o">::</span><span class="n">pretty_print</span><span class="p">(</span><span class="n">to</span><span class="p">,</span> <span class="n">fm</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">to</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;the expected type was:&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">to</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; </span><span class="se">\033</span><span class="s">[34m&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">left</span><span class="o">-&gt;</span><span class="n">print</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">to</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">to</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;</span><span class="se">\033</span><span class="s">[0mwhile the actual type was:&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">to</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; </span><span class="se">\033</span><span class="s">[32m&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">right</span><span class="o">-&gt;</span><span class="n">print</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">to</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">to</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;</span><span class="se">\033</span><span class="s">[0m&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>There&rsquo;s a subtle change here. Compared to the previous type-printing code (which we had in <code>main</code>), what we wrote here deals with &ldquo;expected&rdquo; and &ldquo;actual&rdquo; types. The <code>left</code> type passed to the exception is printed first, and is treat like the &ldquo;correct&rdquo; type. The <code>right</code> type, on the other hand, is treated like the &ldquo;wrong&rdquo; type that should have been unifiable with <code>left</code>. This will affect the calling conventions of our unification code.</p> <p>Now, we can go through and find all the places where we <code>throw 0</code>. One such place was in the data type definition code, where declaring the same type parameter twice is invalid. We replace the <code>0</code> with a <code>compiler_error</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/definition.cpp" data-first-line="66" data-last-line="69"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/definition.cpp#L66-L69">definition.cpp</a>, lines 66 through 69</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">66 </span><span class="lnt">67 </span><span class="lnt">68 </span><span class="lnt">69 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">var_set</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">var</span><span class="p">)</span> <span class="o">!=</span> <span class="n">var_set</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> </span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="n">compiler_error</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;type variable &#34;</span> <span class="o">+</span> <span class="n">var</span> <span class="o">+</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34; used twice in data type definition.&#34;</span><span class="p">,</span> <span class="n">loc</span><span class="p">);</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Not all <code>throw 0</code> statements should become exceptions. For example, here&rsquo;s code from the previous version of the compiler:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/definition.cpp" data-first-line="123" data-last-line="127"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/definition.cpp#L123-L127">definition.cpp</a>, lines 123 through 127</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">123 </span><span class="lnt">124 </span><span class="lnt">125 </span><span class="lnt">126 </span><span class="lnt">127 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">dependency</span> <span class="p">:</span> <span class="n">def_defn</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">nearby_variables</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">defs_defn</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">dependency</span><span class="p">)</span> <span class="o">==</span> <span class="n">defs_defn</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> </span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">dependency_graph</span><span class="p">.</span><span class="n">add_edge</span><span class="p">(</span><span class="n">def_defn</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">,</span> <span class="n">dependency</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>If a definition <code>def_defn</code> has a dependency on a &ldquo;nearby&rdquo; (declared in the same group) definition called <code>dependency</code>, and if <code>dependency</code> does not exist within the same definition group, we throw an exception. But this error is impossible for a user to trigger: the only reason for a variable to appear in the <code>nearby_variables</code> vector is that it was previously found in the definition group. Here&rsquo;s the code that proves this (from the current version of the compiler):</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/definition.cpp" data-first-line="102" data-last-line="106"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/definition.cpp#L102-L106">definition.cpp</a>, lines 102 through 106</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">102 </span><span class="lnt">103 </span><span class="lnt">104 </span><span class="lnt">105 </span><span class="lnt">106 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">defs_defn</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">free_var</span><span class="p">)</span> <span class="o">==</span> <span class="n">defs_defn</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">into</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">free_var</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">def_pair</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">nearby_variables</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">free_var</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Not being able to find the variable in the definition group is a compiler bug, and should never occur. So, instead of throwing an exception, we&rsquo;ll use an assertion:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/definition.cpp" data-first-line="128" data-last-line="128"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/definition.cpp#L128-L128">definition.cpp</a>, line 128</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">128 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="n">assert</span><span class="p">(</span><span class="n">defs_defn</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">dependency</span><span class="p">)</span> <span class="o">!=</span> <span class="n">defs_defn</span><span class="p">.</span><span class="n">end</span><span class="p">());</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>For more complicated error messages, we can use a <code>stringstream</code>. Here&rsquo;s an example from <code>parsed_type</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/parsed_type.cpp" data-first-line="16" data-last-line="23"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/parsed_type.cpp#L16-L23">parsed_type.cpp</a>, lines 16 through 23</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">base_type</span><span class="o">-&gt;</span><span class="n">arity</span> <span class="o">!=</span> <span class="n">arguments</span><span class="p">.</span><span class="n">size</span><span class="p">())</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">ostringstream</span> <span class="n">error_stream</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">error_stream</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;invalid application of type &#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">error_stream</span> <span class="o">&lt;&lt;</span> <span class="n">name</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">error_stream</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; (&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">base_type</span><span class="o">-&gt;</span><span class="n">arity</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; argument(s) expected, &#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">error_stream</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;but &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">arguments</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; provided)&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="nf">type_error</span><span class="p">(</span><span class="n">error_stream</span><span class="p">.</span><span class="n">str</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>In general, this change is also rather mechanical. Before we move on, to maintain a balance between exceptions and assertions, here are a couple more assertions from <code>type_env</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/type_env.cpp" data-first-line="81" data-last-line="82"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/type_env.cpp#L81-L82">type_env.cpp</a>, lines 81 through 82</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">81 </span><span class="lnt">82 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="n">assert</span><span class="p">(</span><span class="n">names_it</span> <span class="o">!=</span> <span class="n">names</span><span class="p">.</span><span class="n">end</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="n">assert</span><span class="p">(</span><span class="n">names_it</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">.</span><span class="n">type</span><span class="o">-&gt;</span><span class="n">forall</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Once again, it should not be possible for the compiler to try generalize the type of a variable that doesn&rsquo;t exist, and nor should generalization occur twice.</p> <p>While we&rsquo;re on the topic of types, let&rsquo;s talk about <code>type_mgr::unify</code>. In practice, I suspect that a lot of errors in our compiler will originate from this method. However, at present, this method does not in any way track the locations of where a unification error occurred. To fix this, we add a new <code>loc</code> parameter to <code>unify</code>, which we make optional to allow for unification without a known location. Here&rsquo;s the declaration:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/type.hpp" data-first-line="92" data-last-line="92"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/type.hpp#L92-L92">type.hpp</a>, line 92</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">92 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">unify</span><span class="p">(</span><span class="n">type_ptr</span> <span class="n">l</span><span class="p">,</span> <span class="n">type_ptr</span> <span class="n">r</span><span class="p">,</span> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">optional</span><span class="o">&lt;</span><span class="n">yy</span><span class="o">::</span><span class="n">location</span><span class="o">&gt;&amp;</span> <span class="n">loc</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">nullopt</span><span class="p">);</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The change to the implementation is mechanical and repetitive, so instead of showing you the whole method, I&rsquo;ll settle for a couple of lines:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/type.cpp" data-first-line="121" data-last-line="122"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/type.cpp#L121-L122">type.cpp</a>, lines 121 through 122</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">121 </span><span class="lnt">122 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="n">unify</span><span class="p">(</span><span class="n">larr</span><span class="o">-&gt;</span><span class="n">left</span><span class="p">,</span> <span class="n">rarr</span><span class="o">-&gt;</span><span class="n">left</span><span class="p">,</span> <span class="n">loc</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">unify</span><span class="p">(</span><span class="n">larr</span><span class="o">-&gt;</span><span class="n">right</span><span class="p">,</span> <span class="n">rarr</span><span class="o">-&gt;</span><span class="n">right</span><span class="p">,</span> <span class="n">loc</span><span class="p">);</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We want to make sure that a location provided to the top-level call to <code>unify</code> is also forwarded to the recursive calls, so we have to explicitly add it to the call.</p> <p>We&rsquo;ll also have to update the &lsquo;main&rsquo; code to call the <code>pretty_print</code> methods, but there&rsquo;s another big change that we&rsquo;re going to make before then. However, once that change is made, our errors will look a lot better. Here is what&rsquo;s printed out to the user when a type error occurs:</p> <pre tabindex="0"><code>an error occured while checking the types of the program: failed to unify types occuring on line 2: 3 + False the expected type was: Int while the actual type was: Bool </code></pre><p>Here&rsquo;s an error that was previously a <code>throw 0</code> statement in our code:</p> <pre tabindex="0"><code>an error occured while compiling the program: type variable a used twice in data type definition. occuring on line 1: data Pair a a = { MkPair a a } </code></pre><p>Now, not only have we eliminated the lazy uses of <code>throw 0</code> in our code, but we&rsquo;ve also improved the presentation of the errors to the user!</p> <a href="#rethinking-name-mangling"> <h3 id="rethinking-name-mangling">Rethinking Name Mangling</h3> </a> <p>In the previous post, I said the following:</p> <blockquote> <p>One more thing. Let’s adopt the convention of storing mangled names into the compilation environment. This way, rather than looking up mangled names only for global functions, which would be a ‘gotcha’ for anyone working on the compiler, we will always use the mangled names during compilation.</p> </blockquote> <p>Now that I&rsquo;ve had some more time to think about it (and now that I&rsquo;ve returned to the compiler after a brief hiatus), I think that this was not the right call. Mangled names make sense when translating to LLVM; we certainly don&rsquo;t want to declare two LLVM functions <span class="sidenote"> <label class="sidenote-label" for="mangling-note">with the same name.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="mangling-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> By the way, LLVM has its own name mangling functionality. If you declare two functions with the same name, they'll appear as <code>function</code> and <code>function.0</code>. Since LLVM uses the <code>Function*</code> C++ values to refer to functions, as long as we keep them seaprate on <em>our</em> end, things will work.<br> <br> However, in our compiler, name mangling occurs before LLVM is introduced, at translation time. We could create LLVM functions at that time, too, and associate them with variables. But then, our G-machine instructions will be coupled to LLVM, which would not be as clean. <span class="sidenote-delimiter">]</span> </span> </span> But things are different for local variables. Our local variables are graphs on a stack, and are not actually compiled to LLVM definitions. It doesn&rsquo;t make sense to mangle their names, since their names aren&rsquo;t present anywhere in the final executable. It&rsquo;s not even &ldquo;consistent&rdquo; to mangle them, since global definitions are compiled directly to <strong>PushGlobal</strong> instructions, while local variables are only referenced through the current <code>env</code>. So, I opted to reverse my decision. We will go back to placing variable names directly into <code>env_var</code>. Here&rsquo;s an example of this from <code>global_scope.cpp</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/global_scope.cpp" data-first-line="6" data-last-line="8"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/global_scope.cpp#L6-L8">global_scope.cpp</a>, lines 6 through 8</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">params</span><span class="p">.</span><span class="n">rbegin</span><span class="p">();</span> <span class="n">it</span> <span class="o">!=</span> <span class="n">params</span><span class="p">.</span><span class="n">rend</span><span class="p">();</span> <span class="n">it</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">new_env</span> <span class="o">=</span> <span class="n">env_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">env_var</span><span class="p">(</span><span class="o">*</span><span class="n">it</span><span class="p">,</span> <span class="n">new_env</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Now that we&rsquo;ve started using assertions, I also think it&rsquo;s worth to put our new invariant &ndash; &ldquo;only global definitions have mangled names&rdquo; &ndash; into code:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/type_env.cpp" data-first-line="36" data-last-line="45"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/type_env.cpp#L36-L45">type_env.cpp</a>, lines 36 through 45</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">type_env</span><span class="o">::</span><span class="n">set_mangled_name</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">,</span> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">mangled</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">names</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1">// Can&#39;t set mangled name for non-existent variable. </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">assert</span><span class="p">(</span><span class="n">it</span> <span class="o">!=</span> <span class="n">names</span><span class="p">.</span><span class="n">end</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="c1">// Local names shouldn&#39;t need mangling. </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">assert</span><span class="p">(</span><span class="n">it</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">.</span><span class="n">vis</span> <span class="o">==</span> <span class="n">visibility</span><span class="o">::</span><span class="n">global</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">it</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">.</span><span class="n">mangled_name</span> <span class="o">=</span> <span class="n">mangled</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Furthermore, we&rsquo;ll <em>require</em> that a global definition has a mangled name. This way, we can be more confident that a variable from a <strong>PushGlobal</strong> instruction is referencing the right function. To achieve this, we change <code>get_mangled_name</code> to stop returning the input string if a mangled name was not found; doing so makes it impossible to check if a mangled name was explicitly defined. Instead, we add two assertions. First, if an environment scope doesn&rsquo;t contain a variable, then it <em>must</em> have a parent. If it does contain variable, that variable <em>must</em> have a mangled name. We end up with the following:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/type_env.cpp" data-first-line="47" data-last-line="55"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/type_env.cpp#L47-L55">type_env.cpp</a>, lines 47 through 55</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">type_env</span><span class="o">::</span><span class="n">get_mangled_name</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">names</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">it</span> <span class="o">!=</span> <span class="n">names</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">assert</span><span class="p">(</span><span class="n">it</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">.</span><span class="n">mangled_name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">*</span><span class="n">it</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">.</span><span class="n">mangled_name</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">assert</span><span class="p">(</span><span class="n">parent</span> <span class="o">!=</span> <span class="k">nullptr</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">parent</span><span class="o">-&gt;</span><span class="n">get_mangled_name</span><span class="p">(</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>For this to work, we make one more change. Now that we&rsquo;ve enabled C++17, we have access to <code>std::optional</code>. We can thus represent the presence or absence of mangled names using an optional field, rather than with the empty string <code>&quot;&quot;</code>. I hear that C++ compilers have pretty good <a href="https://www.youtube.com/watch?v=kPR8h4-qZdk"class="external-link">empty string optimizations<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, but nonetheless, I think it makes more sense semantically to use &ldquo;absent&rdquo; (<code>nullopt</code>) instead of &ldquo;empty&rdquo; (<code>&quot;&quot;</code>). Here&rsquo;s the definition of <code>type_env::variable_data</code> now:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/type_env.hpp" data-first-line="16" data-last-line="25"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/type_env.hpp#L16-L25">type_env.hpp</a>, lines 16 through 25</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="k">struct</span> <span class="nc">variable_data</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_scheme_ptr</span> <span class="n">type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">visibility</span> <span class="n">vis</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">optional</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">mangled_name</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">variable_data</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">variable_data</span><span class="p">(</span><span class="k">nullptr</span><span class="p">,</span> <span class="n">visibility</span><span class="o">::</span><span class="n">local</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">nullopt</span><span class="p">)</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> <span class="n">variable_data</span><span class="p">(</span><span class="n">type_scheme_ptr</span> <span class="n">t</span><span class="p">,</span> <span class="n">visibility</span> <span class="n">v</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">optional</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">n</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">type</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">t</span><span class="p">)),</span> <span class="n">vis</span><span class="p">(</span><span class="n">v</span><span class="p">),</span> <span class="n">mangled_name</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">n</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Since looking up a mangled name for non-global variable <span class="sidenote"> <label class="sidenote-label" for="unrepresentable-note">will now result in an assertion failure,</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="unrepresentable-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> A very wise human at the very dawn of our species once said, "make illegal states unrepresentable". Their friends and family were a little busy making a fire, and didn't really understand what the heck they meant. Now, we kind of do.<br> <br> It's <em>possible</em> for our <code>type_env</code> to include a <code>variable_data</code> entry that is both global and has no mangled name. But it doesn't have to be this way. We could define two subclasses of <code>variable_data</code>, one global and one local, where only the global one has a <code>mangled_name</code> field. It would be impossible to reach this assertion failure then. <span class="sidenote-delimiter">]</span> </span> </span> we have to change <code>ast_lid::compile</code> to only call <code>get_mangled_name</code> once it ensures that the variable being compiled is, in fact, global:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/ast.cpp" data-first-line="58" data-last-line="63"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/ast.cpp#L58-L63">ast.cpp</a>, lines 58 through 63</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast_lid</span><span class="o">::</span><span class="n">compile</span><span class="p">(</span><span class="k">const</span> <span class="n">env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">instruction_ptr</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">into</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="k">this</span><span class="o">-&gt;</span><span class="n">env</span><span class="o">-&gt;</span><span class="n">is_global</span><span class="p">(</span><span class="n">id</span><span class="p">))</span> <span class="o">?</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">instruction</span><span class="o">*</span><span class="p">)</span> <span class="k">new</span> <span class="n">instruction_pushglobal</span><span class="p">(</span><span class="k">this</span><span class="o">-&gt;</span><span class="n">env</span><span class="o">-&gt;</span><span class="n">get_mangled_name</span><span class="p">(</span><span class="n">id</span><span class="p">))</span> <span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">instruction</span><span class="o">*</span><span class="p">)</span> <span class="k">new</span> <span class="n">instruction_push</span><span class="p">(</span><span class="n">env</span><span class="o">-&gt;</span><span class="n">get_offset</span><span class="p">(</span><span class="n">id</span><span class="p">))));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Since all global functions now need to have mangled names, we run into a bit of a problem. What are the mangled names of <code>(+)</code>, <code>(-)</code>, and so on? We could continue to hardcode them as <code>plus</code>, <code>minus</code>, etc., but this can (and currently does!) lead to errors. Consider the following piece of code:</p> <pre tabindex="0"><code>defn plus x y = { x + y } defn main = { plus 320 6 } </code></pre><p>We&rsquo;ve hardcoded the mangled name of <code>(+)</code> to be <code>plus</code>. However, <code>global_scope</code> doesn&rsquo;t know about this, so when the actual <code>plus</code> function gets translated, it also gets assigned the mangled name <code>plus</code>. The name is also overwritten in the <code>llvm_context</code>, which effectively means that <code>(+)</code> is now compiled to a call of the user-defined <code>plus</code> function. If we didn&rsquo;t overwrite the name, we would&rsquo;ve run into an assertion failure in this scenario anyway. In short, this example illustrates an important point: mangling information needs to be available outside of a <code>global_scope</code>. We don&rsquo;t want to do this by having every function take in a <code>global_scope</code> to access the mangling information; instead, we&rsquo;ll store the mangling information in a new <code>mangler</code> class, which <code>global_scope</code> will take as an argument. The new class is very simple:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/mangler.hpp" data-first-line="5" data-last-line="11"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/mangler.hpp#L5-L11">mangler.hpp</a>, lines 5 through 11</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">mangler</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">private</span><span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">,</span> <span class="kt">int</span><span class="o">&gt;</span> <span class="n">occurence_count</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">public</span><span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">new_mangled_name</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">str</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">};</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>As with <code>parse_driver</code>, <code>global_scope</code> takes <code>mangler</code> by reference and stores a pointer:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/global_scope.hpp" data-first-line="50" data-last-line="50"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/global_scope.hpp#L50-L50">global_scope.hpp</a>, line 50</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">50 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="n">global_scope</span><span class="p">(</span><span class="n">mangler</span><span class="o">&amp;</span> <span class="n">m</span><span class="p">)</span> <span class="o">:</span> <span class="n">mng</span><span class="p">(</span><span class="o">&amp;</span><span class="n">m</span><span class="p">)</span> <span class="p">{}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The implementation of <code>new_mangled_name</code> doesn&rsquo;t change, so I&rsquo;m not going to show it here. With this new mangling information in hand, we can now correctly set the mangled names of binary operators:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/compiler.cpp" data-first-line="22" data-last-line="27"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/compiler.cpp#L22-L27">compiler.cpp</a>, lines 22 through 27</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">compiler</span><span class="o">::</span><span class="n">add_binop_type</span><span class="p">(</span><span class="n">binop</span> <span class="n">op</span><span class="p">,</span> <span class="n">type_ptr</span> <span class="n">type</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">name</span> <span class="o">=</span> <span class="n">mng</span><span class="p">.</span><span class="n">new_mangled_name</span><span class="p">(</span><span class="n">op_action</span><span class="p">(</span><span class="n">op</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="n">global_env</span><span class="o">-&gt;</span><span class="n">bind</span><span class="p">(</span><span class="n">op_name</span><span class="p">(</span><span class="n">op</span><span class="p">),</span> <span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">type</span><span class="p">),</span> <span class="n">visibility</span><span class="o">::</span><span class="n">global</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">global_env</span><span class="o">-&gt;</span><span class="n">set_mangled_name</span><span class="p">(</span><span class="n">op_name</span><span class="p">(</span><span class="n">op</span><span class="p">),</span> <span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div> </div> <p>Wait a moment, what&rsquo;s a <code>compiler</code>? Let&rsquo;s talk about that next.</p> <a href="#a-top-level-class"> <h3 id="a-top-level-class">A Top-Level Class</h3> </a> <p>Now that we&rsquo;ve moved name mangling out of <code>global_scope</code>, we have to put it somewhere. The same goes for global definition group and the file manager that are given to <code>parse_driver</code>. The two classes <em>make use</em> of the other data, but they don&rsquo;t <em>own it</em>. That&rsquo;s why they take it by reference, and store it as a pointer. They&rsquo;re just temporarily allowed access.</p> <p>So, what should be the owner of all of these disparate components? Thus far, that has been the <code>main</code> function, or the utility functions that it calls out to. However, this is sloppy: we have related data and operations on it, but we don&rsquo;t group them into an object. We can group all of the components of our compiler into a <code>compiler</code> object, and leave <code>main.cpp</code> with exception printing code.</p> <p>The definition of the <code>compiler</code> class begins with all of the data structures that we use in the process of compilation:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/compiler.hpp" data-first-line="12" data-last-line="20"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/compiler.hpp#L12-L20">compiler.hpp</a>, lines 12 through 20</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="k">private</span><span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="n">file_mgr</span> <span class="n">file_m</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">definition_group</span> <span class="n">global_defs</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">parse_driver</span> <span class="n">driver</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_env_ptr</span> <span class="n">global_env</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_mgr</span> <span class="n">type_m</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">mangler</span> <span class="n">mng</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">global_scope</span> <span class="n">global_scp</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">llvm_context</span> <span class="n">ctx</span><span class="p">;</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>There&rsquo;s a loose ordering to these fields. In C++, class members are initialized in the order they are declared; we therefore want to make sure that fields that are depended on by other fields are initialized first. Otherwise, I tried to keep the order consistent with the conceptual path of the code through the compiler.</p> <ul> <li>Parsing happens first, so we begin with <code>parse_driver</code>, which needs a <code>file_manager</code> (to populate with line information) and a <code>definition_group</code> (to receive the global definitions from the parser).</li> <li>We then proceed to typechecking, for which we use a global <code>type_env_ptr</code> (to define the built-in functions and constructors) and a <code>type_mgr</code> (to manage the assignments of type variables).</li> <li>Once a program is typechecked, we transform it, eliminating local function definitions and lambda functions. This is done by storing newly-emitted global functions into the <code>global_scope</code>, which requires a <code>mangler</code> to generate new names for the target functions.</li> <li>Finally, to generate LLVM IR, we need our <code>llvm_context</code> class.</li> </ul> <p>The methods of the compiler are arranged similarly:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/compiler.hpp" data-first-line="22" data-last-line="31"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/compiler.hpp#L22-L31">compiler.hpp</a>, lines 22 through 31</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">add_default_types</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">add_binop_type</span><span class="p">(</span><span class="n">binop</span> <span class="n">op</span><span class="p">,</span> <span class="n">type_ptr</span> <span class="n">type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">add_default_function_types</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">parse</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">typecheck</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">translate</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">compile</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">create_llvm_binop</span><span class="p">(</span><span class="n">binop</span> <span class="n">op</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">generate_llvm</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">output_llvm</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">into</span><span class="p">);</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The methods go as follows:</p> <ul> <li><code>add_default_types</code> adds the built-in types to the <code>global_env</code>. At this point, these types only include <code>Int</code>.</li> <li><code>add_binop_type</code> adds a single binary operator to the global type environment. We saw its implementation earlier: it deals with both binding a type, and setting a mangled name.</li> <li><code>add_default_types</code> adds the types for each binary operator.</li> <li><code>parse</code>, <code>typecheck</code>, <code>translate</code> and <code>compile</code> all do exactly what they say. In this case, compilation refers to creating G-machine instructions.</li> <li><code>create_llvm_binop</code> creates an internal function that forces the evaluation of its two arguments, and actually applies the given binary operator. Recall that the <code>(+)</code> in user code constructs a call to this function, but leaves it unevaluated until it&rsquo;s needed.</li> <li><code>generate_llvm</code> converts all the definitions in <code>global_scope</code>, which are at this point compiled into G-machine <code>instruction</code>s, into LLVM IR.</li> <li><code>output_llvm</code> contains all the code to actually generate an object file from the LLVM IR.</li> </ul> <p>These functions are mostly taken from part 12&rsquo;s <code>main.cpp</code>, and adjusted to use the <code>compiler</code>&rsquo;s members rather than local definitions or arguments. You should compare part 12&rsquo;s <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/compiler/12/main.cpp"class="external-link"><code>main.cpp</code><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> file with the <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/compiler/13/compiler.cpp"class="external-link"><code>compiler.cpp</code><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> file that we end up with at the end of this post.</p> <p>Next, we have the compiler&rsquo;s constructor, and its <code>operator()</code>. The latter, analogously to our parsing driver, will trigger the compilation process. Their implementations are straightforward:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/compiler.cpp" data-first-line="131" data-last-line="145"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/compiler.cpp#L131-L145">compiler.cpp</a>, lines 131 through 145</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">131 </span><span class="lnt">132 </span><span class="lnt">133 </span><span class="lnt">134 </span><span class="lnt">135 </span><span class="lnt">136 </span><span class="lnt">137 </span><span class="lnt">138 </span><span class="lnt">139 </span><span class="lnt">140 </span><span class="lnt">141 </span><span class="lnt">142 </span><span class="lnt">143 </span><span class="lnt">144 </span><span class="lnt">145 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">compiler</span><span class="o">::</span><span class="n">compiler</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">filename</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">file_m</span><span class="p">(),</span> <span class="n">global_defs</span><span class="p">(),</span> <span class="n">driver</span><span class="p">(</span><span class="n">file_m</span><span class="p">,</span> <span class="n">global_defs</span><span class="p">,</span> <span class="n">filename</span><span class="p">),</span> </span></span><span class="line"><span class="cl"> <span class="n">global_env</span><span class="p">(</span><span class="k">new</span> <span class="n">type_env</span><span class="p">),</span> <span class="n">type_m</span><span class="p">(),</span> <span class="n">mng</span><span class="p">(),</span> <span class="n">global_scp</span><span class="p">(</span><span class="n">mng</span><span class="p">),</span> <span class="n">ctx</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">add_default_types</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">add_default_function_types</span><span class="p">();</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">compiler</span><span class="o">::</span><span class="k">operator</span><span class="p">()(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">parse</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">typecheck</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">translate</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">compile</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">generate_llvm</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">output_llvm</span><span class="p">(</span><span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We also add a couple of methods to give external code access to some of the compiler&rsquo;s data structures. I omit their (trivial) implementations, but they have the following signatures:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/compiler.hpp" data-first-line="35" data-last-line="36"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/compiler.hpp#L35-L36">compiler.hpp</a>, lines 35 through 36</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">35 </span><span class="lnt">36 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="n">file_mgr</span><span class="o">&amp;</span> <span class="n">get_file_manager</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">get_type_manager</span><span class="p">();</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>With all the compilation code tucked into our new <code>compiler</code> class, <code>main</code> becomes very simple. We also finally get to use our exception pretty printing code:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/main.cpp" data-first-line="11" data-last-line="27"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/main.cpp#L11-L27">main.cpp</a>, lines 11 through 27</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span><span class="o">**</span> <span class="n">argv</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">argc</span> <span class="o">!=</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">cerr</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;please enter a file to compile.&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">compiler</span> <span class="n">cmp</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">cmp</span><span class="p">(</span><span class="s">&#34;program.o&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="n">unification_error</span><span class="o">&amp;</span> <span class="n">err</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">err</span><span class="p">.</span><span class="n">pretty_print</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">cerr</span><span class="p">,</span> <span class="n">cmp</span><span class="p">.</span><span class="n">get_file_manager</span><span class="p">(),</span> <span class="n">cmp</span><span class="p">.</span><span class="n">get_type_manager</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="n">type_error</span><span class="o">&amp;</span> <span class="n">err</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">err</span><span class="p">.</span><span class="n">pretty_print</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">cerr</span><span class="p">,</span> <span class="n">cmp</span><span class="p">.</span><span class="n">get_file_manager</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="n">compiler_error</span><span class="o">&amp;</span> <span class="n">err</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">err</span><span class="p">.</span><span class="n">pretty_print</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">cerr</span><span class="p">,</span> <span class="n">cmp</span><span class="p">.</span><span class="n">get_file_manager</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>With this, we complete our transition to a compiler object. All that&rsquo;s left is to clean up the code style.</p> <a href="#keeping-things-private"> <h3 id="keeping-things-private">Keeping Things Private</h3> </a> <p>Hand-writing or generating hundreds of trivial getters and setters for the fields of a data class (which is standard in the world of Java) seems absurd to me. So, for most of this project, I stuck with <code>struct</code>s, rather than classes. But this is not a good policy to apply <em>everywhere</em>. I still think it makes sense to make data structures like <code>ast</code> and <code>type</code> public-by-default; however, I <em>don&rsquo;t</em> think that way about classes like <code>type_mgr</code>, <code>llvm_context</code>, <code>type_env</code>, and <code>env</code>. All of these have information that we should never be accessing directly. Some guard this information with assertions. In short, it should be protected.</p> <p>For most classes, the changes are mechanical. For instance, we can make <code>type_env</code> a class simply by changing its declaration, and marking all of its functions public. This requires a slight refactoring of a line that used its <code>parent</code> field. Here&rsquo;s what it used to be (in context):</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/main.cpp" data-first-line="57" data-last-line="60"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/main.cpp#L57-L60">main.cpp</a>, lines 57 through 60</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">defn</span> <span class="p">:</span> <span class="n">group</span><span class="p">.</span><span class="n">defs_defn</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span><span class="o">&amp;</span> <span class="n">function</span> <span class="o">=</span> <span class="n">defn</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">into_global</span><span class="p">(</span><span class="n">scope</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">function</span><span class="p">.</span><span class="n">body</span><span class="o">-&gt;</span><span class="n">env</span><span class="o">-&gt;</span><span class="n">parent</span><span class="o">-&gt;</span><span class="n">set_mangled_name</span><span class="p">(</span><span class="n">defn</span><span class="p">.</span><span class="n">first</span><span class="p">,</span> <span class="n">function</span><span class="p">.</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>And here&rsquo;s what it is now:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/compiler.cpp" data-first-line="55" data-last-line="58"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/compiler.cpp#L55-L58">compiler.cpp</a>, lines 55 through 58</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">defn</span> <span class="p">:</span> <span class="n">global_defs</span><span class="p">.</span><span class="n">defs_defn</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span><span class="o">&amp;</span> <span class="n">function</span> <span class="o">=</span> <span class="n">defn</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">into_global</span><span class="p">(</span><span class="n">global_scp</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">defn</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">env</span><span class="o">-&gt;</span><span class="n">set_mangled_name</span><span class="p">(</span><span class="n">defn</span><span class="p">.</span><span class="n">first</span><span class="p">,</span> <span class="n">function</span><span class="p">.</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Rather than traversing the chain of environments from the body of the definition, we just use the definition&rsquo;s own <code>env_ptr</code>. This is cleaner and more explicit, and it helps us not use the private members of <code>type_env</code>!</p> <p>The deal with <code>env</code> is about as simple. We just make it and its two descendants classes, and mark their methods and constructors public. The same goes for <code>global_scope</code>. To make <code>type_mgr</code> a class, we have to add a new method: <code>lookup</code>. Here&rsquo;s its implementation:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/type.cpp" data-first-line="81" data-last-line="85"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/type.cpp#L81-L85">type.cpp</a>, lines 81 through 85</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">81 </span><span class="lnt">82 </span><span class="lnt">83 </span><span class="lnt">84 </span><span class="lnt">85 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">type_ptr</span> <span class="n">type_mgr</span><span class="o">::</span><span class="n">lookup</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">var</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">types_it</span> <span class="o">=</span> <span class="n">types</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">var</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">types_it</span> <span class="o">!=</span> <span class="n">types</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> <span class="k">return</span> <span class="n">types_it</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="k">nullptr</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>It&rsquo;s used in <code>type_var::print</code> as follows:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/type.cpp" data-first-line="28" data-last-line="35"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/type.cpp#L28-L35">type.cpp</a>, lines 28 through 35</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">type_var</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="k">const</span> <span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">type</span> <span class="o">=</span> <span class="n">mgr</span><span class="p">.</span><span class="n">lookup</span><span class="p">(</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">type</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type</span><span class="o">-&gt;</span><span class="n">print</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">to</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">to</span> <span class="o">&lt;&lt;</span> <span class="n">name</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We can&rsquo;t use <code>resolve</code> here because it takes (and returns) a <code>type_ptr</code>. If we make it <em>take</em> a <code>type*</code>, it won&rsquo;t be able to return its argument if it&rsquo;s already resolved. If we allow it to <em>return</em> <code>type*</code>, we won&rsquo;t have an owning reference. We also don&rsquo;t want to duplicate the method just for this one call. Notice, though, how similar <code>type_var::print</code>/<code>lookup</code> and <code>resolve</code> are in terms of execution.</p> <p>The change for <code>llvm_context</code> requires a little more work. Right now, <code>ctx.builder</code> is used a <em>lot</em> in <code>instruction.cpp</code>. Since we don&rsquo;t want to forward each of the LLVM builder methods, and since it feels weird to make <code>llvm_context</code> extend <code>llvm::IRBuilder</code>, we&rsquo;ll just provide a getter for the <code>builder</code> field. The same goes for <code>module</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/llvm_context.hpp" data-first-line="46" data-last-line="47"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/llvm_context.hpp#L46-L47">llvm_context.hpp</a>, lines 46 through 47</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">46 </span><span class="lnt">47 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">IRBuilder</span><span class="o">&lt;&gt;&amp;</span> <span class="n">get_builder</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">Module</span><span class="o">&amp;</span> <span class="n">get_module</span><span class="p">();</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Here&rsquo;s what some of the code from <code>instruction.cpp</code> looks like now:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/instruction.cpp" data-first-line="144" data-last-line="145"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/instruction.cpp#L144-L145">instruction.cpp</a>, lines 144 through 145</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">144 </span><span class="lnt">145 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="k">case</span> <span class="nl">PLUS</span><span class="p">:</span> <span class="n">result</span> <span class="o">=</span> <span class="n">ctx</span><span class="p">.</span><span class="n">get_builder</span><span class="p">().</span><span class="n">CreateAdd</span><span class="p">(</span><span class="n">left_int</span><span class="p">,</span> <span class="n">right_int</span><span class="p">);</span> <span class="k">break</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nl">MINUS</span><span class="p">:</span> <span class="n">result</span> <span class="o">=</span> <span class="n">ctx</span><span class="p">.</span><span class="n">get_builder</span><span class="p">().</span><span class="n">CreateSub</span><span class="p">(</span><span class="n">left_int</span><span class="p">,</span> <span class="n">right_int</span><span class="p">);</span> <span class="k">break</span><span class="p">;</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Right now, the <code>ctx</code> field of the <code>llvm_context</code> (which contains the <code>llvm::LLVMContext</code>) is only externally used to create instances of <code>llvm::BasicBlock</code>. We&rsquo;ll add a proxy method for this functionality:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/llvm_context.cpp" data-first-line="174" data-last-line="176"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/llvm_context.cpp#L174-L176">llvm_context.cpp</a>, lines 174 through 176</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">174 </span><span class="lnt">175 </span><span class="lnt">176 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">BasicBlock</span><span class="o">*</span> <span class="n">llvm_context</span><span class="o">::</span><span class="n">create_basic_block</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">,</span> <span class="n">llvm</span><span class="o">::</span><span class="n">Function</span><span class="o">*</span> <span class="n">f</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">BasicBlock</span><span class="o">::</span><span class="n">Create</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">f</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Finally, <code>instruction_pushglobal</code> needs to access the <code>llvm::Function</code> instances that we create in the process of compilation. We add a new <code>get_custom_function</code> method to support this, which automatically prefixes the function name with <code>f_</code>, much like <code>create_custom_function</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/13/llvm_context.cpp" data-first-line="292" data-last-line="294"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/13/llvm_context.cpp#L292-L294">llvm_context.cpp</a>, lines 292 through 294</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">292 </span><span class="lnt">293 </span><span class="lnt">294 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">llvm_context</span><span class="o">::</span><span class="n">custom_function</span><span class="o">&amp;</span> <span class="n">llvm_context</span><span class="o">::</span><span class="n">get_custom_function</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">*</span><span class="n">custom_functions</span><span class="p">.</span><span class="n">at</span><span class="p">(</span><span class="s">&#34;f_&#34;</span> <span class="o">+</span> <span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>I think that&rsquo;s enough. If we chose to turn more compiler data structures into classes, I think we would&rsquo;ve quickly drowned in one-line getter and setter methods.</p> <p>That&rsquo;s all for the cleanup! We&rsquo;ve added locations and more errors to the compiler, stopped throwing <code>0</code> in favor of proper exceptions or assertions, made name mangling more reasonable, fixed a bug with accidentally shadowing default functions, organized our compilation process into a <code>compiler</code> class, and made more things into classes. In the next post, I hope to tackle <strong>strings</strong> and <strong>Input/Output</strong>. I also think that implementing <strong>modules</strong> would be a good idea, though at the moment I don&rsquo;t know too much on the subject. I hope you&rsquo;ll join me in my future writing!</p> <a href="#appendix-optimization"> <h3 id="appendix-optimization">Appendix: Optimization</h3> </a> <p>When I started working on the compiler after the previous post, I went a little overboard. I started working on optimizing the generated programs, but eventually decided I wasn&rsquo;t doing a <span class="sidenote"> <label class="sidenote-label" for="good-note">good enough</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="good-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> I think authors should feel a certain degree of responsibility for the content they create. If I do something badly, somebody else trusts me and learns from it, who knows how much damage I've done. I try not to do damage.<br> <br> If anyone reads what I write, anyway! <span class="sidenote-delimiter">]</span> </span> </span> job to present it to others, and scrapped that part of the compiler altogether. I&rsquo;m not sure if I will try again in the near future. But, if you&rsquo;re curious about optimization, here are a few avenues I&rsquo;ve explored or thought about:</p> <ul> <li><strong>Unboxing numbers</strong>. Right now, numbers are allocated and garbage collected just like the rest of the graph nodes. This is far from ideal. We could use pointers to represent numbers, by tagging their most significant bits on 64-bit CPUs. Rather than allocating a node, the runtime will just cast a number to a pointer, tag it, and push it on the stack.</li> <li><strong>Converting enumeration data types to numbers</strong>. If no constructor of a data type takes any arguments, then the tag uniquely identifies each constructor. Combined with unboxed numbers, this can save unnecessary allocations and memory accesses.</li> <li><strong>Special treatment for global constants</strong>. It makes sense for global functions to be converted into LLVM functions, but the same is not the case for <span class="sidenote"> <label class="sidenote-label" for="constant-note">constants.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="constant-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Yeah, yeah, a constant is just a nullary function. Get out of here with your pedantry! <span class="sidenote-delimiter">]</span> </span> </span> We can find a way to initialize global constants once, which would save some work. To make more constants suitable for this, we could employ <a href="https://wiki.haskell.org/Monomorphism_restriction"class="external-link">monomorphism restriction<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>.</li> <li><strong>Optimizing stack operations.</strong> If you read through the LLVM IR we produce, you can see a lot of code that peeks at something twice, or pops-then-pushes the same value, or does other absurd things. LLVM isn&rsquo;t aware of the semantics of our stacks, but perhaps we could write an optimization pass to deal with some of the more blatant instances of this issue.</li> </ul> <p>If you attempt any of these, let me know how it goes, please!</p> How Many Values Does a Boolean Have? https://danilafe.com/blog/boolean_values/ Fri, 21 Aug 2020 23:05:55 -0700 https://danilafe.com/blog/boolean_values/ <p>A friend of mine recently had an interview for a software engineering position. They later recounted to me the content of the technical questions that they had been asked. Some had been pretty standard:</p> <ul> <li><strong>&ldquo;What&rsquo;s the difference between concurrency and parallelism?&rdquo;</strong> &ndash; a reasonable question given that Go was the company&rsquo;s language of choice.</li> <li><strong>&ldquo;What&rsquo;s the difference between a method and a function?&rdquo;</strong> &ndash; a little more strange, in my opinion, since the difference is of little <em>practical</em> use.</li> </ul> <p>But then, they recounted a rather interesting question:</p> <blockquote> <p>How many values does a bool have?</p> </blockquote> <p>Innocuous at first, isn&rsquo;t it? Probably a bit simpler, in fact, than the questions about methods and functions, concurrency and parallelism. It&rsquo;s plausible that a candidate has not done much concurrent or parallel programming in their life, or that they came from a language in which functions were rare and methods were ubiquitous. It&rsquo;s not plausible, on the other hand, that a candidate applying to a software engineering position has not encountered booleans.</p> <p>If you&rsquo;re genuinely unsure about the answer to the question, I think there&rsquo;s no reason for me to mess with you. The simple answer to the question &ndash; as far as I know &ndash; is that a boolean has two values. They are <code>true</code> and <code>false</code> in Java, or <code>True</code> and <code>False</code> in Haskell, and <code>1</code> and <code>0</code> in C. A boolean value is either true or false.</p> <p>So, what&rsquo;s there to think about? There are a few things, <em>ackshually</em>. Let&rsquo;s explore them, starting from the theoretical perspective.</p> <a href="#types-values-and-expressions"> <h3 id="types-values-and-expressions">Types, Values, and Expressions</h3> </a> <p>Boolean, or <code>bool</code>, is a type. Broadly speaking, a type is a property of <em>something</em> that defines what the <em>something</em> means and what you can do with it. That <em>something</em> can be several things; for our purposes, it can either be an <em>expression</em> in a programming language (like those in the form <code>fact(n)</code>) or a value in that same programming language (like <code>5</code>).</p> <p>Dealing with values is rather simple. Most languages have finite numbers, usually with \(2^{32}\) values, which have type <code>int</code>, <code>i32</code>, or something in a similar vein. Most languages also have strings, of which there are as many as you have memory to contain, and which have the type <code>string</code>, <code>String</code>, or occasionally the more confusing <code>char*</code>. Most languages also have booleans, as we discussed above.</p> <p>The deal with expressions is a more interesting. Presumably expressions evaluate to values, and the type of an expression is then the type of values it can yield. Consider the following snippet in C++:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">square</span><span class="p">(</span><span class="kt">int</span> <span class="n">x</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">x</span> <span class="o">*</span> <span class="n">x</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></div><p>Here, the expression <code>x</code> is known to have type <code>int</code> from the type signature provided by the user. Multiplication of integers yields an integer, and so the type of <code>x*x</code> is also of type <code>int</code>. Since <code>square(x)</code> returns <code>x*x</code>, it is also of type <code>int</code>. So far, so good.</p> <p>Okay, how about this:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">meaningOfLife</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">meaningOfLife</span><span class="p">();</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></div><p>No, wait, doesn&rsquo;t say &ldquo;stack overflow&rdquo; just yet. That&rsquo;s no fun. And anyway, this is technically a tail call, so maybe our C++ compiler can avoid growing the stack. And indeed, flicking on the <code>-O2</code> flag in this <a href="https://godbolt.org/z/9cv4nY"class="external-link">compiler explorer example<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, we can see that no stack growth is necessary: it&rsquo;s just an infinite loop. But <code>meaningOfLife</code> will never return a value. One could say this computation <em>diverges</em>.</p> <p>Well, if it diverges, just throw the expression out of the window! That&rsquo;s no <code>int</code>! We only want <em>real</em> <code>int</code>s!</p> <p>And here, we can do that. But what about the following:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">inf_int</span> <span class="nf">collatz</span><span class="p">(</span><span class="n">inf_int</span> <span class="n">x</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">x</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">x</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="n">collatz</span><span class="p">(</span><span class="n">x</span><span class="o">/</span><span class="mi">2</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">collatz</span><span class="p">(</span><span class="n">x</span> <span class="o">*</span> <span class="mi">3</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></div><p>Notice that I&rsquo;ve used the fictitious type <code>inf_int</code> to represent integers that can hold arbitrarily large integer values, not just the 32-bit ones. That is important for this example, and I&rsquo;ll explain why shortly.</p> <p>The code in the example is a simulation of the process described in the <a href="https://en.wikipedia.org/wiki/Collatz_conjecture"class="external-link">Collatz conjecture<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. Given an input number <code>x</code>, if the number is even, it&rsquo;s divided in half, and the process continues with the halved number. If, on the other hand, the number is odd, it&rsquo;s multiplied by 3, 1 is added to it, and the process continues with <em>that</em> number. The only way for the process to terminate is for the computation to reach the value 1.</p> <p>Why does this matter? Because as of right now, <strong>nobody knows</strong> whether or not the process terminates for all possible input numbers. We have a strong hunch that it does; we&rsquo;ve checked a <strong>lot</strong> of numbers and found that the process terminates for them. This is why 32-bit integers are not truly sufficient for this example; we know empirically that the function will terminate for them.</p> <p>But why does <em>this</em> matter? Well, it matters because we don&rsquo;t know whether or not this function will diverge, and thus, we can&rsquo;t &rsquo;throw it out of the window&rsquo; like we wanted to with <code>meaningOfLife</code>! In general, it&rsquo;s <em>impossible to tell</em> whether or not a program will terminate; that is the <a href="https://en.wikipedia.org/wiki/Halting_problem"class="external-link">halting problem<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. So, what do we do?</p> <p>It turns out to be convenient &ndash; formally &ndash; to treat the result of a diverging computation as its own value. This value is usually called &lsquo;bottom&rsquo;, and written as \(\bot\). Since in most programming languages, you can write a nonterminating expression or function of any type, this &lsquo;bottom&rsquo; is included in <em>all</em> types. So in fact, the possible values of <code>unsigned int</code> are \(\bot, 0, 1, 2, ...\) and so on. As you may have by now guessed, the same is true for a boolean: we have \(\bot\), <code>true</code>, and <code>false</code>.</p> <a href="#haskell-and-bottom"> <h3 id="haskell-and-bottom">Haskell and Bottom</h3> </a> <p>You may be thinking:</p> <blockquote> <p>Now he&rsquo;s done it; he&rsquo;s gone off the deep end with all that programming language theory. Tell me, Daniel, where the heck have you ever encountered \(\bot\) in code? This question was for a software engineering interview, after all!</p> </blockquote> <p>You&rsquo;re right; I haven&rsquo;t <em>specifically</em> seen the symbol \(\bot\) in my time programming. But I have frequently used an equivalent notation for the same idea: <code>undefined</code>. In fact, here&rsquo;s a possible definition of <code>undefined</code> in Haskell:</p> <pre tabindex="0"><code>undefined = undefined </code></pre><p>Just like <code>meaningOfLife</code>, this is a divergent computation! What&rsquo;s more is that the type of this computation is, in Haskell, <code>a</code>. More explicitly &ndash; and retreating to more mathematical notation &ndash; we can write this type as: \(\forall \alpha . \alpha\). That is, for any type \(\alpha\), <code>undefined</code> has that type! This means <code>undefined</code> can take on <em>any</em> type, and so, we can write:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">myTrue</span> <span class="ow">::</span> <span class="kt">Bool</span> </span></span><span class="line"><span class="cl"><span class="nf">myTrue</span> <span class="ow">=</span> <span class="kt">True</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="nf">myFalse</span> <span class="ow">::</span> <span class="kt">Bool</span> </span></span><span class="line"><span class="cl"><span class="nf">myFalse</span> <span class="ow">=</span> <span class="kt">False</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="nf">myBool</span> <span class="ow">::</span> <span class="kt">Bool</span> </span></span><span class="line"><span class="cl"><span class="nf">myBool</span> <span class="ow">=</span> <span class="n">undefined</span> </span></span></code></pre></div><p>In Haskell, this is quite useful. For instance, if one&rsquo;s in the middle of writing a complicated function, and wants to check their work so far, they can put &lsquo;undefined&rsquo; for the part of the function they haven&rsquo;t written. They can then compile their program; the typechecker will find any mistakes they&rsquo;ve made so far, but, since the type of <code>undefined</code> can be <em>anything</em>, that part of the program will be accepted without second thought.</p> <p>The language Idris extends this practice with the idea of typed holes, where you can leave fragments of your program unwritten, and ask the compiler what kind of <em>thing</em> you need to write to fill that hole.</p> <a href="#java-and-null"> <h3 id="java-and-null">Java and <code>null</code></h3> </a> <p>Now you may be thinking:</p> <blockquote> <p>This whole deal with Haskell&rsquo;s <code>undefined</code> is beside the point; it doesn&rsquo;t really count as a value, since it&rsquo;s just a nonterminating expression. What you&rsquo;re doing is a kind of academic autofellatio.</p> </blockquote> <p>Alright, I can accept this criticism. Perhaps just calling a nonterminating function a value <em>is</em> far-fetched (even though in <a href="https://en.wikipedia.org/wiki/Denotational_semantics"class="external-link">denotational semantics<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> we <em>do</em> extend types with \(\bot\)). But denotational semantics are not the only place where types are implicitly extend with an extra value; let&rsquo;s look at Java.</p> <p>In Java, we have <code>null</code>. At the core language level, any function or method that accepts a class can also take <code>null</code>; if <code>null</code> is not to that function or method&rsquo;s liking, it has to explicitly check for it using <code>if(x == null)</code>.</p> <p>This <code>null</code> value does not at first interact with booleans. After all, Java&rsquo;s booleans are not classes. Unlike classes, which you have to allocate using <code>new</code>, you can just throw around <code>true</code> and <code>false</code> as you see fit. Also unlike classes, you simply can&rsquo;t assign <code>null</code> to a boolean value.</p> <p>The trouble is, the parts of Java dealing with <em>generics</em>, which allow you to write polymorphic functions, can&rsquo;t handle &lsquo;primitives&rsquo; like <code>bool</code>. If you want to have an <code>ArrayList</code> of something, that something <em>must</em> be a class. But what if you really <em>do</em> want an <code>ArrayList</code> of booleans? Java solves this problem by introducing &lsquo;boxed&rsquo; booleans: they&rsquo;re primitives wrapped in a class, called <code>Boolean</code>. This class can then be used for generics.</p> <p>But see, this is where <code>null</code> has snuck in again. By allowing <code>Boolean</code> to be a class (thereby granting it access to generics), we&rsquo;ve also given it the ability to be null. This example is made especially compelling because Java supports something they call <a href="https://docs.oracle.com/javase/tutorial/java/data/autoboxing.html"class="external-link">autoboxing<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>: you can directly assign a primitive to a variable of the corresponding boxed type. Consider the example:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="n">Boolean</span><span class="w"> </span><span class="n">myTrue</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">true</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">Boolean</span><span class="w"> </span><span class="n">myFalse</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">false</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">Boolean</span><span class="w"> </span><span class="n">myBool</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">null</span><span class="p">;</span><span class="w"> </span></span></span></code></pre></div><p>Beautiful, isn&rsquo;t it? Better yet, unlike Haskell, where you can&rsquo;t <em>really</em> check if your <code>Bool</code> is <code>undefined</code> (because you can&rsquo;t tell whether a non-terminating computation is as such), you can very easily check if your <code>Boolean</code> is <code>true</code>, <code>false</code>, or <code>null</code>:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="k">assert</span><span class="w"> </span><span class="n">myTrue</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="n">myFalse</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">assert</span><span class="w"> </span><span class="n">myFalse</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="n">myBool</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">assert</span><span class="w"> </span><span class="n">myTrue</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="n">myBool</span><span class="p">;</span><span class="w"> </span></span></span></code></pre></div><p>We&rsquo;re okay to use <code>!=</code> here, instead of <code>equals</code>, because it so happens each boxed instance of a <code>boolean</code> value <a href="https://stackoverflow.com/questions/28636738/equality-of-boxed-boolean"class="external-link">refers to the same <code>Boolean</code> object<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. In fact, this means that a <code>Boolean</code> variable can have <strong>exactly</strong> 3 values!</p> <a href="#c-and-integers"> <h3 id="c-and-integers">C and Integers</h3> </a> <p>Oh the luxury of having a type representing booleans in your language! It&rsquo;s almost overly indulgent compared to the spartan minimalism of C. In C, boolean conditions are represented as numbers. You can perhaps get away with throwing around <code>char</code> or <code>short int</code>, but even then, these types allow far more values than two!</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">test</span> <span class="o">=</span> <span class="mi">255</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="k">while</span><span class="p">(</span><span class="n">test</span><span class="p">)</span> <span class="n">test</span> <span class="o">-=</span> <span class="mi">1</span><span class="p">;</span> </span></span></code></pre></div><p>This loop will run 255 times, thereby demonstrating that C has at least 255 values that can be used to represent the boolean <code>true</code>.</p> <p>There are other languages with this notion of &rsquo;truthy&rsquo; and &lsquo;falsey&rsquo; values, in which something not exactly <code>true</code> or <code>false</code> can be used as a condition. However, some of them differ from C in that they also extend this idea to equality. In JavaScript:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">assert</span><span class="p">(</span><span class="kc">true</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> </span></span><span class="line"><span class="cl"><span class="nx">console</span><span class="p">.</span><span class="nx">assert</span><span class="p">(</span><span class="kc">false</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> </span></span></code></pre></div><p>Then, there are still exactly two distinct boolean values modulo <code>==</code>. No such luck in C, though! We have 256 values that fit in <code>unsigned char</code>, all of which are also distinct modulo <code>==</code>. Our boolean variable can contain all of these values. And there is no respite to be found with <code>enum</code>s, either. We could try define:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="k">enum</span> <span class="kt">bool</span> <span class="p">{</span> <span class="n">TRUE</span><span class="p">,</span> <span class="n">FALSE</span> <span class="p">};</span> </span></span></code></pre></div><p>Unfortunately, all this does is define <code>bool</code> to be a numeric type that can hold at least 2 distinct values, and define numeric constants <code>TRUE</code> and <code>FALSE</code>. So in fact, you can <em>still</em> write the following code:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="k">enum</span> <span class="kt">bool</span> <span class="n">b1</span> <span class="o">=</span> <span class="n">TRUE</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="k">enum</span> <span class="kt">bool</span> <span class="n">b2</span> <span class="o">=</span> <span class="n">FALSE</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="k">enum</span> <span class="kt">bool</span> <span class="n">b3</span> <span class="o">=</span> <span class="mi">15</span><span class="p">;</span> </span></span></code></pre></div><p>And so, no matter how hard you try, your &lsquo;boolean&rsquo; variable can have many, many values!</p> <a href="#conclusion"> <h3 id="conclusion">Conclusion</h3> </a> <p>I think that &lsquo;how many values does a boolean have&rsquo; is a strange question. Its purpose can be one of two things:</p> <ul> <li>The interviewer expected a long-form response such as this one. This is a weird expectation for a software engineering candidate - how does knowing about \(\bot\), <code>undefined</code>, or <code>null</code> help in creating software, especially if this information is irrelevant to the company&rsquo;s language of choice?</li> <li>The interviewer expected the simple answer. In that case, my previous observation applies: what software engineering candidate has <em>not</em> seen a boolean in their time programming? Surely candidates are better screened before they are offered an interview?</li> </ul> <p>Despite the question&rsquo;s weirdness, I think that the resulting investigation of the matter &ndash; outside of the interview setting &ndash; is useful, and perhaps, in a way, enlightening. It may help one understand the design choices made in <em>their</em> language of choice, and how those choices shape the code that they write.</p> <p>That&rsquo;s all I have! I hope that you found it interesting.</p> Meaningfully Typechecking a Language in Idris, With Tuples https://danilafe.com/blog/typesafe_interpreter_tuples/ Wed, 12 Aug 2020 15:48:04 -0700 https://danilafe.com/blog/typesafe_interpreter_tuples/ <p>Some time ago, I wrote a post titled <a href="https://danilafe.com/blog/typesafe_interpreter/">Meaningfully Typechecking a Language in Idris</a>. I then followed it up with <a href="https://danilafe.com/blog/typesafe_interpreter_revisited/">Meaningfully Typechecking a Language in Idris, Revisited</a>. In these posts, I described a hypothetical way of &rsquo;typechecking&rsquo; an expression data type <code>Expr</code> into a typesafe form <code>SafeExpr</code>. A <code>SafeExpr</code> can be evaluated without any code to handle type errors, since it&rsquo;s by definition impossible to construct ill-typed expressions using it. In the first post, we implemented the method only for simple arithmetic expressions; in my latter post, we extended this to support <code>if</code>-expressions. Near the end of the post, I made the following comment:</p> <blockquote> <p>When we add polymorphic tuples and lists, we start being able to construct an arbitrary number of types: <code>[a]</code>. <code>[[a]]</code>, and so on. Then, we cease to be able t enumerate all possible pairs of types, and require a recursive solution. I think that this leads us back to [our method].</p> </blockquote> <p>Recently, I thought about this some more, and decided that it&rsquo;s rather simple to add tuples into our little language. The addition of tuples mean that our language will have an infinite number of possible types. We would have <code>Int</code>, <code>(Int, Int)</code>, <code>((Int, Int), Int)</code>, and so on. This would make it impossible to manually test every possible case in our typechecker, but our approach of returning <code>Dec (a = b)</code> will work just fine.</p> <a href="#extending-the-syntax"> <h3 id="extending-the-syntax">Extending The Syntax</h3> </a> <p>First, let&rsquo;s extend our existing language with expressions for tuples. For simplicity, let&rsquo;s use pairs <code>(a,b)</code> instead of general <code>n</code>-element tuples. This would make typechecking less cumbersome while still having the interesting effect of making the number of types in our language infinite. We can always represent the 3-element tuple <code>(a,b,c)</code> as <code>((a,b), c)</code>, after all. To be able to extract values from our pairs, we&rsquo;ll add the <code>fst</code> and <code>snd</code> functions into our language, which accept a tuple and return its first or second element, respectively.</p> <p>Our <code>Expr</code> data type, which allows ill-typed expressions, ends up as follows:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-interpreter/TypesafeIntrV3.idr" data-first-line="31" data-last-line="39"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntrV3.idr#L31-L39">TypesafeIntrV3.idr</a>, lines 31 through 39</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="hl"><span class="lnt">37 </span></span><span class="hl"><span class="lnt">38 </span></span><span class="hl"><span class="lnt">39 </span></span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="kr">data</span> <span class="kt">Expr</span> </span></span><span class="line"><span class="cl"> <span class="ow">=</span> <span class="kt">IntLit</span> <span class="kt">Int</span> </span></span><span class="line"><span class="cl"> <span class="ow">|</span> <span class="kt">BoolLit</span> <span class="kt">Bool</span> </span></span><span class="line"><span class="cl"> <span class="ow">|</span> <span class="kt">StringLit</span> <span class="kt">String</span> </span></span><span class="line"><span class="cl"> <span class="ow">|</span> <span class="kt">BinOp</span> <span class="kt">Op</span> <span class="kt">Expr</span> <span class="kt">Expr</span> </span></span><span class="line"><span class="cl"> <span class="ow">|</span> <span class="kt">IfElse</span> <span class="kt">Expr</span> <span class="kt">Expr</span> <span class="kt">Expr</span> </span></span><span class="line hl"><span class="cl"> <span class="ow">|</span> <span class="kt">Pair</span> <span class="kt">Expr</span> <span class="kt">Expr</span> </span></span><span class="line hl"><span class="cl"> <span class="ow">|</span> <span class="kt">Fst</span> <span class="kt">Expr</span> </span></span><span class="line hl"><span class="cl"> <span class="ow">|</span> <span class="kt">Snd</span> <span class="kt">Expr</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>I&rsquo;ve highlighted the new lines. The additions consist of the <code>Pair</code> constructor, which represents the tuple expression <code>(a, b)</code>, and the <code>Fst</code> and <code>Snd</code> constructors, which represent the <code>fst e</code> and <code>snd e</code> expressions, respectively. In a similar manner, we extend our <code>SafeExpr</code> GADT:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-interpreter/TypesafeIntrV3.idr" data-first-line="41" data-last-line="49"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntrV3.idr#L41-L49">TypesafeIntrV3.idr</a>, lines 41 through 49</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="hl"><span class="lnt">47 </span></span><span class="hl"><span class="lnt">48 </span></span><span class="hl"><span class="lnt">49 </span></span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="kr">data</span> <span class="kt">SafeExpr</span> <span class="ow">:</span> <span class="kt">ExprType</span> <span class="ow">-&gt;</span> <span class="kt">Type</span> <span class="kr">where</span> </span></span><span class="line"><span class="cl"> <span class="nf">IntLiteral</span> <span class="ow">:</span> <span class="kt">Int</span> <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> <span class="kt">IntType</span> </span></span><span class="line"><span class="cl"> <span class="nf">BoolLiteral</span> <span class="ow">:</span> <span class="kt">Bool</span> <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> <span class="kt">BoolType</span> </span></span><span class="line"><span class="cl"> <span class="nf">StringLiteral</span> <span class="ow">:</span> <span class="kt">String</span> <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> <span class="kt">StringType</span> </span></span><span class="line"><span class="cl"> <span class="nf">BinOperation</span> <span class="ow">:</span> <span class="ow">(</span>repr a <span class="ow">-&gt;</span> repr b <span class="ow">-&gt;</span> repr c<span class="ow">)</span> <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> a <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> b <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> c </span></span><span class="line"><span class="cl"> <span class="nf">IfThenElse</span> <span class="ow">:</span> <span class="kt">SafeExpr</span> <span class="kt">BoolType</span> <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> t <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> t <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> t </span></span><span class="line hl"><span class="cl"> <span class="nf">NewPair</span> <span class="ow">:</span> <span class="kt">SafeExpr</span> t1 <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> t2 <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> <span class="ow">(</span><span class="kt">PairType</span> t1 t2<span class="ow">)</span> </span></span><span class="line hl"><span class="cl"> <span class="nf">First</span> <span class="ow">:</span> <span class="kt">SafeExpr</span> <span class="ow">(</span><span class="kt">PairType</span> t1 t2<span class="ow">)</span> <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> t1 </span></span><span class="line hl"><span class="cl"> <span class="nf">Second</span> <span class="ow">:</span> <span class="kt">SafeExpr</span> <span class="ow">(</span><span class="kt">PairType</span> t1 t2<span class="ow">)</span> <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> t2</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Finally, to provide the <code>PairType</code> constructor, we extend the <code>ExprType</code> and <code>repr</code> functions:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-interpreter/TypesafeIntrV3.idr" data-first-line="1" data-last-line="11"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntrV3.idr#L1-L11">TypesafeIntrV3.idr</a>, lines 1 through 11</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="hl"><span class="lnt"> 5 </span></span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="hl"><span class="lnt">11 </span></span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="kr">data</span> <span class="kt">ExprType</span> </span></span><span class="line"><span class="cl"> <span class="ow">=</span> <span class="kt">IntType</span> </span></span><span class="line"><span class="cl"> <span class="ow">|</span> <span class="kt">BoolType</span> </span></span><span class="line"><span class="cl"> <span class="ow">|</span> <span class="kt">StringType</span> </span></span><span class="line hl"><span class="cl"> <span class="ow">|</span> <span class="kt">PairType</span> <span class="kt">ExprType</span> <span class="kt">ExprType</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="nf">repr</span> <span class="ow">:</span> <span class="kt">ExprType</span> <span class="ow">-&gt;</span> <span class="kt">Type</span> </span></span><span class="line"><span class="cl">repr <span class="kt">IntType</span> <span class="ow">=</span> <span class="kt">Int</span> </span></span><span class="line"><span class="cl">repr <span class="kt">BoolType</span> <span class="ow">=</span> <span class="kt">Bool</span> </span></span><span class="line"><span class="cl">repr <span class="kt">StringType</span> <span class="ow">=</span> <span class="kt">String</span> </span></span><span class="line hl"><span class="cl">repr <span class="ow">(</span><span class="kt">PairType</span> t1 t2<span class="ow">)</span> <span class="ow">=</span> <span class="kt">Pair</span> <span class="ow">(</span>repr t1<span class="ow">)</span> <span class="ow">(</span>repr t2<span class="ow">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#implementing-equality"> <h3 id="implementing-equality">Implementing Equality</h3> </a> <p>An important part of this change is the extension of the <code>decEq</code> function, which compares two types for equality. The kind folks over at <code>#idris</code> previously recommended the use of the <code>Dec</code> data type for this purpose. A value of type <code>Dec P</code> <span class="sidenote"> <label class="sidenote-label" for="decideable-note">is either a proof that \(P\) is true, or a proof that \(P\) is false.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="decideable-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> It's possible that a proposition \(P\) is not provable, and neither is \(\lnot P\). It is therefore not possible to construct a value of type <code>Dec P</code> for any proposition <code>P</code>. Having a value of type <code>Dec P</code>, then, provides us nontrivial information. <span class="sidenote-delimiter">]</span> </span> </span> Our <code>decEq</code> function, given two types <code>a</code> and <code>b</code>, returns <code>Dec (a = b)</code>. Thus, it will return either a proof that <code>a = b</code> (which we can then use to convince the Idris type system that two <code>SafeExpr</code> values are, in fact, of the same type), or a proof of <code>a = b -&gt; Void</code> (which tells us that <code>a</code> and <code>b</code> are definitely not equal). If you&rsquo;re not sure what the deal with <code>(=)</code> and <code>Void</code> is, check out <a href="https://danilafe.com/blog/typesafe_interpreter_revisited/#curry-howard-correspondence">this section</a> of the previous article.</p> <p>A lot of the work in implementing <code>decEq</code> went into constructing proofs of falsity. That is, we needed to explicitly list every case like <code>decEq IntType BoolType</code>, and create a proof that <code>IntType</code> cannot equal <code>BoolType</code>. However, here&rsquo;s how we use <code>decEq</code> in the typechecking function:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-interpreter/TypesafeIntrV2.idr" data-first-line="76" data-last-line="78"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntrV2.idr#L76-L78">TypesafeIntrV2.idr</a>, lines 76 through 78</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"> <span class="kr">case</span> decEq tt et <span class="kr">of</span> </span></span><span class="line"><span class="cl"> <span class="kt">Yes</span> p <span class="ow">=&gt;</span> pure <span class="ow">(</span><span class="kr">_</span> <span class="ow">**</span> <span class="kt">IfThenElse</span> ce <span class="ow">(</span>replace p te<span class="ow">)</span> ee<span class="ow">)</span> </span></span><span class="line"><span class="cl"> <span class="kt">No</span> <span class="kr">_</span> <span class="ow">=&gt;</span> <span class="kt">Left</span> <span class="s">&#34;Incompatible branch types.&#34;</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We always throw away the proof inequality! So, rather than spending the time constructing useless proofs like this, we can just switch <code>decEq</code> to return a <code>Maybe (a = b)</code>. The <code>Just</code> case will tell us that the two types are equal (and, as before, provide a proof); the <code>Nothing</code> case will tell us that the two types are <em>not</em> equal, and provide no further information. Let&rsquo;s see the implementation of <code>decEq</code> now:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-interpreter/TypesafeIntrV3.idr" data-first-line="13" data-last-line="23"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntrV3.idr#L13-L23">TypesafeIntrV3.idr</a>, lines 13 through 23</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">decEq</span> <span class="ow">:</span> <span class="ow">(</span>a <span class="ow">:</span> <span class="kt">ExprType</span><span class="ow">)</span> <span class="ow">-&gt;</span> <span class="ow">(</span>b <span class="ow">:</span> <span class="kt">ExprType</span><span class="ow">)</span> <span class="ow">-&gt;</span> <span class="kt">Maybe</span> <span class="ow">(</span>a <span class="ow">=</span> b<span class="ow">)</span> </span></span><span class="line"><span class="cl">decEq <span class="kt">IntType</span> <span class="kt">IntType</span> <span class="ow">=</span> <span class="kt">Just</span> <span class="kt">Refl</span> </span></span><span class="line"><span class="cl">decEq <span class="kt">BoolType</span> <span class="kt">BoolType</span> <span class="ow">=</span> <span class="kt">Just</span> <span class="kt">Refl</span> </span></span><span class="line"><span class="cl">decEq <span class="kt">StringType</span> <span class="kt">StringType</span> <span class="ow">=</span> <span class="kt">Just</span> <span class="kt">Refl</span> </span></span><span class="line"><span class="cl">decEq <span class="ow">(</span><span class="kt">PairType</span> lt1 lt2<span class="ow">)</span> <span class="ow">(</span><span class="kt">PairType</span> rt1 rt2<span class="ow">)</span> <span class="ow">=</span> <span class="kr">do</span> </span></span><span class="line"><span class="cl"> subEq1 <span class="ow">&lt;-</span> decEq lt1 rt1 </span></span><span class="line"><span class="cl"> subEq2 <span class="ow">&lt;-</span> decEq lt2 rt2 </span></span><span class="line"><span class="cl"> <span class="kr">let</span> firstEqual <span class="ow">=</span> replace <span class="ow">{</span><span class="kt">P</span> <span class="ow">=</span> <span class="ow">\</span>t1 <span class="ow">=&gt;</span> <span class="kt">PairType</span> lt1 lt2 <span class="ow">=</span> <span class="kt">PairType</span> t1 lt2<span class="ow">}</span> subEq1 <span class="kt">Refl</span> </span></span><span class="line"><span class="cl"> <span class="kr">let</span> secondEqual <span class="ow">=</span> replace <span class="ow">{</span><span class="kt">P</span> <span class="ow">=</span> <span class="ow">\</span>t2 <span class="ow">=&gt;</span> <span class="kt">PairType</span> lt1 lt2 <span class="ow">=</span> <span class="kt">PairType</span> rt1 t2<span class="ow">}</span> subEq2 firstEqual </span></span><span class="line"><span class="cl"> pure secondEqual </span></span><span class="line"><span class="cl">decEq <span class="kr">_</span> <span class="kr">_</span> <span class="ow">=</span> <span class="kt">Nothing</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Lines 14 through 16 are pretty simple; in this case, we can tell at a glance that the two types are equal, and Idris can infer an equality proof in the form of <code>Refl</code>. We return this proof by writing it in a <code>Just</code>. Line 23 is the catch-all case for any combination of types we didn&rsquo;t handle. Any combination of types we don&rsquo;t handle is invalid, and thus, this case returns <code>Nothing</code>.</p> <p>What about lines 17 through 22? This is the case for handling the equality of two pair types, <code>(lt1, lt2)</code> and <code>(rt1, rt2)</code>. The equality of the two types depends on the equality of their constituents. That is, if we know that <code>lt1 = rt1</code> and <code>lt2 = rt2</code>, we know that the two pair types are also equal. If one of the two equalities doesn&rsquo;t hold, the two pairs obviously aren&rsquo;t equal, and thus, we should return <code>Nothing</code>. This should remind us of <code>Maybe</code>&rsquo;s monadic nature: we can first compute <code>decEq lt1 rt1</code>, and then, if it succeeds, compute <code>decEq lt2 rt2</code>. If both succeed, we will have in hand the two proofs, <code>lt1 = rt1</code> and <code>lt2 = rt2</code>. We achieve this effect using <code>do</code>-notation, storing the sub-proofs into <code>subEq1</code> and <code>subEq2</code>.</p> <p>What now? Once again, we have to use <code>replace</code>. Recall its type:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">replace</span> <span class="ow">:</span> <span class="ow">{</span>a<span class="ow">:</span><span class="kr">_</span><span class="ow">}</span> <span class="ow">-&gt;</span> <span class="ow">{</span>x<span class="ow">:</span><span class="kr">_</span><span class="ow">}</span> <span class="ow">-&gt;</span> <span class="ow">{</span>y<span class="ow">:</span><span class="kr">_</span><span class="ow">}</span> <span class="ow">-&gt;</span> <span class="ow">{</span><span class="kt">P</span> <span class="ow">:</span> a <span class="ow">-&gt;</span> <span class="kt">Type</span><span class="ow">}</span> <span class="ow">-&gt;</span> x <span class="ow">=</span> y <span class="ow">-&gt;</span> <span class="kt">P</span> x <span class="ow">-&gt;</span> <span class="kt">P</span> y </span></span></code></pre></div><p>Given some proposition in terms of <code>a</code>, and knowing that <code>a = b</code>, <code>replace</code> returns the original proposition, but now in terms of <code>b</code>. We know for sure that:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="kt">PairType</span> lt1 lt2 <span class="ow">=</span> <span class="kt">PairType</span> lt1 lt2 </span></span></code></pre></div><p>We can start from there. Let&rsquo;s handle one thing at a time, and try to replace the second <code>lt1</code> with <code>rt1</code>. Then, we can replace the second <code>lt2</code> with <code>rt2</code>, and we&rsquo;ll have our equality!</p> <p>Easier said than done, though. How do we tell Idris which <code>lt1</code> we want to substitute? After all, of the following are possible:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="kt">PairType</span> rt1 lt2 <span class="ow">=</span> <span class="kt">PairType</span> lt1 lt2 <span class="c1">-- First lt1 replaced</span> </span></span><span class="line"><span class="cl"><span class="kt">PairType</span> lt1 lt2 <span class="ow">=</span> <span class="kt">PairType</span> rt1 lt2 <span class="c1">-- Second lt1 replaced</span> </span></span><span class="line"><span class="cl"><span class="kt">PairType</span> rt1 lt2 <span class="ow">=</span> <span class="kt">PairType</span> rt1 lt2 <span class="c1">-- Both replaced</span> </span></span></code></pre></div><p>The key is in the signature, specifically the expressions <code>P x</code> and <code>P y</code>. We can think of <code>P</code> as a function, and of <code>replace</code> as creating a value of <code>P</code> applied to another argument. Thus, the substitution will occur exactly where the argument of <code>P</code> is used. Then, to achieve each of the above substitution, we can write <code>P</code> as follows:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="hl"><span class="lnt">2 </span></span><span class="lnt">3 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl">t1 <span class="ow">=&gt;</span> <span class="kt">PairType</span> t1 lt2 <span class="ow">=</span> <span class="kt">PairType</span> lt1 lt2 </span></span><span class="line hl"><span class="cl">t1 <span class="ow">=&gt;</span> <span class="kt">PairType</span> lt1 lt2 <span class="ow">=</span> <span class="kt">PairType</span> t1 lt2 </span></span><span class="line"><span class="cl">t1 <span class="ow">=&gt;</span> <span class="kt">PairType</span> t1 lt2 <span class="ow">=</span> <span class="kt">PairType</span> t1 lt2 </span></span></code></pre></td></tr></table> </div> </div><p>The second function (highlighted) is the one we&rsquo;ll need to use to attain the desired result. Since <code>P</code> is an implicit argument to <code>replace</code>, we can explicitly provide it with <code>{P=...}</code>, leading to the following line:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-interpreter/TypesafeIntrV3.idr" data-first-line="20" data-last-line="20"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntrV3.idr#L20-L20">TypesafeIntrV3.idr</a>, line 20</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">20 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"> <span class="kr">let</span> firstEqual <span class="ow">=</span> replace <span class="ow">{</span><span class="kt">P</span> <span class="ow">=</span> <span class="ow">\</span>t1 <span class="ow">=&gt;</span> <span class="kt">PairType</span> lt1 lt2 <span class="ow">=</span> <span class="kt">PairType</span> t1 lt2<span class="ow">}</span> subEq1 <span class="kt">Refl</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We now have a proof of the following proposition:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="kt">PairType</span> lt1 lt2 <span class="ow">=</span> <span class="kt">PairType</span> rt1 lt2 </span></span></code></pre></div><p>We want to replace the second <code>lt2</code> with <code>rt2</code>, which means that we write our <code>P</code> as follows:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl">t2 <span class="ow">=&gt;</span> <span class="kt">PairType</span> lt1 lt2 <span class="ow">=</span> <span class="kt">PairType</span> rt1 t2 </span></span></code></pre></div><p>Finally, we perform the second replacement, and return the result:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-interpreter/TypesafeIntrV3.idr" data-first-line="21" data-last-line="22"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntrV3.idr#L21-L22">TypesafeIntrV3.idr</a>, lines 21 through 22</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">21 </span><span class="lnt">22 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"> <span class="kr">let</span> secondEqual <span class="ow">=</span> replace <span class="ow">{</span><span class="kt">P</span> <span class="ow">=</span> <span class="ow">\</span>t2 <span class="ow">=&gt;</span> <span class="kt">PairType</span> lt1 lt2 <span class="ow">=</span> <span class="kt">PairType</span> rt1 t2<span class="ow">}</span> subEq2 firstEqual </span></span><span class="line"><span class="cl"> pure secondEqual</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Great! We have finished implement <code>decEq</code>.</p> <a href="#adjusting-the-typechecker"> <h3 id="adjusting-the-typechecker">Adjusting The Typechecker</h3> </a> <p>It&rsquo;s time to make our typechecker work with tuples. First, we need to fix the <code>IfElse</code> case to accept <code>Maybe (a=b)</code> instead of <code>Dec (a=b)</code>:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-interpreter/TypesafeIntrV3.idr" data-first-line="71" data-last-line="78"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntrV3.idr#L71-L78">TypesafeIntrV3.idr</a>, lines 71 through 78</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">71 </span><span class="lnt">72 </span><span class="lnt">73 </span><span class="lnt">74 </span><span class="lnt">75 </span><span class="lnt">76 </span><span class="hl"><span class="lnt">77 </span></span><span class="hl"><span class="lnt">78 </span></span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl">typecheck <span class="ow">(</span><span class="kt">IfElse</span> c t e<span class="ow">)</span> <span class="ow">=</span> </span></span><span class="line"><span class="cl"> <span class="kr">do</span> </span></span><span class="line"><span class="cl"> ce <span class="ow">&lt;-</span> typecheck c <span class="ow">&gt;&gt;=</span> requireBool </span></span><span class="line"><span class="cl"> <span class="ow">(</span>tt <span class="ow">**</span> te<span class="ow">)</span> <span class="ow">&lt;-</span> typecheck t </span></span><span class="line"><span class="cl"> <span class="ow">(</span>et <span class="ow">**</span> ee<span class="ow">)</span> <span class="ow">&lt;-</span> typecheck e </span></span><span class="line"><span class="cl"> <span class="kr">case</span> decEq tt et <span class="kr">of</span> </span></span><span class="line hl"><span class="cl"> <span class="kt">Just</span> p <span class="ow">=&gt;</span> pure <span class="ow">(</span><span class="kr">_</span> <span class="ow">**</span> <span class="kt">IfThenElse</span> ce <span class="ow">(</span>replace p te<span class="ow">)</span> ee<span class="ow">)</span> </span></span><span class="line hl"><span class="cl"> <span class="kt">Nothing</span> <span class="ow">=&gt;</span> <span class="kt">Left</span> <span class="s">&#34;Incompatible branch types.&#34;</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Note that the only change is from <code>Dec</code> to <code>Maybe</code>; we didn&rsquo;t need to add new cases or even to know what sort of types are available in the language.</p> <p>Next, we can write the cases for the new expressions in our language. We can start with <code>Pair</code>, which, given expressions of types <code>a</code> and <code>b</code>, creates an expression of type <code>(a,b)</code>. As long as the arguments to <code>Pair</code> are well-typed, so is the <code>Pair</code> expression itself; thus, there are no errors to handle.</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-interpreter/TypesafeIntrV3.idr" data-first-line="79" data-last-line="83"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntrV3.idr#L79-L83">TypesafeIntrV3.idr</a>, lines 79 through 83</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">79 </span><span class="lnt">80 </span><span class="lnt">81 </span><span class="lnt">82 </span><span class="lnt">83 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl">typecheck <span class="ow">(</span><span class="kt">Pair</span> l r<span class="ow">)</span> <span class="ow">=</span> </span></span><span class="line"><span class="cl"> <span class="kr">do</span> </span></span><span class="line"><span class="cl"> <span class="ow">(</span>lt <span class="ow">**</span> le<span class="ow">)</span> <span class="ow">&lt;-</span> typecheck l </span></span><span class="line"><span class="cl"> <span class="ow">(</span>rt <span class="ow">**</span> re<span class="ow">)</span> <span class="ow">&lt;-</span> typecheck r </span></span><span class="line"><span class="cl"> pure <span class="ow">(</span><span class="kr">_</span> <span class="ow">**</span> <span class="kt">NewPair</span> le re<span class="ow">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The case for <code>Fst</code> is more complicated. If the argument to <code>Fst</code> is a tuple of type <code>(a, b)</code>, then <code>Fst</code> constructs from it an expression of type <code>a</code>. Otherwise, the expression is ill-typed, and we return an error.</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-interpreter/TypesafeIntrV3.idr" data-first-line="84" data-last-line="89"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntrV3.idr#L84-L89">TypesafeIntrV3.idr</a>, lines 84 through 89</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">84 </span><span class="lnt">85 </span><span class="lnt">86 </span><span class="lnt">87 </span><span class="lnt">88 </span><span class="lnt">89 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl">typecheck <span class="ow">(</span><span class="kt">Fst</span> p<span class="ow">)</span> <span class="ow">=</span> </span></span><span class="line"><span class="cl"> <span class="kr">do</span> </span></span><span class="line"><span class="cl"> <span class="ow">(</span>pt <span class="ow">**</span> pe<span class="ow">)</span> <span class="ow">&lt;-</span> typecheck p </span></span><span class="line"><span class="cl"> <span class="kr">case</span> pt <span class="kr">of</span> </span></span><span class="line"><span class="cl"> <span class="kt">PairType</span> <span class="kr">_</span> <span class="kr">_</span> <span class="ow">=&gt;</span> pure <span class="ow">$</span> <span class="ow">(</span><span class="kr">_</span> <span class="ow">**</span> <span class="kt">First</span> pe<span class="ow">)</span> </span></span><span class="line"><span class="cl"> <span class="kr">_</span> <span class="ow">=&gt;</span> <span class="kt">Left</span> <span class="s">&#34;Applying fst to non-pair.&#34;</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The case for <code>Snd</code> is very similar:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-interpreter/TypesafeIntrV3.idr" data-first-line="90" data-last-line="96"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntrV3.idr#L90-L96">TypesafeIntrV3.idr</a>, lines 90 through 96</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">90 </span><span class="lnt">91 </span><span class="lnt">92 </span><span class="lnt">93 </span><span class="lnt">94 </span><span class="lnt">95 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl">typecheck <span class="ow">(</span><span class="kt">Snd</span> p<span class="ow">)</span> <span class="ow">=</span> </span></span><span class="line"><span class="cl"> <span class="kr">do</span> </span></span><span class="line"><span class="cl"> <span class="ow">(</span>pt <span class="ow">**</span> pe<span class="ow">)</span> <span class="ow">&lt;-</span> typecheck p </span></span><span class="line"><span class="cl"> <span class="kr">case</span> pt <span class="kr">of</span> </span></span><span class="line"><span class="cl"> <span class="kt">PairType</span> <span class="kr">_</span> <span class="kr">_</span> <span class="ow">=&gt;</span> pure <span class="ow">$</span> <span class="ow">(</span><span class="kr">_</span> <span class="ow">**</span> <span class="kt">Second</span> pe<span class="ow">)</span> </span></span><span class="line"><span class="cl"> <span class="kr">_</span> <span class="ow">=&gt;</span> <span class="kt">Left</span> <span class="s">&#34;Applying snd to non-pair.&#34;</span> </span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#evaluation-function-and-conclusion"> <h3 id="evaluation-function-and-conclusion">Evaluation Function and Conclusion</h3> </a> <p>We conclude with our final <code>eval</code> and <code>resultStr</code> functions, which now look as follows.</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-interpreter/TypesafeIntrV3.idr" data-first-line="97" data-last-line="111"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntrV3.idr#L97-L111">TypesafeIntrV3.idr</a>, lines 97 through 111</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 97 </span><span class="lnt"> 98 </span><span class="lnt"> 99 </span><span class="lnt">100 </span><span class="lnt">101 </span><span class="lnt">102 </span><span class="hl"><span class="lnt">103 </span></span><span class="hl"><span class="lnt">104 </span></span><span class="hl"><span class="lnt">105 </span></span><span class="lnt">106 </span><span class="lnt">107 </span><span class="lnt">108 </span><span class="hl"><span class="lnt">109 </span></span><span class="hl"><span class="lnt">110 </span></span><span class="hl"><span class="lnt">111 </span></span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">eval</span> <span class="ow">:</span> <span class="kt">SafeExpr</span> t <span class="ow">-&gt;</span> repr t </span></span><span class="line"><span class="cl">eval <span class="ow">(</span><span class="kt">IntLiteral</span> i<span class="ow">)</span> <span class="ow">=</span> i </span></span><span class="line"><span class="cl">eval <span class="ow">(</span><span class="kt">BoolLiteral</span> b<span class="ow">)</span> <span class="ow">=</span> b </span></span><span class="line"><span class="cl">eval <span class="ow">(</span><span class="kt">StringLiteral</span> s<span class="ow">)</span> <span class="ow">=</span> s </span></span><span class="line"><span class="cl">eval <span class="ow">(</span><span class="kt">BinOperation</span> f l r<span class="ow">)</span> <span class="ow">=</span> f <span class="ow">(</span>eval l<span class="ow">)</span> <span class="ow">(</span>eval r<span class="ow">)</span> </span></span><span class="line"><span class="cl">eval <span class="ow">(</span><span class="kt">IfThenElse</span> c t e<span class="ow">)</span> <span class="ow">=</span> <span class="kr">if</span> <span class="ow">(</span>eval c<span class="ow">)</span> <span class="kr">then</span> <span class="ow">(</span>eval t<span class="ow">)</span> <span class="kr">else</span> <span class="ow">(</span>eval e<span class="ow">)</span> </span></span><span class="line hl"><span class="cl">eval <span class="ow">(</span><span class="kt">NewPair</span> l r<span class="ow">)</span> <span class="ow">=</span> <span class="ow">(</span>eval l, eval r<span class="ow">)</span> </span></span><span class="line hl"><span class="cl">eval <span class="ow">(</span><span class="kt">First</span> p<span class="ow">)</span> <span class="ow">=</span> fst <span class="ow">(</span>eval p<span class="ow">)</span> </span></span><span class="line hl"><span class="cl">eval <span class="ow">(</span><span class="kt">Second</span> p<span class="ow">)</span> <span class="ow">=</span> snd <span class="ow">(</span>eval p<span class="ow">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="nf">resultStr</span> <span class="ow">:</span> <span class="ow">{</span>t <span class="ow">:</span> <span class="kt">ExprType</span><span class="ow">}</span> <span class="ow">-&gt;</span> repr t <span class="ow">-&gt;</span> <span class="kt">String</span> </span></span><span class="line"><span class="cl">resultStr <span class="ow">{</span>t<span class="ow">=</span><span class="kt">IntType</span><span class="ow">}</span> i <span class="ow">=</span> show i </span></span><span class="line hl"><span class="cl">resultStr <span class="ow">{</span>t<span class="ow">=</span><span class="kt">BoolType</span><span class="ow">}</span> b <span class="ow">=</span> show b </span></span><span class="line hl"><span class="cl">resultStr <span class="ow">{</span>t<span class="ow">=</span><span class="kt">StringType</span><span class="ow">}</span> s <span class="ow">=</span> show s </span></span><span class="line hl"><span class="cl">resultStr <span class="ow">{</span>t<span class="ow">=</span><span class="kt">PairType</span> t1 t2<span class="ow">}</span> <span class="ow">(</span>l,r<span class="ow">)</span> <span class="ow">=</span> <span class="s">&#34;(&#34;</span> <span class="ow">++</span> resultStr l <span class="ow">++</span> <span class="s">&#34;, &#34;</span> <span class="ow">++</span> resultStr r <span class="ow">++</span> <span class="s">&#34;)&#34;</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>As you can see, we require no error handling in <code>eval</code>; the expressions returned by <code>typecheck</code> are guaranteed to evaluate to valid Idris values. We have achieved our goal, with very little changes to <code>typecheck</code> other than the addition of new language constructs. In my opinion, this is a win!</p> <p>As always, you can see the code on my Git server. Here&rsquo;s <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntrV3.idr"class="external-link">the latest Idris file,<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> if you want to check it out (and maybe verify that it compiles). I hope you found this interesting!</p> Time Traveling In Haskell: How It Works And How To Use It https://danilafe.com/blog/haskell_lazy_evaluation/ Thu, 30 Jul 2020 00:58:10 -0700 https://danilafe.com/blog/haskell_lazy_evaluation/ <p>I recently got to use a very curious Haskell technique <span class="sidenote"> <label class="sidenote-label" for="production-note">in production:</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="production-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> As production as research code gets, anyway! <span class="sidenote-delimiter">]</span> </span> </span> time traveling. I say this with the utmost seriousness. This technique worked like magic for the problem I was trying to solve, and so I thought I&rsquo;d share what I learned. In addition to the technique and its workings, I will also explain how time traveling can be misused, yielding computations that never terminate.</p> <a href="#time-traveling"> <h3 id="time-traveling">Time Traveling</h3> </a> <p>Some time ago, I read <a href="https://kcsongor.github.io/time-travel-in-haskell-for-dummies/"class="external-link">this post<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> by Csongor Kiss about time traveling in Haskell. It&rsquo;s really cool, and makes a lot of sense if you have wrapped your head around lazy evaluation. I&rsquo;m going to present my take on it here, but please check out Csongor&rsquo;s original post if you are interested.</p> <p>Say that you have a list of integers, like <code>[3,2,6]</code>. Next, suppose that you want to find the maximum value in the list. You can implement such behavior quite simply with pattern matching:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">myMax</span> <span class="ow">::</span> <span class="p">[</span><span class="kt">Int</span><span class="p">]</span> <span class="ow">-&gt;</span> <span class="kt">Int</span> </span></span><span class="line"><span class="cl"><span class="nf">myMax</span> <span class="kt">[]</span> <span class="ow">=</span> <span class="ne">error</span> <span class="s">&#34;Being total sucks&#34;</span> </span></span><span class="line"><span class="cl"><span class="nf">myMax</span> <span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="n">xs</span><span class="p">)</span> <span class="ow">=</span> <span class="n">max</span> <span class="n">x</span> <span class="o">$</span> <span class="n">myMax</span> <span class="n">xs</span> </span></span></code></pre></div><p>You could even get fancy with a <code>fold</code>:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">myMax</span> <span class="ow">::</span> <span class="p">[</span><span class="kt">Int</span><span class="p">]</span> <span class="ow">-&gt;</span> <span class="kt">Int</span> </span></span><span class="line"><span class="cl"><span class="nf">myMax</span> <span class="ow">=</span> <span class="n">foldr1</span> <span class="n">max</span> </span></span></code></pre></div><p>All is well, and this is rather elementary Haskell. But now let&rsquo;s look at something that Csongor calls the <code>repMax</code> problem:</p> <blockquote> <p>Imagine you had a list, and you wanted to replace all the elements of the list with the largest element, by only passing the list once.</p> </blockquote> <p>How can we possibly do this in one pass? First, we need to find the maximum element, and only then can we have something to replace the other numbers with! It turns out, though, that we can just expect to have the future value, and all will be well. Csongor provides the following example:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">repMax</span> <span class="ow">::</span> <span class="p">[</span><span class="kt">Int</span><span class="p">]</span> <span class="ow">-&gt;</span> <span class="kt">Int</span> <span class="ow">-&gt;</span> <span class="p">(</span><span class="kt">Int</span><span class="p">,</span> <span class="p">[</span><span class="kt">Int</span><span class="p">])</span> </span></span><span class="line"><span class="cl"><span class="nf">repMax</span> <span class="kt">[]</span> <span class="n">rep</span> <span class="ow">=</span> <span class="p">(</span><span class="n">rep</span><span class="p">,</span> <span class="kt">[]</span><span class="p">)</span> </span></span><span class="line"><span class="cl"><span class="nf">repMax</span> <span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="n">rep</span> <span class="ow">=</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="p">[</span><span class="n">rep</span><span class="p">])</span> </span></span><span class="line"><span class="cl"><span class="nf">repMax</span> <span class="p">(</span><span class="n">l</span> <span class="kt">:</span> <span class="n">ls</span><span class="p">)</span> <span class="n">rep</span> <span class="ow">=</span> <span class="p">(</span><span class="n">m&#39;</span><span class="p">,</span> <span class="n">rep</span> <span class="kt">:</span> <span class="n">ls&#39;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="kr">where</span> <span class="p">(</span><span class="n">m</span><span class="p">,</span> <span class="n">ls&#39;</span><span class="p">)</span> <span class="ow">=</span> <span class="n">repMax</span> <span class="n">ls</span> <span class="n">rep</span> </span></span><span class="line"><span class="cl"> <span class="n">m&#39;</span> <span class="ow">=</span> <span class="n">max</span> <span class="n">m</span> <span class="n">l</span> </span></span></code></pre></div><p>In this example, <code>repMax</code> takes the list of integers, each of which it must replace with their maximum element. It also takes <strong>as an argument</strong> the maximum element, as if it had already been computed. It does, however, still compute the intermediate maximum element, in the form of <code>m'</code>. Otherwise, where would the future value even come from?</p> <p>Thus far, nothing too magical has happened. It&rsquo;s a little strange to expect the result of the computation to be given to us; it just looks like wishful thinking. The real magic happens in Csongor&rsquo;s <code>doRepMax</code> function:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">doRepMax</span> <span class="ow">::</span> <span class="p">[</span><span class="kt">Int</span><span class="p">]</span> <span class="ow">-&gt;</span> <span class="p">[</span><span class="kt">Int</span><span class="p">]</span> </span></span><span class="line"><span class="cl"><span class="nf">doRepMax</span> <span class="n">xs</span> <span class="ow">=</span> <span class="n">xs&#39;</span> </span></span><span class="line"><span class="cl"> <span class="kr">where</span> <span class="p">(</span><span class="n">largest</span><span class="p">,</span> <span class="n">xs&#39;</span><span class="p">)</span> <span class="ow">=</span> <span class="n">repMax</span> <span class="n">xs</span> <span class="n">largest</span> </span></span></code></pre></div><p>Look, in particular, on the line with the <code>where</code> clause. We see that <code>repMax</code> returns the maximum element of the list, <code>largest</code>, and the resulting list <code>xs'</code> consisting only of <code>largest</code> repeated as many times as <code>xs</code> had elements. But what&rsquo;s curious is the call to <code>repMax</code> itself. It takes as input <code>xs</code>, the list we&rsquo;re supposed to process&hellip; and <code>largest</code>, the value that <em>it itself returns</em>.</p> <p>This works because Haskell&rsquo;s evaluation model is, effectively, <a href="https://en.wikipedia.org/wiki/Graph_reduction"class="external-link">lazy graph reduction<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. That is, you can think of Haskell as manipulating your code as <span class="sidenote"> <label class="sidenote-label" for="tree-note">a syntax tree,</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="tree-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Why is it called graph reduction, you may be wondering, if the runtime is manipulating syntax trees? To save on work, if a program refers to the same value twice, Haskell has both of those references point to the exact same graph. This violates the tree's property of having only one path from the root to any node, and makes our program a DAG (at least). Graph nodes that refer to themselves (which are also possible in the model) also violate the properties of a a DAG, and thus, in general, we are working with graphs. <span class="sidenote-delimiter">]</span> </span> </span> performing substitutions and simplifications as necessary until it reaches a final answer. What the lazy part means is that parts of the syntax tree that are not yet needed to compute the final answer can exist, unsimplified, in the tree. Why don&rsquo;t we draw a few graphs to get familiar with the idea?</p> <a href="#visualizing-graphs-and-their-reduction"> <h3 id="visualizing-graphs-and-their-reduction">Visualizing Graphs and Their Reduction</h3> </a> <p><strong>A word of caution</strong>: the steps presented below may significantly differ from the actual graph reduction algorithms used by modern compilers. In particular, this section draws a lot of ideas from Simon Peyton Jones&rsquo; book, <a href="https://www.microsoft.com/en-us/research/publication/implementing-functional-languages-a-tutorial/"class="external-link"><em>Implementing functional languages: a tutorial</em><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. However, modern functional compilers (i.e. GHC) use a much more complicated abstract machine for evaluating graph-based code, based on &ndash; from what I know &ndash; the <a href="https://www.microsoft.com/en-us/research/wp-content/uploads/1992/04/spineless-tagless-gmachine.pdf"class="external-link">spineless tagless G-machine<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. In short, this section, in order to build intuition, walks through how a functional program evaluated using graph reduction <em>may</em> behave; the actual details depend on the compiler.</p> <p>Let&rsquo;s start with something that doesn&rsquo;t have anything fancy. We can take a look at the graph of the expression:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">length</span> <span class="p">[</span><span class="mi">1</span><span class="p">]</span> </span></span></code></pre></div><p>Stripping away Haskell&rsquo;s syntax sugar for lists, we can write this expression as:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">length</span> <span class="p">(</span><span class="mi">1</span><span class="kt">:[]</span><span class="p">)</span> </span></span></code></pre></div><p>Then, recalling that <code>(:)</code>, or &lsquo;cons&rsquo;, is just a binary function, we rewrite:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">length</span> <span class="p">((</span><span class="kt">:</span><span class="p">)</span> <span class="mi">1</span> <span class="kt">[]</span><span class="p">)</span> </span></span></code></pre></div><p>We&rsquo;re now ready to draw the graph; in this case, it&rsquo;s pretty much identical to the syntax tree of the last form of our expression:</p> <figure class="small"><img src="https://danilafe.com/blog/haskell_lazy_evaluation/length_1.png" alt="The initial graph of length [1]."><figcaption> <p>The initial graph of <code>length [1]</code>.</p> </figcaption> </figure> <p>In this image, the <code>@</code> nodes represent function application. The root node is an application of the function <code>length</code> to the graph that represents the list <code>[1]</code>. The list itself is represented using two application nodes: <code>(:)</code> takes two arguments, the head and tail of the list, and function applications in Haskell are <a href="https://en.wikipedia.org/wiki/Currying"class="external-link">curried<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. Eventually, in the process of evaluation, the body of <code>length</code> will be reached, and leave us with the following graph:</p> <figure class="small"><img src="https://danilafe.com/blog/haskell_lazy_evaluation/length_2.png" alt="The graph of length [1] after the body of length is expanded."><figcaption> <p>The graph of <code>length [1]</code> after the body of <code>length</code> is expanded.</p> </figcaption> </figure> <p>Conceptually, we only took one reduction step, and thus, we haven&rsquo;t yet gotten to evaluating the recursive call to <code>length</code>. Since <code>(+)</code> is also a binary function, <code>1+length xs</code> is represented in this new graph as two applications of <code>(+)</code>, first to <code>1</code>, and then to <code>length []</code>.</p> <p>But what is that box at the root? This box <em>used to be</em> the root of the first graph, which was an application node. However, it is now a an <em>indirection</em>. Conceptually, reducing this indirection amounts to reducing the graph it points to. But why have we <span class="sidenote"> <label class="sidenote-label" for="altered-note">altered the graph</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="altered-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> This is a key aspect of implementing functional languages. The language itself may be pure, while the runtime can be, and usually is, impure and stateful. After all, computers are impure and stateful, too! <span class="sidenote-delimiter">]</span> </span> </span> in this manner? Because Haskell is a pure language, of course! If we know that a particular graph reduces to some value, there&rsquo;s no reason to reduce it again. However, as we will soon see, it may be <em>used</em> again, so we want to preserve its value. Thus, when we&rsquo;re done reducing a graph, we replace its root node with an indirection that points to its result.</p> <p>When can a graph be used more than once? Well, how about this:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kr">let</span> <span class="n">x</span> <span class="ow">=</span> <span class="n">square</span> <span class="mi">5</span> <span class="kr">in</span> <span class="n">x</span> <span class="o">+</span> <span class="n">x</span> </span></span></code></pre></div><p>Here, the initial graph looks as follows:</p> <figure class="small"><img src="https://danilafe.com/blog/haskell_lazy_evaluation/square_1.png" alt="The initial graph of let x = square 5 in x &#43; x."><figcaption> <p>The initial graph of <code>let x = square 5 in x + x</code>.</p> </figcaption> </figure> <p>As you can see, this <em>is</em> a graph, but not a tree! Since both variables <code>x</code> refer to the same expression, <code>square 5</code>, they are represented by the same subgraph. Then, when we evaluate <code>square 5</code> for the first time, and replace its root node with an indirection, we end up with the following:</p> <figure class="small"><img src="https://danilafe.com/blog/haskell_lazy_evaluation/square_2.png" alt="The graph of let x = square 5 in x &#43; x after square 5 is reduced."><figcaption> <p>The graph of <code>let x = square 5 in x + x</code> after <code>square 5</code> is reduced.</p> </figcaption> </figure> <p>There are two <code>25</code>s in the graph, and no more <code>square</code>s! We only had to evaluate <code>square 5</code> exactly once, even though <code>(+)</code> will use it twice (once for the left argument, and once for the right).</p> <p>Our graphs can also include cycles. A simple, perhaps <em>the most</em> simple example of this in practice is Haskell&rsquo;s <code>fix</code> function. It computes a function&rsquo;s fixed point, <span class="sidenote"> <label class="sidenote-label" for="fixpoint-note">and can be used to write recursive functions.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="fixpoint-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> In fact, in the lambda calculus, <code>fix</code> is pretty much <em>the only</em> way to write recursive functions. In the untyped lambda calculus, it can be written as: $$\lambda f . (\lambda x . f (x \ x)) \ (\lambda x . f (x \ x))$$ In the simply typed lambda calculus, it cannot be written in any way, and needs to be added as an extension, typically written as \(\textbf{fix}\). <span class="sidenote-delimiter">]</span> </span> </span> It&rsquo;s implemented as follows:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">fix</span> <span class="n">f</span> <span class="ow">=</span> <span class="kr">let</span> <span class="n">x</span> <span class="ow">=</span> <span class="n">f</span> <span class="n">x</span> <span class="kr">in</span> <span class="n">x</span> </span></span></code></pre></div><p>See how the definition of <code>x</code> refers to itself? This is what it looks like in graph form:</p> <figure class="tiny"><img src="https://danilafe.com/blog/haskell_lazy_evaluation/fixpoint_1.png" alt="The initial graph of let x = f x in x."><figcaption> <p>The initial graph of <code>let x = f x in x</code>.</p> </figcaption> </figure> <p>I think it&rsquo;s useful to take a look at how this graph is processed. Let&rsquo;s pick <code>f = (1:)</code>. That is, <code>f</code> is a function that takes a list, and prepends <code>1</code> to it. Then, after constructing the graph of <code>f x</code>, we end up with the following:</p> <figure class="small"><img src="https://danilafe.com/blog/haskell_lazy_evaluation/fixpoint_2.png" alt="The graph of fix (1:) after it&rsquo;s been reduced."><figcaption> <p>The graph of <code>fix (1:)</code> after it&rsquo;s been reduced.</p> </figcaption> </figure> <p>We see the body of <code>f</code>, which is the application of <code>(:)</code> first to the constant <code>1</code>, and then to <code>f</code>&rsquo;s argument (<code>x</code>, in this case). As before, once we evaluated <code>f x</code>, we replaced the application with an indirection; in the image, this indirection is the top box. But the argument, <code>x</code>, is itself an indirection which points to the root of <code>f x</code>, thereby creating a cycle in our graph. Traversing this graph looks like traversing an infinite list of <code>1</code>s.</p> <p>Almost there! A node can refer to itself, and, when evaluated, it is replaced with its own value. Thus, a node can effectively reference its own value! The last piece of the puzzle is how a node can access <em>parts</em> of its own value: recall that <code>doRepMax</code> calls <code>repMax</code> with only <code>largest</code>, while <code>repMax</code> returns <code>(largest, xs')</code>. I have to admit, I don&rsquo;t know the internals of GHC, but I suspect this is done by translating the code into something like:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">doRepMax</span> <span class="ow">::</span> <span class="p">[</span><span class="kt">Int</span><span class="p">]</span> <span class="ow">-&gt;</span> <span class="p">[</span><span class="kt">Int</span><span class="p">]</span> </span></span><span class="line"><span class="cl"><span class="nf">doRepMax</span> <span class="n">xs</span> <span class="ow">=</span> <span class="n">snd</span> <span class="n">t</span> </span></span><span class="line"><span class="cl"> <span class="kr">where</span> <span class="n">t</span> <span class="ow">=</span> <span class="n">repMax</span> <span class="n">xs</span> <span class="p">(</span><span class="n">fst</span> <span class="n">t</span><span class="p">)</span> </span></span></code></pre></div><a href="#detailed-example-reducing-dorepmax"> <h4 id="detailed-example-reducing-dorepmax">Detailed Example: Reducing <code>doRepMax</code></h4> </a> <p>If the above examples haven&rsquo;t elucidated how <code>doRepMax</code> works, stick around in this section and we will go through it step-by-step. This is a rather long and detailed example, so feel free to skip this section to read more about actually using time traveling.</p> <p>If you&rsquo;re sticking around, why don&rsquo;t we watch how the graph of <code>doRepMax [1, 2]</code> unfolds. This example will be more complex than the ones we&rsquo;ve seen so far; to avoid overwhelming ourselves with notation, let&rsquo;s adopt a different convention of writing functions. Instead of using application nodes <code>@</code>, let&rsquo;s draw an application of a function <code>f</code> to arguments <code>x1</code> through <code>xn</code> as a subgraph with root <code>f</code> and children <code>x</code>s. The below figure demonstrates what I mean:</p> <figure class="small"><img src="https://danilafe.com/blog/haskell_lazy_evaluation/notation.png" alt="The new visual notation used in this section."><figcaption> <p>The new visual notation used in this section.</p> </figcaption> </figure> <p>Now, let&rsquo;s write the initial graph for <code>doRepMax [1,2]</code>:</p> <figure class="small"><img src="https://danilafe.com/blog/haskell_lazy_evaluation/repmax_1.png" alt="The initial graph of doRepMax [1,2]."><figcaption> <p>The initial graph of <code>doRepMax [1,2]</code>.</p> </figcaption> </figure> <p>Other than our new notation, there&rsquo;s nothing too surprising here. The first step of our hypothetical reduction would replace the application of <code>doRepMax</code> with its body, and create our graph&rsquo;s first cycle. At a high level, all we want is the second element of the tuple returned by <code>repMax</code>, which contains the output list. To get the tuple, we apply <code>repMax</code> to the list <code>[1,2]</code> and the first element of its result. The list <code>[1,2]</code> itself consists of two uses of the <code>(:)</code> function.</p> <figure class="small"><img src="https://danilafe.com/blog/haskell_lazy_evaluation/repmax_2.png" alt="The first step of reducing doRepMax [1,2]."><figcaption> <p>The first step of reducing <code>doRepMax [1,2]</code>.</p> </figcaption> </figure> <p>Next, we would also expand the body of <code>repMax</code>. In the following diagram, to avoid drawing a noisy amount of crossing lines, I marked the application of <code>fst</code> with a star, and replaced the two edges to <code>fst</code> with edges to similar looking stars. This is merely a visual trick; an edge leading to a little star is actually an edge leading to <code>fst</code>. Take a look:</p> <figure class="medium"><img src="https://danilafe.com/blog/haskell_lazy_evaluation/repmax_3.png" alt="The second step of reducing doRepMax [1,2]."><figcaption> <p>The second step of reducing <code>doRepMax [1,2]</code>.</p> </figcaption> </figure> <p>Since <code>(,)</code> is a constructor, let&rsquo;s say that it doesn&rsquo;t need to be evaluated, and that its <span class="sidenote"> <label class="sidenote-label" for="normal-note">graph cannot be reduced further</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="normal-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> A graph that can't be reduced further is said to be in <em>normal form</em>, by the way. <span class="sidenote-delimiter">]</span> </span> </span> (in practice, other things like packing may occur here, but they are irrelevant to us). If <code>(,)</code> can&rsquo;t be reduced, we can move on to evaluating <code>snd</code>. Given a pair, <code>snd</code> simply returns the second element, which in our case is the subgraph starting at <code>(:)</code>. We thus replace the application of <code>snd</code> with an indirection to this subgraph. This leaves us with the following:</p> <figure class="medium"><img src="https://danilafe.com/blog/haskell_lazy_evaluation/repmax_4.png" alt="The third step of reducing doRepMax [1,2]."><figcaption> <p>The third step of reducing <code>doRepMax [1,2]</code>.</p> </figcaption> </figure> <p>Since it&rsquo;s becoming hard to keep track of what part of the graph is actually being evaluated, I marked the former root of <code>doRepMax [1,2]</code> with a blue star. If our original expression occured at the top level, the graph reduction would probably stop here. After all, we&rsquo;re evaluating our graphs using call-by-need, and there doesn&rsquo;t seem to be a need for knowing the what the arguments of <code>(:)</code> are. However, stopping at <code>(:)</code> wouldn&rsquo;t be very interesting, and we wouldn&rsquo;t learn much from doing so. So instead, let&rsquo;s assume that <em>something</em> is trying to read the elements of our list; perhaps we are trying to print this list to the screen in GHCi.</p> <p>In this case, our mysterious external force starts unpacking and inspecting the arguments to <code>(:)</code>. The first argument to <code>(:)</code> is the list&rsquo;s head, which is the subgraph starting with the starred application of <code>fst</code>. We evaluate it in a similar manner to <code>snd</code>. That is, we replace this <code>fst</code> with an indirection to the first element of the argument tuple, which happens to be the subgraph starting with <code>max</code>:</p> <figure class="medium"><img src="https://danilafe.com/blog/haskell_lazy_evaluation/repmax_5.png" alt="The fourth step of reducing doRepMax [1,2]."><figcaption> <p>The fourth step of reducing <code>doRepMax [1,2]</code>.</p> </figcaption> </figure> <p>Phew! Next, we need to evaluate the body of <code>max</code>. Let&rsquo;s make one more simplification here: rather than substitututing <code>max</code> for its body here, let&rsquo;s just reason about what evaluating <code>max</code> would entail. We would need to evaluate its two arguments, compare them, and return the larger one. The argument <code>1</code> can&rsquo;t be reduced any more (it&rsquo;s just a number!), but the second argument, a call to <code>fst</code>, needs to be processed. To do so, we need to evaluate the call to <code>repMax</code>. We thus replace <code>repMax</code> with its body:</p> <figure class="medium"><img src="https://danilafe.com/blog/haskell_lazy_evaluation/repmax_6.png" alt="The fifth step of reducing doRepMax [1,2]."><figcaption> <p>The fifth step of reducing <code>doRepMax [1,2]</code>.</p> </figcaption> </figure> <p>We&rsquo;ve reached one of the base cases here, and there are no more calls to <code>max</code> or <code>repMax</code>. The whole reason we&rsquo;re here is to evaluate the call to <code>fst</code> that&rsquo;s one of the arguments to <code>max</code>. Given this graph, doing so is easy. We can clearly see that <code>2</code> is the first element of the tuple returned by <code>repMax [2]</code>. We thus replace <code>fst</code> with an indirection to this node:</p> <figure class="medium"><img src="https://danilafe.com/blog/haskell_lazy_evaluation/repmax_7.png" alt="The sixth step of reducing doRepMax [1,2]."><figcaption> <p>The sixth step of reducing <code>doRepMax [1,2]</code>.</p> </figcaption> </figure> <p>This concludes our task of evaluating the arguments to <code>max</code>. Comparing them, we see that <code>2</code> is greater than <code>1</code>, and thus, we replace <code>max</code> with an indirection to <code>2</code>:</p> <figure class="medium"><img src="https://danilafe.com/blog/haskell_lazy_evaluation/repmax_8.png" alt="The seventh step of reducing doRepMax [1,2]."><figcaption> <p>The seventh step of reducing <code>doRepMax [1,2]</code>.</p> </figcaption> </figure> <p>The node that we starred in our graph is now an indirection (the one that used to be the call to <code>fst</code>) which points to another indirection (formerly the call to <code>max</code>), which points to <code>2</code>. Thus, any edge pointing to a star now points to the value 2.</p> <p>By finding the value of the starred node, we have found the first argument of <code>(:)</code>, and returned it to our mysterious external force. If we were printing to GHCi, the number <code>2</code> would appear on the screen right about now. The force then moves on to the second argument of <code>(:)</code>, which is the call to <code>snd</code>. This <code>snd</code> is applied to an instance of <code>(,)</code>, which can&rsquo;t be reduced any further. Thus, all we have to do is take the second element of the tuple, and replace <code>snd</code> with an indirection to it:</p> <figure class="medium"><img src="https://danilafe.com/blog/haskell_lazy_evaluation/repmax_9.png" alt="The eighth step of reducing doRepMax [1,2]."><figcaption> <p>The eighth step of reducing <code>doRepMax [1,2]</code>.</p> </figcaption> </figure> <p>The second element of the tuple was a call to <code>(:)</code>, and that&rsquo;s what the mysterious force is processing now. Just like it did before, it starts by looking at the first argument of this list, which is the list&rsquo;s head. This argument is a reference to the starred node, which, as we&rsquo;ve established, eventually points to <code>2</code>. Another <code>2</code> pops up on the console.</p> <p>Finally, the mysterious force reaches the second argument of the <code>(:)</code>, which is the empty list. The empty list also cannot be evaluated any further, so that&rsquo;s what the mysterious force receives. Just like that, there&rsquo;s nothing left to print to the console. The mysterious force ceases. After removing the unused nodes, we are left with the following graph:</p> <figure class="small"><img src="https://danilafe.com/blog/haskell_lazy_evaluation/repmax_10.png" alt="The result of reducing doRepMax [1,2]."><figcaption> <p>The result of reducing <code>doRepMax [1,2]</code>.</p> </figcaption> </figure> <p>As we would have expected, two <code>2</code>s were printed to the console, and our final graph represents the list <code>[2,2]</code>.</p> <a href="#using-time-traveling"> <h3 id="using-time-traveling">Using Time Traveling</h3> </a> <p>Is time tarveling even useful? I would argue yes, especially in cases where Haskell&rsquo;s purity can make certain things difficult.</p> <p>As a first example, Csongor provides an assembler that works in a single pass. The challenge in this case is to resolve jumps to code segments occuring <em>after</em> the jump itself; in essence, the address of the target code segment needs to be known before the segment itself is processed. Csongor&rsquo;s code uses the <a href="https://hackage.haskell.org/package/tardis-0.4.1.0/docs/Control-Monad-Tardis.html"class="external-link">Tardis monad<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, which combines regular state, to which you can write and then later read from, and future state, from which you can read values before your write them. Check out <a href="https://kcsongor.github.io/time-travel-in-haskell-for-dummies/#a-single-pass-assembler-an-example"class="external-link">his complete example<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> here.</p> <p>Alternatively, here&rsquo;s an example from my research, which my coworker and coauthor Kai helped me formulate. I&rsquo;ll be fairly vague, since all of this is still in progress. The gist is that we have some kind of data structure (say, a list or a tree), and we want to associate with each element in this data structure a &lsquo;score&rsquo; of how useful it is. There are many possible heuristics of picking &lsquo;scores&rsquo;; a very simple one is to make it inversely propertional to the number of times an element occurs. To be more concrete, suppose we have some element type <code>Element</code>:</p> <div class="highlight-group" data-base-path="" data-file-path="time-traveling/ValueScore.hs" data-first-line="5" data-last-line="6"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/time-traveling/ValueScore.hs#L5-L6">ValueScore.hs</a>, lines 5 through 6</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">5 </span><span class="lnt">6 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kr">data</span> <span class="kt">Element</span> <span class="ow">=</span> <span class="kt">A</span> <span class="o">|</span> <span class="kt">B</span> <span class="o">|</span> <span class="kt">C</span> <span class="o">|</span> <span class="kt">D</span> </span></span><span class="line"><span class="cl"> <span class="kr">deriving</span> <span class="p">(</span><span class="kt">Eq</span><span class="p">,</span> <span class="kt">Ord</span><span class="p">,</span> <span class="kt">Show</span><span class="p">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Suppose also that our data structure is a binary tree:</p> <div class="highlight-group" data-base-path="" data-file-path="time-traveling/ValueScore.hs" data-first-line="14" data-last-line="16"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/time-traveling/ValueScore.hs#L14-L16">ValueScore.hs</a>, lines 14 through 16</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kr">data</span> <span class="kt">BinaryTree</span> <span class="n">a</span> <span class="ow">=</span> <span class="kt">Empty</span> <span class="o">|</span> <span class="kt">Node</span> <span class="n">a</span> <span class="p">(</span><span class="kt">BinaryTree</span> <span class="n">a</span><span class="p">)</span> <span class="p">(</span><span class="kt">BinaryTree</span> <span class="n">a</span><span class="p">)</span> <span class="kr">deriving</span> <span class="kt">Show</span> </span></span><span class="line"><span class="cl"><span class="kr">type</span> <span class="kt">ElementTree</span> <span class="ow">=</span> <span class="kt">BinaryTree</span> <span class="kt">Element</span> </span></span><span class="line"><span class="cl"><span class="kr">type</span> <span class="kt">ScoredElementTree</span> <span class="ow">=</span> <span class="kt">BinaryTree</span> <span class="p">(</span><span class="kt">Element</span><span class="p">,</span> <span class="kt">Float</span><span class="p">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We then want to transform an input <code>ElementTree</code>, such as:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kt">Node</span> <span class="kt">A</span> <span class="p">(</span><span class="kt">Node</span> <span class="kt">A</span> <span class="kt">Empty</span> <span class="kt">Empty</span><span class="p">)</span> <span class="kt">Empty</span> </span></span></code></pre></div><p>Into a scored tree, like:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kt">Node</span> <span class="p">(</span><span class="kt">A</span><span class="p">,</span><span class="mf">0.5</span><span class="p">)</span> <span class="p">(</span><span class="kt">Node</span> <span class="p">(</span><span class="kt">A</span><span class="p">,</span><span class="mf">0.5</span><span class="p">)</span> <span class="kt">Empty</span> <span class="kt">Empty</span><span class="p">)</span> <span class="kt">Empty</span> </span></span></code></pre></div><p>Since <code>A</code> occured twice, its score is <code>1/2 = 0.5</code>.</p> <p>Let&rsquo;s define some utility functions before we get to the meat of the implementation:</p> <div class="highlight-group" data-base-path="" data-file-path="time-traveling/ValueScore.hs" data-first-line="8" data-last-line="12"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/time-traveling/ValueScore.hs#L8-L12">ValueScore.hs</a>, lines 8 through 12</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">addElement</span> <span class="ow">::</span> <span class="kt">Element</span> <span class="ow">-&gt;</span> <span class="kt">Map</span> <span class="kt">Element</span> <span class="kt">Int</span> <span class="ow">-&gt;</span> <span class="kt">Map</span> <span class="kt">Element</span> <span class="kt">Int</span> </span></span><span class="line"><span class="cl"><span class="nf">addElement</span> <span class="ow">=</span> <span class="n">alter</span> <span class="p">((</span><span class="o">&lt;|&gt;</span> <span class="kt">Just</span> <span class="mi">1</span><span class="p">)</span> <span class="o">.</span> <span class="n">fmap</span> <span class="p">(</span><span class="o">+</span><span class="mi">1</span><span class="p">))</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="nf">getScore</span> <span class="ow">::</span> <span class="kt">Element</span> <span class="ow">-&gt;</span> <span class="kt">Map</span> <span class="kt">Element</span> <span class="kt">Int</span> <span class="ow">-&gt;</span> <span class="kt">Float</span> </span></span><span class="line"><span class="cl"><span class="nf">getScore</span> <span class="n">e</span> <span class="n">m</span> <span class="ow">=</span> <span class="n">fromMaybe</span> <span class="mf">1.0</span> <span class="o">$</span> <span class="p">((</span><span class="mf">1.0</span><span class="o">/</span><span class="p">)</span> <span class="o">.</span> <span class="n">fromIntegral</span><span class="p">)</span> <span class="o">&lt;$&gt;</span> <span class="kt">Map</span><span class="o">.</span><span class="n">lookup</span> <span class="n">e</span> <span class="n">m</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The <code>addElement</code> function simply increments the counter for a particular element in the map, adding the number <code>1</code> if it doesn&rsquo;t exist. The <code>getScore</code> function computes the score of a particular element, defaulting to <code>1.0</code> if it&rsquo;s not found in the map.</p> <p>Just as before &ndash; noticing that passing around the future values is getting awfully bothersome &ndash; we write our scoring function as though we have a &lsquo;future value&rsquo;.</p> <div class="highlight-group" data-base-path="" data-file-path="time-traveling/ValueScore.hs" data-first-line="18" data-last-line="24"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/time-traveling/ValueScore.hs#L18-L24">ValueScore.hs</a>, lines 18 through 24</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">assignScores</span> <span class="ow">::</span> <span class="kt">ElementTree</span> <span class="ow">-&gt;</span> <span class="kt">Map</span> <span class="kt">Element</span> <span class="kt">Int</span> <span class="ow">-&gt;</span> <span class="p">(</span><span class="kt">Map</span> <span class="kt">Element</span> <span class="kt">Int</span><span class="p">,</span> <span class="kt">ScoredElementTree</span><span class="p">)</span> </span></span><span class="line"><span class="cl"><span class="nf">assignScores</span> <span class="kt">Empty</span> <span class="n">m</span> <span class="ow">=</span> <span class="p">(</span><span class="kt">Map</span><span class="o">.</span><span class="n">empty</span><span class="p">,</span> <span class="kt">Empty</span><span class="p">)</span> </span></span><span class="line"><span class="cl"><span class="nf">assignScores</span> <span class="p">(</span><span class="kt">Node</span> <span class="n">e</span> <span class="n">t1</span> <span class="n">t2</span><span class="p">)</span> <span class="n">m</span> <span class="ow">=</span> <span class="p">(</span><span class="n">m&#39;</span><span class="p">,</span> <span class="kt">Node</span> <span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="n">getScore</span> <span class="n">e</span> <span class="n">m</span><span class="p">)</span> <span class="n">t1&#39;</span> <span class="n">t2&#39;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="kr">where</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">m1</span><span class="p">,</span> <span class="n">t1&#39;</span><span class="p">)</span> <span class="ow">=</span> <span class="n">assignScores</span> <span class="n">t1</span> <span class="n">m</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">m2</span><span class="p">,</span> <span class="n">t2&#39;</span><span class="p">)</span> <span class="ow">=</span> <span class="n">assignScores</span> <span class="n">t2</span> <span class="n">m</span> </span></span><span class="line"><span class="cl"> <span class="n">m&#39;</span> <span class="ow">=</span> <span class="n">addElement</span> <span class="n">e</span> <span class="o">$</span> <span class="n">unionWith</span> <span class="p">(</span><span class="o">+</span><span class="p">)</span> <span class="n">m1</span> <span class="n">m2</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The actual <code>doAssignScores</code> function is pretty much identical to <code>doRepMax</code>:</p> <div class="highlight-group" data-base-path="" data-file-path="time-traveling/ValueScore.hs" data-first-line="26" data-last-line="28"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/time-traveling/ValueScore.hs#L26-L28">ValueScore.hs</a>, lines 26 through 28</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">doAssignScores</span> <span class="ow">::</span> <span class="kt">ElementTree</span> <span class="ow">-&gt;</span> <span class="kt">ScoredElementTree</span> </span></span><span class="line"><span class="cl"><span class="nf">doAssignScores</span> <span class="n">t</span> <span class="ow">=</span> <span class="n">t&#39;</span> </span></span><span class="line"><span class="cl"> <span class="kr">where</span> <span class="p">(</span><span class="n">m</span><span class="p">,</span> <span class="n">t&#39;</span><span class="p">)</span> <span class="ow">=</span> <span class="n">assignScores</span> <span class="n">t</span> <span class="n">m</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>There&rsquo;s quite a bit of repetition here, especially in the handling of future values - all of our functions now accept an extra future argument, and return a work-in-progress future value. This is what the <code>Tardis</code> monad, and its corresponding <code>TardisT</code> monad transformer, aim to address. Just like the <code>State</code> monad helps us avoid writing plumbing code for forward-traveling values, <code>Tardis</code> helps us do the same for backward-traveling ones.</p> <a href="#cycles-in-monadic-bind"> <h4 id="cycles-in-monadic-bind">Cycles in Monadic Bind</h4> </a> <p>We&rsquo;ve seen that we&rsquo;re able to write code like the following:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span> <span class="ow">=</span> <span class="n">f</span> <span class="n">a</span> <span class="n">c</span> </span></span></code></pre></div><p>That is, we were able to write function calls that referenced their own return values. What if we try doing this inside a <code>do</code> block? Say, for example, we want to sprinkle some time traveling into our program, but don&rsquo;t want to add a whole new transformer into our monad stack. We could write code as follows:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kr">do</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span> <span class="ow">&lt;-</span> <span class="n">f</span> <span class="n">a</span> <span class="n">c</span> </span></span><span class="line"><span class="cl"> <span class="n">return</span> <span class="n">b</span> </span></span></code></pre></div><p>Unfortunately, this doesn&rsquo;t work. However, it&rsquo;s entirely possible to enable this using the <code>RecursiveDo</code> language extension:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="cm">{-# LANGUAGE RecursiveDo #-}</span> </span></span></code></pre></div><p>Then, we can write the above as follows:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kr">do</span> </span></span><span class="line"><span class="cl"> <span class="n">rec</span> <span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span> <span class="ow">&lt;-</span> <span class="n">f</span> <span class="n">a</span> <span class="n">c</span> </span></span><span class="line"><span class="cl"> <span class="n">return</span> <span class="n">b</span> </span></span></code></pre></div><p>This power, however, comes at a price. It&rsquo;s not as straightforward to build graphs from recursive monadic computations; in fact, it&rsquo;s not possible in general. The translation of the above code uses <code>MonadFix</code>. A monad that satisfies <code>MonadFix</code> has an operation <code>mfix</code>, which is the monadic version of the <code>fix</code> function we saw earlier:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">mfix</span> <span class="ow">::</span> <span class="kt">Monad</span> <span class="n">m</span> <span class="ow">=&gt;</span> <span class="p">(</span><span class="n">a</span> <span class="ow">-&gt;</span> <span class="n">m</span> <span class="n">a</span><span class="p">)</span> <span class="ow">-&gt;</span> <span class="n">m</span> <span class="n">a</span> </span></span><span class="line"><span class="cl"><span class="c1">-- Regular fix, for comparison</span> </span></span><span class="line"><span class="cl"><span class="nf">fix</span> <span class="ow">::</span> <span class="p">(</span><span class="n">a</span> <span class="ow">-&gt;</span> <span class="n">a</span><span class="p">)</span> <span class="ow">-&gt;</span> <span class="n">a</span> </span></span></code></pre></div><p>To really understand how the translation works, check out the <a href="http://leventerkok.github.io/papers/recdo.pdf"class="external-link">paper on recursive do notation<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>.</p> <a href="#beware-the-strictness"> <h3 id="beware-the-strictness">Beware The Strictness</h3> </a> <p>Though Csongor points out other problems with the time traveling approach, I think he doesn&rsquo;t mention an important idea: you have to be <em>very</em> careful about introducing strictness into your programs when running time-traveling code. For example, suppose we wanted to write a function, <code>takeUntilMax</code>, which would return the input list, cut off after the first occurence of the maximum number. Following the same strategy, we come up with:</p> <div class="highlight-group" data-base-path="" data-file-path="time-traveling/TakeMax.hs" data-first-line="1" data-last-line="12"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/time-traveling/TakeMax.hs#L1-L12">TakeMax.hs</a>, lines 1 through 12</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">takeUntilMax</span> <span class="ow">::</span> <span class="p">[</span><span class="kt">Int</span><span class="p">]</span> <span class="ow">-&gt;</span> <span class="kt">Int</span> <span class="ow">-&gt;</span> <span class="p">(</span><span class="kt">Int</span><span class="p">,</span> <span class="p">[</span><span class="kt">Int</span><span class="p">])</span> </span></span><span class="line"><span class="cl"><span class="nf">takeUntilMax</span> <span class="kt">[]</span> <span class="n">m</span> <span class="ow">=</span> <span class="p">(</span><span class="n">m</span><span class="p">,</span> <span class="kt">[]</span><span class="p">)</span> </span></span><span class="line"><span class="cl"><span class="nf">takeUntilMax</span> <span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="kr">_</span> <span class="ow">=</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="p">[</span><span class="n">x</span><span class="p">])</span> </span></span><span class="line"><span class="cl"><span class="nf">takeUntilMax</span> <span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="n">xs</span><span class="p">)</span> <span class="n">m</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">x</span> <span class="o">==</span> <span class="n">m</span> <span class="ow">=</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="p">[</span><span class="n">x</span><span class="p">])</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">otherwise</span> <span class="ow">=</span> </span></span><span class="line"><span class="cl"> <span class="kr">let</span> <span class="p">(</span><span class="n">m&#39;</span><span class="p">,</span> <span class="n">xs&#39;</span><span class="p">)</span> <span class="ow">=</span> <span class="n">takeUntilMax</span> <span class="n">xs</span> <span class="n">m</span> </span></span><span class="line"><span class="cl"> <span class="kr">in</span> <span class="p">(</span><span class="n">max</span> <span class="n">m&#39;</span> <span class="n">x</span><span class="p">,</span> <span class="n">x</span><span class="kt">:</span><span class="n">xs&#39;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="nf">doTakeUntilMax</span> <span class="ow">::</span> <span class="p">[</span><span class="kt">Int</span><span class="p">]</span> <span class="ow">-&gt;</span> <span class="p">[</span><span class="kt">Int</span><span class="p">]</span> </span></span><span class="line"><span class="cl"><span class="nf">doTakeUntilMax</span> <span class="n">l</span> <span class="ow">=</span> <span class="n">l&#39;</span> </span></span><span class="line"><span class="cl"> <span class="kr">where</span> <span class="p">(</span><span class="n">m</span><span class="p">,</span> <span class="n">l&#39;</span><span class="p">)</span> <span class="ow">=</span> <span class="n">takeUntilMax</span> <span class="n">l</span> <span class="n">m</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>In short, if we encounter our maximum number, we just return a list of that maximum number, since we do not want to recurse further. On the other hand, if we encounter a number that&rsquo;s <em>not</em> the maximum, we continue our recursion.</p> <p>Unfortunately, this doesn&rsquo;t work; our program never terminates. You may be thinking:</p> <blockquote> <p>Well, obviously this doesn&rsquo;t work! We didn&rsquo;t actually compute the maximum number properly, since we stopped recursing too early. We need to traverse the whole list, and not just the part before the maximum number.</p> </blockquote> <p>To address this, we can reformulate our <code>takeUntilMax</code> function as follows:</p> <div class="highlight-group" data-base-path="" data-file-path="time-traveling/TakeMax.hs" data-first-line="14" data-last-line="21"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/time-traveling/TakeMax.hs#L14-L21">TakeMax.hs</a>, lines 14 through 21</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">takeUntilMax&#39;</span> <span class="ow">::</span> <span class="p">[</span><span class="kt">Int</span><span class="p">]</span> <span class="ow">-&gt;</span> <span class="kt">Int</span> <span class="ow">-&gt;</span> <span class="p">(</span><span class="kt">Int</span><span class="p">,</span> <span class="p">[</span><span class="kt">Int</span><span class="p">])</span> </span></span><span class="line"><span class="cl"><span class="nf">takeUntilMax&#39;</span> <span class="kt">[]</span> <span class="n">m</span> <span class="ow">=</span> <span class="p">(</span><span class="n">m</span><span class="p">,</span> <span class="kt">[]</span><span class="p">)</span> </span></span><span class="line"><span class="cl"><span class="nf">takeUntilMax&#39;</span> <span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="kr">_</span> <span class="ow">=</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="p">[</span><span class="n">x</span><span class="p">])</span> </span></span><span class="line"><span class="cl"><span class="nf">takeUntilMax&#39;</span> <span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="n">xs</span><span class="p">)</span> <span class="n">m</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">x</span> <span class="o">==</span> <span class="n">m</span> <span class="ow">=</span> <span class="p">(</span><span class="n">maximum</span> <span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="n">xs</span><span class="p">),</span> <span class="p">[</span><span class="n">x</span><span class="p">])</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">otherwise</span> <span class="ow">=</span> </span></span><span class="line"><span class="cl"> <span class="kr">let</span> <span class="p">(</span><span class="n">m&#39;</span><span class="p">,</span> <span class="n">xs&#39;</span><span class="p">)</span> <span class="ow">=</span> <span class="n">takeUntilMax&#39;</span> <span class="n">xs</span> <span class="n">m</span> </span></span><span class="line"><span class="cl"> <span class="kr">in</span> <span class="p">(</span><span class="n">max</span> <span class="n">m&#39;</span> <span class="n">x</span><span class="p">,</span> <span class="n">x</span><span class="kt">:</span><span class="n">xs&#39;</span><span class="p">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Now we definitely compute the maximum correctly! Alas, this doesn&rsquo;t work either. The issue lies on lines 5 and 18, more specifically in the comparison <code>x == m</code>. Here, we are trying to base the decision of what branch to take on a future value. This is simply impossible; to compute the value, we need to know the value!</p> <p>This is no &lsquo;silly mistake&rsquo;, either! In complicated programs that use time traveling, strictness lurks behind every corner. In my research work, I was at one point inserting a data structure into a set; however, deep in the structure was a data type containing a &lsquo;future&rsquo; value, and using the default <code>Eq</code> instance! Adding the data structure to a set ended up invoking <code>(==)</code> (or perhaps some function from the <code>Ord</code> typeclass), which, in turn, tried to compare the lazily evaluated values. My code therefore didn&rsquo;t terminate, much like <code>takeUntilMax</code>.</p> <p>Debugging time traveling code is, in general, a pain. This is especially true since future values don&rsquo;t look any different from regular values. You can see it in the type signatures of <code>repMax</code> and <code>takeUntilMax</code>: the maximum number is just an <code>Int</code>! And yet, trying to see what its value is will kill the entire program. As always, remember Brian W. Kernighan&rsquo;s wise words:</p> <blockquote> <p>Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.</p> </blockquote> <a href="#conclusion"> <h3 id="conclusion">Conclusion</h3> </a> <p>This is about it! In a way, time traveling can make code performing certain operations more expressive. Furthermore, even if it&rsquo;s not groundbreaking, thinking about time traveling is a good exercise to get familiar with lazy evaluation in general. I hope you found this useful!</p> DELL Is A Horrible Company And You Should Avoid Them At All Costs https://danilafe.com/blog/dell_is_horrible/ Thu, 23 Jul 2020 13:40:05 -0700 https://danilafe.com/blog/dell_is_horrible/ <p>I really do not want this to be a consumer electronics blog. Such things aren&rsquo;t interesting to me, and nor do I have much knowledge about them. However, sometimes, ripples from these areas make their way into my life, and this is one such instance. Let me tell you <span class="sidenote"> <label class="sidenote-label" for="source-note">a story</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="source-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> I originally wrote about this in <a href="https://www.dell.com/community/XPS/Ridiculously-Bad-Support-Experience/td-p/7554383">a thread on DELL's support website</a>. Some of this post is going to be adapted from the support website, but some things have happened since. You will probably notice the change between the terse language I used in the original post and the fresh text that I'm writing now. <span class="sidenote-delimiter">]</span> </span> </span> of my experience with DELL and their XPS 2-in-1 laptop, which has gone on since around January of 2020, and is still going at the time of writing, in July 2020, half a year later.</p> <p>I was, until recently, an undergraduate student in Computer Science. I will soon be starting my Masters in Computer Science, too. I say this to make one thing clear: I need a computer. Not only is it a necessity for my major, but the majority of my hobbies &ndash; including this blog &ndash; are digital, too. Since my university is a couple of hours from my home, I travel back and forth a lot. I also have a cozy little spot in the <span class="sidenote"> <label class="sidenote-label" for="offices-note">graduate student offices</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="offices-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> They're a bunch of cubicles in a keycard-protected room, really. Nothing fancy. <span class="sidenote-delimiter">]</span> </span> </span> at my university, but travel by bus, so I find myself spending roughly equal portions of my work time at home and &rsquo;elsewhere&rsquo;. A laptop as my primary machine, I thought, made sense. But it had to be a decent one. Persuaded by one of my instructors, who stressed the importance of vision and a decent screen, I settled on a DELL XPS, which at the time came with a 4k display.</p> <p>As is commonplace, things went great at first. The screen <em>was</em> really nice, all of my code compiled swiftly, and even the games I occasionally played ran at a solid 60fps. I was happy with my purchase.</p> <p>There was one hiccup before things went really downhill, a sort of foreshadowing of things to come. My trackpad didn&rsquo;t work at peculiar times.</p> <a href="#prologue-trackpad-hiccups"> <h3 id="prologue-trackpad-hiccups">Prologue: Trackpad Hiccups</h3> </a> <p>While working, booted into Linux, I noticed that my trackpad was having some trouble. It was stuttering, and occasionally wouldn&rsquo;t work at all for seconds at a time. I assumed that this was a problem with the trackpad drivers on Linux, or perhaps the whole system was freezing up. I rebooted, and the problem went away.</p> <p>Until it came back.</p> <p>A few days later, my trackpad was freezing virtually every minute. It was strange, but fortunately, I&rsquo;m used to a keyboard-based workflow, and the malfunctions did not affect me too much. It was just a little troubling. What soon made it more troubling, was that I noticed this exact same issue occurring on Windows. To me, this meant one dreadful thing: it was a hardware issue.</p> <p>I poked and prodded for a little bit, and finally discovered the cause: whenever I put my hand on the left palmrest, the trackpad would reliably stop working. Knowing what the issue was, I called DELL. I spoke to a guy on the other end, who had me run through diagnostics, driver updates, and BIOS settings (I imagined this was procedure, so I didn&rsquo;t mind doing the extra work to make the other guy&rsquo;s job easier). Finally, he scheduled a repair appointment. A technician came into my house, took off the laptop cover, and said something along the lines of:</p> <blockquote> <p>Now look. They gave me a whole new motherboard and case to replace yours, but in my personal opinion, this is a bad idea. Things are bound to break when you do this. See how the replacement case has an insulating piece of fabric under the left palmrest, and yours doesn&rsquo;t? Why don&rsquo;t we rip the fabric off the replacement case, and tape it in place on your machine, without any reassembly?</p> </blockquote> <p>This man was wiser than any of the other DELL technicians, I now understand. The repair went without a hitch. He grilled me for going to college instead of just picking up a trade, which was cheaper and offered more job security. In the end, I felt a little weird about having a piece of fabric duct taped inside my computer, but the trackpad had no more issues ever since. All was well.</p> <a href="#service-request-1-broken-d-key"> <h3 id="service-request-1-broken-d-key">Service Request 1: Broken D Key</h3> </a> <p>All was well, that is, until the middle of winter term. I was typing up an assignment for a university class. I was working as usual, when I suddenly noticed that the &ldquo;d&rdquo; key stopped working - it had to be pressed rather weird to register on the computer. I looked down, and discovered that the key had snapped in half. The top part of the key fell off shortly thereafter.</p> <figure><img src="https://danilafe.com/blog/dell_is_horrible/brokenkey.jpg" alt="The broken D key shortly after the above events."><figcaption> <p>The broken D key shortly after the above events.</p> </figcaption> </figure> <p>At that point, I was more surprised than anything. I hadn&rsquo;t heard of something like this ever happening, especially under circumstances as normal as typing. Regardless, I contacted support, and set up a repair appointment. Things only went downhill from there.</p> <p>Again, the appointment was scheduled, and only a few days later, another technician arrived at my house. The only way to repair the key, he said, was to replace the whole keyboard. They keyboard happens to be located underneath all the other hardware, and so, the entire laptop had to be disassembled and reassembled from scratch. He worked for about an hour, and eventually, he put the machine together. The words of the previous technician, who wanted to avoid doing exactly what had just been done, echoed in my head:</p> <blockquote> <p>Things are bound to break when you do this.</p> </blockquote> <p>I asked him to test it, just to make sure everything works. Sure enough, not everything did work: the machine no longer had sound!</p> <a href="#service-request-2-no-sound"> <h3 id="service-request-2-no-sound">Service Request 2: No sound</h3> </a> <p>During diagnostics, the laptop did not emit the &ldquo;beep&rdquo; it usually does. This was the first sign. Booting into Windows, the sound icon was crossed out in red, and no sound was present. Booting into Linux led to similar results. The microphone on the machine did not seem to work either. The service technician said that he didn&rsquo;t have the parts to repair it, told me he&rsquo;d call it in, and left. Soon after, I got an email asking for times I&rsquo;m available to call: I said &ldquo;any time except for 1-4 pacific time&rdquo;. DELL support proceeded to call me at 3pm pacific time, when I had no service. Unable to reach me, they promptly notified me that they are archiving my service request.</p> <p>This all occurred near finals week at my university, so I had to put the issue on hold. I had to maintain my grades, and I had to grade heaps of assignments from other students. Though the lack of sound was annoying, it wasn&rsquo;t as pressing as preparing for exams, so it was during spring break that I finally called again, and scheduled the service appointment. By then, <span class="sidenote"> <label class="sidenote-label" for="pandemic-note">the pandemic was in full swing,</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="pandemic-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Just for posterity, in 2020, there had been an outbreak of COVID-19, a Coronavirus. Many states in the U.S., including my own, issued the orders for lockdown and social distancing, which meant the closing of schools, restaurants, and, apparently, the cessation of in-person repairs. <span class="sidenote-delimiter">]</span> </span> </span> and DELL told me they&rsquo;d mail me a box to put my laptop in, and I&rsquo;d have to mail it off to their service center. Sure, I thought, that&rsquo;s fine. If it&rsquo;s at the service center, they won&rsquo;t ever &ldquo;not have the required parts&rdquo;. I told the tech support person my address, he read it back to me, and so it was settled.</p> <p>Until, that is, the box arrived at the wrong address.</p> <p>I had received the machine as a gift from my family, who purchased the computer to arrive at their address. The box arrived at that address too, despite my explicit instructions to have it deliver to my current residence. Since my family and I live 2 hours apart, it took 4 total hours to get the box to me (a drive that couldn&rsquo;t be made right away!), and by the time I had it, DELL was already threatening me again with closing the service request. Eventually, I was able to mail the machine off, and about 5 business days later (business days during which I did not have a working machine, which is very necessary for my school and job) I received it back. I was excited to have the machine back, but that didn&rsquo;t last very long. As I was using the computer with Wolfram Mathematica (a rather heavy piece of software running under Linux), I noticed that it was discharging even while plugged in. I booted into Windows, and was greeted with a warning, something along the lines of: &ldquo;you are using a slow charger. Please use the official adapter&rdquo;. But I was using the official adapter! I also tried to plug my mouse into the relevant USB-C port, only to discover that it did not work. I had to make another service requests.</p> <a href="#service-request-3-broken-charging-port"> <h3 id="service-request-3-broken-charging-port">Service Request 3: Broken Charging Port</h3> </a> <p>This time, I made sure to tell the person on the other end of the support call to please send it to my address. I asked if there was anything I can do, or anyone I can contact, and was told &ldquo;no, just mail the computer in again.&rdquo; I obliged. The box arrived at the right address this time, so I was able to ship it off.</p> <p>In the &ldquo;describe your issue&rdquo; field on the provided form, I begged the technicians to send me a working machine. &ldquo;Please&rdquo;, I wrote &ldquo;Last time I got a machine back from support, it was still broken. I really need it for school and work!&rdquo;. 5 business days later, I received the machine back. I plugged it in to make sure it worked, only to find out . . . that the very same charging port that I requested be repaired, is still broken! It would&rsquo;ve been funny, if it wasn&rsquo;t infuriating. How is it possible for me to receive a machine from repairs, without the thing I asked to repair being as much as improved?!</p> <p>Worse, a day after I received the machine back (I was able to keep using it thanks to it having two USB-C ports capable of charging), the LCD suddenly flashed, and started flickering. Thinking it was a software glitch, I restarted the machine, only to discover the same flickering during the boot animation and menu. Not only was the charging port not repaired, but now my LCD was broken! (in the below picture, the screen is meant to be blue, but the bottom part of the display is purple and flickering).</p> <figure><img src="https://danilafe.com/blog/dell_is_horrible/brokenlcd.jpg" alt="The broken LCD."><figcaption> <p>The broken LCD.</p> </figcaption> </figure> <a href="#service-request-4-broken-lcd"> <h3 id="service-request-4-broken-lcd">Service Request 4: Broken LCD</h3> </a> <p>I called in to support again, and they once again told me to ship the machine off. What&rsquo;s worse, they accused me of breaking the port myself, and told me this was no longer covered under basic warranty. I had to explain all over again that the port worked fine before the fateful day the D-key snapped. They told me they&rsquo;d &ldquo;look into it&rdquo;. Eventually, I received a box in the mail. I wasn&rsquo;t told I would be receiving a box, but that wasn&rsquo;t a big deal. I mailed off the machine.</p> <p>The UPS shipping was always the most streamlined part of the process. A day later, I was told my machine was received intact. Another day, and I was informed that the technicians are starting to work on it. And then, a few hours later:</p> <blockquote> <p><strong>Current Status:</strong> The part(s) needed to repair your system are not currently in stock. <strong>What&rsquo;s Next:</strong> In most cases the parts are available is less than five days.</p> </blockquote> <p>A few days is no big deal, and it made sense that DELL wouldn&rsquo;t just have screens lying around. So I waited. And waited. And waited. Two weeks later, I got a little tired of waiting, and called the repair center. An automated message told me:</p> <blockquote> <p>We&rsquo;re currently experiencing heavy call volumes. Please try again later. Goodbye.</p> </blockquote> <p>And the call was dropped. This happened every time I tried to call, no matter the hour. The original status update &ndash; the one that notified me about the part shortage &ndash; came on May 8th, but the machine finally arrived to me (without prior warning) on June 2nd, almost a month later.</p> <p>The charging port worked. Sound worked. The screen wasn&rsquo;t flickering. I was happy for the brief moments that my computer was loading. As soon as I started vim, though, I noticed something was off: the fonts looked more pixelated. The DPI settings I&rsquo;d painstakingly tweaked were wrong. Now that I thought about it, even the GRUB menu was larger. My suspicion growing, I booted into Windows, and looked at the display settings. Noticeably fewer resolutions were listed in the drop-down menu; worse, the highest resolution was 1080p. After almost a month of waiting, DELL replaced my 4k laptop display with a 1080p one.</p> <a href="#system-replacement-worse-lcd-screen"> <h3 id="system-replacement-worse-lcd-screen">System Replacement: Worse LCD Screen</h3> </a> <p>I admit, I was angry. At the same time, the absurdity of it all was also unbearable. Was this constant loop of hardware damage, the endless number of support calls filled with hoarse jazz music, part of some kind of Kafkaesque dream? I didn&rsquo;t know. I was at the end of my wits as to what to do. As a last resort, I made <a href="https://twitter.com/DanilaTheWalrus/status/1268056637383692289"class="external-link">a tweet<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> from my almost-abandoned account. DELL Support&rsquo;s Twitter account <a href="https://twitter.com/DellCares/status/1268064691416334344"class="external-link">quickly responded<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, eager as always to destroy any semblance of transparency by switching to private messages. I let them know my thoughts on the matter. I wanted a new machine.</p> <figure><img src="https://danilafe.com/blog/dell_is_horrible/dm_1.png" alt="The first real exchange between me and DELL support."><figcaption> <p>The first real exchange between me and DELL support.</p> </figcaption> </figure> <p>Of course we can proceed further. I wanted to know what kind of machine I was getting, though. As long as it was the same model that I originally bought, <span class="sidenote"> <label class="sidenote-label" for="replacement-note">it would be better than what I have.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="replacement-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> At least in principle, it would be. Perhaps the wear and tear on the replacement parts would be greater, but at least I would have, presumably, a machine in good condition that had the 4k screen that made me buy it in the first place. <span class="sidenote-delimiter">]</span> </span> </span> Despite this, I knew that the machine I was getting was likely refurbished. This <em>had</em> to mean that some of the parts would come from other, used, machines. This irked me, because, well, I payed for a new machine.</p> <figure><img src="https://danilafe.com/blog/dell_is_horrible/dm_2.png" alt="Ah, the classic use of canned responses."><figcaption> <p>Ah, the classic use of canned responses.</p> </figcaption> </figure> <p>Their use of the canned response, and their unwillingness to answer this simple question, was transparent. Indeed, the machine would be made of used parts. I still wanted to proceed. DELL requested that I sent an image of my machine which included its service tag, together with a piece of paper which included my name and email address. I obliged, and quickly got a response:</p> <figure><img src="https://danilafe.com/blog/dell_is_horrible/dm_3.png" alt="If it was me who was silent for 4 days, my request would&rsquo;ve long been cancelled."><figcaption> <p>If it was me who was silent for 4 days, my request would&rsquo;ve long been cancelled.</p> </figcaption> </figure> <p>Thanks, Kalpana. You will never hear this name again, not in this post. Only one or two messages from DELL support are ever from the same person. About a week later, I get the following beauty:</p> <figure><img src="https://danilafe.com/blog/dell_is_horrible/dm_4.png" alt="Excuse me? What&rsquo;s going on?"><figcaption> <p>Excuse me? What&rsquo;s going on?</p> </figcaption> </figure> <p>My initial request was cancelled? Why wasn&rsquo;t I told? What was the reason? What the heck was going on at DELL Support? Should I be worried? My question of &ldquo;Why&rdquo; was answered with the apt response of &ldquo;Yes&rdquo;, and a message meant to pacify me. While this was going on, I ordered a <span class="sidenote"> <label class="sidenote-label" for="pinebook-note">Pinebook Pro.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="pinebook-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> The Pinebook – a $200 machine – has, thus far, worked more reliably than any DELL product I've had the misfortune of owning. <span class="sidenote-delimiter">]</span> </span> </span> It was not a replacement for the DELL machine, but rather the first step towards migrating my setup to a stationary computer, and a small, lightweight SSH device. At this point, there was no more faith in DELL left in my mind.</p> <p>Soon, DELL required my attention, only to tell me that they put in a request to see that status of my request. How bureaucratic. Two more names &ndash; Kareem and JKC &ndash; flickered through the chats, also never to be seen again.</p> <figure><img src="https://danilafe.com/blog/dell_is_horrible/dm_5.png" alt="Not much of a conversation, really."><figcaption> <p>Not much of a conversation, really.</p> </figcaption> </figure> <p>Finally, on July 9th (a month and six days after my first real message to DELL support), I was notified by my roommates that FedEx tried to deliver a package to our house, but gave up when no one came to sign for it. On one hand, this is great: FedEx didn&rsquo;t just leave my laptop on the porch. On the other hand, though, this was the first time I heard about receiving the machine. I got to the house the next day, unpacked the new computer, and tested all the things that had, at one point, failed. Everything seemed to work. I transfered all my files, wiped the old computer clean, and mailed it off. I also spent some time dealing with the fallout of DELL PremierColor starting on its own, and permanently altering the color profile of my display. I don&rsquo;t have the special, physical calibration device, and therefore still suspect that my screen is somewhat green.</p> <p>Today, I discovered that the microphone of the replacement machine didn&rsquo;t work.</p> <a href="#am-i-the-problem"> <h3 id="am-i-the-problem">Am I The Problem?</h3> </a> <p>When the mysterious FedEx package arrived at my door on July 9th, I did some digging to verify my suspicion that it was from DELL. I discovered their HQ in Lebanon, TN. This gave me an opportunity to <span class="sidenote"> <label class="sidenote-label" for="reviews-note">see</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="reviews-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> See, of course, modulo whatever bias arises when only those who feel strongly leave reviews. <span class="sidenote-delimiter">]</span> </span> </span> whether or not I was alone in this situation. I was genuinely worried that I was suffering from the technical variant of <a href="https://www.webmd.com/mental-health/munchausen-syndrome#1"class="external-link">Munchausen Syndrome<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, and that I was compulsively breaking my electronics. These worries were dispelled by the reviews on Google:</p> <figure><img src="https://danilafe.com/blog/dell_is_horrible/reviews_1.png" alt="Most of the reviews are pretty terse, but the ratings convey the general idea."><figcaption> <p>Most of the reviews are pretty terse, but the ratings convey the general idea.</p> </figcaption> </figure> <p>There were even some that were shockingly similar in terms of the apparent incompetence of the DELL technicians:</p> <figure><img src="https://danilafe.com/blog/dell_is_horrible/reviews_2.png" alt="Now, now, Maggie, I wouldn&rsquo;t go as far as recommending Apple."><figcaption> <p>Now, now, Maggie, I wouldn&rsquo;t go as far as recommending Apple.</p> </figcaption> </figure> <p>So, this is not uncommon. This is how DELL deals with customers now. It&rsquo;s awfully tiring, really; I&rsquo;ve been in and out of repairs continuously for almost half a year, now. That&rsquo;s 2.5% of my life at the time of writing, all non-stop since the D-key. And these people probably have spent considerable amounts of time, too.</p> <a href="#its-about-the-principle"> <h3 id="its-about-the-principle">It&rsquo;s About the Principle</h3> </a> <p>The microphone on my machine is rather inconsequential to me. I can, and regularly do, teleconference from my phone (a habit that I developed thanks to DELL, since my computer was so often unavailable). I don&rsquo;t need to dictate anything. Most of my communication is via chat.</p> <p>Really, compared to the other issues (keyboard, sound, charging, USB ports, the broken or low-resolution screen) the microphone is a benign problem. As I have now learned, things could be worse.</p> <p>But why should the thought, <em>&ldquo;It could be worse&rdquo;</em>, even cross my mind when dealing with such a matter? Virtually every issue that has occurred with my computer thus far could &ndash; should! &ndash; have been diagnosed at the repair center. The &lsquo;slow charger&rsquo; warning shows up in BIOS, so just turning the computer on while plugged in should make it obvious something is wrong; doubly so when the very reason that the laptop was in repairs in the first place was because of the faulty charger. I refuse to believe that screens with different resolutions have the same part identifier, either. How have the standards of service from DELL fallen so low? How come this absurd scenario plays out not just for me, but for others as well? It would be comforting, in a way, to think that I was just the &rsquo;exceptional case&rsquo;. But apparently, I&rsquo;m not. This is standard practice.</p> <a href="#tldr"> <h3 id="tldr">Tl;DR</h3> </a> <p>Here are he problems I&rsquo;ve had with DELL:</p> <ul> <li>The machine shipped, apparently, with a missing piece of insulation.</li> <li>The &ldquo;D&rdquo; key on the keyboard snapped after only a few months of use.</li> <li>While repairing the &ldquo;D&rdquo; key, the DELL technician broke the computer&rsquo;s sound and microphone.</li> <li>While repairing the sound and microphone, the DELL technicians broke a charging port.</li> <li>The DELL technicians failed to repair the charging port, mailing me back a machine exhibiting the same issues, in addition to a broken LCD screen.</li> <li>The repair of the LCD screen took almost a month, and concluded with me receiving a worse quality screen than I originally had.</li> <li>The system replacement that followed the botched LCD repair took over a month to go through.</li> <li>The replaced system was made partially of used parts, which DELL refused to admit.</li> <li>The microphone on the replacement system was broken.</li> </ul> <a href="#closing-thoughts"> <h3 id="closing-thoughts">Closing Thoughts</h3> </a> <p>I will not be sending my system in again. It doesn&rsquo;t make sense to do so - after mailing my system in for repairs three times, I&rsquo;ve measured empirically that the chance of failure is 100%. Every service request is a lottery, dutifully giving out a random prize of another broken part. I no longer wish to play; as any person who gambles should, I will quit while I&rsquo;m ahead, and cut my losses. However, I hope for this story, which may be unusual in its level of detail, but not its content, to be seen by seen by someone. I hope to prevent someone out there from feeling the frustration, and anger, and peculiar amusement that I felt during this process. I hope for someone else to purchase a computer with money, and not with their sanity. A guy can hope.</p> <p>If you&rsquo;re reading this, please take this as a warning. <strong>DELL is a horrible company. They have the lowest standards of customer support of any U.S. company that I&rsquo;ve encountered. Their technicians are largely incompetent. Their quality assurance is non-existent. Stay away from them.</strong></p> Meaningfully Typechecking a Language in Idris, Revisited https://danilafe.com/blog/typesafe_interpreter_revisited/ Wed, 22 Jul 2020 14:37:35 -0700 https://danilafe.com/blog/typesafe_interpreter_revisited/ <p>Some time ago, I wrote a post titled <a href="https://danilafe.com/blog/typesafe_interpreter/">Meaningfully Typechecking a Language in Idris</a>. The gist of the post was as follows:</p> <ul> <li><em>Programming Language Fundamentals</em> students were surprised that, despite having run their expression through (object language) typechecking, they still had to have a <code>Maybe</code> type in their evaluation functions. This was due to the fact that the (meta language) type system was not certain that (object language) typechecking worked.</li> <li>A potential solution was to write separate expression types such as <code>ArithExpr</code> and <code>BoolExpr</code>, which are known to produce booleans or integers. However, this required the re-implementation of most of the logic for <code>IfElse</code>, for which the branches could have integers, booleans, or strings.</li> <li>An alternative solution was to use dependent types, and index the <code>Expr</code> type with the type it evaluates to. We defined a data type <code>data ExprType = IntType | StringType | BoolType</code>, and then were able to write types like <code>SafeExpr IntType</code> that we <em>knew</em> would evaluate to an integer, or <code>SafeExpr BoolType</code>, which we also <em>knew</em> would evaluate to a boolean. We then made our <code>typecheck</code> function return a pair of <code>(type, SafeExpr of that type)</code>.</li> </ul> <p>Unfortunately, I think that post is rather incomplete. I noted at the end of it that I was not certain on how to implement if-expressions, which were my primary motivation for not just sticking with <code>ArithExpr</code> and <code>BoolExpr</code>. It didn&rsquo;t seem too severe then, but now I just feel like a charlatan. Today, I decided to try again, and managed to figure it out with the excellent help from people in the <code>#idris</code> channel on Freenode. It required a more advanced use of dependent types: in particular, I ended up using Idris&rsquo; theorem proving facilities to get my code to pass typechecking. In this post, I will continue from where we left off in the previous post, adding support for if-expressions.</p> <p>Let&rsquo;s start with the new <code>Expr</code> and <code>SafeExpr</code> types. Here they are:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-interpreter/TypesafeIntrV2.idr" data-first-line="37" data-last-line="49"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntrV2.idr#L37-L49">TypesafeIntrV2.idr</a>, lines 37 through 49</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="kr">data</span> <span class="kt">Expr</span> </span></span><span class="line"><span class="cl"> <span class="ow">=</span> <span class="kt">IntLit</span> <span class="kt">Int</span> </span></span><span class="line"><span class="cl"> <span class="ow">|</span> <span class="kt">BoolLit</span> <span class="kt">Bool</span> </span></span><span class="line"><span class="cl"> <span class="ow">|</span> <span class="kt">StringLit</span> <span class="kt">String</span> </span></span><span class="line"><span class="cl"> <span class="ow">|</span> <span class="kt">BinOp</span> <span class="kt">Op</span> <span class="kt">Expr</span> <span class="kt">Expr</span> </span></span><span class="line"><span class="cl"> <span class="ow">|</span> <span class="kt">IfElse</span> <span class="kt">Expr</span> <span class="kt">Expr</span> <span class="kt">Expr</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kr">data</span> <span class="kt">SafeExpr</span> <span class="ow">:</span> <span class="kt">ExprType</span> <span class="ow">-&gt;</span> <span class="kt">Type</span> <span class="kr">where</span> </span></span><span class="line"><span class="cl"> <span class="nf">IntLiteral</span> <span class="ow">:</span> <span class="kt">Int</span> <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> <span class="kt">IntType</span> </span></span><span class="line"><span class="cl"> <span class="nf">BoolLiteral</span> <span class="ow">:</span> <span class="kt">Bool</span> <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> <span class="kt">BoolType</span> </span></span><span class="line"><span class="cl"> <span class="nf">StringLiteral</span> <span class="ow">:</span> <span class="kt">String</span> <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> <span class="kt">StringType</span> </span></span><span class="line"><span class="cl"> <span class="nf">BinOperation</span> <span class="ow">:</span> <span class="ow">(</span>repr a <span class="ow">-&gt;</span> repr b <span class="ow">-&gt;</span> repr c<span class="ow">)</span> <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> a <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> b <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> c </span></span><span class="line"><span class="cl"> <span class="nf">IfThenElse</span> <span class="ow">:</span> <span class="kt">SafeExpr</span> <span class="kt">BoolType</span> <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> t <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> t <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> t</span></span></code></pre></td></tr></table> </div> </div> </div> <p>For <code>Expr</code>, the <code>IfElse</code> constructor is very straightforward. It takes three expressions: the condition, the &rsquo;then&rsquo; branch, and the &rsquo;else&rsquo; branch. With <code>SafeExpr</code> and <code>IfThenElse</code>, things are more rigid. The condition of the expression has to be of a boolean type, so we make the first argument <code>SafeExpr BoolType</code>. Also, the two branches of the if-expression have to be of the same type. We encode this by making both of the input expressions be of type <code>SafeExpr t</code>. Since the result of the if-expression will be the output of one of the branches, the whole if-expression is also of type <code>SafeExpr t</code>.</p> <a href="#what-stumped-me-equality"> <h3 id="what-stumped-me-equality">What Stumped Me: Equality</h3> </a> <p>Typechecking if-expressions is where things get interesting. First, we want to require that the condition of the expression evaluates to a boolean. For this, we can write a function <code>requireBool</code>, that takes a dependent pair produced by <code>typecheck</code>. This function does one of two things:</p> <ul> <li>If the dependent pair contains a <code>BoolType</code>, and therefore also an expression of type <code>SafeExpr BoolType</code>, <code>requireBool</code> succeeds, and returns the expression.</li> <li>If the dependent pair contains any type other than <code>BoolType</code>, <code>requireBool</code> fails with an error message. Since we&rsquo;re using <code>Either</code> for error handling, this amounts to using the <code>Left</code> constructor.</li> </ul> <p>Such a function is quite easy to write:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-interpreter/TypesafeIntrV2.idr" data-first-line="58" data-last-line="60"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntrV2.idr#L58-L60">TypesafeIntrV2.idr</a>, lines 58 through 60</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">requireBool</span> <span class="ow">:</span> <span class="ow">(</span>n <span class="ow">:</span> <span class="kt">ExprType</span> <span class="ow">**</span> <span class="kt">SafeExpr</span> n<span class="ow">)</span> <span class="ow">-&gt;</span> <span class="kt">Either</span> <span class="kt">String</span> <span class="ow">(</span><span class="kt">SafeExpr</span> <span class="kt">BoolType</span><span class="ow">)</span> </span></span><span class="line"><span class="cl">requireBool <span class="ow">(</span><span class="kt">BoolType</span> <span class="ow">**</span> e<span class="ow">)</span> <span class="ow">=</span> <span class="kt">Right</span> e </span></span><span class="line"><span class="cl">requireBool <span class="kr">_</span> <span class="ow">=</span> <span class="kt">Left</span> <span class="s">&#34;Not a boolean.&#34;</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We can then write all of the recursive calls to <code>typecheck</code> as follows:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-interpreter/TypesafeIntrV2.idr" data-first-line="71" data-last-line="75"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntrV2.idr#L71-L75">TypesafeIntrV2.idr</a>, lines 71 through 75</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">71 </span><span class="lnt">72 </span><span class="lnt">73 </span><span class="lnt">74 </span><span class="lnt">75 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl">typecheck <span class="ow">(</span><span class="kt">IfElse</span> c t e<span class="ow">)</span> <span class="ow">=</span> </span></span><span class="line"><span class="cl"> <span class="kr">do</span> </span></span><span class="line"><span class="cl"> ce <span class="ow">&lt;-</span> typecheck c <span class="ow">&gt;&gt;=</span> requireBool </span></span><span class="line"><span class="cl"> <span class="ow">(</span>tt <span class="ow">**</span> te<span class="ow">)</span> <span class="ow">&lt;-</span> typecheck t </span></span><span class="line"><span class="cl"> <span class="ow">(</span>et <span class="ow">**</span> ee<span class="ow">)</span> <span class="ow">&lt;-</span> typecheck e</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Alright, so we have the types of the <code>t</code> and <code>e</code> branches. All we have to do now is use <code>(==)</code>. We could implement <code>(==)</code> as follows:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl">implementation <span class="kt">Eq</span> <span class="kt">ExprType</span> <span class="kr">where</span> </span></span><span class="line"><span class="cl"> <span class="kt">IntType</span> <span class="ow">==</span> <span class="kt">IntType</span> <span class="ow">=</span> <span class="kt">True</span> </span></span><span class="line"><span class="cl"> <span class="kt">BoolType</span> <span class="ow">==</span> <span class="kt">BoolType</span> <span class="ow">=</span> <span class="kt">True</span> </span></span><span class="line"><span class="cl"> <span class="kt">StringType</span> <span class="ow">==</span> <span class="kt">StringType</span> <span class="ow">=</span> <span class="kt">True</span> </span></span><span class="line"><span class="cl"> <span class="kr">_</span> <span class="ow">==</span> <span class="kr">_</span> <span class="ow">=</span> <span class="kt">False</span> </span></span></code></pre></div><p>Now we&rsquo;re golden, right? We can just write the following:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"> <span class="kr">if</span> tt <span class="ow">==</span> et </span></span><span class="line"><span class="cl"> <span class="kr">then</span> pure <span class="ow">(</span><span class="kr">_</span> <span class="ow">**</span> <span class="kt">IfThenElse</span> ce te ee<span class="ow">)</span> </span></span><span class="line"><span class="cl"> <span class="kr">else</span> <span class="kt">Left</span> <span class="s">&#34;Incompatible branch types.&#34;</span> </span></span></code></pre></td></tr></table> </div> </div><p>No, this is not quire right. Idris complains:</p> <pre tabindex="0"><code>Type mismatch between et and tt </code></pre><p>Huh? But we just saw that <code>et == tt</code>! What&rsquo;s the problem? The problem is, in fact, that <code>(==)</code> is meaningless as far as the Idris typechecker is concerned. We could have just as well written,</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl">implementation <span class="kt">Eq</span> <span class="kt">ExprType</span> <span class="kr">where</span> </span></span><span class="line"><span class="cl"> <span class="kr">_</span> <span class="ow">==</span> <span class="kr">_</span> <span class="ow">=</span> <span class="kt">True</span> </span></span></code></pre></div><p>This would tell us that <code>IntType == BoolType</code>. But of course, <code>SafeExpr IntType</code> is not the same as <code>SafeExpr BoolType</code>; I would be very worried if the typechecker allowed me to assert otherwise. There is, however, a kind of equality that we can use to convince the Idris typechecker that two types are the same. This equality, too, is a type.</p> <a href="#curry-howard-correspondence"> <h3 id="curry-howard-correspondence">Curry-Howard Correspondence</h3> </a> <p>Spend enough time learning about Programming Language Theory, and you will hear the term <em>Curry Howard Correspondence</em>. If you&rsquo;re the paper kind of person, I suggest reading Philip Wadler&rsquo;s <em>Propositions as Types</em> paper. Alternatively, you can take a look at <em>Logical Foundations</em>&rsquo; <a href="https://softwarefoundations.cis.upenn.edu/lf-current/ProofObjects.html"class="external-link">Proof Objects<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> chapter. I will give a very brief explanation here, too, for the sake of completeness. The general gist is as follows: <strong>propositions (the logical kind) correspond to program types</strong>, and proofs of the propositions correspond to values of the types.</p> <p>To get settled into this idea, let&rsquo;s look at a few &lsquo;well-known&rsquo; examples:</p> <ul> <li><code>(A,B)</code>, the tuple of two types <code>A</code> and <code>B</code> is equivalent to the proposition \(A \land B\), which means \(A\) and \(B\). Intuitively, to provide a proof of \(A \land B\), we have to provide the proofs of \(A\) and \(B\).</li> <li><code>Either A B</code>, which contains one of <code>A</code> or <code>B</code>, is equivalent to the proposition \(A \lor B\), which means \(A\) or \(B\). Intuitively, to provide a proof that either \(A\) or \(B\) is true, we need to provide one of them.</li> <li><code>A -&gt; B</code>, the type of a function from <code>A</code> to <code>B</code>, is equivalent to the proposition \(A \rightarrow B\), which reads \(A\) implies \(B\). We can think of a function <code>A -&gt; B</code> as creating a proof of <code>B</code> given a proof of <code>A</code>.</li> </ul> <p>Now, consider Idris&rsquo; unit type <code>()</code>:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="kr">data</span> <span class="ow">()</span> <span class="ow">=</span> <span class="ow">()</span> </span></span></code></pre></div><p>This type takes no arguments, and there&rsquo;s only one way to construct it. We can create a value of type <code>()</code> at any time, by just writing <code>()</code>. This type is equivalent to \(\text{true}\): only one proof of it exists, and it requires no premises. It just is.</p> <p>Consider also the type <code>Void</code>, which too is present in Idris:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="c1">-- Note: this is probably not valid code.</span> </span></span><span class="line"><span class="cl"><span class="kr">data</span> <span class="kt">Void</span> <span class="ow">=</span> <span class="c1">-- Nothing</span> </span></span></code></pre></div><p>The type <code>Void</code> has no constructors: it&rsquo;s impossible to create a value of this type, and therefore, it&rsquo;s impossible to create a proof of <code>Void</code>. Thus, as you may have guessed, <code>Void</code> is equivalent to \(\text{false}\).</p> <p>Finally, we get to a more complicated example:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="kr">data</span> <span class="ow">(=)</span> <span class="ow">:</span> a <span class="ow">-&gt;</span> b <span class="ow">-&gt;</span> <span class="kt">Type</span> <span class="kr">where</span> </span></span><span class="line"><span class="cl"> <span class="nf">Refl</span> <span class="ow">:</span> x <span class="ow">=</span> x </span></span></code></pre></div><p>This defines <code>a = b</code> as a type, equivalent to the proposition that <code>a</code> is equal to <code>b</code>. The only way to construct such a type is to give it a single value <code>x</code>, creating the proof that <code>x = x</code>. This makes sense: equality is reflexive.</p> <p>This definition isn&rsquo;t some loosey-goosey boolean-based equality! If we can construct a value of type <code>a = b</code>, we can prove to Idris&rsquo; typechecker that <code>a</code> and <code>b</code> are equivalent. In fact, Idris&rsquo; standard library gives us the following function:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">replace</span> <span class="ow">:</span> <span class="ow">{</span>a<span class="ow">:</span><span class="kr">_</span><span class="ow">}</span> <span class="ow">-&gt;</span> <span class="ow">{</span>x<span class="ow">:</span><span class="kr">_</span><span class="ow">}</span> <span class="ow">-&gt;</span> <span class="ow">{</span>y<span class="ow">:</span><span class="kr">_</span><span class="ow">}</span> <span class="ow">-&gt;</span> <span class="ow">{</span><span class="kt">P</span> <span class="ow">:</span> a <span class="ow">-&gt;</span> <span class="kt">Type</span><span class="ow">}</span> <span class="ow">-&gt;</span> x <span class="ow">=</span> y <span class="ow">-&gt;</span> <span class="kt">P</span> x <span class="ow">-&gt;</span> <span class="kt">P</span> y </span></span></code></pre></div><p>This reads, given a type <code>a</code>, and values <code>x</code> and <code>y</code> of type <code>a</code>, if we know that <code>x = y</code>, then we can rewrite any proposition in terms of <code>x</code> into another, also valid proposition in terms of <code>y</code>. Let&rsquo;s make this concrete. Suppose <code>a</code> is <code>Int</code>, and <code>P</code> (the type of which is now <code>Int -&gt; Type</code>), is <code>Even</code>, a proposition that claims that its argument is even. <span class="sidenote"> <label class="sidenote-label" for="specialize-note">Then, we have:</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="specialize-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> I'm only writing type signatures for <code>replace'</code> to avoid overloading. There's no need to define a new function; <code>replace'</code> is just a specialization of <code>replace</code>, so we can use the former anywhere we can use the latter. <span class="sidenote-delimiter">]</span> </span> </span> </p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">replace&#39;</span> <span class="ow">:</span> <span class="ow">{</span>x <span class="ow">:</span> <span class="kt">Int</span><span class="ow">}</span> <span class="ow">-&gt;</span> <span class="ow">{</span>y <span class="ow">:</span> <span class="kt">Int</span><span class="ow">}</span> <span class="ow">-&gt;</span> x <span class="ow">=</span> y <span class="ow">-&gt;</span> <span class="kt">Even</span> x <span class="ow">-&gt;</span> <span class="kt">Even</span> y </span></span></code></pre></div><p>That is, if we know that <code>x</code> is equal to <code>y</code>, and we know that <code>x</code> is even, it follows that <code>y</code> is even too. After all, they&rsquo;re one and the same! We can take this further. Recall:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-interpreter/TypesafeIntrV2.idr" data-first-line="44" data-last-line="44"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntrV2.idr#L44-L44">TypesafeIntrV2.idr</a>, line 44</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">44 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="kr">data</span> <span class="kt">SafeExpr</span> <span class="ow">:</span> <span class="kt">ExprType</span> <span class="ow">-&gt;</span> <span class="kt">Type</span> <span class="kr">where</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We can therefore write:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">replace&#39;&#39;</span> <span class="ow">:</span> <span class="ow">{</span>x <span class="ow">:</span> <span class="kt">ExprType</span><span class="ow">}</span> <span class="ow">-&gt;</span> <span class="ow">{</span>y <span class="ow">:</span> <span class="kt">ExprType</span><span class="ow">}</span> <span class="ow">-&gt;</span> x <span class="ow">=</span> y <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> x <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> y </span></span></code></pre></div><p>This is exactly what we want! Given a proof that one <code>ExprType</code>, <code>x</code>, is equal to another <code>ExprType</code>, <code>y</code>, we can safely convert <code>SafeExpr x</code> to <code>SafeExpr y</code>. We will use this to convince the Idris typechecker to accept our program.</p> <a href="#first-attempt-eq-implies-equality"> <h3 id="first-attempt-eq-implies-equality">First Attempt: <code>Eq</code> implies Equality</h3> </a> <p>It&rsquo;s pretty trivial to see that we <em>did</em> define <code>(==)</code> correctly (<code>IntType</code> is equal to <code>IntType</code>, <code>StringType</code> is equal to <code>StringType</code>, and so on). Thus, if we know that <code>x == y</code> is <code>True</code>, it should follow that <code>x = y</code>. We can thus define the following proposition:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">eqCorrect</span> <span class="ow">:</span> <span class="ow">{</span>a <span class="ow">:</span> <span class="kt">ExprType</span><span class="ow">}</span> <span class="ow">-&gt;</span> <span class="ow">{</span>b <span class="ow">:</span> <span class="kt">ExprType</span><span class="ow">}</span> <span class="ow">-&gt;</span> <span class="ow">(</span>a <span class="ow">==</span> b <span class="ow">=</span> <span class="kt">True</span><span class="ow">)</span> <span class="ow">-&gt;</span> a <span class="ow">=</span> b </span></span></code></pre></div><p>We will see shortly why this is <em>not</em> the best solution, and thus, I won&rsquo;t bother creating a proof / implementation for this proposition / function. It reads:</p> <blockquote> <p>If we have a proof that <code>(==)</code> returned true for some <code>ExprType</code>s <code>a</code> and <code>b</code>, it must be that <code>a</code> is the same as <code>b</code>.</p> </blockquote> <p>We can then define a function to cast a <code>SafeExpr a</code> to <code>SafeExpr b</code>, given that <code>(==)</code> returned <code>True</code> for some <code>a</code> and <code>b</code>:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">safeCast</span> <span class="ow">:</span> <span class="ow">{</span>a <span class="ow">:</span> <span class="kt">ExprType</span><span class="ow">}</span> <span class="ow">-&gt;</span> <span class="ow">{</span>b <span class="ow">:</span> <span class="kt">ExprType</span><span class="ow">}</span> <span class="ow">-&gt;</span> <span class="ow">(</span>a <span class="ow">==</span> b <span class="ow">=</span> <span class="kt">True</span><span class="ow">)</span> <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> a <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> b </span></span><span class="line"><span class="cl">safeCast h e <span class="ow">=</span> replace <span class="ow">(</span>eqCorrect h<span class="ow">)</span> e </span></span></code></pre></div><p>Awesome! All that&rsquo;s left now is to call <code>safeCast</code> from our <code>typecheck</code> function:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"> <span class="kr">if</span> tt <span class="ow">==</span> et </span></span><span class="line"><span class="cl"> <span class="kr">then</span> pure <span class="ow">(</span><span class="kr">_</span> <span class="ow">**</span> <span class="kt">IfThenElse</span> ce te <span class="ow">(</span>safeCast <span class="ow">?</span>uhOh ee<span class="ow">))</span> </span></span><span class="line"><span class="cl"> <span class="kr">else</span> <span class="kt">Left</span> <span class="s">&#34;Incompatible branch types.&#34;</span> </span></span></code></pre></td></tr></table> </div> </div><p>No, this doesn&rsquo;t work after all. What do we put for <code>?uhOh</code>? We need to have a value of type <code>tt == et = True</code>, but we don&rsquo;t have one. Idris&rsquo; own if-then-else expressions do not provide us with such proofs about their conditions. The awesome people at <code>#idris</code> pointed out that the <code>with</code> clause can provide such a proof. We could therefore write:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl">createIfThenElse ce <span class="ow">(</span>tt <span class="ow">**</span> et<span class="ow">)</span> <span class="ow">(</span>et <span class="ow">**</span> ee<span class="ow">)</span> <span class="kr">with</span> <span class="ow">(</span>et <span class="ow">==</span> tt<span class="ow">)</span> <span class="kr">proof</span> p </span></span><span class="line"><span class="cl"> <span class="ow">|</span> <span class="kt">True</span> <span class="ow">=</span> pure <span class="ow">(</span>tt <span class="ow">**</span> <span class="kt">IfThenElse</span> ce te <span class="ow">(</span>safeCast p ee<span class="ow">))</span> </span></span><span class="line"><span class="cl"> <span class="ow">|</span> <span class="kt">False</span> <span class="ow">=</span> <span class="kt">Left</span> <span class="s">&#34;Incompatible branch types.&#34;</span> </span></span></code></pre></div><p>Here, the <code>with</code> clause effectively adds another argument equal to <code>(et == tt)</code> to <code>createIfThenElse</code>, and tries to pattern match on its value. When we combine this with the <code>proof</code> keyword, Idris will give us a handle to a proof, named <code>p</code>, that asserts the new argument evaluates to the value in the pattern match. In our case, this is exactly the proof we need to give to <code>safeCast</code>.</p> <p>However, this is ugly. Idris&rsquo; <code>with</code> clause only works at the top level of a function, so we have to define a function just to use it. It also shows that we&rsquo;re losing information when we call <code>(==)</code>, and we have to reconstruct or recapture it using some other means.</p> <a href="#second-attempt-decidable-propositions"> <h3 id="second-attempt-decidable-propositions">Second Attempt: Decidable Propositions</h3> </a> <p>More awesome folks over at <code>#idris</code> pointed out that the whole deal with <code>(==)</code> is inelegant; they suggested I use <strong>decidable propositions</strong>, using the <code>Dec</code> type. The type is defined as follows:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="kr">data</span> <span class="kt">Dec</span> <span class="ow">:</span> <span class="kt">Type</span> <span class="ow">-&gt;</span> <span class="kt">Type</span> <span class="kr">where</span> </span></span><span class="line"><span class="cl"> <span class="nf">Yes</span> <span class="ow">:</span> <span class="ow">(</span>prf <span class="ow">:</span> prop<span class="ow">)</span> <span class="ow">-&gt;</span> <span class="kt">Dec</span> prop </span></span><span class="line"><span class="cl"> <span class="nf">No</span> <span class="ow">:</span> <span class="ow">(</span>contra <span class="ow">:</span> prop <span class="ow">-&gt;</span> <span class="kt">Void</span><span class="ow">)</span> <span class="ow">-&gt;</span> <span class="kt">Dec</span> prop </span></span></code></pre></div><p>There are two ways to construct a value of type <code>Dec prop</code>:</p> <ul> <li>We use the <code>Yes</code> constructor, which means that the proposition <code>prop</code> is true. To use this constructor, we have to give it a proof of <code>prop</code>, called <code>prf</code> in the constructor.</li> <li>We use the <code>No</code> constructor, which means that the proposition <code>prop</code> is false. We need a proof of type <code>prop -&gt; Void</code> to represent this: if we have a proof of <code>prop</code>, we arrive at a contradiction.</li> </ul> <p>This combines the nice <code>True</code> and <code>False</code> of <code>Bool</code>, with the &lsquo;real&rsquo; proofs of the truthfulness or falsity. At the moment that we would have been creating a boolean, we also create a proof of that boolean&rsquo;s value. Thus, we don&rsquo;t lose information. Here&rsquo;s how we can go about this:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-interpreter/TypesafeIntrV2.idr" data-first-line="20" data-last-line="29"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntrV2.idr#L20-L29">TypesafeIntrV2.idr</a>, lines 20 through 29</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">decEq</span> <span class="ow">:</span> <span class="ow">(</span>a <span class="ow">:</span> <span class="kt">ExprType</span><span class="ow">)</span> <span class="ow">-&gt;</span> <span class="ow">(</span>b <span class="ow">:</span> <span class="kt">ExprType</span><span class="ow">)</span> <span class="ow">-&gt;</span> <span class="kt">Dec</span> <span class="ow">(</span>a <span class="ow">=</span> b<span class="ow">)</span> </span></span><span class="line"><span class="cl">decEq <span class="kt">IntType</span> <span class="kt">IntType</span> <span class="ow">=</span> <span class="kt">Yes</span> <span class="kt">Refl</span> </span></span><span class="line"><span class="cl">decEq <span class="kt">BoolType</span> <span class="kt">BoolType</span> <span class="ow">=</span> <span class="kt">Yes</span> <span class="kt">Refl</span> </span></span><span class="line"><span class="cl">decEq <span class="kt">StringType</span> <span class="kt">StringType</span> <span class="ow">=</span> <span class="kt">Yes</span> <span class="kt">Refl</span> </span></span><span class="line"><span class="cl">decEq <span class="kt">IntType</span> <span class="kt">BoolType</span> <span class="ow">=</span> <span class="kt">No</span> intBoolImpossible </span></span><span class="line"><span class="cl">decEq <span class="kt">BoolType</span> <span class="kt">IntType</span> <span class="ow">=</span> <span class="kt">No</span> <span class="ow">$</span> intBoolImpossible <span class="ow">.</span> sym </span></span><span class="line"><span class="cl">decEq <span class="kt">IntType</span> <span class="kt">StringType</span> <span class="ow">=</span> <span class="kt">No</span> intStringImpossible </span></span><span class="line"><span class="cl">decEq <span class="kt">StringType</span> <span class="kt">IntType</span> <span class="ow">=</span> <span class="kt">No</span> <span class="ow">$</span> intStringImpossible <span class="ow">.</span> sym </span></span><span class="line"><span class="cl">decEq <span class="kt">BoolType</span> <span class="kt">StringType</span> <span class="ow">=</span> <span class="kt">No</span> boolStringImpossible </span></span><span class="line"><span class="cl">decEq <span class="kt">StringType</span> <span class="kt">BoolType</span> <span class="ow">=</span> <span class="kt">No</span> <span class="ow">$</span> boolStringImpossible <span class="ow">.</span> sym</span></span></code></pre></td></tr></table> </div> </div> </div> <p>We pattern match on the input expression types. If the types are the same, we return <code>Yes</code>, and couple it with <code>Refl</code> (since we&rsquo;ve pattern matched on the types in the left-hand side of the function definition, the typechecker has enough information to create that <code>Refl</code>). On the other hand, if the expression types do not match, we have to provide a proof that their equality would be absurd. For this we use helper functions / theorems like <code>intBoolImpossible</code>:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-interpreter/TypesafeIntrV2.idr" data-first-line="11" data-last-line="12"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntrV2.idr#L11-L12">TypesafeIntrV2.idr</a>, lines 11 through 12</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">11 </span><span class="lnt">12 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">intBoolImpossible</span> <span class="ow">:</span> <span class="kt">IntType</span> <span class="ow">=</span> <span class="kt">BoolType</span> <span class="ow">-&gt;</span> <span class="kt">Void</span> </span></span><span class="line"><span class="cl">intBoolImpossible <span class="kt">Refl</span> <span class="kr">impossible</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>I&rsquo;m not sure if there&rsquo;s a better way of doing this than using <code>impossible</code>. This does the job, though: Idris understands that there&rsquo;s no way we can get an input of type <code>IntType = BoolType</code>, and allows us to skip writing a right-hand side.</p> <p>We can finally use this new <code>decEq</code> function in our type checker:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-interpreter/TypesafeIntrV2.idr" data-first-line="76" data-last-line="78"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntrV2.idr#L76-L78">TypesafeIntrV2.idr</a>, lines 76 through 78</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"> <span class="kr">case</span> decEq tt et <span class="kr">of</span> </span></span><span class="line"><span class="cl"> <span class="kt">Yes</span> p <span class="ow">=&gt;</span> pure <span class="ow">(</span><span class="kr">_</span> <span class="ow">**</span> <span class="kt">IfThenElse</span> ce <span class="ow">(</span>replace p te<span class="ow">)</span> ee<span class="ow">)</span> </span></span><span class="line"><span class="cl"> <span class="kt">No</span> <span class="kr">_</span> <span class="ow">=&gt;</span> <span class="kt">Left</span> <span class="s">&#34;Incompatible branch types.&#34;</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Idris is happy with this! We should also add <code>IfThenElse</code> to our <code>eval</code> function. This part is very easy:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-interpreter/TypesafeIntrV2.idr" data-first-line="80" data-last-line="85"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntrV2.idr#L80-L85">TypesafeIntrV2.idr</a>, lines 80 through 85</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">80 </span><span class="lnt">81 </span><span class="lnt">82 </span><span class="lnt">83 </span><span class="lnt">84 </span><span class="lnt">85 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">eval</span> <span class="ow">:</span> <span class="kt">SafeExpr</span> t <span class="ow">-&gt;</span> repr t </span></span><span class="line"><span class="cl">eval <span class="ow">(</span><span class="kt">IntLiteral</span> i<span class="ow">)</span> <span class="ow">=</span> i </span></span><span class="line"><span class="cl">eval <span class="ow">(</span><span class="kt">BoolLiteral</span> b<span class="ow">)</span> <span class="ow">=</span> b </span></span><span class="line"><span class="cl">eval <span class="ow">(</span><span class="kt">StringLiteral</span> s<span class="ow">)</span> <span class="ow">=</span> s </span></span><span class="line"><span class="cl">eval <span class="ow">(</span><span class="kt">BinOperation</span> f l r<span class="ow">)</span> <span class="ow">=</span> f <span class="ow">(</span>eval l<span class="ow">)</span> <span class="ow">(</span>eval r<span class="ow">)</span> </span></span><span class="line"><span class="cl">eval <span class="ow">(</span><span class="kt">IfThenElse</span> c t e<span class="ow">)</span> <span class="ow">=</span> <span class="kr">if</span> <span class="ow">(</span>eval c<span class="ow">)</span> <span class="kr">then</span> <span class="ow">(</span>eval t<span class="ow">)</span> <span class="kr">else</span> <span class="ow">(</span>eval e<span class="ow">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Since the <code>c</code> part of the <code>IfThenElse</code> is indexed with <code>BoolType</code>, we know that evaluating it will give us a boolean. Thus, we can use that directly in the Idris if-then-else expression. Let&rsquo;s try this with a few expressions:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="kt">BinOp</span> <span class="kt">Add</span> <span class="ow">(</span><span class="kt">IfElse</span> <span class="ow">(</span><span class="kt">BoolLit</span> <span class="kt">True</span><span class="ow">)</span> <span class="ow">(</span><span class="kt">IntLit</span> <span class="mi">6</span><span class="ow">)</span> <span class="ow">(</span><span class="kt">IntLit</span> <span class="mi">7</span><span class="ow">))</span> <span class="ow">(</span><span class="kt">BinOp</span> <span class="kt">Multiply</span> <span class="ow">(</span><span class="kt">IntLit</span> <span class="mi">160</span><span class="ow">)</span> <span class="ow">(</span><span class="kt">IntLit</span> <span class="mi">2</span><span class="ow">))</span> </span></span></code></pre></div><p>This evaluates <code>326</code>, as it should. What if we make the condition non-boolean?</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="kt">BinOp</span> <span class="kt">Add</span> <span class="ow">(</span><span class="kt">IfElse</span> <span class="ow">(</span><span class="kt">IntLit</span> <span class="mi">1</span><span class="ow">)</span> <span class="ow">(</span><span class="kt">IntLit</span> <span class="mi">6</span><span class="ow">)</span> <span class="ow">(</span><span class="kt">IntLit</span> <span class="mi">7</span><span class="ow">))</span> <span class="ow">(</span><span class="kt">BinOp</span> <span class="kt">Multiply</span> <span class="ow">(</span><span class="kt">IntLit</span> <span class="mi">160</span><span class="ow">)</span> <span class="ow">(</span><span class="kt">IntLit</span> <span class="mi">2</span><span class="ow">))</span> </span></span></code></pre></div><p>Our typechecker catches this, and we end up with the following output:</p> <pre tabindex="0"><code>Type error: Not a boolean. </code></pre><p>Alright, let&rsquo;s make one of the branches of the if-expression be a boolean, while the other remains an integer.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="kt">BinOp</span> <span class="kt">Add</span> <span class="ow">(</span><span class="kt">IfElse</span> <span class="ow">(</span><span class="kt">BoolLit</span> <span class="kt">True</span><span class="ow">)</span> <span class="ow">(</span><span class="kt">BoolLit</span> <span class="kt">True</span><span class="ow">)</span> <span class="ow">(</span><span class="kt">IntLit</span> <span class="mi">7</span><span class="ow">))</span> <span class="ow">(</span><span class="kt">BinOp</span> <span class="kt">Multiply</span> <span class="ow">(</span><span class="kt">IntLit</span> <span class="mi">160</span><span class="ow">)</span> <span class="ow">(</span><span class="kt">IntLit</span> <span class="mi">2</span><span class="ow">))</span> </span></span></code></pre></div><p>Our typechecker catches this, too:</p> <pre tabindex="0"><code>Type error: Incompatible branch types. </code></pre><a href="#conclusion"> <h3 id="conclusion">Conclusion</h3> </a> <p>I think this is a good approach. Should we want to add more types to our language, such as tuples, lists, and so on, we will be able to extend our <code>decEq</code> approach to construct more complex equality proofs, and keep the <code>typecheck</code> method the same. Had we not used this approach, and instead decided to pattern match on types inside of <code>typecheck</code>, we would&rsquo;ve quickly found that this only works for languages with finitely many types. When we add polymorphic tuples and lists, we start being able to construct an arbitrary number of types: <code>[a]</code>. <code>[[a]]</code>, and so on. Then, we cease to be able to enumerate all possible pairs of types, and require a recursive solution. I think that this leads us back to <code>decEq</code>.</p> <p>I also hope that I&rsquo;ve now redeemed myself as far as logical arguments go. We used dependent types and made our typechecking function save us from error-checking during evaluation. We did this without having to manually create different types of expressions like <code>ArithExpr</code> and <code>BoolExpr</code>, and without having to duplicate any code.</p> <p>That&rsquo;s all I have for today, thank you for reading! As always, you can check out the <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntrV2.idr"class="external-link">full source code for the typechecker and interpreter<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> on my Git server.</p> Rendering Mathematics On The Back End https://danilafe.com/blog/backend_math_rendering/ Tue, 21 Jul 2020 14:54:26 -0700 https://danilafe.com/blog/backend_math_rendering/ <p>Due to something of a streak of bad luck when it came to computers, I spent a significant amount of time using a Linux-based Chromebook, and then a Pinebook Pro. It was, in some way, enlightening. The things that I used to take for granted with a &lsquo;powerful&rsquo; machine now became a rare luxury: StackOverflow, and other relatively static websites, took upwards of ten seconds to finish loading. On Slack, each of my keypresses could take longer than 500ms to appear on the screen, and sometimes, it would take several seconds. Some websites would present me with a white screen, and remain that way for much longer than I had time to wait. It was awful.</p> <p>At one point, I installed uMatrix, and made it the default policy to block all JavaScript. For the most part, this worked well. Of course, I had to enable JavaScript for applications that needed to be interactive, like Slack, and Discord. But for the most part, I was able to browse the majority of the websites I normally browse. This went on until I started working on the <a href="https://danilafe.com/blog/00_compiler_intro/">compiler series</a> again, and discovered that the LaTeX math on my page, which was required for displaying things like inference rules, didn&rsquo;t work without JavaScript. I was left with two options:</p> <ul> <li>Allow JavaScript, and continue using MathJax to render my math.</li> <li>Make it so that the mathematics are rendered on the back end.</li> </ul> <p>I&rsquo;ve <a href="https://danilafe.com/blog/math_rendering_is_wrong/">previously written about math rendering</a>, and made the observation that MathJax&rsquo;s output for LaTeX is <strong>identical</strong> on every computer. From the MathJax 2.6 change log:</p> <blockquote> <p><em>Improved CommonHTML output</em>. The CommonHTML output now provides the same layout quality and MathML support as the HTML-CSS and SVG output. It is on average 40% faster than the other outputs and the markup it produces are identical on all browsers and thus can also be pre-generated on the server via MathJax-node.</p> </blockquote> <p>It seems absurd, then, to offload this kind of work into the users, to be done over and over again. As should be clear from the title of this post, this made me settle for the second option: it was <strong>obviously within reach</strong>, especially for a statically-generated website like mine, to render math on the backend.</p> <p>I settled on the following architecture:</p> <ul> <li>As before, I would generate my pages using Hugo.</li> <li>I would use the KaTeX NPM package to render math.</li> <li>To build the website no matter what system I was on, I would use Nix.</li> </ul> <p>It so happens that Nix isn&rsquo;t really required for using my approach in general. I will give my setup here, but feel free to skip ahead.</p> <a href="#setting-up-a-nix-build"> <h3 id="setting-up-a-nix-build">Setting Up A Nix Build</h3> </a> <p>My <code>default.nix</code> file looks like this:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Nix" data-lang="Nix"><span class="line"><span class="cl"> <span class="p">{</span> <span class="n">stdenv</span><span class="o">,</span> <span class="n">hugo</span><span class="o">,</span> <span class="n">fetchgit</span><span class="o">,</span> <span class="n">pkgs</span><span class="o">,</span> <span class="n">nodejs</span><span class="o">,</span> <span class="n">ruby</span> <span class="p">}:</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">let</span> </span></span><span class="line"><span class="cl"> <span class="n">url</span> <span class="o">=</span> <span class="s2">&#34;https://dev.danilafe.com/Web-Projects/blog-static.git&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">rev</span> <span class="o">=</span> <span class="s2">&#34;&lt;commit&gt;&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">sha256</span> <span class="o">=</span> <span class="s2">&#34;&lt;hash&gt;&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">requiredPackages</span> <span class="o">=</span> <span class="kn">import</span> <span class="sr">./required-packages.nix</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">inherit</span> <span class="n">pkgs</span> <span class="n">nodejs</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span> </span></span><span class="line"><span class="cl"> <span class="k">in</span> </span></span><span class="line"><span class="cl"> <span class="n">stdenv</span><span class="o">.</span><span class="n">mkDerivation</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">name</span> <span class="o">=</span> <span class="s2">&#34;blog-static&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">version</span> <span class="o">=</span> <span class="n">rev</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">src</span> <span class="o">=</span> <span class="n">fetchgit</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">inherit</span> <span class="n">url</span> <span class="n">rev</span> <span class="n">sha256</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span> </span></span><span class="line"><span class="cl"> <span class="n">builder</span> <span class="o">=</span> <span class="sr">./builder.sh</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">converter</span> <span class="o">=</span> <span class="sr">./convert.rb</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">buildInputs</span> <span class="o">=</span> <span class="p">[</span> </span></span><span class="line"><span class="cl"> <span class="n">hugo</span> </span></span><span class="line"><span class="cl"> <span class="n">requiredPackages</span><span class="o">.</span><span class="n">katex</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">ruby</span><span class="o">.</span><span class="n">withPackages</span> <span class="p">(</span><span class="n">ps</span><span class="p">:</span> <span class="p">[</span> <span class="n">ps</span><span class="o">.</span><span class="n">nokogiri</span> <span class="p">]))</span> </span></span><span class="line"><span class="cl"> <span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div><p>I&rsquo;m using <code>node2nix</code> to generate the <code>required-packages.nix</code> file, which allows me, even from a sandboxed Nix build, to download and install <code>npm</code> packages. This is needed so that I have access to the <code>katex</code> binary at build time. I fed the following JSON file to <code>node2nix</code>:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-JSON" data-lang="JSON"><span class="line"><span class="cl"><span class="p">[</span> </span></span><span class="line"><span class="cl"> <span class="s2">&#34;katex&#34;</span> </span></span><span class="line"><span class="cl"><span class="p">]</span> </span></span></code></pre></td></tr></table> </div> </div><p>The Ruby script I wrote for this (more on that soon) required the <code>nokogiri</code> gem, which I used for traversing the HTML generated for my site. Hugo was obviously required to generate the HTML.</p> <a href="#converting-latex-to-html"> <h3 id="converting-latex-to-html">Converting LaTeX To HTML</h3> </a> <p>After my first post complaining about the state of mathematics on the web, I received the following email (which the author allowed me to share):</p> <blockquote> <p>Sorry for having a random stranger email you, but in your blog post <a href="https://danilafe.com/blog/math_rendering_is_wrong/">(link)</a> you seem to focus on MathJax&rsquo;s difficulty in rendering things server-side, while quietly ignoring that KaTeX&rsquo;s front page advertises server-side rendering. Their documentation <a href="https://katex.org/docs/options.html"class="external-link">(link)<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> even shows (at least as of the time this email was sent) that it renders both HTML (to be arranged nicely with their CSS) for visuals and MathML for accessibility.</p> </blockquote> <p>The author of the email then kindly provided a link to a page they generated using KaTeX and some Bash scripts. The math on this page was rendered at the time it was generated.</p> <p>This is a great point, and KaTeX is indeed usable for server-side rendering. But I&rsquo;ve seen few people who do actually use it. Unfortunately, as I pointed out in my previous post on the subject, few tools actually take your HTML page and replace LaTeX with rendered math. Here&rsquo;s what I wrote about this last time:</p> <blockquote> <p>[In MathJax,] The bigger issue, though, was that the <code>page2html</code> program, which rendered all the mathematics in a single HTML page, was gone. I found <code>tex2html</code> and <code>text2htmlcss</code>, which could only render equations without the surrounding HTML. I also found <code>mjpage</code>, which replaced mathematical expressions in a page with their SVG forms.</p> </blockquote> <p>This is still the case, in both MathJax and KaTeX. The ability to render math in one step is the main selling point of front-end LaTeX renderers: all you have to do is drop in a file from a CDN, and voila, you have your math. There are no such easy answers for back-end rendering. In fact, as we will soon see, it&rsquo;s not possible to just search-and-replace occurences of mathematics on your page, either. To actually get KaTeX working on the backend, you need access to tools that handle the potential variety of edge cases associated with HTML. Such tools, to my knowledge, do not currently exist.</p> <p>I decided to write my own Ruby script to get the job done. From this script, I would call the <code>katex</code> command-line program, which would perform the heavy lifting of rendering the mathematics.</p> <p>There are two types of math on my website: inline math and display math. On the command line (<a href="https://katex.org/docs/cli.html"class="external-link">here are the docs<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>), the distinction is made using the <code>--display-mode</code> argument. So, the general algorithm is to replace the code inside the <code>$$...$$</code> with their display-rendered version, and the code inside the <code>\(...\)</code> with the inline-rendered version. I came up with the following Ruby function:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Ruby" data-lang="Ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">render_cached</span><span class="p">(</span><span class="n">cache</span><span class="p">,</span> <span class="n">command</span><span class="p">,</span> <span class="n">string</span><span class="p">,</span> <span class="n">render_comment</span> <span class="o">=</span> <span class="kp">nil</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">cache</span><span class="o">.</span><span class="n">fetch</span><span class="p">(</span><span class="n">string</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="kp">new</span><span class="o">|</span> </span></span><span class="line"><span class="cl"> <span class="nb">puts</span> <span class="s2">&#34; Rendering </span><span class="si">#{</span><span class="n">render_comment</span> <span class="o">||</span> <span class="kp">new</span><span class="si">}</span><span class="s2">&#34;</span> </span></span><span class="line"><span class="cl"> <span class="n">cache</span><span class="o">[</span><span class="n">string</span><span class="o">]</span> <span class="o">=</span> <span class="no">Open3</span><span class="o">.</span><span class="n">popen3</span><span class="p">(</span><span class="n">command</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">i</span><span class="p">,</span> <span class="n">o</span><span class="p">,</span> <span class="n">e</span><span class="p">,</span> <span class="n">t</span><span class="o">|</span> </span></span><span class="line"><span class="cl"> <span class="n">i</span><span class="o">.</span><span class="n">write</span> <span class="kp">new</span> </span></span><span class="line"><span class="cl"> <span class="n">i</span><span class="o">.</span><span class="n">close</span> </span></span><span class="line"><span class="cl"> <span class="n">o</span><span class="o">.</span><span class="n">read</span><span class="o">.</span><span class="n">force_encoding</span><span class="p">(</span><span class="no">Encoding</span><span class="o">::</span><span class="no">UTF_8</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"><span class="k">end</span> </span></span></code></pre></td></tr></table> </div> </div><p>Here, the <code>cache</code> argument is used to prevent re-running the <code>katex</code> command on an equation that was already rendered before (the output is the same, after all). The <code>command</code> is the specific shell command that we want to invoke; this would be either <code>katex</code> or <code>katex -d</code>. The <code>string</code> is the math equation to render, and the <code>render_comment</code> is the string to print to the console instead of the equation (so that long, display math equations are not printed out to standard out).</p> <p>Then, given a substring of the HTML file, we use regular expressions to find the <code>\(...\)</code> and <code>$$...$$</code>s, and use the <code>render_cached</code> method on the LaTeX code inside.</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span><span class="lnt">9 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Ruby" data-lang="Ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">perform_katex_sub</span><span class="p">(</span><span class="n">inline_cache</span><span class="p">,</span> <span class="n">display_cache</span><span class="p">,</span> <span class="n">content</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">rendered</span> <span class="o">=</span> <span class="n">content</span><span class="o">.</span><span class="n">gsub</span> <span class="sr">/\\\(((?:[^\\]|\\[^\)])*)\\\)/</span> <span class="k">do</span> <span class="o">|</span><span class="n">match</span><span class="o">|</span> </span></span><span class="line"><span class="cl"> <span class="n">render_cached</span><span class="p">(</span><span class="n">inline_cache</span><span class="p">,</span> <span class="s2">&#34;katex&#34;</span><span class="p">,</span> <span class="vg">$~</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"> <span class="n">rendered</span> <span class="o">=</span> <span class="n">rendered</span><span class="o">.</span><span class="n">gsub</span> <span class="sr">/\$\$((?:[^\$]|$[^\$])*)\$\$/</span> <span class="k">do</span> <span class="o">|</span><span class="n">match</span><span class="o">|</span> </span></span><span class="line"><span class="cl"> <span class="n">render_cached</span><span class="p">(</span><span class="n">display_cache</span><span class="p">,</span> <span class="s2">&#34;katex -d&#34;</span><span class="p">,</span> <span class="vg">$~</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span><span class="p">,</span> <span class="s2">&#34;display&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">rendered</span> </span></span><span class="line"><span class="cl"><span class="k">end</span> </span></span></code></pre></td></tr></table> </div> </div><p>There&rsquo;s a bit of a trick to the final layer of this script. We want to be really careful about where we replace LaTeX, and where we don&rsquo;t. In particular, we <em>don&rsquo;t</em> want to go into the <code>code</code> tags. Otherwise, it wouldn&rsquo;t be possible to talk about LaTeX code! I also suspect that some captions, alt texts, and similar elements should also be left alone. However, I don&rsquo;t have those on my website (yet), and I won&rsquo;t worry about them now. Either way, because of the code tags, we can&rsquo;t just search-and-replace over the entire page; we need to be context aware. This is where <code>nokogiri</code> comes in. We parse the HTML, and iterate over all of the &rsquo;text&rsquo; nodes, calling <code>perform_katex_sub</code> on all of those that <em>aren&rsquo;t</em> inside code tags.</p> <p>Fortunately, this kind of iteration is pretty easy to specify thanks to something called XPath. This was my first time encountering it, but it seems extremely useful: it&rsquo;s a sort of language for selecting XML nodes. First, you provide an &lsquo;axis&rsquo;, which is used to specify the positions of the nodes you want to look at relative to the root node. The axis <code>/</code> looks at the immediate children (this would be the <code>html</code> tag in a properly formatted document, I would imagine). The axis <code>//</code> looks at all the transitive children. That is, it will look at the children of the root, then its children, and so on. There&rsquo;s also the <code>self</code> axis, which looks at the node itself.</p> <p>After you provide an axis, you need to specify the type of node that you want to select. We can write <code>code</code>, for instance, to pick only the <code>&lt;code&gt;....&lt;/code&gt;</code> tags from the axis we&rsquo;ve chosen. We can also use <code>*</code> to select any node, and we can use <code>text()</code> to select text nodes, such as the <code>Hello</code> inside of <code>&lt;b&gt;Hello&lt;/b&gt;</code>.</p> <p>We can also apply some more conditions to the nodes we pick using <code>[]</code>. For us, the relevant feature here is <code>not(...)</code>, which allows us to select nodes that do <strong>not</strong> match a particular condition. This is all we need to know.</p> <p>We write:</p> <ul> <li><code>//</code>, starting to search for nodes everywhere, not just the root of the document.</li> <li><code>*</code>, to match <em>any</em> node. We want to replace math inside of <code>div</code>s, <code>span</code>s, <code>nav</code>s, all of the <code>h</code>s, and so on.</li> <li><code>[not(self::code)]</code>, cutting out all the <code>code</code> tags.</li> <li><code>/</code>, now selecting the nodes that are immediate descendants of the nodes we&rsquo;ve selected.</li> <li><code>text()</code>, giving us the text contents of all the nodes we&rsquo;ve selected.</li> </ul> <p>All in all:</p> <pre tabindex="0"><code>//*[not(self::code)]/text() </code></pre><p>Finally, we use this XPath from <code>nokogiri</code>:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Ruby" data-lang="Ruby"><span class="line"><span class="cl"><span class="n">files</span> <span class="o">=</span> <span class="no">ARGV</span><span class="o">[</span><span class="mi">0</span><span class="o">..-</span><span class="mi">1</span><span class="o">]</span> </span></span><span class="line"><span class="cl"><span class="n">inline_cache</span><span class="p">,</span> <span class="n">display_cache</span> <span class="o">=</span> <span class="p">{},</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">files</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">file</span><span class="o">|</span> </span></span><span class="line"><span class="cl"> <span class="nb">puts</span> <span class="s2">&#34;Rendering file: </span><span class="si">#{</span><span class="n">file</span><span class="si">}</span><span class="s2">&#34;</span> </span></span><span class="line"><span class="cl"> <span class="n">document</span> <span class="o">=</span> <span class="no">Nokogiri</span><span class="o">::</span><span class="no">HTML</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="no">File</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="n">file</span><span class="p">))</span> </span></span><span class="line"><span class="cl"> <span class="n">document</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="s1">&#39;//*[not(self::code)]/text()&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">t</span><span class="o">|</span> </span></span><span class="line"><span class="cl"> <span class="n">t</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="n">perform_katex_sub</span><span class="p">(</span><span class="n">inline_cache</span><span class="p">,</span> <span class="n">display_cache</span><span class="p">,</span> <span class="n">t</span><span class="o">.</span><span class="n">content</span><span class="p">))</span> </span></span><span class="line"><span class="cl"> <span class="k">end</span> </span></span><span class="line"><span class="cl"> <span class="no">File</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">file</span><span class="p">,</span> <span class="n">document</span><span class="o">.</span><span class="n">to_html</span><span class="p">)</span> </span></span><span class="line"><span class="cl"><span class="k">end</span> </span></span></code></pre></td></tr></table> </div> </div><p>I named this script <code>convert.rb</code>; it&rsquo;s used from inside of the Nix expression and its builder, which we will cover below.</p> <a href="#tying-it-all-together"> <h3 id="tying-it-all-together">Tying it All Together</h3> </a> <p>Finally, I wanted an end-to-end script to generate HTML pages and render the LaTeX in them. I used Nix for this, but the below script will largely be compatible with a non-Nix system. I came up with the following, commenting on Nix-specific commands:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="c1"># Nix-specific; set up paths.</span> </span></span><span class="line"><span class="cl"><span class="nb">source</span> <span class="nv">$stdenv</span>/setup </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># Build site with Hugo</span> </span></span><span class="line"><span class="cl"><span class="c1"># The cp is Nix-specific; it copies the blog source into the current directory.</span> </span></span><span class="line"><span class="cl">cp -r <span class="nv">$src</span>/* . </span></span><span class="line"><span class="cl">hugo --baseUrl<span class="o">=</span><span class="s2">&#34;https://danilafe.com&#34;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># Render math in HTML and XML files.</span> </span></span><span class="line"><span class="cl"><span class="c1"># $converter is Nix-specific; you can just use convert.rb.</span> </span></span><span class="line"><span class="cl">find public/ -regex <span class="s2">&#34;public/.*\.html&#34;</span> <span class="p">|</span> xargs ruby <span class="nv">$converter</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1"># Output result</span> </span></span><span class="line"><span class="cl"><span class="c1"># $out is Nix-specific; you can replace it with your destination folder.</span> </span></span><span class="line"><span class="cl">mkdir <span class="nv">$out</span> </span></span><span class="line"><span class="cl">cp -r public/* <span class="nv">$out</span>/ </span></span></code></pre></td></tr></table> </div> </div><p>This is it! Using the two scripts, <code>convert.rb</code> and <code>builder.sh</code>, I was able to generate my blog with the math rendered on the back-end. Please note, though, that I had to add the KaTeX CSS to my website&rsquo;s <code>&lt;head&gt;</code>.</p> <a href="#caveats"> <h3 id="caveats">Caveats</h3> </a> <p>The main caveat of my approach is performance. For every piece of mathematics that I render, I invoke the <code>katex</code> command. This incurs the penalty of Node&rsquo;s startup time, every time, and makes my approach take a few dozen seconds to run on my relatively small site. The better approach would be to use a NodeJS script, rather than a Ruby one, to perform the conversion. KaTeX also provides an API, so such a NodeJS script can find the files, parse the HTML, and perform the substitutions. I did quite like using <code>nokogiri</code> here, though, and I hope that an equivalently pleasant solution exists in JavaScript.</p> <p>Re-rendering the whole website is also pretty wasteful. I rarely change the mathematics on more than one page at a time, but every time I do so, I have to re-run the script, and therefore re-render every page. This makes sense for me, since I use Nix, and my builds are pretty much always performed from scratch. On the other hand, for others, this may not be the best solution.</p> <a href="#alternatives"> <h3 id="alternatives">Alternatives</h3> </a> <p>The same person who sent me the original email above also pointed out <a href="https://github.com/Zaharid/pandoc_static_katex"class="external-link">this <code>pandoc</code> filter for KaTeX<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. I do not use Pandoc, but from what I can see, this fitler relies on Pandoc&rsquo;s <code>Math</code> AST nodes, and applies KaTeX to each of those. This should work, but wasn&rsquo;t applicable in my case, since Hugo&rsquo;s shrotcodes don&rsquo;t mix well with Pandoc. However, it certainly seems like a workable solution.</p> <a href="#conclusion"> <h3 id="conclusion">Conclusion</h3> </a> <p>With the removal of MathJax from my site, it is now completely JavaScript free, and contains virtually the same HTML that it did beforehand. This, I hope, makes it work better on devices where computational power is more limited. I also hope that it illustrates a general principle - it&rsquo;s very possible, and plausible, to render LaTeX on the back-end for a static site.</p> Compiling a Functional Language Using C++, Part 12 - Let/In and Lambdas https://danilafe.com/blog/12_compiler_let_in_lambda/ Sun, 21 Jun 2020 00:50:07 -0700 https://danilafe.com/blog/12_compiler_let_in_lambda/ <p>Now that our language&rsquo;s type system is more fleshed out and pleasant to use, it&rsquo;s time to shift our focus to the ergonomics of the language itself. I&rsquo;ve been mentioning <code>let/in</code> and <strong>lambda</strong> expressions for a while now. The former will let us create names for expressions that are limited to a certain scope (without having to create global variable bindings), while the latter will allow us to create functions without giving them any name at all.</p> <p>Let&rsquo;s take a look at <code>let/in</code> expressions first, to make sure we&rsquo;re all on the same page about what it is we&rsquo;re trying to implement. Let&rsquo;s start with some rather basic examples, and then move on to more complex ones. A very basic use of a <code>let/in</code> expression is, in Haskell:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kr">let</span> <span class="n">x</span> <span class="ow">=</span> <span class="mi">5</span> <span class="kr">in</span> <span class="n">x</span> <span class="o">+</span> <span class="n">x</span> </span></span></code></pre></div><p>In the above example, we bind the variable <code>x</code> to the value <code>5</code>, and then refer to <code>x</code> twice in the expression after the <code>in</code>. The whole snippet is one expression, evaluating to what the <code>in</code> part evaluates to. Additionally, the variable <code>x</code> does not escape the expression - <span class="sidenote"> <label class="sidenote-label" for="used-note">it cannot be used anywhere else.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="used-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Unless, of course, you bind it elsewhere; naturally, using <code>x</code> here does not forbid you from re-using the variable. <span class="sidenote-delimiter">]</span> </span> </span> </p> <p>Now, consider a slightly more complicated example:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kr">let</span> <span class="n">sum</span> <span class="n">xs</span> <span class="ow">=</span> <span class="n">foldl</span> <span class="p">(</span><span class="o">+</span><span class="p">)</span> <span class="mi">0</span> <span class="n">xs</span> <span class="kr">in</span> <span class="n">sum</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">]</span> </span></span></code></pre></div><p>Here, we&rsquo;re defining a <em>function</em> <code>sum</code>, <span class="sidenote"> <label class="sidenote-label" for="eta-note">which takes a single argument:</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="eta-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Those who favor the <a href="https://en.wikipedia.org/wiki/Tacit_programming#Functional_programming">point-free</a> programming style may be slightly twitching right now, the words <em>eta reduction</em> swirling in their mind. What do you know, <code>fold</code>-based <code>sum</code> is even one of the examples on the Wikipedia page! I assure you, I left the code as you see it deliberately, to demonstrate a principle. <span class="sidenote-delimiter">]</span> </span> </span> the list to be summed. We will want this to be valid in our language, as well. We will soon see how this particular feature is related to lambda functions, and why I&rsquo;m covering these two features in the same post.</p> <p>Let&rsquo;s step up the difficulty a bit more, with an example that, <span class="sidenote"> <label class="sidenote-label" for="translate-note">though it does not immediately translate to our language,</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="translate-note"></input><span class="sidenote-content sidenote-left"><span class="sidenote-delimiter">[note:</span> The part that doesn't translate well is the whole deal with patterns in function arguments, as well as the notion of having more than one equation for a single function, as is the case with <code>safeTail</code>. <br><br> It's not that these things are <em>impossible</em> to translate; it's just that translating them may be worthy of a post in and of itself, and would only serve to bloat and complicate this part. What can be implemented with pattern arguments can just as well be implemented using regular case expressions; I dare say most "big" functional languages actually just convert from the former to the latter as part of the compillation process. <span class="sidenote-delimiter">]</span> </span> </span> illustrates another important principle:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kr">let</span> </span></span><span class="line"><span class="cl"> <span class="n">safeTail</span> <span class="kt">[]</span> <span class="ow">=</span> <span class="kt">Nothing</span> </span></span><span class="line"><span class="cl"> <span class="n">safeTail</span> <span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="ow">=</span> <span class="kt">Just</span> <span class="n">x</span> </span></span><span class="line"><span class="cl"> <span class="n">safeTail</span> <span class="p">(</span><span class="kr">_</span><span class="kt">:</span><span class="n">xs</span><span class="p">)</span> <span class="ow">=</span> <span class="n">safeTail</span> <span class="n">xs</span> </span></span><span class="line"><span class="cl"> <span class="n">myTail</span> <span class="ow">=</span> <span class="n">safeTail</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">]</span> </span></span><span class="line"><span class="cl"><span class="kr">in</span> </span></span><span class="line"><span class="cl"> <span class="n">myTail</span> </span></span></code></pre></td></tr></table> </div> </div><p>The principle here is that definitions in <code>let/in</code> can be <strong>recursive and polymorphic</strong>. Remember the note in <a href="https://danilafe.com/blog/10_compiler_polymorphism/">part 10</a> about <a href="https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system#Let-polymorphism"class="external-link">let-polymorphism<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>? This is it: we&rsquo;re allowing polymorphic variable bindings, but only when they&rsquo;re bound in a <code>let/in</code> expression (or at the top level).</p> <p>The principles demonstrated by the last two snippets mean that compiling <code>let/in</code> expressions, at least with the power we want to give them, will require the same kind of dependency analysis we had to go through when we implemented polymorphically typed functions. That is, we will need to analyze which functions calls which other functions, and typecheck the callees before the callers. We will continue to represent callee-caller relationships using a dependency graph, in which nodes represent functions, and an edge from one function node to another means that the former function calls the latter. Below is an image of one such graph:</p> <figure><img src="https://danilafe.com/blog/12_compiler_let_in_lambda/fig_graph.png" alt="Example dependency graph without let/in expressions."><figcaption> <p>Example dependency graph without <code>let/in</code> expressions.</p> </figcaption> </figure> <p>Since we want to typecheck callees first, we effectively want to traverse the graph in reverse topological order. However, there&rsquo;s a slight issue: a topological order is only defined for acyclic graphs, and it is very possible for functions in our language to mutually call each other. To deal with this, we have to find groups of mutually recursive functions, and and treat them as a single unit, thereby eliminating cycles. In the above graph, there are two groups, as follows:</p> <figure><img src="https://danilafe.com/blog/12_compiler_let_in_lambda/fig_colored_ordered.png" alt="Previous depndency graph with mutually recursive groups highlighted."><figcaption> <p>Previous depndency graph with mutually recursive groups highlighted.</p> </figcaption> </figure> <p>As seen in the second image, according to the reverse topological order of the given graph, we will typecheck the blue group containing three functions first, since the sole function in the orange group calls one of the blue functions.</p> <p>Things are more complicated now that <code>let/in</code> expressions are able to introduce their own, polymorphic and recursive declarations. However, there is a single invariant we can establish: function definitions can only depend on functions defined at the same time as them. That is, for our purposes, functions declared in the global scope can only depend on other functions declared in the global scope, and functions declared in a <code>let/in</code> expression can only depend on other functions declared in that same expression. That&rsquo;s not to say that a function declared in a <code>let/in</code> block inside some function <code>f</code> can&rsquo;t call another globally declared function <code>g</code> - rather, we allow this, but treat the situation as though <code>f</code> depends on <code>g</code>. In contrast, it&rsquo;s not at all possible for a global function to depend on a local function, because bindings created in a <code>let/in</code> expression do not escape the expression itself. This invariant tells us that in the presence of nested function definitions, the situation looks like this:</p> <figure><img src="https://danilafe.com/blog/12_compiler_let_in_lambda/fig_subgraphs.png" alt="Previous depndency graph augmented with let/in subgraphs."><figcaption> <p>Previous depndency graph augmented with <code>let/in</code> subgraphs.</p> </figcaption> </figure> <p>In the above image, some of the original nodes in our graph now contain other, smaller graphs. Those subgraphs are the graphs created by function declarations in <code>let/in</code> expressions. Just like our top-level nodes, the nodes of these smaller graphs can depend on other nodes, and even form cycles. Within each subgraph, we will have to perform the same kind of cycle detection, resulting in something like this:</p> <figure><img src="https://danilafe.com/blog/12_compiler_let_in_lambda/fig_subgraphs_colored_all.png" alt="Augmented dependency graph with mutually recursive groups highlighted."><figcaption> <p>Augmented dependency graph with mutually recursive groups highlighted.</p> </figcaption> </figure> <p>When typechecking a function, we must be ready to perform dependency analysis at any point. What&rsquo;s more is that the free variable analysis we used to perform must now be extended to differentiate between free variables that refer to &ldquo;nearby&rdquo; definitions (i.e. within the same <code>let/in</code> expression), and &ldquo;far away&rdquo; definitions (i.e. outside of the <code>let/in</code> expression). And speaking of free variables&hellip;</p> <p>What do we do about variables that are captured by a local definition? Consider the following snippet:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">addToAll</span> <span class="n">n</span> <span class="n">xs</span> <span class="ow">=</span> <span class="n">map</span> <span class="n">addSingle</span> <span class="n">xs</span> </span></span><span class="line"><span class="cl"> <span class="kr">where</span> </span></span><span class="line"><span class="cl"> <span class="n">addSingle</span> <span class="n">x</span> <span class="ow">=</span> <span class="n">n</span> <span class="o">+</span> <span class="n">x</span> </span></span></code></pre></td></tr></table> </div> </div><p>In the code above, the variable <code>n</code>, bound on line 1, is used by <code>addSingle</code> on line 3. When a function refers to variables bound outside of itself (as <code>addSingle</code> does), it is said to be <em>capturing</em> these variables, and the function is called a <em>closure</em>. Why does this matter? On the machine level, functions are represented as sequences of instructions, and there&rsquo;s a finite number of them (as there is finite space on the machine). But there is an infinite number of <code>addSingle</code> functions! When we write <code>addToAll 5 [1,2,3]</code>, <code>addSingle</code> becomes <code>5+x</code>. When, on the other hand, we write <code>addToAll 6 [1,2,3]</code>, <code>addSingle</code> becomes <code>6+x</code>. There are certain ways to work around this - we could, for instance, dynamically create machine code in memory, and then execute it (this is called <a href="https://en.wikipedia.org/wiki/Just-in-time_compilation"class="external-link">just-in-time compilation<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>). This would end up with a collections of runtime-defined functions that can be represented as follows:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="c1">-- Version of addSingle when n = 5</span> </span></span><span class="line"><span class="cl"><span class="nf">addSingle5</span> <span class="n">x</span> <span class="ow">=</span> <span class="mi">5</span> <span class="o">+</span> <span class="n">x</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1">-- Version of addSingle when n = 6</span> </span></span><span class="line"><span class="cl"><span class="nf">addSingle6</span> <span class="n">x</span> <span class="ow">=</span> <span class="mi">6</span> <span class="o">+</span> <span class="n">x</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1">-- ... and so on ...</span> </span></span></code></pre></td></tr></table> </div> </div><p>But now, we end up creating several functions with almost identical bodies, with the exception of the free variables themselves. Wouldn&rsquo;t it be better to perform the well-known strategy of reducing code duplication by factoring out parameters, and leaving only one instance of the repeated code? We would end up with:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">addToAll</span> <span class="n">n</span> <span class="n">xs</span> <span class="ow">=</span> <span class="n">map</span> <span class="p">(</span><span class="n">addSingle</span> <span class="n">n</span><span class="p">)</span> <span class="n">xs</span> </span></span><span class="line"><span class="cl"><span class="nf">addSingle</span> <span class="n">n</span> <span class="n">x</span> <span class="ow">=</span> <span class="n">n</span> <span class="o">+</span> <span class="n">x</span> </span></span></code></pre></td></tr></table> </div> </div><p>Observe that we no longer have the &ldquo;infinite&rdquo; number of functions - the infinitude of possible behaviors is created via currying. Also note that <code>addSingle</code> <span class="sidenote"> <label class="sidenote-label" for="global-note">is now declared at the global scope,</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="global-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Wait a moment, didn't we just talk about nested polymorphic definitions, and how they change our typechecking model? If we transform our program into a bunch of global definitions, we don't need to make adjustments to our typechecking. <br><br> This is true, but why should we perform transformations on a malformed program? Typechecking before pulling functions to the global scope will help us save the work, and breaking down one dependency-searching problem (which is \(O(n^3)\) thanks to Warshall's) into smaller, independent problems may even lead to better performance. Furthermore, typechecking before program transformations will help us come up with more helpful error messages. <span class="sidenote-delimiter">]</span> </span> </span> and can be transformed into a sequence of instructions just like any other global function. It has been pulled from its <code>where</code> (which, by the way, is pretty much equivalent to a <code>let/in</code>) to the top level.</p> <p>Now, see how <code>addSingle</code> became <code>(addSingle n)</code>? If we chose to rewrite the program this way, we&rsquo;d have to find-and-replace every instance of <code>addSingle</code> in the function body, which would be tedious and require us to keep track of shadowed variables and the like. Also, what if we used a local definition twice in the original piece of code? How about something like this:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">fourthPower</span> <span class="n">x</span> <span class="ow">=</span> <span class="n">square</span> <span class="o">*</span> <span class="n">square</span> </span></span><span class="line"><span class="cl"> <span class="kr">where</span> </span></span><span class="line"><span class="cl"> <span class="n">square</span> <span class="ow">=</span> <span class="n">x</span> <span class="o">*</span> <span class="n">x</span> </span></span></code></pre></td></tr></table> </div> </div><p>Applying the strategy we saw above, we get:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">fourthPower</span> <span class="n">x</span> <span class="ow">=</span> <span class="p">(</span><span class="n">square</span> <span class="n">x</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">square</span> <span class="n">x</span><span class="p">)</span> </span></span><span class="line"><span class="cl"><span class="nf">square</span> <span class="n">x</span> <span class="ow">=</span> <span class="n">x</span> <span class="o">*</span> <span class="n">x</span> </span></span></code></pre></td></tr></table> </div> </div><p>This is valid, except that in our evaluation model, the two instances of <code>(square x)</code> will be built independently of one another, and thus, will not be shared. This, in turn, will mean that <code>square</code> will be called twice, which is not what we would expect from looking at the original program. This isn&rsquo;t good. Instead, why don&rsquo;t we keep the <code>where</code>, but modify it as follows:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">fourthPower</span> <span class="n">x</span> <span class="ow">=</span> <span class="n">square</span> <span class="o">*</span> <span class="n">square</span> </span></span><span class="line"><span class="cl"> <span class="kr">where</span> <span class="n">square</span> <span class="ow">=</span> <span class="n">square&#39;</span> <span class="n">x</span> </span></span><span class="line"><span class="cl"><span class="nf">square&#39;</span> <span class="n">x</span> <span class="ow">=</span> <span class="n">x</span> <span class="o">*</span> <span class="n">x</span> </span></span></code></pre></td></tr></table> </div> </div><p>This time, assuming we can properly implement <code>where</code>, the call to <code>square' x</code> should only occur once. Though I&rsquo;ve been using <code>where</code>, which leads to less clutter in Haskell code, the exact same approach applies to <code>let/in</code>, and that&rsquo;s what we&rsquo;ll be using in our language.</p> <p>This technique of replacing captured variables with arguments, and pulling closures into the global scope to aid compilation, is called <a href="https://en.wikipedia.org/wiki/Lambda_lifting"class="external-link">Lambda Lifting<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. Its name is no coincidence - lambda functions need to undergo the same kind of transformation as our nested definitions (unlike nested definitions, though, lambda functions need to be named). This is why they are included in this post together with <code>let/in</code>!</p> <p>What are lambda functions, by the way? A lambda function is just a function expression that doesn&rsquo;t have a name. For example, if we had Haskell code like this:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">double</span> <span class="n">x</span> <span class="ow">=</span> <span class="n">x</span> <span class="o">+</span> <span class="n">x</span> </span></span><span class="line"><span class="cl"><span class="nf">doubleList</span> <span class="n">xs</span> <span class="ow">=</span> <span class="n">map</span> <span class="n">double</span> <span class="n">xs</span> </span></span></code></pre></div><p>We could rewrite it using a lambda function as follows:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">doubleList</span> <span class="n">xs</span> <span class="ow">=</span> <span class="n">map</span> <span class="p">(</span><span class="nf">\</span><span class="n">x</span> <span class="ow">-&gt;</span> <span class="n">x</span> <span class="o">+</span> <span class="n">x</span><span class="p">)</span> <span class="n">xs</span> </span></span></code></pre></div><p>As you can see, a lambda is an expression in the form <code>\x -&gt; y</code> where <code>x</code> can be any variable and <code>y</code> can be any expression (including another lambda). This represents a function that, when applied to a value <code>x</code>, will perform the computation given by <code>y</code>. Lambdas are useful when creating single-use functions that we don&rsquo;t want to make globally available.</p> <p>Lifting lambda functions will effectively rewrite our program in the opposite direction to the one shown, replacing the lambda with a reference to a global declaration which will hold the function&rsquo;s body. Just like with <code>let/in</code>, we will represent captured variables using arguments and partial appliciation. For instance, when starting with:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">addToAll</span> <span class="n">n</span> <span class="n">xs</span> <span class="ow">=</span> <span class="n">map</span> <span class="p">(</span><span class="nf">\</span><span class="n">x</span> <span class="ow">-&gt;</span> <span class="n">n</span> <span class="o">+</span> <span class="n">x</span><span class="p">)</span> <span class="n">xs</span> </span></span></code></pre></div><p>We would output the following:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">addToAll</span> <span class="n">n</span> <span class="n">xs</span> <span class="ow">=</span> <span class="n">map</span> <span class="p">(</span><span class="n">lambda</span> <span class="n">n</span><span class="p">)</span> <span class="n">xs</span> </span></span><span class="line"><span class="cl"><span class="nf">lambda</span> <span class="n">n</span> <span class="n">x</span> <span class="ow">=</span> <span class="n">n</span> <span class="o">+</span> <span class="n">x</span> </span></span></code></pre></div><a href="#implementation"> <h3 id="implementation">Implementation</h3> </a> <p>Now that we understand what we have to do, it&rsquo;s time to jump straight into doing it. First, we need to refactor our current code to allow for the changes we&rsquo;re going to make; then, we will use the new tools we defined to implement <code>let/in</code> expressions and lambda functions.</p> <a href="#infrastructure-changes"> <h4 id="infrastructure-changes">Infrastructure Changes</h4> </a> <p>When finding captured variables, the notion of <em>free variables</em> once again becomes important. Recall that a free variable in an expression is a variable that is defined outside of that expression. Consider, for example, the expression:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kr">let</span> <span class="n">x</span> <span class="ow">=</span> <span class="mi">5</span> <span class="kr">in</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span> </span></span></code></pre></div><p>In this expression, <code>x</code> is <em>not</em> a free variable, since it&rsquo;s defined in the <code>let/in</code> expression. On the other hand, <code>y</code> <em>is</em> a free variable, since it&rsquo;s not defined locally.</p> <p>The algorithm that we used for computing free variables was rather biased. Previously, we only cared about the difference between a local variable (defined somewhere in a function&rsquo;s body, or referring to one of the function&rsquo;s parameters) and a global variable (referring to a global function). This shows in our code for <code>find_free</code>. Consider, for example, this snippet:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/11/ast.cpp" data-first-line="33" data-last-line="36"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/11/ast.cpp#L33-L36">ast.cpp</a>, lines 33 through 36</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast_lid</span><span class="o">::</span><span class="n">find_free</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="o">-&gt;</span><span class="n">env</span> <span class="o">=</span> <span class="n">env</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">env</span><span class="o">-&gt;</span><span class="n">lookup</span><span class="p">(</span><span class="n">id</span><span class="p">)</span> <span class="o">==</span> <span class="k">nullptr</span><span class="p">)</span> <span class="n">into</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">id</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We created bindings in our type environment whenever we saw a new variable being introduced, which led us to only count variables that we did not bind <em>anywhere</em> as &lsquo;free&rsquo;. This approach is no longer sufficient. Consider, for example, the following Haskell code:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">someFunction</span> <span class="n">x</span> <span class="ow">=</span> </span></span><span class="line"><span class="cl"> <span class="kr">let</span> </span></span><span class="line"><span class="cl"> <span class="n">y</span> <span class="ow">=</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">5</span> </span></span><span class="line"><span class="cl"> <span class="kr">in</span> </span></span><span class="line"><span class="cl"> <span class="n">x</span><span class="o">*</span><span class="n">y</span> </span></span></code></pre></td></tr></table> </div> </div><p>We can see that the variable <code>x</code> is introduced on line 1. Thus, our current algorithm will happily store <code>x</code> in an environment, and not count it as free. But clearly, the definition of <code>y</code> on line 3 captures <code>x</code>! If we were to lift <code>y</code> into global scope, we would need to pass <code>x</code> to it as an argument. To fix this, we have to separate the creation and assignment of type environments from free variable detection. Why don&rsquo;t we start with <code>ast</code> and its descendants? Our signatures become:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast</span><span class="o">::</span><span class="n">find_free</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="n">type_ptr</span> <span class="n">ast</span><span class="o">::</span><span class="n">typecheck</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">);</span> </span></span></code></pre></div><p>For the most part, the code remains unchanged. We avoid using <code>env</code> (and <code>this-&gt;env</code>), and default to marking any variable as a free variable:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/ast.cpp" data-first-line="39" data-last-line="41"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/ast.cpp#L39-L41">ast.cpp</a>, lines 39 through 41</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast_lid</span><span class="o">::</span><span class="n">find_free</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">into</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">id</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Since we no longer use the environment, we resort to an alternative method of removing bound variables. Here&rsquo;s <code>ast_case::find_free</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/ast.cpp" data-first-line="169" data-last-line="181"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/ast.cpp#L169-L181">ast.cpp</a>, lines 169 through 181</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">169 </span><span class="lnt">170 </span><span class="lnt">171 </span><span class="lnt">172 </span><span class="lnt">173 </span><span class="lnt">174 </span><span class="lnt">175 </span><span class="lnt">176 </span><span class="lnt">177 </span><span class="lnt">178 </span><span class="lnt">179 </span><span class="lnt">180 </span><span class="lnt">181 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast_case</span><span class="o">::</span><span class="n">find_free</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">of</span><span class="o">-&gt;</span><span class="n">find_free</span><span class="p">(</span><span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">branch</span> <span class="p">:</span> <span class="n">branches</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">free_in_branch</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">pattern_variables</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">branch</span><span class="o">-&gt;</span><span class="n">pat</span><span class="o">-&gt;</span><span class="n">find_variables</span><span class="p">(</span><span class="n">pattern_variables</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">branch</span><span class="o">-&gt;</span><span class="n">expr</span><span class="o">-&gt;</span><span class="n">find_free</span><span class="p">(</span><span class="n">free_in_branch</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">free</span> <span class="p">:</span> <span class="n">free_in_branch</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">pattern_variables</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">free</span><span class="p">)</span> <span class="o">==</span> <span class="n">pattern_variables</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> </span></span><span class="line"><span class="cl"> <span class="n">into</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">free</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>For each branch, we find the free variables. However, we want to avoid marking variables that were introduced through pattern matching as free (they are not). Thus, we use <code>pattern::find_variables</code> to see which of the variables were bound by that pattern, and remove them from the list of free variables. We can then safely add the list of free variables in the pattern to the overall list of free variables. Other <code>ast</code> descendants experience largely cosmetic changes (such as the removal of the <code>env</code> parameter).</p> <p>Of course, we must implement <code>find_variables</code> for each of our <code>pattern</code> subclasses. Here&rsquo;s what I got for <code>pattern_var</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/ast.cpp" data-first-line="402" data-last-line="404"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/ast.cpp#L402-L404">ast.cpp</a>, lines 402 through 404</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">402 </span><span class="lnt">403 </span><span class="lnt">404 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">pattern_var</span><span class="o">::</span><span class="n">find_variables</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">into</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">var</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>And here&rsquo;s an equally terse implementation for <code>pattern_constr</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/ast.cpp" data-first-line="417" data-last-line="419"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/ast.cpp#L417-L419">ast.cpp</a>, lines 417 through 419</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">417 </span><span class="lnt">418 </span><span class="lnt">419 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">pattern_constr</span><span class="o">::</span><span class="n">find_variables</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">into</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">params</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">params</span><span class="p">.</span><span class="n">end</span><span class="p">());</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We also want to update <code>definition_defn</code> with this change. Our signatures become:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">definition_defn</span><span class="o">::</span><span class="n">find_free</span><span class="p">();</span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">definition_defn</span><span class="o">::</span><span class="n">insert_types</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">,</span> <span class="n">visibility</span> <span class="n">v</span><span class="p">);</span> </span></span></code></pre></div><p>We&rsquo;ll get to the <code>visiblity</code> parameter later. The implementations are fairly simple. Just like <code>ast_case</code>, we want to erase each function&rsquo;s parameters from its list of free variables:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/definition.cpp" data-first-line="13" data-last-line="18"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/definition.cpp#L13-L18">definition.cpp</a>, lines 13 through 18</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">definition_defn</span><span class="o">::</span><span class="n">find_free</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">body</span><span class="o">-&gt;</span><span class="n">find_free</span><span class="p">(</span><span class="n">free_variables</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">param</span> <span class="p">:</span> <span class="n">params</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">free_variables</span><span class="p">.</span><span class="n">erase</span><span class="p">(</span><span class="n">param</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Since <code>find_free</code> no longer creates any type bindings or environments, this functionality is shouldered by <code>insert_types</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/definition.cpp" data-first-line="20" data-last-line="32"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/definition.cpp#L20-L32">definition.cpp</a>, lines 20 through 32</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">definition_defn</span><span class="o">::</span><span class="n">insert_types</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">,</span> <span class="n">visibility</span> <span class="n">v</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="o">-&gt;</span><span class="n">env</span> <span class="o">=</span> <span class="n">env</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">var_env</span> <span class="o">=</span> <span class="n">type_scope</span><span class="p">(</span><span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">return_type</span> <span class="o">=</span> <span class="n">mgr</span><span class="p">.</span><span class="n">new_type</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">full_type</span> <span class="o">=</span> <span class="n">return_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">params</span><span class="p">.</span><span class="n">rbegin</span><span class="p">();</span> <span class="n">it</span> <span class="o">!=</span> <span class="n">params</span><span class="p">.</span><span class="n">rend</span><span class="p">();</span> <span class="n">it</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">param_type</span> <span class="o">=</span> <span class="n">mgr</span><span class="p">.</span><span class="n">new_type</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">full_type</span> <span class="o">=</span> <span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_arr</span><span class="p">(</span><span class="n">param_type</span><span class="p">,</span> <span class="n">full_type</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="n">var_env</span><span class="o">-&gt;</span><span class="n">bind</span><span class="p">(</span><span class="o">*</span><span class="n">it</span><span class="p">,</span> <span class="n">param_type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">env</span><span class="o">-&gt;</span><span class="n">bind</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">full_type</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Now that free variables are properly computed, we are able to move on to bigger and better things.</p> <a href="#nested-definitions"> <h4 id="nested-definitions">Nested Definitions</h4> </a> <p>At present, our code for typechecking the whole program is located in <code>main.cpp</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/11/main.cpp" data-first-line="43" data-last-line="61"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/11/main.cpp#L43-L61">main.cpp</a>, lines 43 through 61</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">def_data</span> <span class="p">:</span> <span class="n">defs_data</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">def_data</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">insert_types</span><span class="p">(</span><span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">def_data</span> <span class="p">:</span> <span class="n">defs_data</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">def_data</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">insert_constructors</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">function_graph</span> <span class="n">dependency_graph</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">def_defn</span> <span class="p">:</span> <span class="n">defs_defn</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">def_defn</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">find_free</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">dependency_graph</span><span class="p">.</span><span class="n">add_function</span><span class="p">(</span><span class="n">def_defn</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">dependency</span> <span class="p">:</span> <span class="n">def_defn</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">free_variables</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">defs_defn</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">dependency</span><span class="p">)</span> <span class="o">==</span> <span class="n">defs_defn</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> </span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">dependency_graph</span><span class="p">.</span><span class="n">add_edge</span><span class="p">(</span><span class="n">def_defn</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">,</span> <span class="n">dependency</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This piece of code goes on. We now want this to be more general. Soon, <code>let/in</code> expressions with bring with them definitions that are inside other definitions, which will not be reachable at the top level. The fundamental topological sorting algorithm, though, will remain the same. We can abstract a series of definitions that need to be ordered and then typechecked into a new struct, <code>definition_group</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/definition.hpp" data-first-line="73" data-last-line="83"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/definition.hpp#L73-L83">definition.hpp</a>, lines 73 through 83</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">73 </span><span class="lnt">74 </span><span class="lnt">75 </span><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span><span class="lnt">79 </span><span class="lnt">80 </span><span class="lnt">81 </span><span class="lnt">82 </span><span class="lnt">83 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">definition_group</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">,</span> <span class="n">definition_data_ptr</span><span class="o">&gt;</span> <span class="n">defs_data</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">,</span> <span class="n">definition_defn_ptr</span><span class="o">&gt;</span> <span class="n">defs_defn</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">visibility</span> <span class="n">vis</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_env_ptr</span> <span class="n">env</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">definition_group</span><span class="p">(</span><span class="n">visibility</span> <span class="n">v</span> <span class="o">=</span> <span class="n">visibility</span><span class="o">::</span><span class="n">local</span><span class="p">)</span> <span class="o">:</span> <span class="n">vis</span><span class="p">(</span><span class="n">v</span><span class="p">)</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">find_free</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">typecheck</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">};</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This will be exactly like a list of <code>defn</code>/<code>data</code> definitions we have at the top level, except now, it can also occur in other places, like <code>let/in</code> expressions. Once again, ignore for the moment the <code>visibility</code> field.</p> <p>The way we defined function ordering requires some extra work from <code>definition_group</code>. Recall that conceptually, functions can only depend on other functions defined in the same <code>let/in</code> expression, or, more generally, in the same <code>definition_group</code>. This means that we now classify free variables in definitions into two categories: free variables that refer to &ldquo;nearby&rdquo; definitions (i.e. definitions in the same group) and free variables that refer to &ldquo;far away&rdquo; definitions. The &ldquo;nearby&rdquo; variables will be used to do topological ordering, while the &ldquo;far away&rdquo; variables can be passed along further up, perhaps into an enclosing <code>let/in</code> expression (for which &ldquo;nearby&rdquo; variables aren&rsquo;t actually free, since they are bound in the <code>let</code>). We implement this partitioning of variables in <code>definition_group::find_free</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/definition.cpp" data-first-line="94" data-last-line="105"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/definition.cpp#L94-L105">definition.cpp</a>, lines 94 through 105</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 94 </span><span class="lnt"> 95 </span><span class="lnt"> 96 </span><span class="lnt"> 97 </span><span class="lnt"> 98 </span><span class="lnt"> 99 </span><span class="lnt">100 </span><span class="lnt">101 </span><span class="lnt">102 </span><span class="lnt">103 </span><span class="lnt">104 </span><span class="lnt">105 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">definition_group</span><span class="o">::</span><span class="n">find_free</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">def_pair</span> <span class="p">:</span> <span class="n">defs_defn</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">def_pair</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">find_free</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">free_var</span> <span class="p">:</span> <span class="n">def_pair</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">free_variables</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">defs_defn</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">free_var</span><span class="p">)</span> <span class="o">==</span> <span class="n">defs_defn</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">into</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">free_var</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">def_pair</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">nearby_variables</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">free_var</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Notice that we have added a new <code>nearby_variables</code> field to <code>definition_defn</code>. This is used on line 101, and will be once again used in <code>definition_group::typecheck</code>. Speaking of <code>typecheck</code>, let&rsquo;s look at its definition:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/definition.cpp" data-first-line="107" data-last-line="145"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/definition.cpp#L107-L145">definition.cpp</a>, lines 107 through 145</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">107 </span><span class="lnt">108 </span><span class="lnt">109 </span><span class="lnt">110 </span><span class="lnt">111 </span><span class="lnt">112 </span><span class="lnt">113 </span><span class="lnt">114 </span><span class="lnt">115 </span><span class="lnt">116 </span><span class="lnt">117 </span><span class="lnt">118 </span><span class="lnt">119 </span><span class="lnt">120 </span><span class="lnt">121 </span><span class="lnt">122 </span><span class="lnt">123 </span><span class="lnt">124 </span><span class="lnt">125 </span><span class="lnt">126 </span><span class="lnt">127 </span><span class="lnt">128 </span><span class="lnt">129 </span><span class="lnt">130 </span><span class="lnt">131 </span><span class="lnt">132 </span><span class="lnt">133 </span><span class="lnt">134 </span><span class="lnt">135 </span><span class="lnt">136 </span><span class="lnt">137 </span><span class="lnt">138 </span><span class="lnt">139 </span><span class="lnt">140 </span><span class="lnt">141 </span><span class="lnt">142 </span><span class="lnt">143 </span><span class="lnt">144 </span><span class="lnt">145 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">definition_group</span><span class="o">::</span><span class="n">typecheck</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="o">-&gt;</span><span class="n">env</span> <span class="o">=</span> <span class="n">type_scope</span><span class="p">(</span><span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">def_data</span> <span class="p">:</span> <span class="n">defs_data</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">def_data</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">insert_types</span><span class="p">(</span><span class="k">this</span><span class="o">-&gt;</span><span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">def_data</span> <span class="p">:</span> <span class="n">defs_data</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">def_data</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">insert_constructors</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">function_graph</span> <span class="n">dependency_graph</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">def_defn</span> <span class="p">:</span> <span class="n">defs_defn</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">def_defn</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">find_free</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">dependency_graph</span><span class="p">.</span><span class="n">add_function</span><span class="p">(</span><span class="n">def_defn</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">dependency</span> <span class="p">:</span> <span class="n">def_defn</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">nearby_variables</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">defs_defn</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">dependency</span><span class="p">)</span> <span class="o">==</span> <span class="n">defs_defn</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> </span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">dependency_graph</span><span class="p">.</span><span class="n">add_edge</span><span class="p">(</span><span class="n">def_defn</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">,</span> <span class="n">dependency</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">group_ptr</span><span class="o">&gt;</span> <span class="n">groups</span> <span class="o">=</span> <span class="n">dependency_graph</span><span class="p">.</span><span class="n">compute_order</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">groups</span><span class="p">.</span><span class="n">rbegin</span><span class="p">();</span> <span class="n">it</span> <span class="o">!=</span> <span class="n">groups</span><span class="p">.</span><span class="n">rend</span><span class="p">();</span> <span class="n">it</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span><span class="o">&amp;</span> <span class="n">group</span> <span class="o">=</span> <span class="o">*</span><span class="n">it</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">def_defnn_name</span> <span class="p">:</span> <span class="n">group</span><span class="o">-&gt;</span><span class="n">members</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span><span class="o">&amp;</span> <span class="n">def_defn</span> <span class="o">=</span> <span class="n">defs_defn</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">def_defnn_name</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">def_defn</span><span class="o">-&gt;</span><span class="n">insert_types</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="k">this</span><span class="o">-&gt;</span><span class="n">env</span><span class="p">,</span> <span class="n">vis</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">def_defnn_name</span> <span class="p">:</span> <span class="n">group</span><span class="o">-&gt;</span><span class="n">members</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span><span class="o">&amp;</span> <span class="n">def_defn</span> <span class="o">=</span> <span class="n">defs_defn</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">def_defnn_name</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">def_defn</span><span class="o">-&gt;</span><span class="n">typecheck</span><span class="p">(</span><span class="n">mgr</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">def_defnn_name</span> <span class="p">:</span> <span class="n">group</span><span class="o">-&gt;</span><span class="n">members</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="o">-&gt;</span><span class="n">env</span><span class="o">-&gt;</span><span class="n">generalize</span><span class="p">(</span><span class="n">def_defnn_name</span><span class="p">,</span> <span class="o">*</span><span class="n">group</span><span class="p">,</span> <span class="n">mgr</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This function is a little long, but conceptually, each <code>for</code> loop contains a step of the process:</p> <ul> <li>The first loop declares all data types, so that constructors can be verified to properly reference them.</li> <li>The second loop creates all the data type constructors.</li> <li>The third loop adds edges to our dependency graph.</li> <li>The fourth loop performs typechecking on the now-ordered groups of mutually recursive functions. <ul> <li>The first inner loop inserts the types of all the functions into the environment.</li> <li>The second inner loop actually performs typechecking.</li> <li>The third inner loop makes as many things polymorphic as possible.</li> </ul> </li> </ul> <p>We can now adjust our <code>parser.y</code> to use a <code>definition_group</code> instead of two global vectors. First, we declare a global <code>definition_group</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/parser.y" data-first-line="10" data-last-line="10"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/parser.y#L10-L10">parser.y</a>, line 10</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">10 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">definition_group</span> <span class="n">global_defs</span><span class="p">;</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Then, we adjust <code>definitions</code> to create <code>definition_group</code>s:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/parser.y" data-first-line="59" data-last-line="68"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/parser.y#L59-L68">parser.y</a>, lines 59 through 68</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span><span class="lnt">65 </span><span class="lnt">66 </span><span class="lnt">67 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">program </span></span><span class="line"><span class="cl"> : definitions { global_defs = std::move($1); global_defs.vis = visibility::global; } </span></span><span class="line"><span class="cl"> ; </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">definitions </span></span><span class="line"><span class="cl"> : definitions defn { $$ = std::move($1); auto name = $2-&gt;name; $$.defs_defn[name] = std::move($2); } </span></span><span class="line"><span class="cl"> | definitions data { $$ = std::move($1); auto name = $2-&gt;name; $$.defs_data[name] = std::move($2); } </span></span><span class="line"><span class="cl"> | %empty { $$ = definition_group(); } </span></span><span class="line"><span class="cl"> ; </span></span></code></pre></td></tr></table> </div> </div> </div> <p>We can now adjust <code>main.cpp</code> to use the global <code>definition_group</code>. Among other changes (such as removing <code>extern</code> references to <code>vector</code>s, and updating function signatures) we also update the <code>typecheck_program</code> function:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/main.cpp" data-first-line="41" data-last-line="49"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/main.cpp#L41-L49">main.cpp</a>, lines 41 through 49</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">free</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">defs</span><span class="p">.</span><span class="n">find_free</span><span class="p">(</span><span class="n">free</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">defs</span><span class="p">.</span><span class="n">typecheck</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">pair</span> <span class="p">:</span> <span class="n">defs</span><span class="p">.</span><span class="n">env</span><span class="o">-&gt;</span><span class="n">names</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">pair</span><span class="p">.</span><span class="n">first</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;: &#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">pair</span><span class="p">.</span><span class="n">second</span><span class="p">.</span><span class="n">type</span><span class="o">-&gt;</span><span class="n">print</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Now, our code is ready for typechecking nested definitions, but not for compiling them. The main thing that we still have to address is the addition of new definitions to the global scope. Let&rsquo;s take a look at that next.</p> <a href="#global-definitions"> <h4 id="global-definitions">Global Definitions</h4> </a> <p>We want every function (and even non-function definitions that capture surrounding variables), regardless of whether or not it was declared in the global scope, to be processed and converted to LLVM code. The LLVM code conversion takes several steps. First, the function&rsquo;s AST is translated into G-machine instructions, which we covered in <a href="https://danilafe.com/blog/05_compiler_execution/">part 5</a>, by a process we covered in <a href="https://danilafe.com/blog/06_compiler_compilation/">part 6</a>. Then, an LLVM function is created for every function, and registered globally. Finally, the G-machine instructions are converted into LLVM IR, which is inserted into the previously created functions. These things can&rsquo;t be done in a single pass: at the very least, we can&rsquo;t start translating G-machine instructions into LLVM IR until functions are globally declared, because we would otherwise have no means of referencing other functions. It makes sense to me, then, to pull out all the &lsquo;global&rsquo; definitions into a single top-level list (perhaps somewhere in <code>main.cpp</code>).</p> <p>Let&rsquo;s start implementing this with a new <code>global_scope</code> struct. This struct will contain all of the global function and constructor definitions:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/global_scope.hpp" data-first-line="42" data-last-line="55"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/global_scope.hpp#L42-L55">global_scope.hpp</a>, lines 42 through 55</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">global_scope</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">,</span> <span class="kt">int</span><span class="o">&gt;</span> <span class="n">occurence_count</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">global_function_ptr</span><span class="o">&gt;</span> <span class="n">functions</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">global_constructor_ptr</span><span class="o">&gt;</span> <span class="n">constructors</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">global_function</span><span class="o">&amp;</span> <span class="n">add_function</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">n</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">ps</span><span class="p">,</span> <span class="n">ast_ptr</span> <span class="n">b</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">global_constructor</span><span class="o">&amp;</span> <span class="n">add_constructor</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">n</span><span class="p">,</span> <span class="kt">int8_t</span> <span class="n">t</span><span class="p">,</span> <span class="n">size_t</span> <span class="n">a</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">compile</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">generate_llvm</span><span class="p">(</span><span class="n">llvm_context</span><span class="o">&amp;</span> <span class="n">ctx</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">private</span><span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">mangle_name</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">n</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">};</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This struct will allow us to keep track of all the global definitions, emitting them as we go, and then coming back to them as necessary. There are also signs of another piece of functionality: <code>occurence_count</code> and <code>mangle_name</code>. These two will be used to handle duplicate names.</p> <p>We cannot have two global functions named the same thing, but we can easily imagine a situation in which two separate <code>let/in</code> expressions define a variable like <code>x</code>, which then needs to be lifted to the global scope. We resolve such conflicts by slightly changing - &ldquo;mangling&rdquo; - the name of one of the resulting global definitions. We allow the first global definition to be named the same as it was originally (in our example, this would be <code>x</code>). However, if we detect that a global definition <code>x</code> already exists (we track this using <code>occurence_count</code>), we rename it to <code>x_1</code>. Subsequent global definitions will end up being named <code>x_2</code>, <code>x_3</code>, and so on.</p> <p>Alright, let&rsquo;s take a look at <code>global_function</code> and <code>global_constructor</code>. Here&rsquo;s the former:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/global_scope.hpp" data-first-line="11" data-last-line="27"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/global_scope.hpp#L11-L27">global_scope.hpp</a>, lines 11 through 27</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">global_function</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">params</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">ast_ptr</span> <span class="n">body</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">instruction_ptr</span><span class="o">&gt;</span> <span class="n">instructions</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">Function</span><span class="o">*</span> <span class="n">generated_function</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">global_function</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">n</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">ps</span><span class="p">,</span> <span class="n">ast_ptr</span> <span class="n">b</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">name</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">n</span><span class="p">)),</span> <span class="n">params</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">ps</span><span class="p">)),</span> <span class="n">body</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">b</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">compile</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">declare_llvm</span><span class="p">(</span><span class="n">llvm_context</span><span class="o">&amp;</span> <span class="n">ctx</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">generate_llvm</span><span class="p">(</span><span class="n">llvm_context</span><span class="o">&amp;</span> <span class="n">ctx</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="n">global_function_ptr</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">unique_ptr</span><span class="o">&lt;</span><span class="n">global_function</span><span class="o">&gt;</span><span class="p">;</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>There&rsquo;s nothing really surprising here: all of the fields are reminiscent of <code>definition_defn</code>, though some type-related variables are missing. We also include the three compilation-related methods, <code>compile</code>, <code>declare_llvm</code>, and <code>generate_llvm</code>, which were previously in <code>definition_defn</code>. Let&rsquo;s look at <code>global_constructor</code> now:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/global_scope.hpp" data-first-line="29" data-last-line="40"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/global_scope.hpp#L29-L40">global_scope.hpp</a>, lines 29 through 40</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">global_constructor</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int8_t</span> <span class="n">tag</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">size_t</span> <span class="n">arity</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">global_constructor</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">n</span><span class="p">,</span> <span class="kt">int8_t</span> <span class="n">t</span><span class="p">,</span> <span class="n">size_t</span> <span class="n">a</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">name</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">n</span><span class="p">)),</span> <span class="n">tag</span><span class="p">(</span><span class="n">t</span><span class="p">),</span> <span class="n">arity</span><span class="p">(</span><span class="n">a</span><span class="p">)</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">generate_llvm</span><span class="p">(</span><span class="n">llvm_context</span><span class="o">&amp;</span> <span class="n">ctx</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="n">global_constructor_ptr</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">unique_ptr</span><span class="o">&lt;</span><span class="n">global_constructor</span><span class="o">&gt;</span><span class="p">;</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This maps pretty closely to a single <code>definition_data::constructor</code>. There&rsquo;s a difference here that is not clear at a glance, though. Whereas the <code>name</code> in a <code>definition_defn</code> or <code>definition_data</code> refers to the name as given by the user in the code, the <code>name</code> of a <code>global_function</code> or <code>global_constructor</code> has gone through mangling, and thus, should be unique.</p> <p>Let&rsquo;s now look at the implementation of these structs&rsquo; methods. The methods <code>add_function</code> and <code>add_constructor</code> are pretty straightforward. Here&rsquo;s the former:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/global_scope.cpp" data-first-line="39" data-last-line="43"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/global_scope.cpp#L39-L43">global_scope.cpp</a>, lines 39 through 43</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">global_function</span><span class="o">&amp;</span> <span class="n">global_scope</span><span class="o">::</span><span class="n">add_function</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">n</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">ps</span><span class="p">,</span> <span class="n">ast_ptr</span> <span class="n">b</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">global_function</span><span class="o">*</span> <span class="n">new_function</span> <span class="o">=</span> <span class="k">new</span> <span class="n">global_function</span><span class="p">(</span><span class="n">mangle_name</span><span class="p">(</span><span class="n">n</span><span class="p">),</span> <span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">ps</span><span class="p">),</span> <span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">b</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="n">functions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">global_function_ptr</span><span class="p">(</span><span class="n">new_function</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">*</span><span class="n">new_function</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>And here&rsquo;s the latter:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/global_scope.cpp" data-first-line="45" data-last-line="49"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/global_scope.cpp#L45-L49">global_scope.cpp</a>, lines 45 through 49</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">global_constructor</span><span class="o">&amp;</span> <span class="n">global_scope</span><span class="o">::</span><span class="n">add_constructor</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">n</span><span class="p">,</span> <span class="kt">int8_t</span> <span class="n">t</span><span class="p">,</span> <span class="n">size_t</span> <span class="n">a</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">global_constructor</span><span class="o">*</span> <span class="n">new_constructor</span> <span class="o">=</span> <span class="k">new</span> <span class="n">global_constructor</span><span class="p">(</span><span class="n">mangle_name</span><span class="p">(</span><span class="n">n</span><span class="p">),</span> <span class="n">t</span><span class="p">,</span> <span class="n">a</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">constructors</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">global_constructor_ptr</span><span class="p">(</span><span class="n">new_constructor</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">*</span><span class="n">new_constructor</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>In both of these functions, we return a reference to the new global definition we created. This helps us access the mangled <code>name</code> field, and, in the case of <code>global_function</code>, inspect the <code>ast_ptr</code> that represents its body.</p> <p>Next, we have <code>global_scope::compile</code> and <code>global_scope::generate_llvm</code>, which encapsulate these operations on all global definitions. Their implementations are very straightforward, and are similar to the <code>gen_llvm</code> function we used to have in our <code>main.cpp</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/global_scope.cpp" data-first-line="51" data-last-line="67"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/global_scope.cpp#L51-L67">global_scope.cpp</a>, lines 51 through 67</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span><span class="lnt">65 </span><span class="lnt">66 </span><span class="lnt">67 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">global_scope</span><span class="o">::</span><span class="n">compile</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">function</span> <span class="p">:</span> <span class="n">functions</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">function</span><span class="o">-&gt;</span><span class="n">compile</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">global_scope</span><span class="o">::</span><span class="n">generate_llvm</span><span class="p">(</span><span class="n">llvm_context</span><span class="o">&amp;</span> <span class="n">ctx</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">constructor</span> <span class="p">:</span> <span class="n">constructors</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">constructor</span><span class="o">-&gt;</span><span class="n">generate_llvm</span><span class="p">(</span><span class="n">ctx</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">function</span> <span class="p">:</span> <span class="n">functions</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">function</span><span class="o">-&gt;</span><span class="n">declare_llvm</span><span class="p">(</span><span class="n">ctx</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">function</span> <span class="p">:</span> <span class="n">functions</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">function</span><span class="o">-&gt;</span><span class="n">generate_llvm</span><span class="p">(</span><span class="n">ctx</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Finally, we have <code>mangle</code>, which takes care of potentially duplicate variable names:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/global_scope.cpp" data-first-line="69" data-last-line="83"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/global_scope.cpp#L69-L83">global_scope.cpp</a>, lines 69 through 83</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">69 </span><span class="lnt">70 </span><span class="lnt">71 </span><span class="lnt">72 </span><span class="lnt">73 </span><span class="lnt">74 </span><span class="lnt">75 </span><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span><span class="lnt">79 </span><span class="lnt">80 </span><span class="lnt">81 </span><span class="lnt">82 </span><span class="lnt">83 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">global_scope</span><span class="o">::</span><span class="n">mangle_name</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">occurence_it</span> <span class="o">=</span> <span class="n">occurence_count</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">n</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">occurence</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">occurence_it</span> <span class="o">!=</span> <span class="n">occurence_count</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">occurence</span> <span class="o">=</span> <span class="n">occurence_it</span><span class="o">-&gt;</span><span class="n">second</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">occurence_count</span><span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">=</span> <span class="n">occurence</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">final_name</span> <span class="o">=</span> <span class="n">n</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">occurence</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">final_name</span> <span class="o">+=</span> <span class="s">&#34;_&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">final_name</span> <span class="o">+=</span> <span class="n">std</span><span class="o">::</span><span class="n">to_string</span><span class="p">(</span><span class="n">occurence</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">final_name</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Let&rsquo;s move on to the global definition structs. The <code>compile</code>, <code>declare_llvm</code>, and <code>generate_llvm</code> methods for <code>global_function</code> are pretty much the same as those that we used to have in <code>definition_defn</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/global_scope.cpp" data-first-line="4" data-last-line="24"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/global_scope.cpp#L4-L24">global_scope.cpp</a>, lines 4 through 24</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">global_function</span><span class="o">::</span><span class="n">compile</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">env_ptr</span> <span class="n">new_env</span> <span class="o">=</span> <span class="n">env_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">env_offset</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="k">nullptr</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">params</span><span class="p">.</span><span class="n">rbegin</span><span class="p">();</span> <span class="n">it</span> <span class="o">!=</span> <span class="n">params</span><span class="p">.</span><span class="n">rend</span><span class="p">();</span> <span class="n">it</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">new_env</span> <span class="o">=</span> <span class="n">env_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">env_var</span><span class="p">(</span><span class="o">*</span><span class="n">it</span><span class="p">,</span> <span class="n">new_env</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">body</span><span class="o">-&gt;</span><span class="n">compile</span><span class="p">(</span><span class="n">new_env</span><span class="p">,</span> <span class="n">instructions</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">instructions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_update</span><span class="p">(</span><span class="n">params</span><span class="p">.</span><span class="n">size</span><span class="p">())));</span> </span></span><span class="line"><span class="cl"> <span class="n">instructions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_pop</span><span class="p">(</span><span class="n">params</span><span class="p">.</span><span class="n">size</span><span class="p">())));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">global_function</span><span class="o">::</span><span class="n">declare_llvm</span><span class="p">(</span><span class="n">llvm_context</span><span class="o">&amp;</span> <span class="n">ctx</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">generated_function</span> <span class="o">=</span> <span class="n">ctx</span><span class="p">.</span><span class="n">create_custom_function</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">params</span><span class="p">.</span><span class="n">size</span><span class="p">());</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">global_function</span><span class="o">::</span><span class="n">generate_llvm</span><span class="p">(</span><span class="n">llvm_context</span><span class="o">&amp;</span> <span class="n">ctx</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">builder</span><span class="p">.</span><span class="n">SetInsertPoint</span><span class="p">(</span><span class="o">&amp;</span><span class="n">generated_function</span><span class="o">-&gt;</span><span class="n">getEntryBlock</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">instruction</span> <span class="p">:</span> <span class="n">instructions</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">instruction</span><span class="o">-&gt;</span><span class="n">gen_llvm</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">generated_function</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">builder</span><span class="p">.</span><span class="n">CreateRetVoid</span><span class="p">();</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The same is true for <code>global_constructor</code> and its method <code>generate_llvm</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/global_scope.cpp" data-first-line="26" data-last-line="37"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/global_scope.cpp#L26-L37">global_scope.cpp</a>, lines 26 through 37</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">global_constructor</span><span class="o">::</span><span class="n">generate_llvm</span><span class="p">(</span><span class="n">llvm_context</span><span class="o">&amp;</span> <span class="n">ctx</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">new_function</span> <span class="o">=</span> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">create_custom_function</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">arity</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">instruction_ptr</span><span class="o">&gt;</span> <span class="n">instructions</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">instructions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_pack</span><span class="p">(</span><span class="n">tag</span><span class="p">,</span> <span class="n">arity</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"> <span class="n">instructions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_update</span><span class="p">(</span><span class="mi">0</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">builder</span><span class="p">.</span><span class="n">SetInsertPoint</span><span class="p">(</span><span class="o">&amp;</span><span class="n">new_function</span><span class="o">-&gt;</span><span class="n">getEntryBlock</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">instruction</span> <span class="p">:</span> <span class="n">instructions</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">instruction</span><span class="o">-&gt;</span><span class="n">gen_llvm</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">new_function</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">builder</span><span class="p">.</span><span class="n">CreateRetVoid</span><span class="p">();</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Recall that in this case, we need not have two methods for declaring and generating LLVM, since constructors don&rsquo;t reference other constructors, and are always generated before any function definitions.</p> <a href="#visibility"> <h4 id="visibility">Visibility</h4> </a> <p>Should we really be turning <em>all</em> free variables in a function definition into arguments? Consider the following piece of Haskell code:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">add</span> <span class="n">x</span> <span class="n">y</span> <span class="ow">=</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span> </span></span><span class="line"><span class="cl"><span class="nf">mul</span> <span class="n">x</span> <span class="n">y</span> <span class="ow">=</span> <span class="n">x</span> <span class="o">*</span> <span class="n">y</span> </span></span><span class="line"><span class="cl"><span class="nf">something</span> <span class="ow">=</span> <span class="n">mul</span> <span class="p">(</span><span class="n">add</span> <span class="mi">1</span> <span class="mi">3</span><span class="p">)</span> <span class="mi">3</span> </span></span></code></pre></td></tr></table> </div> </div><p>In the definition of <code>something</code>, <code>mul</code> and <code>add</code> occur free. A very naive lifting algorithm might be tempted to rewrite such a program as follows:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">add</span> <span class="n">x</span> <span class="n">y</span> <span class="ow">=</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span> </span></span><span class="line"><span class="cl"><span class="nf">mul</span> <span class="n">x</span> <span class="n">y</span> <span class="ow">=</span> <span class="n">x</span> <span class="o">*</span> <span class="n">y</span> </span></span><span class="line"><span class="cl"><span class="nf">something&#39;</span> <span class="n">add</span> <span class="n">mul</span> <span class="ow">=</span> <span class="n">mul</span> <span class="p">(</span><span class="n">add</span> <span class="mi">1</span> <span class="mi">3</span><span class="p">)</span> <span class="mi">3</span> </span></span><span class="line"><span class="cl"><span class="nf">something</span> <span class="ow">=</span> <span class="n">something&#39;</span> <span class="n">add</span> <span class="n">mul</span> </span></span></code></pre></td></tr></table> </div> </div><p>But that&rsquo;s absurd! Not only are <code>add</code> and <code>mul</code> available globally, but such a rewrite generates another definition with free variables, which means we didn&rsquo;t really improve our program in any way. From this example, we can see that we don&rsquo;t want to be turning reference to global variables into function parameters. But how can we tell if a variable we&rsquo;re trying to operate on is global or not? I propose a flag in our <code>type_env</code>, which we&rsquo;ll augment to be used as a symbol table. To do this, we update the implementation of <code>type_env</code> to map variables to values of a struct <code>variable_data</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/type_env.hpp" data-first-line="14" data-last-line="23"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/type_env.hpp#L14-L23">type_env.hpp</a>, lines 14 through 23</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="k">struct</span> <span class="nc">variable_data</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_scheme_ptr</span> <span class="n">type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">visibility</span> <span class="n">vis</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">mangled_name</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">variable_data</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">variable_data</span><span class="p">(</span><span class="k">nullptr</span><span class="p">,</span> <span class="n">visibility</span><span class="o">::</span><span class="n">local</span><span class="p">,</span> <span class="s">&#34;&#34;</span><span class="p">)</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> <span class="n">variable_data</span><span class="p">(</span><span class="n">type_scheme_ptr</span> <span class="n">t</span><span class="p">,</span> <span class="n">visibility</span> <span class="n">v</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">n</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">type</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">t</span><span class="p">)),</span> <span class="n">vis</span><span class="p">(</span><span class="n">v</span><span class="p">),</span> <span class="n">mangled_name</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">n</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The <code>visibility</code> enum is defined as follows:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/type_env.hpp" data-first-line="11" data-last-line="11"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/type_env.hpp#L11-L11">type_env.hpp</a>, line 11</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">11 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">enum</span> <span class="k">class</span> <span class="nc">visibility</span> <span class="p">{</span> <span class="n">global</span><span class="p">,</span><span class="n">local</span> <span class="p">};</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>As you can see from the above snippet, we also added a <code>mangled_name</code> field to the new <code>variable_data</code> struct. We will be using this field shortly. We also add a few methods to our <code>type_env</code>, and end up with the following:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/type_env.hpp" data-first-line="32" data-last-line="45"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/type_env.hpp#L32-L45">type_env.hpp</a>, lines 32 through 45</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">find_free</span><span class="p">(</span><span class="k">const</span> <span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">find_free_except</span><span class="p">(</span><span class="k">const</span> <span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="k">const</span> <span class="n">group</span><span class="o">&amp;</span> <span class="n">avoid</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_scheme_ptr</span> <span class="nf">lookup</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">bool</span> <span class="nf">is_global</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">set_mangled_name</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">,</span> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">mangled</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">get_mangled_name</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="nf">lookup_type</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">bind</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">,</span> <span class="n">type_ptr</span> <span class="n">t</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">visibility</span> <span class="n">v</span> <span class="o">=</span> <span class="n">visibility</span><span class="o">::</span><span class="n">local</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">bind</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">,</span> <span class="n">type_scheme_ptr</span> <span class="n">t</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">visibility</span> <span class="n">v</span> <span class="o">=</span> <span class="n">visibility</span><span class="o">::</span><span class="n">local</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">bind_type</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">type_name</span><span class="p">,</span> <span class="n">type_ptr</span> <span class="n">t</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">generalize</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">,</span> <span class="k">const</span> <span class="n">group</span><span class="o">&amp;</span> <span class="n">grp</span><span class="p">,</span> <span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">);</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We will come back to <code>find_free</code> and <code>find_free_except</code>, as well as <code>set_mangled_name</code> and <code>get_mangled_name</code>. For now, we just adjust <code>bind</code> to take a visibility parameter that defaults to <code>local</code>, and implement <code>is_global</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/type_env.cpp" data-first-line="27" data-last-line="32"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/type_env.cpp#L27-L32">type_env.cpp</a>, lines 27 through 32</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">bool</span> <span class="n">type_env</span><span class="o">::</span><span class="n">is_global</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">names</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">it</span> <span class="o">!=</span> <span class="n">names</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> <span class="k">return</span> <span class="n">it</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">.</span><span class="n">vis</span> <span class="o">==</span> <span class="n">visibility</span><span class="o">::</span><span class="n">global</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">parent</span><span class="p">)</span> <span class="k">return</span> <span class="n">parent</span><span class="o">-&gt;</span><span class="n">is_global</span><span class="p">(</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">false</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Remember the <code>visibility::global</code> in <code>parser.y</code>? This is where that comes in. Specifically, we recall that <code>definition_defn::insert_types</code> is responsible for placing function types into the environment, making them accessible during typechecking later. At this time, we already need to know whether or not the definitions are global or local (so that we can create the binding). Thus, we add <code>visibility</code> as a parameter to <code>insert_types</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/definition.hpp" data-first-line="44" data-last-line="44"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/definition.hpp#L44-L44">definition.hpp</a>, line 44</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">44 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">insert_types</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">,</span> <span class="n">visibility</span> <span class="n">v</span><span class="p">);</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Since we are now moving from manually wrangling definitions towards using <code>definition_group</code>, we make it so that the group itself provides this argument. To do this, we add the <code>visibility</code> field from before to it, and set it in the parser. One more thing: since constructors never capture variables, we can always move them straight to the global scope, and thus, we&rsquo;ll always mark them with <code>visibility::global</code>.</p> <a href="#managing-mangled-names"> <h4 id="managing-mangled-names">Managing Mangled Names</h4> </a> <p>Just mangling names is not enough. Consider the following program:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">defn packOne x = { </span></span><span class="line"><span class="cl"> let { </span></span><span class="line"><span class="cl"> data Packed a = { Pack a } </span></span><span class="line"><span class="cl"> } in { </span></span><span class="line"><span class="cl"> Pack x </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl">} </span></span><span class="line"><span class="cl">defn packTwo x = { </span></span><span class="line"><span class="cl"> let { </span></span><span class="line"><span class="cl"> data Packed a = { Pack a } </span></span><span class="line"><span class="cl"> } in { </span></span><span class="line"><span class="cl"> Pack x </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl">} </span></span></code></pre></td></tr></table> </div> </div><p><span class="sidenote"> <label class="sidenote-label" for="lifting-types-note">Lifting the data type declarations</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="lifting-types-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> We are actually not <em>quite</em> doing something like the following snippet. The reason for this is that we don't mangle the names for types. I pointed out this potential issue in a sidenote in the previous post. Since the size of this post is already balooning, I will not deal with this issue here. Even at the end of this post, our compiler will not be able to distinguish between the two <code>Packed</code> types. We will hopefully get to it later. <span class="sidenote-delimiter">]</span> </span> </span> and their constructors into the global scope gives us something like:</p> <pre tabindex="0"><code>data Packed a = { Pack a } data Packed_1 a = { Pack_1 a } defn packOne x = { Pack x } defn packTwo x = { Pack_1 x } </code></pre><p>Notice that we had to rename one of the calls to <code>Pack</code> to be a call to be <code>Pack_1</code>. To actually change our AST to reference <code>Pack_1</code>, we&rsquo;d have to traverse the whole tree, and make sure to keep track of definitions that could shadow <code>Pack</code> further down. This is cumbersome. Instead, we can mark a variable as referring to a mangled version of itself, and access this information when needed. To do this, we add the <code>mangled_name</code> field to the <code>variable_data</code> struct as we&rsquo;ve seen above, and implement the <code>set_mangled_name</code> and <code>get_mangled_name</code> methods. The former:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/type_env.cpp" data-first-line="34" data-last-line="37"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/type_env.cpp#L34-L37">type_env.cpp</a>, lines 34 through 37</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">type_env</span><span class="o">::</span><span class="n">set_mangled_name</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">,</span> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">mangled</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">names</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">it</span> <span class="o">!=</span> <span class="n">names</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> <span class="n">it</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">.</span><span class="n">mangled_name</span> <span class="o">=</span> <span class="n">mangled</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>And the latter:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/type_env.cpp" data-first-line="39" data-last-line="45"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/type_env.cpp#L39-L45">type_env.cpp</a>, lines 39 through 45</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">type_env</span><span class="o">::</span><span class="n">get_mangled_name</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">names</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">it</span> <span class="o">!=</span> <span class="n">names</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">(</span><span class="n">it</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">.</span><span class="n">mangled_name</span> <span class="o">!=</span> <span class="s">&#34;&#34;</span><span class="p">)</span> <span class="o">?</span> <span class="n">it</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">.</span><span class="nl">mangled_name</span> <span class="p">:</span> <span class="n">name</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">parent</span><span class="p">)</span> <span class="k">return</span> <span class="n">parent</span><span class="o">-&gt;</span><span class="n">get_mangled_name</span><span class="p">(</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">name</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We don&rsquo;t allow <code>set_mangled_name</code> to affect variables that are declared above the receiving <code>type_env</code>, and use the empty string as a &rsquo;none&rsquo; value. Now, when lifting data type constructors, we&rsquo;ll be able to use <code>set_mangled_name</code> to make sure constructor calls are made correctly. We will also be able to use this in other cases, like the translation of local function definitions.</p> <a href="#new-ast-nodes"> <h4 id="new-ast-nodes">New AST Nodes</h4> </a> <p>Finally, it&rsquo;s time for us to add new AST nodes to our language. Specifically, these nodes are <code>ast_let</code> (for <code>let/in</code> expressions) and <code>ast_lambda</code> for lambda functions. We declare them as follows:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/ast.hpp" data-first-line="131" data-last-line="166"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/ast.hpp#L131-L166">ast.hpp</a>, lines 131 through 166</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">131 </span><span class="lnt">132 </span><span class="lnt">133 </span><span class="lnt">134 </span><span class="lnt">135 </span><span class="lnt">136 </span><span class="lnt">137 </span><span class="lnt">138 </span><span class="lnt">139 </span><span class="lnt">140 </span><span class="lnt">141 </span><span class="lnt">142 </span><span class="lnt">143 </span><span class="lnt">144 </span><span class="lnt">145 </span><span class="lnt">146 </span><span class="lnt">147 </span><span class="lnt">148 </span><span class="lnt">149 </span><span class="lnt">150 </span><span class="lnt">151 </span><span class="lnt">152 </span><span class="lnt">153 </span><span class="lnt">154 </span><span class="lnt">155 </span><span class="lnt">156 </span><span class="lnt">157 </span><span class="lnt">158 </span><span class="lnt">159 </span><span class="lnt">160 </span><span class="lnt">161 </span><span class="lnt">162 </span><span class="lnt">163 </span><span class="lnt">164 </span><span class="lnt">165 </span><span class="lnt">166 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">ast_let</span> <span class="o">:</span> <span class="k">public</span> <span class="n">ast</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">using</span> <span class="n">basic_definition</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">pair</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">,</span> <span class="n">ast_ptr</span><span class="o">&gt;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">definition_group</span> <span class="n">definitions</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">ast_ptr</span> <span class="n">in</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">basic_definition</span><span class="o">&gt;</span> <span class="n">translated_definitions</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">ast_let</span><span class="p">(</span><span class="n">definition_group</span> <span class="n">g</span><span class="p">,</span> <span class="n">ast_ptr</span> <span class="n">i</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">definitions</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">g</span><span class="p">)),</span> <span class="n">in</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">i</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">print</span><span class="p">(</span><span class="kt">int</span> <span class="n">indent</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">find_free</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="nf">typecheck</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">translate</span><span class="p">(</span><span class="n">global_scope</span><span class="o">&amp;</span> <span class="n">scope</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">compile</span><span class="p">(</span><span class="k">const</span> <span class="n">env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">instruction_ptr</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">ast_lambda</span> <span class="o">:</span> <span class="k">public</span> <span class="n">ast</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">params</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">ast_ptr</span> <span class="n">body</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">type_env_ptr</span> <span class="n">var_env</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">free_variables</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">ast_ptr</span> <span class="n">translated</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">ast_lambda</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">ps</span><span class="p">,</span> <span class="n">ast_ptr</span> <span class="n">b</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">params</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">ps</span><span class="p">)),</span> <span class="n">body</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">b</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">print</span><span class="p">(</span><span class="kt">int</span> <span class="n">indent</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">find_free</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="nf">typecheck</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">translate</span><span class="p">(</span><span class="n">global_scope</span><span class="o">&amp;</span> <span class="n">scope</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">compile</span><span class="p">(</span><span class="k">const</span> <span class="n">env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">instruction_ptr</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>In <code>ast_let</code>, the <code>definitions</code> field corresponds to the original definitions given by the user in the program, and the <code>in</code> field corresponds to the expression which uses these definitions. In the process of lifting, though, we eventually transfer each of the definitions to the global scope, replacing their right hand sides with partial applications. After this transformation, all the data type definitions are effectively gone, and all the function definitions are converted into the simple form <code>x = f a1 ... an</code>. We hold these post-transformation equations in the <code>translated_definitions</code> field, and it&rsquo;s them that we compile in this node&rsquo;s <code>compile</code> method.</p> <p>In <code>ast_lambda</code>, we allow multiple parameters (like Haskell&rsquo;s <code>\x y -&gt; x + y</code>). We store these parameters in the <code>params</code> field, and we store the lambda&rsquo;s expression in the <code>body</code> field. Just like <code>definition_defn</code>, the <code>ast_lambda</code> node maintains a separate environment in which its children have been bound, and a list of variables that occur freely in its body. The former is used for typechecking, while the latter is used for lifting. Finally, the <code>translated</code> field holds the lambda function&rsquo;s form after its body has been transformed into a global function. Similarly to <code>ast_let</code>, this node will be in the form <code>f a1 ... an</code>.</p> <p>The observant reader will have noticed that we have a new method: <code>translate</code>. This is a new method for all <code>ast</code> descendants, and will implement the steps of moving definitions to the global scope and transforming the program. Before we get to it, though, let&rsquo;s look at the other relevant pieces of code for <code>ast_let</code> and <code>ast_lambda</code>. First, their grammar rules in <code>parser.y</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/parser.y" data-first-line="107" data-last-line="115"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/parser.y#L107-L115">parser.y</a>, lines 107 through 115</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">107 </span><span class="lnt">108 </span><span class="lnt">109 </span><span class="lnt">110 </span><span class="lnt">111 </span><span class="lnt">112 </span><span class="lnt">113 </span><span class="lnt">114 </span><span class="lnt">115 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">let </span></span><span class="line"><span class="cl"> : LET OCURLY definitions CCURLY IN OCURLY aAdd CCURLY </span></span><span class="line"><span class="cl"> { $$ = ast_ptr(new ast_let(std::move($3), std::move($7))); } </span></span><span class="line"><span class="cl"> ; </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">lambda </span></span><span class="line"><span class="cl"> : BACKSLASH lowercaseParams ARROW OCURLY aAdd CCURLY </span></span><span class="line"><span class="cl"> { $$ = ast_ptr(new ast_lambda(std::move($2), std::move($5))); } </span></span><span class="line"><span class="cl"> ;</span></span></code></pre></td></tr></table> </div> </div> </div> <p>This is pretty similar to the rest of the grammar, so I will give this no further explanation. Next, their <code>find_free</code> and <code>typecheck</code> code. We can start with <code>ast_let</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/ast.cpp" data-first-line="275" data-last-line="289"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/ast.cpp#L275-L289">ast.cpp</a>, lines 275 through 289</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">275 </span><span class="lnt">276 </span><span class="lnt">277 </span><span class="lnt">278 </span><span class="lnt">279 </span><span class="lnt">280 </span><span class="lnt">281 </span><span class="lnt">282 </span><span class="lnt">283 </span><span class="lnt">284 </span><span class="lnt">285 </span><span class="lnt">286 </span><span class="lnt">287 </span><span class="lnt">288 </span><span class="lnt">289 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast_let</span><span class="o">::</span><span class="n">find_free</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">definitions</span><span class="p">.</span><span class="n">find_free</span><span class="p">(</span><span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">all_free</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">in</span><span class="o">-&gt;</span><span class="n">find_free</span><span class="p">(</span><span class="n">all_free</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">free_var</span> <span class="p">:</span> <span class="n">all_free</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">definitions</span><span class="p">.</span><span class="n">defs_defn</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">free_var</span><span class="p">)</span> <span class="o">==</span> <span class="n">definitions</span><span class="p">.</span><span class="n">defs_defn</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> </span></span><span class="line"><span class="cl"> <span class="n">into</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">free_var</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">type_ptr</span> <span class="n">ast_let</span><span class="o">::</span><span class="n">typecheck</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="o">-&gt;</span><span class="n">env</span> <span class="o">=</span> <span class="n">env</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">definitions</span><span class="p">.</span><span class="n">typecheck</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">in</span><span class="o">-&gt;</span><span class="n">typecheck</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">definitions</span><span class="p">.</span><span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>As you can see, <code>ast_let::find_free</code> works in a similar manner to <code>ast_case::find_free</code>. It finds the free variables in the <code>in</code> node as well as in each of the definitions (taking advantage of the fact that <code>definition_group::find_free</code> populates the given set with &ldquo;far away&rdquo; free variables). It then filters out any variables bound in the <code>let</code> from the set of free variables in <code>in</code>, and returns the result.</p> <p>Typechecking in <code>ast_let</code> relies on <code>definition_group::typecheck</code>, which holds all of the required functionality for checking the new definitions. Once the definitions are typechecked, we use their type information to typecheck the <code>in</code> part of the expression (passing <code>definitions.env</code> to the call to <code>typecheck</code> to make the new definitions visible).</p> <p>Next, we look at <code>ast_lambda</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/ast.cpp" data-first-line="344" data-last-line="366"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/ast.cpp#L344-L366">ast.cpp</a>, lines 344 through 366</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">344 </span><span class="lnt">345 </span><span class="lnt">346 </span><span class="lnt">347 </span><span class="lnt">348 </span><span class="lnt">349 </span><span class="lnt">350 </span><span class="lnt">351 </span><span class="lnt">352 </span><span class="lnt">353 </span><span class="lnt">354 </span><span class="lnt">355 </span><span class="lnt">356 </span><span class="lnt">357 </span><span class="lnt">358 </span><span class="lnt">359 </span><span class="lnt">360 </span><span class="lnt">361 </span><span class="lnt">362 </span><span class="lnt">363 </span><span class="lnt">364 </span><span class="lnt">365 </span><span class="lnt">366 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast_lambda</span><span class="o">::</span><span class="n">find_free</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">body</span><span class="o">-&gt;</span><span class="n">find_free</span><span class="p">(</span><span class="n">free_variables</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">param</span> <span class="p">:</span> <span class="n">params</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">free_variables</span><span class="p">.</span><span class="n">erase</span><span class="p">(</span><span class="n">param</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">into</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">free_variables</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">free_variables</span><span class="p">.</span><span class="n">end</span><span class="p">());</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">type_ptr</span> <span class="n">ast_lambda</span><span class="o">::</span><span class="n">typecheck</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="o">-&gt;</span><span class="n">env</span> <span class="o">=</span> <span class="n">env</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">var_env</span> <span class="o">=</span> <span class="n">type_scope</span><span class="p">(</span><span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">return_type</span> <span class="o">=</span> <span class="n">mgr</span><span class="p">.</span><span class="n">new_type</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">full_type</span> <span class="o">=</span> <span class="n">return_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">params</span><span class="p">.</span><span class="n">rbegin</span><span class="p">();</span> <span class="n">it</span> <span class="o">!=</span> <span class="n">params</span><span class="p">.</span><span class="n">rend</span><span class="p">();</span> <span class="n">it</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">param_type</span> <span class="o">=</span> <span class="n">mgr</span><span class="p">.</span><span class="n">new_type</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">var_env</span><span class="o">-&gt;</span><span class="n">bind</span><span class="p">(</span><span class="o">*</span><span class="n">it</span><span class="p">,</span> <span class="n">param_type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">full_type</span> <span class="o">=</span> <span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_arr</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">param_type</span><span class="p">),</span> <span class="n">full_type</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">mgr</span><span class="p">.</span><span class="n">unify</span><span class="p">(</span><span class="n">return_type</span><span class="p">,</span> <span class="n">body</span><span class="o">-&gt;</span><span class="n">typecheck</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">var_env</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">full_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Again, <code>ast_lambda::find_free</code> works similarly to <code>definition_defn</code>, stripping the variables expected by the function from the body&rsquo;s list of free variables. Also like <code>definition_defn</code>, this new node remembers the free variables in its body, which we will later use for lifting.</p> <p>Typechecking in this node also proceeds similarly to <code>definition_defn</code>. We create new type variables for each parameter and for the return value, and build up a function type called <code>full_type</code>. We then typecheck the body using the new environment (which now includes the variables), and return the function type we came up with.</p> <a href="#translation"> <h4 id="translation">Translation</h4> </a> <p>Recalling the transformations we described earlier, we can observe two major steps to what we have to do:</p> <ol> <li>Move the body of the original definition into its own global definition, adding all the captured variables as arguments.</li> <li>Replace the right hand side of the <code>let/in</code> expression with an application of the global definition to the variables it requires.</li> </ol> <p>We will implement these in a new <code>translate</code> method, with the following signature:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast</span><span class="o">::</span><span class="n">translate</span><span class="p">(</span><span class="n">global_scope</span><span class="o">&amp;</span> <span class="n">scope</span><span class="p">);</span> </span></span></code></pre></div><p>The <code>scope</code> parameter and its <code>add_function</code> and <code>add_constructor</code> methods will be used to add definitions to the global scope. Each AST node will also use this method to implement the second step. Currently, only <code>ast_let</code> and <code>ast_lambda</code> will need to modify themselves - all other nodes will simply recursively call this method on their children. Let&rsquo;s jump straight into implementing this method for <code>ast_let</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/ast.cpp" data-first-line="291" data-last-line="316"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/ast.cpp#L291-L316">ast.cpp</a>, lines 291 through 316</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">291 </span><span class="lnt">292 </span><span class="lnt">293 </span><span class="lnt">294 </span><span class="lnt">295 </span><span class="lnt">296 </span><span class="lnt">297 </span><span class="lnt">298 </span><span class="lnt">299 </span><span class="lnt">300 </span><span class="lnt">301 </span><span class="lnt">302 </span><span class="lnt">303 </span><span class="lnt">304 </span><span class="lnt">305 </span><span class="lnt">306 </span><span class="lnt">307 </span><span class="lnt">308 </span><span class="lnt">309 </span><span class="lnt">310 </span><span class="lnt">311 </span><span class="lnt">312 </span><span class="lnt">313 </span><span class="lnt">314 </span><span class="lnt">315 </span><span class="lnt">316 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast_let</span><span class="o">::</span><span class="n">translate</span><span class="p">(</span><span class="n">global_scope</span><span class="o">&amp;</span> <span class="n">scope</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">def</span> <span class="p">:</span> <span class="n">definitions</span><span class="p">.</span><span class="n">defs_data</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">def</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">into_globals</span><span class="p">(</span><span class="n">scope</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">def</span> <span class="p">:</span> <span class="n">definitions</span><span class="p">.</span><span class="n">defs_defn</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">size_t</span> <span class="n">original_params</span> <span class="o">=</span> <span class="n">def</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">params</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">original_name</span> <span class="o">=</span> <span class="n">def</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span><span class="o">&amp;</span> <span class="n">global_definition</span> <span class="o">=</span> <span class="n">def</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">into_global</span><span class="p">(</span><span class="n">scope</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">size_t</span> <span class="n">captured</span> <span class="o">=</span> <span class="n">global_definition</span><span class="p">.</span><span class="n">params</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">-</span> <span class="n">original_params</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">type_env_ptr</span> <span class="n">mangled_env</span> <span class="o">=</span> <span class="n">type_scope</span><span class="p">(</span><span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">mangled_env</span><span class="o">-&gt;</span><span class="n">bind</span><span class="p">(</span><span class="n">def</span><span class="p">.</span><span class="n">first</span><span class="p">,</span> <span class="n">env</span><span class="o">-&gt;</span><span class="n">lookup</span><span class="p">(</span><span class="n">def</span><span class="p">.</span><span class="n">first</span><span class="p">),</span> <span class="n">visibility</span><span class="o">::</span><span class="n">global</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">mangled_env</span><span class="o">-&gt;</span><span class="n">set_mangled_name</span><span class="p">(</span><span class="n">def</span><span class="p">.</span><span class="n">first</span><span class="p">,</span> <span class="n">global_definition</span><span class="p">.</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">ast_ptr</span> <span class="nf">global_app</span><span class="p">(</span><span class="k">new</span> <span class="n">ast_lid</span><span class="p">(</span><span class="n">original_name</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="n">global_app</span><span class="o">-&gt;</span><span class="n">env</span> <span class="o">=</span> <span class="n">mangled_env</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">param</span> <span class="p">:</span> <span class="n">global_definition</span><span class="p">.</span><span class="n">params</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">captured</span><span class="o">--</span><span class="p">))</span> <span class="k">break</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">ast_ptr</span> <span class="nf">new_arg</span><span class="p">(</span><span class="k">new</span> <span class="n">ast_lid</span><span class="p">(</span><span class="n">param</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="n">new_arg</span><span class="o">-&gt;</span><span class="n">env</span> <span class="o">=</span> <span class="n">env</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">global_app</span> <span class="o">=</span> <span class="n">ast_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">ast_app</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">global_app</span><span class="p">),</span> <span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">new_arg</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"> <span class="n">global_app</span><span class="o">-&gt;</span><span class="n">env</span> <span class="o">=</span> <span class="n">env</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">translated_definitions</span><span class="p">.</span><span class="n">push_back</span><span class="p">({</span> <span class="n">def</span><span class="p">.</span><span class="n">first</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">global_app</span><span class="p">)</span> <span class="p">});</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">in</span><span class="o">-&gt;</span><span class="n">translate</span><span class="p">(</span><span class="n">scope</span><span class="p">);</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Since data type definitions don&rsquo;t really depend on anything else, we process them first. This amounts to simply calling the <code>definition_data::into_globals</code> method, which itself simply calls <code>global_scope::add_constructor</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/definition.cpp" data-first-line="86" data-last-line="92"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/definition.cpp#L86-L92">definition.cpp</a>, lines 86 through 92</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">86 </span><span class="lnt">87 </span><span class="lnt">88 </span><span class="lnt">89 </span><span class="lnt">90 </span><span class="lnt">91 </span><span class="lnt">92 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">definition_data</span><span class="o">::</span><span class="n">into_globals</span><span class="p">(</span><span class="n">global_scope</span><span class="o">&amp;</span> <span class="n">scope</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">constructor</span> <span class="p">:</span> <span class="n">constructors</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">global_constructor</span><span class="o">&amp;</span> <span class="n">c</span> <span class="o">=</span> <span class="n">scope</span><span class="p">.</span><span class="n">add_constructor</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="n">constructor</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">,</span> <span class="n">constructor</span><span class="o">-&gt;</span><span class="n">tag</span><span class="p">,</span> <span class="n">constructor</span><span class="o">-&gt;</span><span class="n">types</span><span class="p">.</span><span class="n">size</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="n">env</span><span class="o">-&gt;</span><span class="n">set_mangled_name</span><span class="p">(</span><span class="n">constructor</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">,</span> <span class="n">c</span><span class="p">.</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Note how <code>into_globals</code> updates the mangled name of its constructor via <code>set_mangled_name</code>. This will help us decide which global function to call during code generation. More on that later.</p> <p>Starting with line 295, we start processing the function definitions in the <code>let/in</code> expression. We remember how many arguments were explicitly added to the function definition, and then call the definition&rsquo;s <code>into_global</code> method. This method is implemented as follows:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/definition.cpp" data-first-line="40" data-last-line="49"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/definition.cpp#L40-L49">definition.cpp</a>, lines 40 through 49</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">global_function</span><span class="o">&amp;</span> <span class="n">definition_defn</span><span class="o">::</span><span class="n">into_global</span><span class="p">(</span><span class="n">global_scope</span><span class="o">&amp;</span> <span class="n">scope</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">all_params</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">free</span> <span class="p">:</span> <span class="n">free_variables</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">env</span><span class="o">-&gt;</span><span class="n">is_global</span><span class="p">(</span><span class="n">free</span><span class="p">))</span> <span class="k">continue</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">all_params</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">free</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">all_params</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">all_params</span><span class="p">.</span><span class="n">end</span><span class="p">(),</span> <span class="n">params</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">params</span><span class="p">.</span><span class="n">end</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="n">body</span><span class="o">-&gt;</span><span class="n">translate</span><span class="p">(</span><span class="n">scope</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">scope</span><span class="p">.</span><span class="n">add_function</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">all_params</span><span class="p">),</span> <span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">body</span><span class="p">));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>First, this method collects all the non-global free variables in its body, which will need to be passed to the global definition as arguments. It then combines this list with the arguments the user explicitly added to it, recursively translates its body, and creates a new global definition using <code>add_function</code>.</p> <p>We return to <code>ast_let::translate</code> at line 299. Here, we determine how many variables ended up being captured, by subtracting the number of explicit parameters from the total number of parameters the new global definition has. This number, combined with the fact that we added all the &lsquo;implict&rsquo; arguments to the function to the beginning of the list, will let us iterate over all implict arguments, creating a chain of partial function applications.</p> <p>But how do we build the application? We could use the mangled name of the function, but this seems inelegant, especially since we alreaady keep track of mangling information in <code>type_env</code>. Instead, we create a new, local environment, in which we place an updated binding for the function, marking it global, and setting its mangled name to the one generated by <code>global_sope</code>. This work is done on lines 301-303. We create a reference to the global function using the new environment on lines 305 and 306, and apply it to all the implict arguments on lines 307-313. Finally, we add the new &lsquo;basic&rsquo; equation into <code>translated_definitions</code>.</p> <p>Let&rsquo;s take a look at translating <code>ast_lambda</code> next:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/ast.cpp" data-first-line="368" data-last-line="392"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/ast.cpp#L368-L392">ast.cpp</a>, lines 368 through 392</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">368 </span><span class="lnt">369 </span><span class="lnt">370 </span><span class="lnt">371 </span><span class="lnt">372 </span><span class="lnt">373 </span><span class="lnt">374 </span><span class="lnt">375 </span><span class="lnt">376 </span><span class="lnt">377 </span><span class="lnt">378 </span><span class="lnt">379 </span><span class="lnt">380 </span><span class="lnt">381 </span><span class="lnt">382 </span><span class="lnt">383 </span><span class="lnt">384 </span><span class="lnt">385 </span><span class="lnt">386 </span><span class="lnt">387 </span><span class="lnt">388 </span><span class="lnt">389 </span><span class="lnt">390 </span><span class="lnt">391 </span><span class="lnt">392 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast_lambda</span><span class="o">::</span><span class="n">translate</span><span class="p">(</span><span class="n">global_scope</span><span class="o">&amp;</span> <span class="n">scope</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">function_params</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">free_variable</span> <span class="p">:</span> <span class="n">free_variables</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">env</span><span class="o">-&gt;</span><span class="n">is_global</span><span class="p">(</span><span class="n">free_variable</span><span class="p">))</span> <span class="k">continue</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">function_params</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">free_variable</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">size_t</span> <span class="n">captured_count</span> <span class="o">=</span> <span class="n">function_params</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">function_params</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">function_params</span><span class="p">.</span><span class="n">end</span><span class="p">(),</span> <span class="n">params</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">params</span><span class="p">.</span><span class="n">end</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">auto</span><span class="o">&amp;</span> <span class="n">new_function</span> <span class="o">=</span> <span class="n">scope</span><span class="p">.</span><span class="n">add_function</span><span class="p">(</span><span class="s">&#34;lambda&#34;</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">function_params</span><span class="p">),</span> <span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">body</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="n">type_env_ptr</span> <span class="n">mangled_env</span> <span class="o">=</span> <span class="n">type_scope</span><span class="p">(</span><span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">mangled_env</span><span class="o">-&gt;</span><span class="n">bind</span><span class="p">(</span><span class="s">&#34;lambda&#34;</span><span class="p">,</span> <span class="n">type_scheme_ptr</span><span class="p">(</span><span class="k">nullptr</span><span class="p">),</span> <span class="n">visibility</span><span class="o">::</span><span class="n">global</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">mangled_env</span><span class="o">-&gt;</span><span class="n">set_mangled_name</span><span class="p">(</span><span class="s">&#34;lambda&#34;</span><span class="p">,</span> <span class="n">new_function</span><span class="p">.</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">ast_ptr</span> <span class="n">new_application</span> <span class="o">=</span> <span class="n">ast_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">ast_lid</span><span class="p">(</span><span class="s">&#34;lambda&#34;</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="n">new_application</span><span class="o">-&gt;</span><span class="n">env</span> <span class="o">=</span> <span class="n">mangled_env</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">param</span> <span class="p">:</span> <span class="n">new_function</span><span class="p">.</span><span class="n">params</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">captured_count</span><span class="o">--</span><span class="p">))</span> <span class="k">break</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">ast_ptr</span> <span class="n">new_arg</span> <span class="o">=</span> <span class="n">ast_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">ast_lid</span><span class="p">(</span><span class="n">param</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="n">new_arg</span><span class="o">-&gt;</span><span class="n">env</span> <span class="o">=</span> <span class="n">env</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">new_application</span> <span class="o">=</span> <span class="n">ast_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">ast_app</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">new_application</span><span class="p">),</span> <span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">new_arg</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"> <span class="n">new_application</span><span class="o">-&gt;</span><span class="n">env</span> <span class="o">=</span> <span class="n">env</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">translated</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">new_application</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Once again, on lines 369-375 we find all the arguments to the global definition. On lines 377-382 we create a new global function and a mangled environment, and start creating the chain of function applications. On lines 384-390, we actually create the arguments and apply the function to them. Finally, on line 391, we store this new chain of applications in the <code>translated</code> field.</p> <a href="#compilation"> <h4 id="compilation">Compilation</h4> </a> <p>There&rsquo;s still another piece of the puzzle missing, and that&rsquo;s how we&rsquo;re going to compile <code>let/in</code> expressions into G-machine instructions. We have allowed these expressions to be recursive, and maybe even mutually recursive. This worked fine with global definitions; instead of specifying where on the stack we can find the reference to a global function, we just created a new global node, and called it good. Things are different now, though, because the definitions we&rsquo;re referencing aren&rsquo;t <em>just</em> global functions; they are partial applications of a global function. And to reference themselves, or their neighbors, they have to have a handle on their own nodes. We do this using an instruction that we foreshadowed in part 5, but didn&rsquo;t use until just now: <strong>Alloc</strong>.</p> <p><strong>Alloc</strong> creates placeholder nodes on the stack. These nodes are indirections, the same kind that we use for lazy evaluation and sharing elsewhere. We create an indirection node for every definition that we then build; when an expression needs access to a definition, we give it the indirection node. After building the partial application graph for an expression, we use <strong>Update</strong>, making the corresponding indirection point to this new graph. This way, the &lsquo;handle&rsquo; to a definition is always accessible, and once the definition&rsquo;s expression is built, the handle correctly points to it. Here&rsquo;s the implementation:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/ast.cpp" data-first-line="319" data-last-line="332"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/ast.cpp#L319-L332">ast.cpp</a>, lines 319 through 332</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">319 </span><span class="lnt">320 </span><span class="lnt">321 </span><span class="lnt">322 </span><span class="lnt">323 </span><span class="lnt">324 </span><span class="lnt">325 </span><span class="lnt">326 </span><span class="lnt">327 </span><span class="lnt">328 </span><span class="lnt">329 </span><span class="lnt">330 </span><span class="lnt">331 </span><span class="lnt">332 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast_let</span><span class="o">::</span><span class="n">compile</span><span class="p">(</span><span class="k">const</span> <span class="n">env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">instruction_ptr</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">into</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_alloc</span><span class="p">(</span><span class="n">translated_definitions</span><span class="p">.</span><span class="n">size</span><span class="p">())));</span> </span></span><span class="line"><span class="cl"> <span class="n">env_ptr</span> <span class="n">new_env</span> <span class="o">=</span> <span class="n">env</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">def</span> <span class="p">:</span> <span class="n">translated_definitions</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">new_env</span> <span class="o">=</span> <span class="n">env_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">env_var</span><span class="p">(</span><span class="n">definitions</span><span class="p">.</span><span class="n">env</span><span class="o">-&gt;</span><span class="n">get_mangled_name</span><span class="p">(</span><span class="n">def</span><span class="p">.</span><span class="n">first</span><span class="p">),</span> <span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">new_env</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">offset</span> <span class="o">=</span> <span class="n">translated_definitions</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">def</span> <span class="p">:</span> <span class="n">translated_definitions</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">def</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">compile</span><span class="p">(</span><span class="n">new_env</span><span class="p">,</span> <span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">into</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_update</span><span class="p">(</span><span class="n">offset</span><span class="o">--</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">in</span><span class="o">-&gt;</span><span class="n">compile</span><span class="p">(</span><span class="n">new_env</span><span class="p">,</span> <span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">into</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_slide</span><span class="p">(</span><span class="n">translated_definitions</span><span class="p">.</span><span class="n">size</span><span class="p">())));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>First, we create the <strong>Alloc</strong> instruction. Then, we update our environment to map each definition name to a location within the newly allocated batch of nodes. Since we iterate the definitions in order, &lsquo;pushing&rsquo; them into our environment, we end up with the convention of having the later definitions closer to the top of the G-machine stack. Thus, when we iterate the definitions again, this time to compile their bodies, we have to do so starting with the highest offset, and working our way down to <strong>Update</strong>-ing the top of the stack. Once the definitions have been compiled, we proceed to compiling the <code>in</code> part of the expression as normal, using our updated environment. Finally, we use <strong>Slide</strong> to get rid of the definition graphs, cleaning up the stack.</p> <p>Compiling the <code>ast_lambda</code> is far more straightforward. We just compile the resulting partial application as we normally would have:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/ast.cpp" data-first-line="394" data-last-line="396"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/ast.cpp#L394-L396">ast.cpp</a>, lines 394 through 396</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">394 </span><span class="lnt">395 </span><span class="lnt">396 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast_lambda</span><span class="o">::</span><span class="n">compile</span><span class="p">(</span><span class="k">const</span> <span class="n">env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">instruction_ptr</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">translated</span><span class="o">-&gt;</span><span class="n">compile</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>One more thing. Let&rsquo;s adopt the convention of storing <strong>mangled</strong> names into the compilation environment. This way, rather than looking up mangled names only for global functions, which would be a &lsquo;gotcha&rsquo; for anyone working on the compiler, we will always use the mangled names during compilation. To make this change, we make sure that <code>ast_case</code> also uses <code>mangled_name</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/ast.cpp" data-first-line="242" data-last-line="242"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/ast.cpp#L242-L242">ast.cpp</a>, line 242</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">242 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="n">new_env</span> <span class="o">=</span> <span class="n">env_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">env_var</span><span class="p">(</span><span class="n">branch</span><span class="o">-&gt;</span><span class="n">expr</span><span class="o">-&gt;</span><span class="n">env</span><span class="o">-&gt;</span><span class="n">get_mangled_name</span><span class="p">(</span><span class="o">*</span><span class="n">it</span><span class="p">),</span> <span class="n">new_env</span><span class="p">));</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We also update the logic for <code>ast_lid::compile</code> to use the mangled name information:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/ast.cpp" data-first-line="52" data-last-line="58"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/ast.cpp#L52-L58">ast.cpp</a>, lines 52 through 58</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast_lid</span><span class="o">::</span><span class="n">compile</span><span class="p">(</span><span class="k">const</span> <span class="n">env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">instruction_ptr</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">mangled_name</span> <span class="o">=</span> <span class="k">this</span><span class="o">-&gt;</span><span class="n">env</span><span class="o">-&gt;</span><span class="n">get_mangled_name</span><span class="p">(</span><span class="n">id</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">into</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">env</span><span class="o">-&gt;</span><span class="n">has_variable</span><span class="p">(</span><span class="n">mangled_name</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="k">this</span><span class="o">-&gt;</span><span class="n">env</span><span class="o">-&gt;</span><span class="n">is_global</span><span class="p">(</span><span class="n">id</span><span class="p">))</span> <span class="o">?</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">instruction</span><span class="o">*</span><span class="p">)</span> <span class="k">new</span> <span class="n">instruction_push</span><span class="p">(</span><span class="n">env</span><span class="o">-&gt;</span><span class="n">get_offset</span><span class="p">(</span><span class="n">mangled_name</span><span class="p">))</span> <span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">instruction</span><span class="o">*</span><span class="p">)</span> <span class="k">new</span> <span class="n">instruction_pushglobal</span><span class="p">(</span><span class="n">mangled_name</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#fixing-type-generalization"> <h4 id="fixing-type-generalization">Fixing Type Generalization</h4> </a> <p>This is a rather serious bug that made its way into the codebase since part 10. Recall that we can only generalize type variables that are free in the environment. Thus far, we haven&rsquo;t done that, and we really should: I ran into incorrectly inferred types in my first test of the <code>let/in</code> language feature.</p> <p>We need to make our code capable of finding free variables in the type environment. This requires the <code>type_mgr</code>, which associates with type variables the real types they represent, if any. We thus create methods with signatures as follows:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">type_env</span><span class="o">::</span><span class="n">find_free</span><span class="p">(</span><span class="k">const</span> <span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">type_env</span><span class="o">::</span><span class="n">find_free_except</span><span class="p">(</span><span class="k">const</span> <span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">avoid</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span></code></pre></div><p>Why <code>find_free_except</code>? When generalizing a variable whose type was already stored in the environment, all the type variables we could generalize would not be &lsquo;free&rsquo;. If they only occur in the type we&rsquo;re generalizing, though, we shouldn&rsquo;t let that stop us! More generally, if we see type variables that are only found in the same mutually recursive group as the binding we&rsquo;re generalizing, we are free to generalize them too. Thus, we pass in a reference to a <code>group</code>, and check if a variable is a member of that group before searching it for free type variables. The implementations of the two methods are straightforward:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/type_env.cpp" data-first-line="4" data-last-line="18"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/type_env.cpp#L4-L18">type_env.cpp</a>, lines 4 through 18</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">type_env</span><span class="o">::</span><span class="n">find_free</span><span class="p">(</span><span class="k">const</span> <span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">parent</span> <span class="o">!=</span> <span class="k">nullptr</span><span class="p">)</span> <span class="n">parent</span><span class="o">-&gt;</span><span class="n">find_free</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">binding</span> <span class="p">:</span> <span class="n">names</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">mgr</span><span class="p">.</span><span class="n">find_free</span><span class="p">(</span><span class="n">binding</span><span class="p">.</span><span class="n">second</span><span class="p">.</span><span class="n">type</span><span class="p">,</span> <span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">type_env</span><span class="o">::</span><span class="n">find_free_except</span><span class="p">(</span><span class="k">const</span> <span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="k">const</span> <span class="n">group</span><span class="o">&amp;</span> <span class="n">avoid</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">parent</span> <span class="o">!=</span> <span class="k">nullptr</span><span class="p">)</span> <span class="n">parent</span><span class="o">-&gt;</span><span class="n">find_free</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">binding</span> <span class="p">:</span> <span class="n">names</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">avoid</span><span class="p">.</span><span class="n">members</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">binding</span><span class="p">.</span><span class="n">first</span><span class="p">)</span> <span class="o">!=</span> <span class="n">avoid</span><span class="p">.</span><span class="n">members</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> <span class="k">continue</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">mgr</span><span class="p">.</span><span class="n">find_free</span><span class="p">(</span><span class="n">binding</span><span class="p">.</span><span class="n">second</span><span class="p">.</span><span class="n">type</span><span class="p">,</span> <span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Note that <code>find_free_except</code> calls <code>find_free</code> in its recursive call. This is not a bug: we <em>do</em> want to include free type variables from bindings that have the same name as the variable we&rsquo;re generalizing, but aren&rsquo;t found in the same scope. As far as we&rsquo;re concerned, they&rsquo;re different variables! The two methods use another <code>find_free</code> method which we add to <code>type_mgr</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/type.cpp" data-first-line="206" data-last-line="219"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/type.cpp#L206-L219">type.cpp</a>, lines 206 through 219</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">206 </span><span class="lnt">207 </span><span class="lnt">208 </span><span class="lnt">209 </span><span class="lnt">210 </span><span class="lnt">211 </span><span class="lnt">212 </span><span class="lnt">213 </span><span class="lnt">214 </span><span class="lnt">215 </span><span class="lnt">216 </span><span class="lnt">217 </span><span class="lnt">218 </span><span class="lnt">219 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">type_mgr</span><span class="o">::</span><span class="n">find_free</span><span class="p">(</span><span class="k">const</span> <span class="n">type_scheme_ptr</span><span class="o">&amp;</span> <span class="n">t</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">monotype_free</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_mgr</span> <span class="n">limited_mgr</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">binding</span> <span class="p">:</span> <span class="n">types</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">existing_position</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">find</span><span class="p">(</span><span class="n">t</span><span class="o">-&gt;</span><span class="n">forall</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">t</span><span class="o">-&gt;</span><span class="n">forall</span><span class="p">.</span><span class="n">end</span><span class="p">(),</span> <span class="n">binding</span><span class="p">.</span><span class="n">first</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">existing_position</span> <span class="o">!=</span> <span class="n">t</span><span class="o">-&gt;</span><span class="n">forall</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> <span class="k">continue</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">limited_mgr</span><span class="p">.</span><span class="n">types</span><span class="p">[</span><span class="n">binding</span><span class="p">.</span><span class="n">first</span><span class="p">]</span> <span class="o">=</span> <span class="n">binding</span><span class="p">.</span><span class="n">second</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">limited_mgr</span><span class="p">.</span><span class="n">find_free</span><span class="p">(</span><span class="n">t</span><span class="o">-&gt;</span><span class="n">monotype</span><span class="p">,</span> <span class="n">monotype_free</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">not_free</span> <span class="p">:</span> <span class="n">t</span><span class="o">-&gt;</span><span class="n">forall</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">monotype_free</span><span class="p">.</span><span class="n">erase</span><span class="p">(</span><span class="n">not_free</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">into</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">monotype_free</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">monotype_free</span><span class="p">.</span><span class="n">end</span><span class="p">());</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This one is a bit of a hack. Typically, while running <code>find_free</code>, a <code>type_mgr</code> will resolve any type variables. However, variables from the <code>forall</code> quantifier of a type scheme should not be resolved, since they are explicitly generic. To prevent the type manager from erroneously resolving such type variables, we create a new type manager that does not have these variables bound to anything, and thus marks them as free. We then filter these variables out of the final list of free variables.</p> <p>Finally, <code>generalize</code> makes sure not to use variables that it finds free:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/type_env.cpp" data-first-line="68" data-last-line="81"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/type_env.cpp#L68-L81">type_env.cpp</a>, lines 68 through 81</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">68 </span><span class="lnt">69 </span><span class="lnt">70 </span><span class="lnt">71 </span><span class="lnt">72 </span><span class="lnt">73 </span><span class="lnt">74 </span><span class="lnt">75 </span><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span><span class="lnt">79 </span><span class="lnt">80 </span><span class="lnt">81 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">type_env</span><span class="o">::</span><span class="n">generalize</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">,</span> <span class="k">const</span> <span class="n">group</span><span class="o">&amp;</span> <span class="n">grp</span><span class="p">,</span> <span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">names_it</span> <span class="o">=</span> <span class="n">names</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">names_it</span> <span class="o">==</span> <span class="n">names</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> <span class="k">throw</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">names_it</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">.</span><span class="n">type</span><span class="o">-&gt;</span><span class="n">forall</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="k">throw</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">free_in_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">free_in_env</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">mgr</span><span class="p">.</span><span class="n">find_free</span><span class="p">(</span><span class="n">names_it</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">.</span><span class="n">type</span><span class="o">-&gt;</span><span class="n">monotype</span><span class="p">,</span> <span class="n">free_in_type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">find_free_except</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">grp</span><span class="p">,</span> <span class="n">free_in_env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">free</span> <span class="p">:</span> <span class="n">free_in_type</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">free_in_env</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">free</span><span class="p">)</span> <span class="o">!=</span> <span class="n">free_in_env</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> <span class="k">continue</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">names_it</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">.</span><span class="n">type</span><span class="o">-&gt;</span><span class="n">forall</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">free</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#putting-it-all-together"> <h4 id="putting-it-all-together">Putting It All Together</h4> </a> <p>All that&rsquo;s left is to tie the parts we&rsquo;ve created into one coherent whole in <code>main.cpp</code>. First of all, since we moved all of the LLVM-related code into <code>global_scope</code>, we can safely replace that functionality in <code>main.cpp</code> with a method call:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/main.cpp" data-first-line="121" data-last-line="132"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/main.cpp#L121-L132">main.cpp</a>, lines 121 through 132</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">121 </span><span class="lnt">122 </span><span class="lnt">123 </span><span class="lnt">124 </span><span class="lnt">125 </span><span class="lnt">126 </span><span class="lnt">127 </span><span class="lnt">128 </span><span class="lnt">129 </span><span class="lnt">130 </span><span class="lnt">131 </span><span class="lnt">132 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">gen_llvm</span><span class="p">(</span><span class="n">global_scope</span><span class="o">&amp;</span> <span class="n">scope</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">llvm_context</span> <span class="n">ctx</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">gen_llvm_internal_op</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">PLUS</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">gen_llvm_internal_op</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">MINUS</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">gen_llvm_internal_op</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">TIMES</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">gen_llvm_internal_op</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">DIVIDE</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">scope</span><span class="p">.</span><span class="n">generate_llvm</span><span class="p">(</span><span class="n">ctx</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">module</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="n">llvm</span><span class="o">::</span><span class="n">outs</span><span class="p">(),</span> <span class="k">nullptr</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">output_llvm</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="s">&#34;program.o&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>On the other hand, we need top-level logic to handle <code>definition_group</code>s. This is pretty straightforward, and the main trick is to remember to update the function&rsquo;s mangled name. Right now, depending on the choice of manging algorithm, it&rsquo;s possible even for top-level functions to have their names changed, and we must account for that. The whole code is:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/main.cpp" data-first-line="52" data-last-line="62"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/main.cpp#L52-L62">main.cpp</a>, lines 52 through 62</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">global_scope</span> <span class="nf">translate_program</span><span class="p">(</span><span class="n">definition_group</span><span class="o">&amp;</span> <span class="n">group</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">global_scope</span> <span class="n">scope</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">data</span> <span class="p">:</span> <span class="n">group</span><span class="p">.</span><span class="n">defs_data</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">data</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">into_globals</span><span class="p">(</span><span class="n">scope</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">defn</span> <span class="p">:</span> <span class="n">group</span><span class="p">.</span><span class="n">defs_defn</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span><span class="o">&amp;</span> <span class="n">function</span> <span class="o">=</span> <span class="n">defn</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">into_global</span><span class="p">(</span><span class="n">scope</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">function</span><span class="p">.</span><span class="n">body</span><span class="o">-&gt;</span><span class="n">env</span><span class="o">-&gt;</span><span class="n">parent</span><span class="o">-&gt;</span><span class="n">set_mangled_name</span><span class="p">(</span><span class="n">defn</span><span class="p">.</span><span class="n">first</span><span class="p">,</span> <span class="n">function</span><span class="p">.</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">scope</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Finally, we call <code>global_scope</code>&rsquo;s methods in <code>main()</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/main.cpp" data-first-line="148" data-last-line="151"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/main.cpp#L148-L151">main.cpp</a>, lines 148 through 151</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">148 </span><span class="lnt">149 </span><span class="lnt">150 </span><span class="lnt">151 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="n">typecheck_program</span><span class="p">(</span><span class="n">global_defs</span><span class="p">,</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">global_scope</span> <span class="n">scope</span> <span class="o">=</span> <span class="n">translate_program</span><span class="p">(</span><span class="n">global_defs</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">scope</span><span class="p">.</span><span class="n">compile</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">gen_llvm</span><span class="p">(</span><span class="n">scope</span><span class="p">);</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>That&rsquo;s it! Please note that I&rsquo;ve mentioned or hinted at minor changes to the codebase. Detailing every single change this late into the project is needlessly time consuming and verbose; Gitea reports that I&rsquo;ve made 677 insertions into and 215 deletions from the code. As always, I provide the <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/compiler/12"class="external-link">source code for the compiler<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, and you can also take a look at the <a href="https://dev.danilafe.com/Web-Projects/blog-static/compare/1905601aaa96d11c771eae9c56bb9fc105050cda...21851e3a9c552383ee8c4bc878ea06e7d28c333e"class="external-link">Gitea-generated diff<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> at the time of writing. If you want to follow along, feel free to check them out!</p> <a href="#running-our-programs"> <h3 id="running-our-programs">Running Our Programs</h3> </a> <p>It&rsquo;s important to test all the language features that we just added. This includes recursive definitions, nested function dependency cycles, and uses of lambda functions. Some of the following examples will be rather silly, but they should do a good job of checking that everything works as we expect. Let&rsquo;s start with a simple use of a recursive definition inside a <code>let/in</code>. A classic definition in that form is of <code>fix</code> (the fixpoint combinator):</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">fix</span> <span class="n">f</span> <span class="ow">=</span> <span class="kr">let</span> <span class="n">x</span> <span class="ow">=</span> <span class="n">f</span> <span class="n">x</span> <span class="kr">in</span> <span class="n">x</span> </span></span></code></pre></div><p>This defines <code>x</code> to be <code>f x</code>, which by substitution becomes <code>f (f x)</code>, and then <code>f (f (f x))</code> and so on. The fixpoint combinator allows one to write a recursive function that doesn&rsquo;t use its own name in the body. Rather, we write a function expecting to receive &lsquo;itself&rsquo; as a value:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">fix</span> <span class="ow">::</span> <span class="p">(</span><span class="n">a</span> <span class="ow">-&gt;</span> <span class="n">a</span><span class="p">)</span> <span class="ow">-&gt;</span> <span class="n">a</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="nf">factRec</span> <span class="ow">::</span> <span class="p">(</span><span class="kt">Int</span> <span class="ow">-&gt;</span> <span class="kt">Int</span><span class="p">)</span> <span class="ow">-&gt;</span> <span class="kt">Int</span> <span class="ow">-&gt;</span> <span class="kt">Int</span> </span></span><span class="line"><span class="cl"><span class="nf">factRec</span> <span class="n">f</span> <span class="n">x</span> <span class="ow">=</span> <span class="kr">if</span> <span class="n">x</span> <span class="o">==</span> <span class="mi">0</span> <span class="kr">then</span> <span class="mi">1</span> <span class="kr">else</span> <span class="n">x</span> <span class="o">*</span> <span class="n">f</span> <span class="n">x</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="nf">fact</span> <span class="ow">::</span> <span class="kt">Int</span> <span class="ow">-&gt;</span> <span class="kt">Int</span> </span></span><span class="line"><span class="cl"><span class="nf">fact</span> <span class="ow">=</span> <span class="n">fix</span> <span class="n">factRec</span> </span></span></code></pre></div><p>Notice that <code>factRec</code> doesn&rsquo;t reference itself, but rather takes as argument a function it expects to be &lsquo;factorial&rsquo; called <code>f</code>, and uses that in its recursive case. We can write something similar in our language, perhaps to create an infinite list of ones:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/examples/fixpoint.txt"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/examples/fixpoint.txt">fixpoint.txt</a>, entire file</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">data List a = { Nil, Cons a (List a) } </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">defn fix f = { let { defn x = { f x } } in { x } } </span></span><span class="line"><span class="cl">defn fixpointOnes fo = { Cons 1 fo } </span></span><span class="line"><span class="cl">defn sumTwo l = { </span></span><span class="line"><span class="cl"> case l of { </span></span><span class="line"><span class="cl"> Nil -&gt; { 0 } </span></span><span class="line"><span class="cl"> Cons x xs -&gt; { </span></span><span class="line"><span class="cl"> x + case xs of { </span></span><span class="line"><span class="cl"> Nil -&gt; { 0 } </span></span><span class="line"><span class="cl"> Cons y ys -&gt; { y } </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl">} </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">defn main = { sumTwo (fix fixpointOnes) } </span></span></code></pre></td></tr></table> </div> </div> </div> <p>We want <code>sumTwo</code> to take the first two elements from the list, and return their sum. For an infinite list of ones, we expect this sum to be equal to 2, and it is:</p> <pre tabindex="0"><code>Result: 2 </code></pre><p>Next, let&rsquo;s try to define a function which has a mutually recursive pair of definitions inside of a <code>let/in</code>. Let&rsquo;s also make these expressions reference a function from the global scope, so that we know our dependency tracking works as expected:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/examples/letin.txt"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/examples/letin.txt">letin.txt</a>, entire file</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">data Bool = { True, False } </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">data List a = { Nil, Cons a (List a) } </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">defn if c t e = { </span></span><span class="line"><span class="cl"> case c of { </span></span><span class="line"><span class="cl"> True -&gt; { t } </span></span><span class="line"><span class="cl"> False -&gt; { e } </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl">} </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">defn mergeUntil l r p = { </span></span><span class="line"><span class="cl"> let { </span></span><span class="line"><span class="cl"> defn mergeLeft nl nr = { </span></span><span class="line"><span class="cl"> case nl of { </span></span><span class="line"><span class="cl"> Nil -&gt; { Nil } </span></span><span class="line"><span class="cl"> Cons x xs -&gt; { if (p x) (Cons x (mergeRight xs nr)) Nil } </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl"> defn mergeRight nl nr = { </span></span><span class="line"><span class="cl"> case nr of { </span></span><span class="line"><span class="cl"> Nil -&gt; { Nil } </span></span><span class="line"><span class="cl"> Cons x xs -&gt; { if (p x) (Cons x (mergeLeft nl xs)) Nil } </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl"> } in { </span></span><span class="line"><span class="cl"> mergeLeft l r </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl">} </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">defn const x y = { x } </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">defn sum l = { </span></span><span class="line"><span class="cl"> case l of { </span></span><span class="line"><span class="cl"> Nil -&gt; { 0 } </span></span><span class="line"><span class="cl"> Cons x xs -&gt; { x + sum xs } </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl">} </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">defn main = { </span></span><span class="line"><span class="cl"> let { </span></span><span class="line"><span class="cl"> defn firstList = { Cons 1 (Cons 3 (Cons 5 Nil)) } </span></span><span class="line"><span class="cl"> defn secondList = { Cons 2 (Cons 4 (Cons 6 Nil)) } </span></span><span class="line"><span class="cl"> } in { </span></span><span class="line"><span class="cl"> sum (mergeUntil firstList secondList (const True)) </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl">} </span></span></code></pre></td></tr></table> </div> </div> </div> <p>Here, we have a function <code>mergeUntil</code> which, given two lists and a predicate, combines the two lists as long as the predicate returns <code>True</code>. It does so using a convoluted pair of mutually recursive functions, one of which unpacks the left list, and the other the right. Each of the functions calls the global function <code>if</code>. We also use two definitions inside of <code>main</code> to create the two lists we&rsquo;re going to merge. The compiler outputs the following (correct) types:</p> <pre tabindex="0"><code>const: forall bb bc . bc -&gt; bb -&gt; bc if: Bool* -&gt; List* Int* -&gt; List* Int* -&gt; List* Int* main: Int* mergeUntil: List* Int* -&gt; List* Int* -&gt; (Int* -&gt; Bool*) -&gt; List* Int* sum: List* Int* -&gt; Int* </code></pre><p>And the result is 21, as would be expected from the sum of the numbers 1-6:</p> <pre tabindex="0"><code>Result: 21 </code></pre><p>Let&rsquo;s try lambda functions now. We can try use them for a higher-order function like <code>map</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/examples/lambda.txt"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/examples/lambda.txt">lambda.txt</a>, entire file</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">data List a = { Nil, Cons a (List a) } </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">defn sum l = { </span></span><span class="line"><span class="cl"> case l of { </span></span><span class="line"><span class="cl"> Nil -&gt; { 0 } </span></span><span class="line"><span class="cl"> Cons x xs -&gt; { x + sum xs} </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl">} </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">defn map f l = { </span></span><span class="line"><span class="cl"> case l of { </span></span><span class="line"><span class="cl"> Nil -&gt; { Nil } </span></span><span class="line"><span class="cl"> Cons x xs -&gt; { Cons (f x) (map f xs) } </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl">} </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">defn main = { </span></span><span class="line"><span class="cl"> sum (map \x -&gt; { x * x } (map (\x -&gt; { x + x }) (Cons 1 (Cons 2 (Cons 3 Nil))))) </span></span><span class="line"><span class="cl">} </span></span></code></pre></td></tr></table> </div> </div> </div> <p>In this example, we first double every element in the list, then square it, and finally take the sum. This should give us 4+16+36 = 56, and so it does:</p> <pre tabindex="0"><code>Result: 56 </code></pre><p>Finally, let&rsquo;s do some magic with a locally-declared data type. We&rsquo;ll make a &ldquo;packer&rdquo; that creates a wrapped instance of a type, <code>Packed a</code>. Since the constructor of this data type is not globally visible, it&rsquo;s not possible to get the value back out, except by using an &lsquo;unpacking&rsquo; function that we provide:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/12/examples/packed.txt"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/12/examples/packed.txt">packed.txt</a>, entire file</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">data Pair a b = { Pair a b } </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">defn packer = { </span></span><span class="line"><span class="cl"> let { </span></span><span class="line"><span class="cl"> data Packed a = { Packed a } </span></span><span class="line"><span class="cl"> defn pack a = { Packed a } </span></span><span class="line"><span class="cl"> defn unpack p = { </span></span><span class="line"><span class="cl"> case p of { </span></span><span class="line"><span class="cl"> Packed a -&gt; { a } </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl"> } in { </span></span><span class="line"><span class="cl"> Pair pack unpack </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl">} </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">defn main = { </span></span><span class="line"><span class="cl"> case packer of { </span></span><span class="line"><span class="cl"> Pair pack unpack -&gt; { </span></span><span class="line"><span class="cl"> unpack (pack 3) </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl">} </span></span></code></pre></td></tr></table> </div> </div> </div> <p>Here, the <code>packer</code> definition returns a pair of the &lsquo;packing&rsquo; and &lsquo;unpacking&rsquo; functions. The &lsquo;packing&rsquo; function simply applies the consntructor of <code>Packed</code> to its argument, while the &lsquo;unpacking&rsquo; function performs pattern matching (which is possible since the data type is still in scope there). We expect <code>unpack (pack 3)</code> to return 3, and it does:</p> <pre tabindex="0"><code>Result: 3 </code></pre><p>Trying to pattern match, though, doesn&rsquo;t work, just like we would want!</p> <p>This is enough to convince me that our changes do, indeed, work! Of the &lsquo;major&rsquo; components that I wanted to cover, only <strong>Input/Output</strong> remains! Additionally, a <a href="https://lobste.rs"class="external-link">lobste.rs<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> user suggested that we also cover namespacing, and perhaps we will look into that as well. Before either of those things, though, I think that I want to go through the compiler and perform another round of improvements, similarly to <a href="https://danilafe.com/blog/04_compiler_improvements/">part 4</a>. It&rsquo;s hard to do a lot of refactoring while covering new content, since major changes need to be explained and presented for the post to make sense. I do this in <a href="https://danilafe.com/blog/13_compiler_cleanup/">part 13</a> - cleanup. I hope to see you there!</p> Building a Crystal Project with Nix, Revisited https://danilafe.com/blog/crystal_nix_revisited/ Sun, 26 Apr 2020 18:37:22 -0700 https://danilafe.com/blog/crystal_nix_revisited/ <p>As I&rsquo;ve described in my <a href="https://danilafe.com/blog/crystal_nix/">previous post</a>, the process for compiling a Crystal project with Nix is a fairly straightforward one. As is standard within the Nix ecosystem, the project&rsquo;s dependencies, as specified by the source language&rsquo;s build system (shards, in Crystal&rsquo;s case), are converted into a Nix expression (<code>shards.nix</code>). These dependencies are then used in a derivation, which, in Crystal&rsquo;s case, can take advantage of <code>buildCrystalPackage</code> to reduce boilerplate build scripts. All is well.</p> <p>Things start to fall apart a little bit when the Crystal project being built is more complex. The predefined infrastructure (like <code>buildCrystalPackage</code>) <span class="sidenote"> <label class="sidenote-label" for="versatility-note">is not written with versatility in mind,</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="versatility-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> This is not a bad thing at all; it's much better to get something working for the practical case, rather than concoct an overcomplicated solution that covers all theoretically possible cases. <span class="sidenote-delimiter">]</span> </span> </span> though it seems to work exceptionally in the common case. Additionally, I discovered that the compiler itself has some quirks, and have killed a few hours of my time trying to figure out some unexpected behaviors.</p> <p>This post will cover the extra, more obscure steps I had to take to build an HTTPS-enabled Crystal project.</p> <a href="#first-problem-git-based-dependencies"> <h3 id="first-problem-git-based-dependencies">First Problem: Git-Based Dependencies</h3> </a> <p>A lot of my projects use Crystal libraries that are not hosted on GitHub at all; I use a private Git server, and most of my non-public code resides on it. The Crystal people within Nix don&rsquo;t seem to like this: let&rsquo;s look at the code for <code>crystal2nix.cr</code> file in the <a href="https://github.com/NixOS/nixpkgs/blob/1ffdf01777360f548cc7c10ef5b168cbe78fd183/pkgs/development/compilers/crystal/crystal2nix.cr"class="external-link">nixpkgs repository<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. In particular, consider lines 18 and 19:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">18 </span><span class="lnt">19 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Crystal" data-lang="Crystal"><span class="line"><span class="cl"><span class="n">yaml</span><span class="o">.</span><span class="n">shards</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">key</span><span class="p">,</span> <span class="n">value</span><span class="o">|</span> </span></span><span class="line"><span class="cl"> <span class="n">owner</span><span class="p">,</span> <span class="n">repo</span> <span class="o">=</span> <span class="n">value</span><span class="o">[</span><span class="s2">&#34;github&#34;</span><span class="o">].</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34;/&#34;</span><span class="p">)</span> </span></span></code></pre></td></tr></table> </div> </div><p>Ouch! If you as much as mention a non-GitHub repository in your <code>shards.lock</code> file, you will experience a good old uncaught exception. Things don&rsquo;t end there, either. Nix provides a convenient <code>fetchFromGitHub</code> function, which only requires a repository name and its enclosing namespace (user or group). <code>crystal2nix</code> uses this, by generating a file with that information:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Crystal" data-lang="Crystal"><span class="line"><span class="cl"><span class="n">file</span><span class="o">.</span><span class="n">puts</span> <span class="sx">%( </span><span class="si">#{</span><span class="n">key</span><span class="si">}</span><span class="sx"> = {)</span> </span></span><span class="line"><span class="cl"><span class="n">file</span><span class="o">.</span><span class="n">puts</span> <span class="sx">%( owner = &#34;</span><span class="si">#{</span><span class="n">owner</span><span class="si">}</span><span class="sx">&#34;;)</span> </span></span><span class="line"><span class="cl"><span class="n">file</span><span class="o">.</span><span class="n">puts</span> <span class="sx">%( repo = &#34;</span><span class="si">#{</span><span class="n">repo</span><span class="si">}</span><span class="sx">&#34;;)</span> </span></span><span class="line"><span class="cl"><span class="n">file</span><span class="o">.</span><span class="n">puts</span> <span class="sx">%( rev = &#34;</span><span class="si">#{</span><span class="n">rev</span><span class="si">}</span><span class="sx">&#34;;)</span> </span></span><span class="line"><span class="cl"><span class="n">file</span><span class="o">.</span><span class="n">puts</span> <span class="sx">%( sha256 = &#34;</span><span class="si">#{</span><span class="n">sha256</span><span class="si">}</span><span class="sx">&#34;;)</span> </span></span><span class="line"><span class="cl"><span class="n">file</span><span class="o">.</span><span class="n">puts</span> <span class="sx">%( };)</span> </span></span></code></pre></td></tr></table> </div> </div><p>And, of course, <code>build-package.nix</code> (of which <a href="https://github.com/NixOS/nixpkgs/blob/912eb6b120eba15237ff053eafc4b5d90577685b/pkgs/development/compilers/crystal/build-package.nix"class="external-link">this is the version at the time of writing<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>) uses this to declare dependencies:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Nix" data-lang="Nix"><span class="line"><span class="cl"><span class="n">crystalLib</span> <span class="o">=</span> <span class="n">linkFarm</span> <span class="s2">&#34;crystal-lib&#34;</span> <span class="p">(</span><span class="n">lib</span><span class="o">.</span><span class="n">mapAttrsToList</span> <span class="p">(</span><span class="n">name</span><span class="p">:</span> <span class="n">value</span><span class="p">:</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">inherit</span> <span class="n">name</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">path</span> <span class="o">=</span> <span class="n">fetchFromGitHub</span> <span class="n">value</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">})</span> <span class="p">(</span><span class="kn">import</span> <span class="n">shardsFile</span><span class="p">));</span> </span></span></code></pre></td></tr></table> </div> </div><p>This effectively creates a folder of dependencies cloned from GitHub, which is then placed into <code>lib</code> as if <code>shards</code> was run:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Nix" data-lang="Nix"><span class="line"><span class="cl"><span class="n">configurePhase</span> <span class="o">=</span> <span class="n">args</span><span class="o">.</span><span class="n">configurePhase</span> <span class="n">or</span> <span class="n">lib</span><span class="o">.</span><span class="n">concatStringsSep</span> <span class="s2">&#34;</span><span class="se">\n</span><span class="s2">&#34;</span> <span class="p">([</span> </span></span><span class="line"><span class="cl"> <span class="s2">&#34;runHook preConfigure&#34;</span> </span></span><span class="line"><span class="cl"><span class="p">]</span> <span class="o">++</span> <span class="n">lib</span><span class="o">.</span><span class="n">optional</span> <span class="p">(</span><span class="n">lockFile</span> <span class="o">!=</span> <span class="no">null</span><span class="p">)</span> <span class="s2">&#34;ln -s </span><span class="si">${</span><span class="n">lockFile</span><span class="si">}</span><span class="s2"> ./shard.lock&#34;</span> </span></span><span class="line"><span class="cl"> <span class="o">++</span> <span class="n">lib</span><span class="o">.</span><span class="n">optional</span> <span class="p">(</span><span class="n">shardsFile</span> <span class="o">!=</span> <span class="no">null</span><span class="p">)</span> <span class="s2">&#34;ln -s </span><span class="si">${</span><span class="n">crystalLib</span><span class="si">}</span><span class="s2"> lib&#34;</span> </span></span><span class="line"><span class="cl"> <span class="o">++</span> <span class="p">[</span> <span class="s2">&#34;runHook postConfigure &#34;</span><span class="p">]);</span> </span></span></code></pre></td></tr></table> </div> </div><p>Sleek, except that there&rsquo;s no place in this flow for dependencies based <em>only</em> on Git! <code>crystalLib</code> is declared locally in a <code>let/in</code> expression, and we don&rsquo;t have access to it; neither can we call <code>linkFarm</code> again, since this results in a derivation, which, with different inputs, will be created at a different path. To work around this, I made my own Nix package, called <code>customCrystal</code>, and had it pass several modifications to <code>buildCrystalPackage</code>:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Nix" data-lang="Nix"><span class="line"><span class="cl"><span class="p">{</span> <span class="n">stdenv</span><span class="o">,</span> <span class="n">lib</span><span class="o">,</span> <span class="n">linkFarm</span><span class="o">,</span> <span class="n">fetchgit</span><span class="o">,</span> <span class="n">fetchFromGitHub</span> <span class="p">}:</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="p">{</span> <span class="n">crystal</span><span class="o">,</span> </span></span><span class="line"><span class="cl"> <span class="n">gitShardsFile</span> <span class="o">?</span> <span class="no">null</span><span class="o">,</span> </span></span><span class="line"><span class="cl"> <span class="n">lockFile</span> <span class="o">?</span> <span class="no">null</span><span class="o">,</span> </span></span><span class="line"><span class="cl"> <span class="n">shardsFile</span> <span class="o">?</span> <span class="no">null</span><span class="o">,</span> <span class="o">...</span><span class="p">}</span><span class="o">@</span><span class="n">args</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">let</span> </span></span><span class="line"><span class="cl"> <span class="n">buildArgs</span> <span class="o">=</span> <span class="nb">builtins</span><span class="o">.</span><span class="nb">removeAttrs</span> <span class="n">args</span> <span class="p">[</span> <span class="s2">&#34;crystal&#34;</span> <span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="n">githubLinks</span> <span class="o">=</span> <span class="n">lib</span><span class="o">.</span><span class="n">mapAttrsToList</span> <span class="p">(</span><span class="n">name</span><span class="p">:</span> <span class="n">value</span><span class="p">:</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">inherit</span> <span class="n">name</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">path</span> <span class="o">=</span> <span class="n">fetchFromGitHub</span> <span class="n">value</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">})</span> <span class="p">(</span><span class="kn">import</span> <span class="n">shardsFile</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">gitLinks</span> <span class="o">=</span> <span class="n">lib</span><span class="o">.</span><span class="n">mapAttrsToList</span> <span class="p">(</span><span class="n">name</span><span class="p">:</span> <span class="n">value</span><span class="p">:</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">inherit</span> <span class="n">name</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">path</span> <span class="o">=</span> <span class="n">fetchgit</span> <span class="p">{</span> <span class="k">inherit</span> <span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="n">url</span> <span class="n">rev</span> <span class="n">sha256</span><span class="p">;</span> <span class="p">};</span> </span></span><span class="line"><span class="cl"> <span class="p">})</span> <span class="p">(</span><span class="kn">import</span> <span class="n">gitShardsFile</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">crystalLib</span> <span class="o">=</span> <span class="n">linkFarm</span> <span class="s2">&#34;crystal-lib&#34;</span> <span class="p">(</span><span class="n">githubLinks</span> <span class="o">++</span> <span class="n">gitLinks</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">configurePhase</span> <span class="o">=</span> <span class="n">args</span><span class="o">.</span><span class="n">configurePhase</span> <span class="n">or</span> <span class="n">lib</span><span class="o">.</span><span class="n">concatStringsSep</span> <span class="s2">&#34;</span><span class="se">\n</span><span class="s2">&#34;</span> <span class="p">([</span> </span></span><span class="line"><span class="cl"> <span class="s2">&#34;runHook preConfigure&#34;</span> </span></span><span class="line"><span class="cl"> <span class="p">]</span> <span class="o">++</span> <span class="n">lib</span><span class="o">.</span><span class="n">optional</span> <span class="p">(</span><span class="n">lockFile</span> <span class="o">!=</span> <span class="no">null</span><span class="p">)</span> <span class="s2">&#34;ln -s </span><span class="si">${</span><span class="n">lockFile</span><span class="si">}</span><span class="s2"> ./shard.lock&#34;</span> </span></span><span class="line"><span class="cl"> <span class="o">++</span> <span class="n">lib</span><span class="o">.</span><span class="n">optional</span> <span class="p">(</span><span class="n">shardsFile</span> <span class="o">!=</span> <span class="no">null</span><span class="p">)</span> <span class="s2">&#34;ln -s </span><span class="si">${</span><span class="n">crystalLib</span><span class="si">}</span><span class="s2"> lib&#34;</span> </span></span><span class="line"><span class="cl"> <span class="o">++</span> <span class="p">[</span> <span class="s2">&#34;runHook postConfigure &#34;</span><span class="p">]);</span> </span></span><span class="line"><span class="cl"><span class="k">in</span> </span></span><span class="line"><span class="cl"> <span class="n">crystal</span><span class="o">.</span><span class="n">buildCrystalPackage</span> <span class="p">(</span><span class="n">buildArgs</span> <span class="o">//</span> <span class="p">{</span> <span class="k">inherit</span> <span class="n">configurePhase</span><span class="p">;</span> <span class="p">})</span> </span></span></code></pre></div><p>This does pretty much the equivalent of what <code>buildCrystalPackage</code> does (indeed, it does the heavy lifting). However, this snippet also retrieves Git repositories from the <code>gitShardsFile</code>, and creates the <code>lib</code> folder using both Git and GitHub dependencies. I didn&rsquo;t bother writing a <code>crystal2nix</code> equivalent for this, since I only had a couple of dependencies. I invoked my new function like <code>buildCrystalPackage</code>, with the addition of passing in the Crystal package, and that problem was solved.</p> <a href="#second-problem-openssl"> <h3 id="second-problem-openssl">Second Problem: OpenSSL</h3> </a> <p>The package I was trying to build used Crystal&rsquo;s built-in HTTP client, which, in turn, required OpenSSL. This, I thought, would be rather straightforward: add <code>openssl</code> to my package&rsquo;s <code>buildInputs</code>, and be done with it. It was not as simple, though, and I was greeted with a wall of errors like this one:</p> <pre tabindex="0"><code>/nix/store/sq2b0dqlq243mqn4ql5h36xmpplyy20k-binutils-2.31.1/bin/ld: _main.o: in function `__crystal_main&#39;: main_module:(.text+0x6f0): undefined reference to `SSL_library_init&#39; /nix/store/sq2b0dqlq243mqn4ql5h36xmpplyy20k-binutils-2.31.1/bin/ld: main_module:(.text+0x6f5): undefined reference to `SSL_load_error_strings&#39; /nix/store/sq2b0dqlq243mqn4ql5h36xmpplyy20k-binutils-2.31.1/bin/ld: main_module:(.text+0x6fa): undefined reference to `OPENSSL_add_all_algorithms_noconf&#39; /nix/store/sq2b0dqlq243mqn4ql5h36xmpplyy20k-binutils-2.31.1/bin/ld: main_module:(.text+0x6ff): undefined reference to `ERR_load_crypto_strings&#39; /nix/store/sq2b0dqlq243mqn4ql5h36xmpplyy20k-binutils-2.31.1/bin/ld: _main.o: in function `*HTTP::Client::new&lt;String, (Int32 | Nil), Bool&gt;:HTTP::Client&#39;: </code></pre><p>Some snooping led me to discover that these symbols were part of OpenSSL 1.0.2, support for which ended in 2019. OpenSSL 1.1.0 has these symbols deprecated, and from what I can tell, they might be missing from the <code>.so</code> file altogether. I tried changing the package to specifically accept OpenSSL 1.0.2, but that didn&rsquo;t work, either: for some reason, the Crystal kept running the <code>gcc</code> command with <code>-L...openssl-1.1.0</code>. It also seemed like the compiler itself was built against the most recent version of OpenSSL, so what&rsquo;s the issue? I discovered this is a problem in the compiler itself. Consider the following line from Crystal&rsquo;s <code>openssl/lib_ssl.cr</code> <a href="https://github.com/crystal-lang/crystal/blob/0.34.0/src/openssl/lib_ssl.cr"class="external-link">source file<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">8 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Crystal" data-lang="Crystal"><span class="line"><span class="cl"> <span class="si">{%</span> <span class="n">ssl_version</span> <span class="o">=</span> <span class="sb">`hash pkg-config 2&gt; /dev/null &amp;&amp; pkg-config --silence-errors --modversion libssl || printf %s 0.0.0`</span><span class="o">.</span><span class="n">split</span><span class="o">.</span><span class="n">last</span><span class="o">.</span><span class="n">gsub</span><span class="p">(</span><span class="sr">/[^0-9.]/</span><span class="p">,</span> <span class="s2">&#34;&#34;</span><span class="p">)</span> <span class="si">%}</span> </span></span></code></pre></td></tr></table> </div> </div><p>Excuse me? If <code>pkg-config</code> is not found (which, in Nix, it won&rsquo;t be by default), Crystal assumes that it&rsquo;s using the <em>least</em> up-to-date version of OpenSSL, <span class="sidenote"> <label class="sidenote-label" for="version-note">indicated by version code 0.0.0.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="version-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> The Crystal compiler compares version numbers based on semantic versioning, it seems, and 0.0.0 will always compare to be less than any other version of OpenSSL. Thus, code 0.0.0 indicates that Crystal should assume it's dealing with an extremely old version of OpenSSL. <span class="sidenote-delimiter">]</span> </span> </span> This matters, because later on in the file, we get this beauty:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">215 </span><span class="lnt">216 </span><span class="lnt">217 </span><span class="lnt">218 </span><span class="lnt">219 </span><span class="lnt">220 </span><span class="lnt">221 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Crystal" data-lang="Crystal"><span class="line"><span class="cl"><span class="si">{%</span> <span class="k">if</span> <span class="n">compare_versions</span><span class="p">(</span><span class="no">OPENSSL_VERSION</span><span class="p">,</span> <span class="s2">&#34;1.1.0&#34;</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="si">%}</span> </span></span><span class="line"><span class="cl"> <span class="k">fun</span> <span class="nf">tls_method</span> <span class="o">=</span> <span class="n">TLS_method</span> <span class="p">:</span> <span class="n">SSLMethod</span> </span></span><span class="line"><span class="cl"><span class="si">{%</span> <span class="k">else</span> <span class="si">%}</span> </span></span><span class="line"><span class="cl"> <span class="k">fun</span> <span class="nf">ssl_library_init</span> <span class="o">=</span> <span class="n">SSL_library_init</span> </span></span><span class="line"><span class="cl"> <span class="k">fun</span> <span class="nf">ssl_load_error_strings</span> <span class="o">=</span> <span class="n">SSL_load_error_strings</span> </span></span><span class="line"><span class="cl"> <span class="k">fun</span> <span class="nf">sslv23_method</span> <span class="o">=</span> <span class="n">SSLv23_method</span> <span class="p">:</span> <span class="n">SSLMethod</span> </span></span><span class="line"><span class="cl"><span class="si">{%</span> <span class="k">end</span> <span class="si">%}</span> </span></span></code></pre></td></tr></table> </div> </div><p>That would be where the linker errors are coming from. Adding <code>pkg-config</code>to <code>buildInputs</code> along with <code>openssl</code> fixes the issue, and my package builds without problems.</p> <a href="#conclusion"> <h3 id="conclusion">Conclusion</h3> </a> <p>Crystal is a rather obscure language, and Nix is a rather obscure build system. I&rsquo;m grateful that the infrastructure I&rsquo;m using exists, and that using it is as streamlined as it is. There is, however, always room for improvement. If I have time, I will be opening pull requests for the <code>crystal2nix</code> tool on GitHub (to allow Git-based repositories), and perhaps on the Crystal compiler as well (to try figure out what to do about <code>pkg-config</code>). If someone else wants to do it themselves, I&rsquo;d be happy to hear how it goes! Otherwise, I hope you found this post useful.</p> Compiling a Functional Language Using C++, Part 11 - Polymorphic Data Types https://danilafe.com/blog/11_compiler_polymorphic_data_types/ Tue, 14 Apr 2020 19:05:42 -0700 https://danilafe.com/blog/11_compiler_polymorphic_data_types/ <p><a href="https://danilafe.com/blog/10_compiler_polymorphism/">In part 10</a>, we managed to get our compiler to accept functions that were polymorphically typed. However, a piece of the puzzle is still missing: while our <em>functions</em> can handle values of different types, the same cannot be said for our <em>data types</em>. This means that we cannot construct data structures that can contain arbitrary types. While we can define and use a list of integers, if we want to also have a list of booleans, we must copy all of our constructors and define a new data type. Worse, not only do we have to duplicate the constructors, but also all the functions that operate on the list. As far as our compiler is concerned, a list of integers and a list of booleans are entirely different beasts, and cannot be operated on by the same code.</p> <p>To make polymorphic data types possible, we must extend our language (and type system) a little. We will now allow for something like this:</p> <pre tabindex="0"><code>data List a = { Nil, Cons a List } </code></pre><p>In the above snippet, we are no longer declaring a single type, but a collection of related types, <strong>parameterized</strong> by a type <code>a</code>. Any type can take the place of <code>a</code> to get a list containing that type of element. Then, <code>List Int</code> is a type, as is <code>List Bool</code> and <code>List (List Int)</code>. The constructors in the snippet also get polymorphic types:</p> $$ \text{Nil} : \forall a \; . \; \text{List} \; a \\ \text{Cons} : \forall a \; . \; a \rightarrow \text{List} \; a \rightarrow \text{List} \; a $$ <p>When you call <code>Cons</code>, the type of the resulting list varies with the type of element you pass in. The empty list <code>Nil</code> is a valid list of any type, since, well, it&rsquo;s empty.</p> <p>Let&rsquo;s talk about <code>List</code> itself, now. I suggest that we ponder the following table:</p> <table> <thead> <tr> <th>\(\text{List}\)</th> <th>\(\text{Cons}\)</th> </tr> </thead> <tbody> <tr> <td>\(\text{List}\) is not a type; it must be followed up with arguments, like \(\text{List} \; \text{Int}\).</td> <td>\(\text{Cons}\) is not a list; it must be followed up with arguments, like \(\text{Cons} \; 3 \; \text{Nil}\).</td> </tr> <tr> <td>\(\text{List} \; \text{Int}\) is in its simplest form.</td> <td>\(\text{Cons} \; 3 \; \text{Nil}\) is in its simplest form.</td> </tr> <tr> <td>\(\text{List} \; \text{Int}\) is a type.</td> <td>\(\text{Cons} \; 3 \; \text{Nil}\) is a value of type \(\text{List} \; \text{Int}\).</td> </tr> </tbody> </table> <p>I hope that the similarities are quite striking. I claim that <code>List</code> is quite similar to a constructor <code>Cons</code>, except that it occurs in a different context: whereas <code>Cons</code> is a way to create values, <code>List</code> is a way to create types. Indeed, while we call <code>Cons</code> a constructor, it&rsquo;s typical to call <code>List</code> a <strong>type constructor</strong>. We know that <code>Cons</code> is a function which assigns to values (like <code>3</code> and <code>Nil</code>) other values (like <code>Cons 3 Nil</code>, or <code>[3]</code> for short). In a similar manner, <code>List</code> can be thought of as a function that assigns to types (like <code>Int</code>) other types (like <code>List Int</code>). We can even claim that it has a type:</p> $$ \text{List} : \text{Type} \rightarrow \text{Type} $$ <p><span class="sidenote"> <label class="sidenote-label" for="dependent-types-note">Unless we get really wacky,</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="dependent-types-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> When your type constructors take as input not only other types but also values such as <code>3</code>, you've ventured into the territory of <a href="https://en.wikipedia.org/wiki/Dependent_type">dependent types</a>. This is a significant step up in complexity from what we'll be doing in this series. If you're interested, check out <a href="https://www.idris-lang.org/">Idris</a> (if you want to know about dependent types for functional programming) or <a href="https://coq.inria.fr/">Coq</a> (to see how propositions and proofs can be encoded in a dependently typed language). <span class="sidenote-delimiter">]</span> </span> </span> our type constructors will only take zero or more types as input, and produce a type as output. In this case, writing \(\text{Type}\) becomes quite repetitive, and we will adopt the convention of writing \(*\) instead. The types of such constructors are called <a href="https://en.wikipedia.org/wiki/Kind_%28type_theory%29"class="external-link">kinds<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. Let&rsquo;s look at a few examples, just to make sure we&rsquo;re on the same page:</p> <ul> <li>The kind of \(\text{Bool}\) is \(*\): it does not accept any type arguments, and is a type in its own right.</li> <li>The kind of \(\text{List}\) is \(*\rightarrow *\): it takes one argument (the type of the things inside the list), and creates a type from it.</li> <li>If we define a pair as <code>data Pair a b = { MkPair a b }</code>, then its kind is \(* \rightarrow * \rightarrow *\), because it requires two parameters.</li> </ul> <p>As one final observation, we note that effectively, all we&rsquo;re doing is tracking the <a href="https://en.wikipedia.org/wiki/Arity"class="external-link">arity<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> of the constructor type.</p> <p>Let&rsquo;s now enumerate all the possible forms that (mono)types can take in our system:</p> <ol> <li>A type can be a placeholder, like \(a\), \(b\), etc.</li> <li>A type can be a type constructor, applied to <span class="sidenote"> <label class="sidenote-label" for="zero-more-note">zero ore more arguments,</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="zero-more-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> It is convenient to treat regular types (like \(\text{Bool}\)) as type constructors of arity 0 (that is, type constructors with kind \(*\)). In effect, they take zero arguments and produce types (themselves). <span class="sidenote-delimiter">]</span> </span> </span> such as \(\text{List} \; \text{Int}\) or \(\text{Bool}\).</li> <li>A function from one type to another, like \(\text{List} \; a \rightarrow \text{Int}\).</li> </ol> <p>Polytypes (type schemes) in our system can be all of the above, but may also include a &ldquo;forall&rdquo; quantifier at the front, generalizing the type (like \(\forall a \; . \; \text{List} \; a \rightarrow \text{Int}\)).</p> <p>Let&rsquo;s start implementing all of this. Why don&rsquo;t we start with the change to the syntax of our language? We have complicated the situation quite a bit. Let&rsquo;s take a look at the <em>old</em> grammar for data type declarations (this is going back as far as <a href="https://danilafe.com/blog/02_compiler_parsing/">part 2</a>). Here, \(L_D\) is the nonterminal for the things that go between the curly braces of a data type declaration, \(D\) is the nonterminal representing a single constructor definition, and \(L_U\) is a list of zero or more uppercase variable names:</p> $$ \begin{aligned} L_D &amp;amp; \rightarrow D \; , \; L_D \\ L_D &amp;amp; \rightarrow D \\ D &amp;amp; \rightarrow \text{upperVar} \; L_U \\ L_U &amp;amp; \rightarrow \text{upperVar} \; L_U \\ L_U &amp;amp; \rightarrow \epsilon \end{aligned} $$ <p>This grammar was actually too simple even for our monomorphically typed language! Since functions are not represented using a single uppercase variable, it wasn&rsquo;t possible for us to define constructors that accept as arguments anything other than integers and user-defined data types. Now, we also need to modify this grammar to allow for constructor applications (which can be nested). To do all of these things, we will define a new nonterminal, \(Y\), for types:</p> $$ \begin{aligned} Y &amp;amp; \rightarrow N \; ``\rightarrow&amp;#34; Y \\ Y &amp;amp; \rightarrow N \end{aligned} $$ <p>We make it right-recursive (because the \(\rightarrow\) operator is right-associative). Next, we define a nonterminal for all types <em>except</em> those constructed with the arrow, \(N\).</p> $$ \begin{aligned} N &amp;amp; \rightarrow \text{upperVar} \; L_Y \\ N &amp;amp; \rightarrow \text{typeVar} \\ N &amp;amp; \rightarrow ( Y ) \end{aligned} $$ <p>The first of the above rules allows a type to be a constructor applied to zero or more arguments (generated by \(L_Y\)). The second rule allows a type to be a placeholder type variable. Finally, the third rule allows for any type (including functions, again) to occur between parentheses. This is so that higher-order functions, like \((a \rightarrow b) \rightarrow a \rightarrow a \), can be represented.</p> <p>Unfortunately, the definition of \(L_Y\) is not as straightforward as we imagine. We could define it as just a list of \(Y\) nonterminals, but this would make the grammar ambigous: something like <code>List Maybe Int</code> could be interpreted as &ldquo;<code>List</code>, applied to types <code>Maybe</code> and <code>Int</code>&rdquo;, and &ldquo;<code>List</code>, applied to type <code>Maybe Int</code>&rdquo;. To avoid this, we define a &ldquo;type list element&rdquo; \(Y'\), which does not take arguments:</p> $$ \begin{aligned} Y&amp;#39; &amp;amp; \rightarrow \text{upperVar} \\ Y&amp;#39; &amp;amp; \rightarrow \text{lowerVar} \\ Y&amp;#39; &amp;amp; \rightarrow ( Y ) \end{aligned} $$ <p>We then make \(L_Y\) a list of \(Y'\):</p> $$ \begin{aligned} L_Y &amp;amp; \rightarrow Y&amp;#39; \; L_Y \\ L_Y &amp;amp; \rightarrow \epsilon \end{aligned} $$ <p>Finally, we update the rules for the data type declaration, as well as for a single constructor. In these new rules, we use \(L_T\) to mean a list of type variables. The rules are as follows:</p> $$ \begin{aligned} T &amp;amp; \rightarrow \text{data} \; \text{upperVar} \; L_T = \{ L_D \} \\ D &amp;amp; \rightarrow \text{upperVar} \; L_Y \\ \end{aligned} $$ <p>Those are all the changes we have to make to our grammar. Let&rsquo;s now move on to implementing the corresponding data structures. We define a new family of structs, which represent types as they are received from the parser. These differ from regular types in that they do not necessarily represent valid types; validating types requires two passes, whereas parsing is done in a single pass. We can define our parsed types as follows:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/11/parsed_type.hpp"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/11/parsed_type.hpp">parsed_type.hpp</a>, entire file</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="cp">#pragma once </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;memory&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;set&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;string&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;type_env.hpp&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">parsed_type</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">virtual</span> <span class="n">type_ptr</span> <span class="nf">to_type</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">vars</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">type_env</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">)</span> <span class="k">const</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="n">parsed_type_ptr</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">unique_ptr</span><span class="o">&lt;</span><span class="n">parsed_type</span><span class="o">&gt;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">parsed_type_app</span> <span class="o">:</span> <span class="n">parsed_type</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">parsed_type_ptr</span><span class="o">&gt;</span> <span class="n">arguments</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">parsed_type_app</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">n</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">parsed_type_ptr</span><span class="o">&gt;</span> <span class="n">as</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">name</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">n</span><span class="p">)),</span> <span class="n">arguments</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">as</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="nf">to_type</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">vars</span><span class="p">,</span> <span class="k">const</span> <span class="n">type_env</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">parsed_type_var</span> <span class="o">:</span> <span class="n">parsed_type</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">var</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">parsed_type_var</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">v</span><span class="p">)</span> <span class="o">:</span> <span class="n">var</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">v</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="nf">to_type</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">vars</span><span class="p">,</span> <span class="k">const</span> <span class="n">type_env</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">parsed_type_arr</span> <span class="o">:</span> <span class="n">parsed_type</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">parsed_type_ptr</span> <span class="n">left</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">parsed_type_ptr</span> <span class="n">right</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">parsed_type_arr</span><span class="p">(</span><span class="n">parsed_type_ptr</span> <span class="n">l</span><span class="p">,</span> <span class="n">parsed_type_ptr</span> <span class="n">r</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">left</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">l</span><span class="p">)),</span> <span class="n">right</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">r</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="nf">to_type</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">vars</span><span class="p">,</span> <span class="k">const</span> <span class="n">type_env</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span></code></pre></td></tr></table> </div> </div> </div> <p>We define the conversion method <code>to_type</code>, which requires a set of type variables that are allowed to occur within a parsed type (which are the variables specified on the left of the <code>=</code> in the data type declaration syntax), and the environment in which to look up the arities of any type constructors. The implementation is as follows:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/11/parsed_type.cpp"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/11/parsed_type.cpp">parsed_type.cpp</a>, entire file</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;parsed_type.hpp&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;type.hpp&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;type_env.hpp&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="n">type_ptr</span> <span class="n">parsed_type_app</span><span class="o">::</span><span class="n">to_type</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">vars</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">type_env</span><span class="o">&amp;</span> <span class="n">e</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">parent_type</span> <span class="o">=</span> <span class="n">e</span><span class="p">.</span><span class="n">lookup_type</span><span class="p">(</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">parent_type</span> <span class="o">==</span> <span class="k">nullptr</span><span class="p">)</span> <span class="k">throw</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_base</span><span class="o">*</span> <span class="n">base_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">base_type</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">type_base</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">parent_type</span><span class="p">.</span><span class="n">get</span><span class="p">())))</span> <span class="k">throw</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">base_type</span><span class="o">-&gt;</span><span class="n">arity</span> <span class="o">!=</span> <span class="n">arguments</span><span class="p">.</span><span class="n">size</span><span class="p">())</span> <span class="k">throw</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">type_app</span><span class="o">*</span> <span class="n">new_app</span> <span class="o">=</span> <span class="k">new</span> <span class="n">type_app</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">parent_type</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="nf">to_return</span><span class="p">(</span><span class="n">new_app</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">arg</span> <span class="p">:</span> <span class="n">arguments</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">new_app</span><span class="o">-&gt;</span><span class="n">arguments</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">arg</span><span class="o">-&gt;</span><span class="n">to_type</span><span class="p">(</span><span class="n">vars</span><span class="p">,</span> <span class="n">e</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">to_return</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">type_ptr</span> <span class="n">parsed_type_var</span><span class="o">::</span><span class="n">to_type</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">vars</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">type_env</span><span class="o">&amp;</span> <span class="n">e</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">vars</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">var</span><span class="p">)</span> <span class="o">==</span> <span class="n">vars</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> <span class="k">throw</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nf">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_var</span><span class="p">(</span><span class="n">var</span><span class="p">));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">type_ptr</span> <span class="n">parsed_type_arr</span><span class="o">::</span><span class="n">to_type</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">vars</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">type_env</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">new_left</span> <span class="o">=</span> <span class="n">left</span><span class="o">-&gt;</span><span class="n">to_type</span><span class="p">(</span><span class="n">vars</span><span class="p">,</span> <span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">new_right</span> <span class="o">=</span> <span class="n">right</span><span class="o">-&gt;</span><span class="n">to_type</span><span class="p">(</span><span class="n">vars</span><span class="p">,</span> <span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nf">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_arr</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">new_left</span><span class="p">),</span> <span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">new_right</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div> </div> <p>Note that this definition requires a new <code>type</code> subclass, <code>type_app</code>, which represents type application. Unlike <code>parsed_type_app</code>, it stores a pointer to the type constructor being applied, rather than its name. This helps validate the type (by making sure the parsed type&rsquo;s name refers to an existing type constructor), and lets us gather information like which constructors the resulting type has. We define this new type as follows:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/11/type.hpp" data-first-line="70" data-last-line="78"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/11/type.hpp#L70-L78">type.hpp</a>, lines 70 through 78</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">70 </span><span class="lnt">71 </span><span class="lnt">72 </span><span class="lnt">73 </span><span class="lnt">74 </span><span class="lnt">75 </span><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">type_app</span> <span class="o">:</span> <span class="k">public</span> <span class="n">type</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">constructor</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">type_ptr</span><span class="o">&gt;</span> <span class="n">arguments</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">type_app</span><span class="p">(</span><span class="n">type_ptr</span> <span class="n">c</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">constructor</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">c</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">print</span><span class="p">(</span><span class="k">const</span> <span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>With our new data structures in hand, we can now update the grammar in our Bison file. First things first, we&rsquo;ll add the type parameters to the data type definition:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/11/parser.y" data-first-line="127" data-last-line="130"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/11/parser.y#L127-L130">parser.y</a>, lines 127 through 130</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">127 </span><span class="lnt">128 </span><span class="lnt">129 </span><span class="lnt">130 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-plaintext" data-lang="plaintext"><span class="line"><span class="cl">data </span></span><span class="line"><span class="cl"> : DATA UID lowercaseParams EQUAL OCURLY constructors CCURLY </span></span><span class="line"><span class="cl"> { $$ = definition_data_ptr(new definition_data(std::move($2), std::move($3), std::move($6))); } </span></span><span class="line"><span class="cl"> ;</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Next, we add the new grammar rules we came up with:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/11/parser.y" data-first-line="138" data-last-line="163"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/11/parser.y#L138-L163">parser.y</a>, lines 138 through 163</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">138 </span><span class="lnt">139 </span><span class="lnt">140 </span><span class="lnt">141 </span><span class="lnt">142 </span><span class="lnt">143 </span><span class="lnt">144 </span><span class="lnt">145 </span><span class="lnt">146 </span><span class="lnt">147 </span><span class="lnt">148 </span><span class="lnt">149 </span><span class="lnt">150 </span><span class="lnt">151 </span><span class="lnt">152 </span><span class="lnt">153 </span><span class="lnt">154 </span><span class="lnt">155 </span><span class="lnt">156 </span><span class="lnt">157 </span><span class="lnt">158 </span><span class="lnt">159 </span><span class="lnt">160 </span><span class="lnt">161 </span><span class="lnt">162 </span><span class="lnt">163 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-plaintext" data-lang="plaintext"><span class="line"><span class="cl">constructor </span></span><span class="line"><span class="cl"> : UID typeList </span></span><span class="line"><span class="cl"> { $$ = constructor_ptr(new constructor(std::move($1), std::move($2))); } </span></span><span class="line"><span class="cl"> ; </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">type </span></span><span class="line"><span class="cl"> : nonArrowType ARROW type { $$ = parsed_type_ptr(new parsed_type_arr(std::move($1), std::move($3))); } </span></span><span class="line"><span class="cl"> | nonArrowType { $$ = std::move($1); } </span></span><span class="line"><span class="cl"> ; </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">nonArrowType </span></span><span class="line"><span class="cl"> : UID typeList { $$ = parsed_type_ptr(new parsed_type_app(std::move($1), std::move($2))); } </span></span><span class="line"><span class="cl"> | LID { $$ = parsed_type_ptr(new parsed_type_var(std::move($1))); } </span></span><span class="line"><span class="cl"> | OPAREN type CPAREN { $$ = std::move($2); } </span></span><span class="line"><span class="cl"> ; </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">typeListElement </span></span><span class="line"><span class="cl"> : OPAREN type CPAREN { $$ = std::move($2); } </span></span><span class="line"><span class="cl"> | UID { $$ = parsed_type_ptr(new parsed_type_app(std::move($1), {})); } </span></span><span class="line"><span class="cl"> | LID { $$ = parsed_type_ptr(new parsed_type_var(std::move($1))); } </span></span><span class="line"><span class="cl"> ; </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">typeList </span></span><span class="line"><span class="cl"> : %empty { $$ = std::vector&lt;parsed_type_ptr&gt;(); } </span></span><span class="line"><span class="cl"> | typeList typeListElement { $$ = std::move($1); $$.push_back(std::move($2)); } </span></span><span class="line"><span class="cl"> ;</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Note in the above rules that even for <code>typeListElement</code>, which can never be applied to any arguments, we still attach a <code>parsed_type_app</code> as the semantic value. This is for consistency; it&rsquo;s easier to view all types in our system as applications to zero or more arguments, than to write coercions from non-applied types to types applied to zero arguments.</p> <p>Finally, we define the types for these new rules at the top of the file:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/11/parser.y" data-first-line="43" data-last-line="44"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/11/parser.y#L43-L44">parser.y</a>, lines 43 through 44</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">43 </span><span class="lnt">44 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-plaintext" data-lang="plaintext"><span class="line"><span class="cl">%type &lt;std::vector&lt;parsed_type_ptr&gt;&gt; typeList </span></span><span class="line"><span class="cl">%type &lt;parsed_type_ptr&gt; type nonArrowType typeListElement</span></span></code></pre></td></tr></table> </div> </div> </div> <p>This concludes our work on the parser, but opens up a whole can of worms elsewhere. First of all, now that we introduced a new <code>type</code> subclass, we must ensure that type unification still works as intended. We therefore have to adjust the <code>type_mgr::unify</code> method:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/11/type.cpp" data-first-line="95" data-last-line="132"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/11/type.cpp#L95-L132">type.cpp</a>, lines 95 through 132</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 95 </span><span class="lnt"> 96 </span><span class="lnt"> 97 </span><span class="lnt"> 98 </span><span class="lnt"> 99 </span><span class="lnt">100 </span><span class="lnt">101 </span><span class="lnt">102 </span><span class="lnt">103 </span><span class="lnt">104 </span><span class="lnt">105 </span><span class="lnt">106 </span><span class="lnt">107 </span><span class="lnt">108 </span><span class="lnt">109 </span><span class="lnt">110 </span><span class="lnt">111 </span><span class="lnt">112 </span><span class="lnt">113 </span><span class="lnt">114 </span><span class="lnt">115 </span><span class="lnt">116 </span><span class="lnt">117 </span><span class="lnt">118 </span><span class="lnt">119 </span><span class="lnt">120 </span><span class="lnt">121 </span><span class="lnt">122 </span><span class="lnt">123 </span><span class="lnt">124 </span><span class="lnt">125 </span><span class="lnt">126 </span><span class="lnt">127 </span><span class="lnt">128 </span><span class="lnt">129 </span><span class="lnt">130 </span><span class="lnt">131 </span><span class="lnt">132 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">type_mgr</span><span class="o">::</span><span class="n">unify</span><span class="p">(</span><span class="n">type_ptr</span> <span class="n">l</span><span class="p">,</span> <span class="n">type_ptr</span> <span class="n">r</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_var</span> <span class="o">*</span><span class="n">lvar</span><span class="p">,</span> <span class="o">*</span><span class="n">rvar</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_arr</span> <span class="o">*</span><span class="n">larr</span><span class="p">,</span> <span class="o">*</span><span class="n">rarr</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_base</span> <span class="o">*</span><span class="n">lid</span><span class="p">,</span> <span class="o">*</span><span class="n">rid</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_app</span> <span class="o">*</span><span class="n">lapp</span><span class="p">,</span> <span class="o">*</span><span class="n">rapp</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">l</span> <span class="o">=</span> <span class="n">resolve</span><span class="p">(</span><span class="n">l</span><span class="p">,</span> <span class="n">lvar</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">r</span> <span class="o">=</span> <span class="n">resolve</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">rvar</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">lvar</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">bind</span><span class="p">(</span><span class="n">lvar</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">,</span> <span class="n">r</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="nf">if</span><span class="p">(</span><span class="n">rvar</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">bind</span><span class="p">(</span><span class="n">rvar</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">,</span> <span class="n">l</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="nf">if</span><span class="p">((</span><span class="n">larr</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">type_arr</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">l</span><span class="p">.</span><span class="n">get</span><span class="p">()))</span> <span class="o">&amp;&amp;</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">rarr</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">type_arr</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">get</span><span class="p">())))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">unify</span><span class="p">(</span><span class="n">larr</span><span class="o">-&gt;</span><span class="n">left</span><span class="p">,</span> <span class="n">rarr</span><span class="o">-&gt;</span><span class="n">left</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">unify</span><span class="p">(</span><span class="n">larr</span><span class="o">-&gt;</span><span class="n">right</span><span class="p">,</span> <span class="n">rarr</span><span class="o">-&gt;</span><span class="n">right</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="nf">if</span><span class="p">((</span><span class="n">lid</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">type_base</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">l</span><span class="p">.</span><span class="n">get</span><span class="p">()))</span> <span class="o">&amp;&amp;</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">rid</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">type_base</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">get</span><span class="p">())))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">lid</span><span class="o">-&gt;</span><span class="n">name</span> <span class="o">==</span> <span class="n">rid</span><span class="o">-&gt;</span><span class="n">name</span> <span class="o">&amp;&amp;</span> <span class="n">lid</span><span class="o">-&gt;</span><span class="n">arity</span> <span class="o">==</span> <span class="n">rid</span><span class="o">-&gt;</span><span class="n">arity</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="nf">if</span><span class="p">((</span><span class="n">lapp</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">type_app</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">l</span><span class="p">.</span><span class="n">get</span><span class="p">()))</span> <span class="o">&amp;&amp;</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">rapp</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">type_app</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">get</span><span class="p">())))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">unify</span><span class="p">(</span><span class="n">lapp</span><span class="o">-&gt;</span><span class="n">constructor</span><span class="p">,</span> <span class="n">rapp</span><span class="o">-&gt;</span><span class="n">constructor</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">left_it</span> <span class="o">=</span> <span class="n">lapp</span><span class="o">-&gt;</span><span class="n">arguments</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">right_it</span> <span class="o">=</span> <span class="n">rapp</span><span class="o">-&gt;</span><span class="n">arguments</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span><span class="p">(</span><span class="n">left_it</span> <span class="o">!=</span> <span class="n">lapp</span><span class="o">-&gt;</span><span class="n">arguments</span><span class="p">.</span><span class="n">end</span><span class="p">()</span> <span class="o">&amp;&amp;</span> </span></span><span class="line"><span class="cl"> <span class="n">right_it</span> <span class="o">!=</span> <span class="n">rapp</span><span class="o">-&gt;</span><span class="n">arguments</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">unify</span><span class="p">(</span><span class="o">*</span><span class="n">left_it</span><span class="p">,</span> <span class="o">*</span><span class="n">right_it</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">left_it</span><span class="o">++</span><span class="p">,</span> <span class="n">right_it</span><span class="o">++</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="nf">unification_error</span><span class="p">(</span><span class="n">l</span><span class="p">,</span> <span class="n">r</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>In the above snippet, we add a new if-statement that checks whether or not both types being unified are type applications, and if so, unifies their constructors and arguments. We also extend our type equality check to ensure that both the names <em>and</em> arities of types match <span class="sidenote"> <label class="sidenote-label" for="type-equality-note">when they are compared for equality.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="type-equality-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> This is actually a pretty silly measure. Consider the following three propositions: 1) types are only declared at the top-level scope. 2) if a type is introduced, and another type with that name already exists, we throw an error. 3) for name equality to be insufficient, we need to have two declared types with the same name. Given these propositions, it will not be possible for us to declare two types that would confuse the name equality check. However, in the near future, these propositions may not all hold: if we allow <code>let/in</code> expressions to contain data type definitions, it will be possible to declare two types with the same name and arity (in different scopes), which would <em>still</em> confuse the check. In the future, if this becomes an issue, we will likely move to unique type identifiers. <span class="sidenote-delimiter">]</span> </span> </span> Note also the more basic fact that we added arity to our <code>type_base</code>, <span class="sidenote"> <label class="sidenote-label" for="base-arity-note">since it may now be a type constructor instead of a plain type.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="base-arity-note"></input><span class="sidenote-content sidenote-left"><span class="sidenote-delimiter">[note:</span> You may be wondering, why did we add arity to base types, rather than data types? Although so far, our language can only create type constructors from data type definitions, it's possible (or even likely) that we will have polymorphic built-in types, such as <a href="https://www.haskell.org/tutorial/io.html">the IO monad</a>. To prepare for this, we will allow our base types to be type constructors too. <span class="sidenote-delimiter">]</span> </span> </span> </p> <p>Jut as we change <code>type_mgr::unify</code>, we need to change <code>type_mgr::find_free</code> to include the new case of <code>type_app</code>. The adjusted function looks as follows:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/11/type.cpp" data-first-line="174" data-last-line="187"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/11/type.cpp#L174-L187">type.cpp</a>, lines 174 through 187</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">174 </span><span class="lnt">175 </span><span class="lnt">176 </span><span class="lnt">177 </span><span class="lnt">178 </span><span class="lnt">179 </span><span class="lnt">180 </span><span class="lnt">181 </span><span class="lnt">182 </span><span class="lnt">183 </span><span class="lnt">184 </span><span class="lnt">185 </span><span class="lnt">186 </span><span class="lnt">187 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">type_mgr</span><span class="o">::</span><span class="n">find_free</span><span class="p">(</span><span class="k">const</span> <span class="n">type_ptr</span><span class="o">&amp;</span> <span class="n">t</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_var</span><span class="o">*</span> <span class="n">var</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">resolved</span> <span class="o">=</span> <span class="n">resolve</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">var</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">var</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">into</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">var</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="nf">if</span><span class="p">(</span><span class="n">type_arr</span><span class="o">*</span> <span class="n">arr</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">type_arr</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">resolved</span><span class="p">.</span><span class="n">get</span><span class="p">()))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">find_free</span><span class="p">(</span><span class="n">arr</span><span class="o">-&gt;</span><span class="n">left</span><span class="p">,</span> <span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">find_free</span><span class="p">(</span><span class="n">arr</span><span class="o">-&gt;</span><span class="n">right</span><span class="p">,</span> <span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="nf">if</span><span class="p">(</span><span class="n">type_app</span><span class="o">*</span> <span class="n">app</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">type_app</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">resolved</span><span class="p">.</span><span class="n">get</span><span class="p">()))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">find_free</span><span class="p">(</span><span class="n">app</span><span class="o">-&gt;</span><span class="n">constructor</span><span class="p">,</span> <span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">arg</span> <span class="p">:</span> <span class="n">app</span><span class="o">-&gt;</span><span class="n">arguments</span><span class="p">)</span> <span class="n">find_free</span><span class="p">(</span><span class="n">arg</span><span class="p">,</span> <span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>There is another adjustment that we have to make to our type code. Recall that we had code that implemented substitutions: replacing free variables with other types to properly implement our type schemes. There was a bug in that code, which becomes much more apparent when the substitution system is put under more pressure. Specifically, the bug was in how type variables were handled.</p> <p>The old substitution code, when it found that a type variable had been bound to another type, always moved on to perform a substitution in that other type. This wasn&rsquo;t really a problem then, since any type variables that needed to be substituted were guaranteed to be free (that&rsquo;s why they were put into the &ldquo;forall&rdquo; quantifier). However, with our new system, we are using user-provided type variables (usually <code>a</code>, <code>b</code>, and so on), which have likely already been used by our compiler internally, and thus have been bound to something. That something is irrelevant to us: when we perform a substitution on a user-defined data type, we <em>know</em> that <em>our</em> <code>a</code> is free, and should be substitited. In short, precedence should be given to substituting type variables, rather than resolving them to what they are bound to.</p> <p>To make this adjustment possible, we need to make <code>substitute</code> a method of <code>type_manager</code>, since it will now require an awareness of existing type bindings. Additionally, this method will now perform its own type resolution, checking if a type variable needs to be substitited between each step. The whole code is as follows:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/11/type.cpp" data-first-line="134" data-last-line="165"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/11/type.cpp#L134-L165">type.cpp</a>, lines 134 through 165</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">134 </span><span class="lnt">135 </span><span class="lnt">136 </span><span class="lnt">137 </span><span class="lnt">138 </span><span class="lnt">139 </span><span class="lnt">140 </span><span class="lnt">141 </span><span class="lnt">142 </span><span class="lnt">143 </span><span class="lnt">144 </span><span class="lnt">145 </span><span class="lnt">146 </span><span class="lnt">147 </span><span class="lnt">148 </span><span class="lnt">149 </span><span class="lnt">150 </span><span class="lnt">151 </span><span class="lnt">152 </span><span class="lnt">153 </span><span class="lnt">154 </span><span class="lnt">155 </span><span class="lnt">156 </span><span class="lnt">157 </span><span class="lnt">158 </span><span class="lnt">159 </span><span class="lnt">160 </span><span class="lnt">161 </span><span class="lnt">162 </span><span class="lnt">163 </span><span class="lnt">164 </span><span class="lnt">165 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">type_ptr</span> <span class="n">type_mgr</span><span class="o">::</span><span class="n">substitute</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">,</span> <span class="n">type_ptr</span><span class="o">&gt;&amp;</span> <span class="n">subst</span><span class="p">,</span> <span class="k">const</span> <span class="n">type_ptr</span><span class="o">&amp;</span> <span class="n">t</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">temp</span> <span class="o">=</span> <span class="n">t</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span><span class="p">(</span><span class="n">type_var</span><span class="o">*</span> <span class="n">var</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">type_var</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">temp</span><span class="p">.</span><span class="n">get</span><span class="p">()))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">subst_it</span> <span class="o">=</span> <span class="n">subst</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">var</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">subst_it</span> <span class="o">!=</span> <span class="n">subst</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> <span class="k">return</span> <span class="n">subst_it</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">var_it</span> <span class="o">=</span> <span class="n">types</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">var</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">var_it</span> <span class="o">==</span> <span class="n">types</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> <span class="k">return</span> <span class="n">t</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">temp</span> <span class="o">=</span> <span class="n">var_it</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">type_arr</span><span class="o">*</span> <span class="n">arr</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">type_arr</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">temp</span><span class="p">.</span><span class="n">get</span><span class="p">()))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">left_result</span> <span class="o">=</span> <span class="n">substitute</span><span class="p">(</span><span class="n">subst</span><span class="p">,</span> <span class="n">arr</span><span class="o">-&gt;</span><span class="n">left</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">right_result</span> <span class="o">=</span> <span class="n">substitute</span><span class="p">(</span><span class="n">subst</span><span class="p">,</span> <span class="n">arr</span><span class="o">-&gt;</span><span class="n">right</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">left_result</span> <span class="o">==</span> <span class="n">arr</span><span class="o">-&gt;</span><span class="n">left</span> <span class="o">&amp;&amp;</span> <span class="n">right_result</span> <span class="o">==</span> <span class="n">arr</span><span class="o">-&gt;</span><span class="n">right</span><span class="p">)</span> <span class="k">return</span> <span class="n">t</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nf">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_arr</span><span class="p">(</span><span class="n">left_result</span><span class="p">,</span> <span class="n">right_result</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="nf">if</span><span class="p">(</span><span class="n">type_app</span><span class="o">*</span> <span class="n">app</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">type_app</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">temp</span><span class="p">.</span><span class="n">get</span><span class="p">()))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">constructor_result</span> <span class="o">=</span> <span class="n">substitute</span><span class="p">(</span><span class="n">subst</span><span class="p">,</span> <span class="n">app</span><span class="o">-&gt;</span><span class="n">constructor</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">bool</span> <span class="n">arg_changed</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">type_ptr</span><span class="o">&gt;</span> <span class="n">new_args</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">arg</span> <span class="p">:</span> <span class="n">app</span><span class="o">-&gt;</span><span class="n">arguments</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">arg_result</span> <span class="o">=</span> <span class="n">substitute</span><span class="p">(</span><span class="n">subst</span><span class="p">,</span> <span class="n">arg</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">arg_changed</span> <span class="o">|=</span> <span class="n">arg_result</span> <span class="o">!=</span> <span class="n">arg</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">new_args</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">arg_result</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">constructor_result</span> <span class="o">==</span> <span class="n">app</span><span class="o">-&gt;</span><span class="n">constructor</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">arg_changed</span><span class="p">)</span> <span class="k">return</span> <span class="n">t</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_app</span><span class="o">*</span> <span class="n">new_app</span> <span class="o">=</span> <span class="k">new</span> <span class="n">type_app</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">constructor_result</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">swap</span><span class="p">(</span><span class="n">new_app</span><span class="o">-&gt;</span><span class="n">arguments</span><span class="p">,</span> <span class="n">new_args</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">type_ptr</span><span class="p">(</span><span class="n">new_app</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">t</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>That&rsquo;s all for types. Definitions, though, need some work. First of all, we&rsquo;ve changed our parser to feed our <code>constructor</code> type a vector of <code>parsed_type_ptr</code>, rather than <code>std::string</code>. We therefore have to update <code>constructor</code> to receive and store this new vector:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/11/definition.hpp" data-first-line="13" data-last-line="20"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/11/definition.hpp#L13-L20">definition.hpp</a>, lines 13 through 20</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">constructor</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">parsed_type_ptr</span><span class="o">&gt;</span> <span class="n">types</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int8_t</span> <span class="n">tag</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">constructor</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">n</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">parsed_type_ptr</span><span class="o">&gt;</span> <span class="n">ts</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">name</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">n</span><span class="p">)),</span> <span class="n">types</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">ts</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"><span class="p">};</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Similarly, <code>definition_data</code> itself needs to accept the list of type variables it has:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/11/definition.hpp" data-first-line="54" data-last-line="70"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/11/definition.hpp#L54-L70">definition.hpp</a>, lines 54 through 70</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span><span class="lnt">65 </span><span class="lnt">66 </span><span class="lnt">67 </span><span class="lnt">68 </span><span class="lnt">69 </span><span class="lnt">70 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">definition_data</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">vars</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">constructor_ptr</span><span class="o">&gt;</span> <span class="n">constructors</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">type_env_ptr</span> <span class="n">env</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">definition_data</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">n</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">vs</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">constructor_ptr</span><span class="o">&gt;</span> <span class="n">cs</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">name</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">n</span><span class="p">)),</span> <span class="n">vars</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">vs</span><span class="p">)),</span> <span class="n">constructors</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">cs</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">insert_types</span><span class="p">(</span><span class="n">type_env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">insert_constructors</span><span class="p">()</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">generate_llvm</span><span class="p">(</span><span class="n">llvm_context</span><span class="o">&amp;</span> <span class="n">ctx</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">};</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We then look at <code>definition_data::insert_constructors</code>, which converts <code>constructor</code> instances to actual constructor functions. The code, which is getting pretty complciated, is as follows:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/11/definition.cpp" data-first-line="64" data-last-line="92"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/11/definition.cpp#L64-L92">definition.cpp</a>, lines 64 through 92</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">64 </span><span class="lnt">65 </span><span class="lnt">66 </span><span class="lnt">67 </span><span class="lnt">68 </span><span class="lnt">69 </span><span class="lnt">70 </span><span class="lnt">71 </span><span class="lnt">72 </span><span class="lnt">73 </span><span class="lnt">74 </span><span class="lnt">75 </span><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span><span class="lnt">79 </span><span class="lnt">80 </span><span class="lnt">81 </span><span class="lnt">82 </span><span class="lnt">83 </span><span class="lnt">84 </span><span class="lnt">85 </span><span class="lnt">86 </span><span class="lnt">87 </span><span class="lnt">88 </span><span class="lnt">89 </span><span class="lnt">90 </span><span class="lnt">91 </span><span class="lnt">92 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">definition_data</span><span class="o">::</span><span class="n">insert_constructors</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">this_type_ptr</span> <span class="o">=</span> <span class="n">env</span><span class="o">-&gt;</span><span class="n">lookup_type</span><span class="p">(</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">type_data</span><span class="o">*</span> <span class="n">this_type</span> <span class="o">=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="n">type_data</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">this_type_ptr</span><span class="p">.</span><span class="n">get</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">next_tag</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">var_set</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_app</span><span class="o">*</span> <span class="n">return_app</span> <span class="o">=</span> <span class="k">new</span> <span class="n">type_app</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">this_type_ptr</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="nf">return_type</span><span class="p">(</span><span class="n">return_app</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">var</span> <span class="p">:</span> <span class="n">vars</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">var_set</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">var</span><span class="p">)</span> <span class="o">!=</span> <span class="n">var_set</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> <span class="k">throw</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">var_set</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">var</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">return_app</span><span class="o">-&gt;</span><span class="n">arguments</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_var</span><span class="p">(</span><span class="n">var</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">constructor</span> <span class="p">:</span> <span class="n">constructors</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">constructor</span><span class="o">-&gt;</span><span class="n">tag</span> <span class="o">=</span> <span class="n">next_tag</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">this_type</span><span class="o">-&gt;</span><span class="n">constructors</span><span class="p">[</span><span class="n">constructor</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="n">next_tag</span><span class="o">++</span> <span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">full_type</span> <span class="o">=</span> <span class="n">return_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">constructor</span><span class="o">-&gt;</span><span class="n">types</span><span class="p">.</span><span class="n">rbegin</span><span class="p">();</span> <span class="n">it</span> <span class="o">!=</span> <span class="n">constructor</span><span class="o">-&gt;</span><span class="n">types</span><span class="p">.</span><span class="n">rend</span><span class="p">();</span> <span class="n">it</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">type</span> <span class="o">=</span> <span class="p">(</span><span class="o">*</span><span class="n">it</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">to_type</span><span class="p">(</span><span class="n">var_set</span><span class="p">,</span> <span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">full_type</span> <span class="o">=</span> <span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_arr</span><span class="p">(</span><span class="n">type</span><span class="p">,</span> <span class="n">full_type</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">type_scheme_ptr</span> <span class="nf">full_scheme</span><span class="p">(</span><span class="k">new</span> <span class="n">type_scheme</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">full_type</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"> <span class="n">full_scheme</span><span class="o">-&gt;</span><span class="n">forall</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">full_scheme</span><span class="o">-&gt;</span><span class="n">forall</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">vars</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">vars</span><span class="p">.</span><span class="n">end</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="n">env</span><span class="o">-&gt;</span><span class="n">bind</span><span class="p">(</span><span class="n">constructor</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">,</span> <span class="n">full_scheme</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>In the above snippet, we do the following things:</p> <ol> <li>We first create a set of type variables that can occur in this type&rsquo;s constructors (the same set that&rsquo;s used by the <code>to_type</code> method we saw earlier). While doing this, we ensure a type variable is not used twice (this is not allowed), and add each type variable to the final return type (which is something like <code>List a</code>), in the order they occur.</li> <li>When the variables have been gathered into a set, we iterate over all constructors, and convert them into types by calling <code>to_type</code> on their arguments, then assembling the resulting argument types into a function. This is not enough, however, <span class="sidenote"> <label class="sidenote-label" for="type-variables-note">since constructors of types that accept type variables are polymorphic,</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="type-variables-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> This is also not enough because without generalization using "forall", we are risking using type variables that have already been bound, or that will be bound. Even if <code>a</code> has not yet been used by the typechecker, it will be once the type manager generates its first type variable, and things will go south. If we, for some reason, wanted type constructors to be monomorphic (but generic, with type variables) we'd need to internally instnatiate fresh type variables for every user-defined type variable, and substitute them appropriately. <span class="sidenote-delimiter">]</span> </span> </span> as we have discussed above with \(\text{Nil}\) and \(\text{Cons}\). To accomodate for this, we also add all type variables to the &ldquo;forall&rdquo; quantifier of a new type scheme, whose monotype is our newly assembled function type. This type scheme is what we store as the type of the constructor.</li> </ol> <p>This was the last major change we have to perform. The rest is cleanup: we have switched our system to dealing with type applications (sometimes with zero arguments), and we must bring the rest of the compiler up to speed with this change. For instance, we update <code>ast_int</code> to create a reference to an existing integer type during typechecking:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/11/ast.cpp" data-first-line="20" data-last-line="22"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/11/ast.cpp#L20-L22">ast.cpp</a>, lines 20 through 22</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">type_ptr</span> <span class="n">ast_int</span><span class="o">::</span><span class="n">typecheck</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nf">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_app</span><span class="p">(</span><span class="n">env</span><span class="o">-&gt;</span><span class="n">lookup_type</span><span class="p">(</span><span class="s">&#34;Int&#34;</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Similarly, we update our code in <code>typecheck_program</code> to use type applications in the type for binary operations:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/11/main.cpp" data-first-line="31" data-last-line="37"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/11/main.cpp#L31-L37">main.cpp</a>, lines 31 through 37</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">int_type</span> <span class="o">=</span> <span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_base</span><span class="p">(</span><span class="s">&#34;Int&#34;</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="n">env</span><span class="o">-&gt;</span><span class="n">bind_type</span><span class="p">(</span><span class="s">&#34;Int&#34;</span><span class="p">,</span> <span class="n">int_type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">int_type_app</span> <span class="o">=</span> <span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_app</span><span class="p">(</span><span class="n">int_type</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">binop_type</span> <span class="o">=</span> <span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_arr</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="n">int_type_app</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_arr</span><span class="p">(</span><span class="n">int_type_app</span><span class="p">,</span> <span class="n">int_type_app</span><span class="p">))));</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Finally, we update <code>ast_case</code> to unwrap type applications to get the needed constructor data from <code>type_data</code>. This has to be done in <code>ast_case::typecheck</code>, as follows:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/11/ast.cpp" data-first-line="163" data-last-line="168"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/11/ast.cpp#L163-L168">ast.cpp</a>, lines 163 through 168</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">163 </span><span class="lnt">164 </span><span class="lnt">165 </span><span class="lnt">166 </span><span class="lnt">167 </span><span class="lnt">168 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="n">input_type</span> <span class="o">=</span> <span class="n">mgr</span><span class="p">.</span><span class="n">resolve</span><span class="p">(</span><span class="n">case_type</span><span class="p">,</span> <span class="n">var</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">type_app</span><span class="o">*</span> <span class="n">app_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">app_type</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">type_app</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">input_type</span><span class="p">.</span><span class="n">get</span><span class="p">()))</span> <span class="o">||</span> </span></span><span class="line"><span class="cl"> <span class="o">!</span><span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">type_data</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">app_type</span><span class="o">-&gt;</span><span class="n">constructor</span><span class="p">.</span><span class="n">get</span><span class="p">()))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="nf">type_error</span><span class="p">(</span><span class="s">&#34;attempting case analysis of non-data type&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Additionally, a similar change needs to be made in <code>ast_case::compile</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/11/ast.cpp" data-first-line="174" data-last-line="175"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/11/ast.cpp#L174-L175">ast.cpp</a>, lines 174 through 175</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">174 </span><span class="lnt">175 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="n">type_app</span><span class="o">*</span> <span class="n">app_type</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">type_app</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">input_type</span><span class="p">.</span><span class="n">get</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="n">type_data</span><span class="o">*</span> <span class="n">type</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">type_data</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">app_type</span><span class="o">-&gt;</span><span class="n">constructor</span><span class="p">.</span><span class="n">get</span><span class="p">());</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>That should be all! Let&rsquo;s try an example:</p> <div class="highlight-group" > <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/11/examples/works3.txt">works3.txt</a>, entire file</div><pre><code>data List a = { Nil, Cons a (List a) } data Bool = { True, False } defn length l = { case l of { Nil -&gt; { 0 } Cons x xs -&gt; { 1 + length xs } } } defn main = { length (Cons 1 (Cons 2 (Cons 3 Nil))) + length (Cons True (Cons False (Cons True Nil))) } </code></pre> </div> <p>The output:</p> <pre tabindex="0"><code>Result: 6 </code></pre><p>Yay! Not only were we able to define a list of any type, but our <code>length</code> function correctly determined the lengths of two lists of different types! Let&rsquo;s try an example with the classic <a href="http://learnyouahaskell.com/higher-order-functions#folds"class="external-link"><code>fold</code> functions<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>:</p> <div class="highlight-group" > <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/11/examples/list.txt">list.txt</a>, entire file</div><pre><code>data List a = { Nil, Cons a (List a) } defn map f l = { case l of { Nil -&gt; { Nil } Cons x xs -&gt; { Cons (f x) (map f xs) } } } defn foldl f b l = { case l of { Nil -&gt; { b } Cons x xs -&gt; { foldl f (f b x) xs } } } defn foldr f b l = { case l of { Nil -&gt; { b } Cons x xs -&gt; { f x (foldr f b xs) } } } defn list = { Cons 1 (Cons 2 (Cons 3 (Cons 4 Nil))) } defn add x y = { x + y } defn sum l = { foldr add 0 l } defn skipAdd x y = { y + 1 } defn length l = { foldr skipAdd 0 l } defn main = { sum list + length list } </code></pre> </div> <p>We expect the sum of the list <code>[1,2,3,4]</code> to be <code>10</code>, and its length to be <code>4</code>, so the sum of the two should be <code>14</code>. And indeed, our program agrees:</p> <pre tabindex="0"><code>Result: 14 </code></pre><p>Let&rsquo;s do one more example, to test types that take more than one type parameter:</p> <div class="highlight-group" > <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/11/examples/pair.txt">pair.txt</a>, entire file</div><pre><code>data Pair a b = { MkPair a b } defn fst p = { case p of { MkPair a b -&gt; { a } } } defn snd p = { case p of { MkPair a b -&gt; { b } } } defn pair = { MkPair 1 (MkPair 2 3) } defn main = { fst pair + snd (snd pair) } </code></pre> </div> <p>Once again, the compiled program gives the expected result:</p> <pre tabindex="0"><code>Result: 4 </code></pre><p>This looks good! We have added support for polymorphic data types to our compiler. We are now free to move on to <code>let/in</code> expressions, <strong>lambda functions</strong>, and <strong>Input/Output</strong>, as promised, starting with <a href="https://danilafe.com/blog/12_compiler_let_in_lambda/">part 12</a> - <code>let/in</code> and lambdas!</p> Compiling a Functional Language Using C++, Part 10 - Polymorphism https://danilafe.com/blog/10_compiler_polymorphism/ Wed, 25 Mar 2020 17:14:20 -0700 https://danilafe.com/blog/10_compiler_polymorphism/ <p><a href="https://danilafe.com/blog/08_compiler_llvm/">In part 8</a>, we wrote some pretty interesting programs in our little language. We successfully expressed arithmetic and recursion. But there&rsquo;s one thing that we cannot express in our language without further changes: an <code>if</code> statement.</p> <p>Suppose we didn&rsquo;t want to add a special <code>if/else</code> expression into our language. Thanks to lazy evaluation, we can express it using a function:</p> <pre tabindex="0"><code>defn if c t e = { case c of { True -&gt; { t } False -&gt; { e } } } </code></pre><p>But an issue still remains: so far, our compiler remains <strong>monomorphic</strong>. That is, a particular function can only have one possible type for each one of its arguments. With our current setup, something like this <span class="sidenote"> <label class="sidenote-label" for="if-note">would not work:</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="if-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> In a polymorphically typed language, the inner <code>if</code> would just evaluate to <code>False</code>, and the whole expression to 3. <span class="sidenote-delimiter">]</span> </span> </span> </p> <pre tabindex="0"><code>if (if True False True) 11 3 </code></pre><p>This is because, for this to work, both of the following would need to hold (borrowing some of our notation from the <a href="https://danilafe.com/blog/03_compiler_typechecking/">typechecking</a> post):</p> $$ \text{if} : \text{Bool} \rightarrow \text{Int} \rightarrow \text{Int} \rightarrow \text{Int} $$ $$ \text{if} : \text{Bool} \rightarrow \text{Bool} \rightarrow \text{Bool} \rightarrow \text{Bool} $$ <p>But using our rules so far, such a thing is impossible, since there is no way for \(\text{Int}\) to be unified with \(\text{Bool}\). We need a more powerful set of rules to describe our program&rsquo;s types.</p> <a href="#hindley-milner-type-system"> <h3 id="hindley-milner-type-system">Hindley-Milner Type System</h3> </a> <p>One such powerful set of rules is the <a href="https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system"class="external-link">Hindley-Milner type system<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, which we have previously alluded to. In fact, the rules we came up with were already very close to Hindley-Milner, with the exception of two: <strong>generalization</strong> and <strong>instantiation</strong>. It&rsquo;s been quite a while since the last time we worked on typechecking, so I&rsquo;m going to present a table with these new rules, as well as all of the ones that we <span class="sidenote"> <label class="sidenote-label" for="rules-note">previously used.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="rules-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> The rules aren't quite the same as the ones we used earlier; note that \(\sigma\) is used in place of \(\tau\) in the first rule, for instance. These changes are slight, and we'll talk about how the rules work together below. <span class="sidenote-delimiter">]</span> </span> </span> I will also give a quick summary of each of these rules.</p> <span class="fold-table"></span> <table> <thead> <tr> <th>Rule</th> <th>Name and Description</th> </tr> </thead> <tbody> <tr> <td>$$ \frac {x:\sigma \in \Gamma} {\Gamma \vdash x:\sigma} $$ </td> <td><strong>Var</strong>: If the variable \(x\) is known to have some polymorphic type \(\sigma\) then an expression consisting only of that variable is of that type.</td> </tr> <tr> <td>$$ \frac {\Gamma \vdash e_1 : \tau_1 \rightarrow \tau_2 \quad \Gamma \vdash e_2 : \tau_1} {\Gamma \vdash e_1 \; e_2 : \tau_2} $$ </td> <td><strong>App</strong>: If an expression \(e_1\), which is a function from monomorphic type \(\tau_1\) to another monomorphic type \(\tau_2\), is applied to an argument \(e_2\) of type \(\tau_1\), then the result is of type \(\tau_2\).</td> </tr> <tr> <td>$$ \frac {\Gamma \vdash e : \tau \quad \text{matcht}(\tau, p_i) = b_i \quad \Gamma,b_i \vdash e_i : \tau_c} {\Gamma \vdash \text{case} \; e \; \text{of} \; \{ (p_1,e_1) \ldots (p_n, e_n) \} : \tau_c } $$ </td> <td><strong>Case</strong>: This rule is not part of Hindley-Milner, and is specific to our language. If the expression being case-analyzed is of type \(\tau\) and each branch \((p_i, e_i)\) is of the same type \(\tau_c\) when the pattern \(p_i\) works with type \(\tau\) producing extra bindings \(b_i\), the whole case expression is of type \(\tau_c\).</td> </tr> <tr> <td>$$ \frac{\Gamma \vdash e : \sigma&amp;#39; \quad \sigma&amp;#39; \sqsubseteq \sigma} {\Gamma \vdash e : \sigma} $$ </td> <td><strong>Inst (New)</strong>: If type \(\sigma\) is an instantiation (or specialization) of type \(\sigma'\) then an expression of type \(\sigma'\) is also an expression of type \(\sigma\).</td> </tr> <tr> <td>$$ \frac {\Gamma \vdash e : \sigma \quad \alpha \not \in \text{free}(\Gamma)} {\Gamma \vdash e : \forall \alpha . \sigma} $$ </td> <td><strong>Gen (New)</strong>: If an expression has a type with free variables, this rule allows us generalize it to allow all possible types to be used for these free variables.</td> </tr> </tbody> </table> <p>Here, there is a distinction between different forms of types. First, there are monomorphic types, or <strong>monotypes</strong>, \(\tau\), which are types such as \(\text{Int}\), \(\text{Int} \rightarrow \text{Bool}\), \(a \rightarrow b\) and so on. These types are what we&rsquo;ve been working with so far. Each of them represents one (hence, &ldquo;mono-&rdquo;), concrete type. This is obvious in the case of \(\text{Int}\) and \(\text{Int} \rightarrow \text{Bool}\), but for \(a \rightarrow b\) things are slightly less clear. Does it really represent a single type, if we can put an arbtirary thing for \(a\)? The answer is &ldquo;yes&rdquo;! Although \(a\) is not currently known, it stands in place of another monotype, which is yet to be determined.</p> <p>So, how do we represent polymorphic types, like that of \(\text{if}\)? We borrow some more notation from mathematics, and use the &ldquo;forall&rdquo; quantifier:</p> $$ \text{if} : \forall a \; . \; \text{Bool} \rightarrow a \rightarrow a \rightarrow a $$ <p>This basically says, &ldquo;the type of \(\text{if}\) is \(\text{Bool} \rightarrow a \rightarrow a \rightarrow a\) for all possible \(a\)&rdquo;. This new expression using &ldquo;forall&rdquo; is what we call a type scheme, or a polytype \(\sigma\). For simplicity, we only allow &ldquo;forall&rdquo; to be at the front of a polytype. That is, expressions like \(a \rightarrow \forall b \; . \; b \rightarrow b\) are not valid polytypes as far as we&rsquo;re concerned.</p> <p>It&rsquo;s key to observe that only some of the typing rules in the above table use polytypes (\(\sigma\)). Whereas expressions consisting of a single variable can be polymorphically typed, this is not true for function applications and case expressions. In fact, according to our rules, there is no way to introduce a polytype anywhere into our system!</p> <p>The reason for this is that we only allow polymorphism at certain locations. In the Hindley-Milner type system, this is called <strong>Let-Polymorphism</strong>, which means that only in <code>let</code>/<code>in</code> expressions can variables or expressions be given a polymorphic type. We, on the other hand, do not (yet) have <code>let</code>/<code>in</code> expressions, so our polymorphism is further limited: only functions (and data type constructors) can be polymorphically typed.</p> <p>Let&rsquo;s talk about what <strong>Inst</strong> does, and what &ldquo;\(\sqsubseteq\)&rdquo; means. First, why don&rsquo;t we go ahead and write the formal inference rule for \(\sqsubseteq\):</p> $$ \frac {\tau&amp;#39;=\{\alpha_i \mapsto \tau_i \}\tau \quad \beta_i \not \in \text{free}(\forall \alpha_1...\forall \alpha_n \; . \; \tau)} {\forall \alpha_1 ... \forall \alpha_n \; . \; \tau \sqsubseteq \forall \beta_1 ... \forall \beta_m \; . \; \tau&amp;#39;} $$ <p>In my opinion, this is one of the more confusing inference rules we have to deal with in Hindley-Milner. In principle, though, it&rsquo;s not too hard to understand. \(\sigma' \sqsubseteq \sigma\) says &ldquo;\(\sigma'\) is more general than \(\sigma\)&rdquo;. Alternatively, we can interpret it as &ldquo;\(\sigma\) is an instance of \(\sigma'\)&rdquo;.</p> <p>What does it mean for one polytype to be more general than another? Intuitively, we might say that \(\forall a \; . \; a \rightarrow a\) is more general than \(\text{Int} \rightarrow \text{Int}\), because the former type can represent the latter, and more. Formally, we define this in terms of <strong>substitutions</strong>. A substitution \(\{\alpha \mapsto \tau \}\) replaces a variable \(\alpha\) with a monotype \(\tau\). If we can use a substitution to convert one type into another, then the first type (the one on which the substitution was performed) is more general than the resulting type. In our previous example, since we can apply the substitution \(\{a \mapsto \text{Int}\}\) to get \(\text{Int} \rightarrow \text{Int}\) from \(\forall a \; . \; a \rightarrow a\), the latter type is more general; using our notation, \(\forall a \; . \; a \rightarrow a \sqsubseteq \text{Int} \rightarrow \text{Int}\).</p> <p>That&rsquo;s pretty much all that the rule says, really. But what about the whole thing with \(\beta\) and \(\text{free}\)? The reason for that part of the rule is that, in principle, we can substitute polytypes into polytypes. However, we can&rsquo;t do this using \(\{ \alpha \mapsto \sigma \}\). Consider, for example:</p> $$ \{ a \mapsto \forall b \; . \; b \rightarrow b \} \; \text{Bool} \rightarrow a \rightarrow a \stackrel{?}{=} \text{Bool} \rightarrow (\forall b \; . \; b \rightarrow b) \rightarrow \forall b \; . \; b \rightarrow b $$ <p>Hmm, this is not good. Didn&rsquo;t we agree that we only want quantifiers at the front of our types? Instead, to make that substitution, we only substitute the monotype \(b \rightarrow b\), and then add the \(\forall b\) at the front. But to do this, we must make sure that \(b\) doesn&rsquo;t occur anywhere in the original type \(\forall a \; . \; \text{Bool} \rightarrow a \rightarrow a\) (otherwise we can accidentally generalize too much). So then, our concrete inference rule is as follows:</p> $$ \frac { \begin{gathered} \text{Bool} \rightarrow (b \rightarrow b) \rightarrow b \rightarrow b =\{a \mapsto (b \rightarrow b) \} \; \text{Bool} \rightarrow a \rightarrow a \\ b \not \in \text{free}(\forall a \; . \; \text{Bool} \rightarrow a \rightarrow a) = \varnothing \end{gathered} } {\forall a \; . \; \text{Bool} \rightarrow a \rightarrow a \sqsubseteq \forall b \; . \; \text{Bool} \rightarrow (b \rightarrow b) \rightarrow b \rightarrow b} $$ <p>In the above rule we:</p> <ol> <li>Replaced \(a\) with \(b \rightarrow b\), getting rid of \(a\) in the quantifier.</li> <li>Observed that \(b\) is not a free variable in the original type, and thus can be generalized.</li> <li>Added the generalization for \(b\) to the front of the resulting type.</li> </ol> <p>Now, <strong>Inst</strong> just allows us to perform specialization / substitution as many times as we need to get to the type we want.</p> <a href="#a-new-typechecking-algorithm"> <h3 id="a-new-typechecking-algorithm">A New Typechecking Algorithm</h3> </a> <p>Alright, now we have all these rules. How does this change our typechecking algorithm? How about the following:</p> <ol> <li>To every declared function, assign the type \(a \rightarrow ... \rightarrow y \rightarrow z\), where <span class="sidenote"> <label class="sidenote-label" for="arguments-note">\(a\) through \(y\) are the types of the arguments to the function,</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="arguments-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Of course, there can be more or less than 25 arguments to any function. This is just a generalization; we use as many input types as are needed. <span class="sidenote-delimiter">]</span> </span> </span> and \(z\) is the function&rsquo;s return type.</li> <li>We typecheck each declared function, using the <strong>Var</strong>, <strong>Case</strong>, <strong>App</strong>, and <strong>Inst</strong> rules.</li> <li>Whatever type variables we don&rsquo;t fill in, we assume can be filled in with any type, and use the <strong>Gen</strong> rule to sprinkle polymorphism where it is needed.</li> </ol> <p>Maybe this is enough. Let&rsquo;s go through an example. Suppose we have three functions:</p> <pre tabindex="0"><code>defn if c t e = { case c of { True -&gt; { t } False -&gt; { e } } } defn testOne = { if True False True } defn testTwo = { if True 0 1 } </code></pre><p>If we go through and typecheck them top-to-bottom, the following happens:</p> <ol> <li>We start by assuming \(\text{if} : a \rightarrow b \rightarrow c \rightarrow d\), \(\text{testOne} : e\) and \(\text{testTwo} : f\).</li> <li>We look at <code>if</code>. We infer the type of <code>c</code> to be \(\text{Bool}\), and thus, \(a = \text{Bool}\). We also infer that \(b = c\), since they occur in two branches of the same case expression. Finally, we infer that \(c = d\), since whatever the case expression returns becomes the return value of the function. Thus, we come out knowing that \(\text{if} : \text{Bool} \rightarrow b \rightarrow b \rightarrow b\).</li> <li>Now, since we never figured out \(b\), we use <strong>Gen</strong>: \(\text{if} : \forall b \; . \; \text{Bool} \rightarrow b \rightarrow b \rightarrow b\). Like we&rsquo;d want, <code>if</code> works with all types, as long as both its inputs are of the same type.</li> <li>When we typecheck the body of <code>testOne</code>, we use <strong>Inst</strong> to turn the above type for <code>if</code> into a single, monomorphic instance. Then, type inference proceeds as before, and all is well.</li> <li>When we typecheck the body of <code>testTwo</code>, we use <strong>Inst</strong> again, instantiating a new monotype, and all is well again.</li> </ol> <p>So far, so good. But what if we started from the bottom, and went to the top?</p> <ol> <li>We start by assuming \(\text{if} : a \rightarrow b \rightarrow c \rightarrow d\), \(\text{testOne} : e\) and \(\text{testTwo} : f\).</li> <li>We look at <code>testTwo</code>. We infer that \(a = \text{Bool}\) (since we pass in <code>True</code> to <code>if</code>). We also infer that \(b = \text{Int}\), and that \(c = \text{Int}\). Not yet sure of the return type of <code>if</code>, this is where we stop. We are left with the knowledge that \(f = d\) (because the return type of <code>if</code> is the return type of <code>testTwo</code>), but that&rsquo;s about it. Since \(f\) is no longer free, we don&rsquo;t generalize, and conclude that \(\text{testTwo} : f\).</li> <li>We look at <code>testOne</code>. We infer that \(a = \text{Bool}\) (already known). We also infer that \(b = \text{Bool}\), and that \(c = \text{Bool}\). But wait a minute! This is not right. We are back to where we started, with a unification error!</li> </ol> <p>What went wrong? I claim that we typechecked the functions that <em>used</em> <code>if</code> before we typechecked <code>if</code> itself, which led us to infer a less-than-general type for <code>if</code>. This less-than-general type was insufficient to correctly check the whole program.</p> <p>To address this, we enforce a particular order of type inference on our declaration, guided by dependencies between functions. Haskell, which has to deal with a similar issue, has <a href="https://www.haskell.org/onlinereport/haskell2010/haskellch4.html#x10-880004.5"class="external-link">a section in the 2010 report on this<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. In short:</p> <ol> <li>We find the <span class="sidenote"> <label class="sidenote-label" for="transitive-closure-note">transitive closure</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="transitive-closure-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> A transitive closure of a vertex in a graph is the list of all vertices reachable from that original vertex. Check out the <a href="https://en.wikipedia.org/wiki/Transitive_closure#In_graph_theory"> Wikipedia page on this</a>. <span class="sidenote-delimiter">]</span> </span> </span> of the function dependencies. We define a function \(f\) to be dependent on another function \(g\) if \(f\) calls \(g\). The transitive closure will help us find functions that are related indirectly. For instance, if \(g\) also depends on \(h\), then the transitive closure of \(f\) will include \(h\), even if \(f\) directly doesn&rsquo;t use \(h\).</li> <li>We isolate groups of mutually dependent functions. If \(f\) depends on \(g\) and \(g\) depends \(f\), they are placed in one group. We then construct a dependency graph <strong>of these groups</strong>.</li> <li>We compute a topological order of the group graph. This helps us typecheck the dependencies of functions before checking the functions themselves. In our specific case, this would ensure we check <code>if</code> first, and only then move on to <code>testOne</code> and <code>testTwo</code>. The order of typechecking within a group does not matter, as long as we generalize only after typechecking all functions in a group.</li> <li>We typecheck the function groups, and functions within them, following the above topological order.</li> </ol> <p>To find the transitive closure of a graph, we can use <a href="https://cs.winona.edu/lin/cs440/ch08-2.pdf"class="external-link">Warshall&rsquo;s Algorithm<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. This algorithm, with complexity \(O(|V|^3)\), goes as follows: $$ \begin{aligned} &amp;amp; A, R^{(i)} \in \mathbb{B}^{n \times n} \\ &amp;amp; \\ &amp;amp; R^{(0)} \leftarrow A \\ &amp;amp; \textbf{for} \; k \leftarrow 1 \; \textbf{to} \; n \; \textbf{do} \\ &amp;amp; \quad \textbf{for} \; i \leftarrow 1 \; \textbf{to} \; n \; \textbf{do} \\ &amp;amp; \quad \quad \textbf{for} \; j \leftarrow 1 \; \textbf{to} \; n \; \textbf{do} \\ &amp;amp; \quad \quad \quad R^{(k)}[i,j] \leftarrow R^{(k-1)}[i,j] \; \textbf{or} \; R^{(k-1)}[i,k] \; \textbf{and} \; R^{(k-1)}[k,j] \\ &amp;amp; \textbf{return} \; R^{(n)} \end{aligned} $$ </p> <p>In the above notation, \(R^{(i)}\) is the \(i\)th matrix \(R\), and \(A\) is the adjacency matrix of the graph in question. All matrices in the algorithm are from \(\mathbb{B}^{n \times n}\), the set of \(n\) by \(n\) boolean matrices. Once this algorithm is complete, we get as output a transitive closure adjacency matrix \(R^{(n)}\). Mutually dependent functions will be pretty easy to isolate from this matrix. If \(R^{(n)}[i,j]\) and \(R^{(n)}[j,i]\), then the functions represented by vertices \(i\) and \(j\) depend on each other.</p> <p>Once we&rsquo;ve identified the groups, and <span class="sidenote"> <label class="sidenote-label" for="group-graph-note">constructed a group graph,</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="group-graph-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> This might seem like a "draw the rest of the owl" situation, but it really isn't. We'll follow a naive algorithm for findings groups, and for translating function dependencies into group dependencies. This algorithm, in C++, will be presented later on. <span class="sidenote-delimiter">]</span> </span> </span> it is time to compute the topological order. For this, we will use <a href="https://en.wikipedia.org/wiki/Topological_sorting#Kahn%27s_algorithm"class="external-link">Kahn&rsquo;s Algorithm<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. The algorithm goes as follows:</p> $$ \begin{aligned} &amp;amp; L \leftarrow \text{empty list} \\ &amp;amp; S \leftarrow \text{set of all nodes with no incoming edges} \\ &amp;amp; \\ &amp;amp; \textbf{while} \; S \; \text{is non-empty} \; \textbf{do} \\ &amp;amp; \quad \text{remove a node} \; n \; \text{from} \; S \\ &amp;amp; \quad \text{add} \; n \; \text{to the end of} \; L \\ &amp;amp; \quad \textbf{for each} \; \text{node} \; m \; \text{with edge} \; e \; \text{from} \; n \; \text{to} \; m \; \textbf{do} \\ &amp;amp; \quad \quad \text{remove edge} \; e \; \text{from the graph} \\ &amp;amp; \quad \quad \textbf{if} \; m \; \text{has no other incoming edges} \; \textbf{then} \\ &amp;amp; \quad \quad \quad \text{insert} \; m \; \text{into} \; S \\ &amp;amp; \\ &amp;amp; \textbf{if} \; \text{the graph has edges} \; \textbf{then} \\ &amp;amp; \quad \textbf{return} \; \text{error} \quad \textit{(graph has at least once cycle)} \\ &amp;amp; \textbf{else} \\ &amp;amp; \quad \textbf{return} \; L \quad \textit{(a topologically sorted order)} \end{aligned} $$ <p>Note that since we&rsquo;ve already isolated all mutually dependent functions into groups, our graph will never have cycles, and this algorithm will always succeed. Also note that since we start with nodes with no incoming edges, our list will <strong>begin with the groups that should be checked last</strong>. This is because a node with no incoming edges might (and probably does) still have outgoing edges, and thus depends on other functions / groups. Like in our successful example, we want to <strong>typecheck functions that are depended on first</strong>.</p> <a href="#implementation"> <h3 id="implementation">Implementation</h3> </a> <p>Let&rsquo;s start working on a C++ implementation of all of this now. First, I think that we should create a C++ class that will represent our function dependency graph. Let&rsquo;s call it <code>function_graph</code>. I propose the following definition:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/graph.hpp" data-first-line="12" data-last-line="52"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/graph.hpp#L12-L52">graph.hpp</a>, lines 12 through 52</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">using</span> <span class="n">function</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">group</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">function</span><span class="o">&gt;</span> <span class="n">members</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="n">group_ptr</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">unique_ptr</span><span class="o">&lt;</span><span class="n">group</span><span class="o">&gt;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">function_graph</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">using</span> <span class="n">group_id</span> <span class="o">=</span> <span class="n">size_t</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="nc">group_data</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">function</span><span class="o">&gt;</span> <span class="n">functions</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">group_id</span><span class="o">&gt;</span> <span class="n">adjacency_list</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">size_t</span> <span class="n">indegree</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">using</span> <span class="n">data_ptr</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">shared_ptr</span><span class="o">&lt;</span><span class="n">group_data</span><span class="o">&gt;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">using</span> <span class="n">edge</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">pair</span><span class="o">&lt;</span><span class="n">function</span><span class="p">,</span> <span class="n">function</span><span class="o">&gt;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">using</span> <span class="n">group_edge</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">pair</span><span class="o">&lt;</span><span class="n">group_id</span><span class="p">,</span> <span class="n">group_id</span><span class="o">&gt;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">function</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">function</span><span class="o">&gt;&gt;</span> <span class="n">adjacency_lists</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">edge</span><span class="o">&gt;</span> <span class="n">edges</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">edge</span><span class="o">&gt;</span> <span class="n">compute_transitive_edges</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">create_groups</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">edge</span><span class="o">&gt;&amp;</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">function</span><span class="p">,</span> <span class="n">group_id</span><span class="o">&gt;&amp;</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">group_id</span><span class="p">,</span> <span class="n">data_ptr</span><span class="o">&gt;&amp;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">create_edges</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">function</span><span class="p">,</span> <span class="n">group_id</span><span class="o">&gt;&amp;</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">group_id</span><span class="p">,</span> <span class="n">data_ptr</span><span class="o">&gt;&amp;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">group_ptr</span><span class="o">&gt;</span> <span class="n">generate_order</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">function</span><span class="p">,</span> <span class="n">group_id</span><span class="o">&gt;&amp;</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">group_id</span><span class="p">,</span> <span class="n">data_ptr</span><span class="o">&gt;&amp;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">public</span><span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">function</span><span class="o">&gt;&amp;</span> <span class="n">add_function</span><span class="p">(</span><span class="k">const</span> <span class="n">function</span><span class="o">&amp;</span> <span class="n">f</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">add_edge</span><span class="p">(</span><span class="k">const</span> <span class="n">function</span><span class="o">&amp;</span> <span class="n">from</span><span class="p">,</span> <span class="k">const</span> <span class="n">function</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">group_ptr</span><span class="o">&gt;</span> <span class="n">compute_order</span><span class="p">();</span> </span></span><span class="line"><span class="cl"><span class="p">};</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>There&rsquo;s a lot to unpack here. First of all, we create a type alias <code>function</code> that represents the label of a function in our graph. It is probably most convenient to work with <code>std::string</code> instances, so we settle for that. Next, we define a struct that will represent a single group of mutually dependent functions. Passing this struct by value seems wrong, so we&rsquo;ll settle for a C++ <code>unique_pt</code> to help carry instances around.</p> <p>Finally, we arrive at the definition of <code>function_graph</code>. Inside this class, we define a helper struct, <code>group_data</code>, which holds information about an individual group as it is being constructed. This information includes the group&rsquo;s adjacency list and <a href="https://en.wikipedia.org/wiki/Directed_graph#Indegree_and_outdegree"class="external-link">indegree<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> (both used for Kahn&rsquo;s topological sorting algorithm), as well as the set of functions in the group (which we will eventually return).</p> <p>The <code>adjacency_lists</code> and <code>edges</code> fields are the meat of the graph representation. Both of the variables provide a different view of the same graph: <code>adjacency_lists</code> associates with every function a list of functions it depends on, while <code>edges</code> holds a set of tuples describing edges in the graph. Having more than one representation makes it more convenient for us to perform different operations on our graphs.</p> <p>Next up are some internal methods that perform the various steps we described above:</p> <ul> <li><code>compute_transitive_edges</code> applies Warshall&rsquo;s algorithm to find the graph&rsquo;s transitive closure.</li> <li><code>create_groups</code> creates two mappings, one from functions to their respective groups&rsquo; IDs, and one from group IDs to information about the corresponding groups. This step is largely used to determine which functions belong to the same group, and as such, uses the set of transitive edges generated by <code>compute_transitive_edges</code>.</li> <li><code>create_edges</code> creates edges <strong>between groups</strong>. During this step, the indegrees of each group are computed, as well as their adjacency lists.</li> <li><code>generate_order</code> uses the indegrees and adjacency lists produced in the prior step to establish a topological order.</li> </ul> <p>Following these, we have three public function definitions:</p> <ul> <li><code>add_function</code> adds a vertex to the graph. Sometimes, a function does not reference any other functions, and would not appear in the list of edges. We will call <code>add_function</code> to make sure that the function graph is aware of such independent functions. For convenience, <code>add_function</code> returns the adjacency list of the added function.</li> <li><code>add_edge</code> adds a new dependency between two functions.</li> <li><code>compute_order</code> method uses the internal methods described above to convert the function dependency graph into a properly ordered list of groups.</li> </ul> <p>Let&rsquo;s start by looking at how to implement the internal methods. <code>compute_transitive_edges</code> is a very straightforward implementation of Warshall&rsquo;s:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/graph.cpp" data-first-line="3" data-last-line="21"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/graph.cpp#L3-L21">graph.cpp</a>, lines 3 through 21</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">function_graph</span><span class="o">::</span><span class="n">edge</span><span class="o">&gt;</span> <span class="n">function_graph</span><span class="o">::</span><span class="n">compute_transitive_edges</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">edge</span><span class="o">&gt;</span> <span class="n">transitive_edges</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">transitive_edges</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">edges</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">edges</span><span class="p">.</span><span class="n">end</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">connector</span> <span class="p">:</span> <span class="n">adjacency_lists</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">from</span> <span class="p">:</span> <span class="n">adjacency_lists</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">edge</span> <span class="n">to_connector</span> <span class="p">{</span> <span class="n">from</span><span class="p">.</span><span class="n">first</span><span class="p">,</span> <span class="n">connector</span><span class="p">.</span><span class="n">first</span> <span class="p">};</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">to</span> <span class="p">:</span> <span class="n">adjacency_lists</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">edge</span> <span class="n">full_jump</span> <span class="p">{</span> <span class="n">from</span><span class="p">.</span><span class="n">first</span><span class="p">,</span> <span class="n">to</span><span class="p">.</span><span class="n">first</span> <span class="p">};</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">transitive_edges</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">full_jump</span><span class="p">)</span> <span class="o">!=</span> <span class="n">transitive_edges</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> <span class="k">continue</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">edge</span> <span class="n">from_connector</span> <span class="p">{</span> <span class="n">connector</span><span class="p">.</span><span class="n">first</span><span class="p">,</span> <span class="n">to</span><span class="p">.</span><span class="n">first</span> <span class="p">};</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">transitive_edges</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">to_connector</span><span class="p">)</span> <span class="o">!=</span> <span class="n">transitive_edges</span><span class="p">.</span><span class="n">end</span><span class="p">()</span> <span class="o">&amp;&amp;</span> </span></span><span class="line"><span class="cl"> <span class="n">transitive_edges</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">from_connector</span><span class="p">)</span> <span class="o">!=</span> <span class="n">transitive_edges</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> </span></span><span class="line"><span class="cl"> <span class="n">transitive_edges</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">full_jump</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">transitive_edges</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Next is <code>create_groups</code>. For each function, we iterate over all other functions. If the other function is mutually dependent with the first function, we add it to the same group. In the outer loop, we skip over functions that have already been added to the group. This is because <span class="sidenote"> <label class="sidenote-label" for="equivalence-note">mutual dependence</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="equivalence-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> There is actually a slight difference between "mutual dependence" the way we defined it and "being in the same group", and it lies in the symmetric property of an equivalence relation. We defined a function to depend on another function if it calls that other function. Then, a recursive function depends on itself, but a non-recursive function does not, and therefore does not satisfy the symmetric property. However, as far as we're concerned, a function should be in a group with itself even if it's not recursive. Thus, the real equivalence relation we use is "in the same group as", and consists of "mutual dependence" extended with symmetry. <span class="sidenote-delimiter">]</span> </span> </span> is an <a href="https://en.wikipedia.org/wiki/Equivalence_relation"class="external-link">equivalence relation<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, which means that if we already added a function to a group, all its group members were also already visited and added.</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/graph.cpp" data-first-line="23" data-last-line="44"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/graph.cpp#L23-L44">graph.cpp</a>, lines 23 through 44</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">function_graph</span><span class="o">::</span><span class="n">create_groups</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">edge</span><span class="o">&gt;&amp;</span> <span class="n">transitive_edges</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">function</span><span class="p">,</span> <span class="n">group_id</span><span class="o">&gt;&amp;</span> <span class="n">group_ids</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">group_id</span><span class="p">,</span> <span class="n">data_ptr</span><span class="o">&gt;&amp;</span> <span class="n">group_data_map</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">group_id</span> <span class="n">id_counter</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">vertex</span> <span class="p">:</span> <span class="n">adjacency_lists</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">group_ids</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">vertex</span><span class="p">.</span><span class="n">first</span><span class="p">)</span> <span class="o">!=</span> <span class="n">group_ids</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> </span></span><span class="line"><span class="cl"> <span class="k">continue</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">data_ptr</span> <span class="nf">new_group</span><span class="p">(</span><span class="k">new</span> <span class="n">group_data</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">new_group</span><span class="o">-&gt;</span><span class="n">functions</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">vertex</span><span class="p">.</span><span class="n">first</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">group_data_map</span><span class="p">[</span><span class="n">id_counter</span><span class="p">]</span> <span class="o">=</span> <span class="n">new_group</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">group_ids</span><span class="p">[</span><span class="n">vertex</span><span class="p">.</span><span class="n">first</span><span class="p">]</span> <span class="o">=</span> <span class="n">id_counter</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">other_vertex</span> <span class="p">:</span> <span class="n">adjacency_lists</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">transitive_edges</span><span class="p">.</span><span class="n">find</span><span class="p">({</span><span class="n">vertex</span><span class="p">.</span><span class="n">first</span><span class="p">,</span> <span class="n">other_vertex</span><span class="p">.</span><span class="n">first</span><span class="p">})</span> <span class="o">!=</span> <span class="n">transitive_edges</span><span class="p">.</span><span class="n">end</span><span class="p">()</span> <span class="o">&amp;&amp;</span> </span></span><span class="line"><span class="cl"> <span class="n">transitive_edges</span><span class="p">.</span><span class="n">find</span><span class="p">({</span><span class="n">other_vertex</span><span class="p">.</span><span class="n">first</span><span class="p">,</span> <span class="n">vertex</span><span class="p">.</span><span class="n">first</span><span class="p">})</span> <span class="o">!=</span> <span class="n">transitive_edges</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">group_ids</span><span class="p">[</span><span class="n">other_vertex</span><span class="p">.</span><span class="n">first</span><span class="p">]</span> <span class="o">=</span> <span class="n">id_counter</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">new_group</span><span class="o">-&gt;</span><span class="n">functions</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">other_vertex</span><span class="p">.</span><span class="n">first</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">id_counter</span><span class="o">++</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Once groups have been created, we use their functions&rsquo; edges to create edges for the groups themselves, using <code>create_edges</code>. We avoid creating edges from a group to itself, to prevent unnecessary cycles. While constructing the edges, we also increment the relevant indegree counter.</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/graph.cpp" data-first-line="46" data-last-line="63"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/graph.cpp#L46-L63">graph.cpp</a>, lines 46 through 63</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">function_graph</span><span class="o">::</span><span class="n">create_edges</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">function</span><span class="p">,</span> <span class="n">group_id</span><span class="o">&gt;&amp;</span> <span class="n">group_ids</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">group_id</span><span class="p">,</span> <span class="n">data_ptr</span><span class="o">&gt;&amp;</span> <span class="n">group_data_map</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">pair</span><span class="o">&lt;</span><span class="n">group_id</span><span class="p">,</span> <span class="n">group_id</span><span class="o">&gt;&gt;</span> <span class="n">group_edges</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">vertex</span> <span class="p">:</span> <span class="n">adjacency_lists</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">vertex_id</span> <span class="o">=</span> <span class="n">group_ids</span><span class="p">[</span><span class="n">vertex</span><span class="p">.</span><span class="n">first</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span><span class="o">&amp;</span> <span class="n">vertex_data</span> <span class="o">=</span> <span class="n">group_data_map</span><span class="p">[</span><span class="n">vertex_id</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">other_vertex</span> <span class="p">:</span> <span class="n">vertex</span><span class="p">.</span><span class="n">second</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">other_id</span> <span class="o">=</span> <span class="n">group_ids</span><span class="p">[</span><span class="n">other_vertex</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">vertex_id</span> <span class="o">==</span> <span class="n">other_id</span><span class="p">)</span> <span class="k">continue</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">group_edges</span><span class="p">.</span><span class="n">find</span><span class="p">({</span><span class="n">vertex_id</span><span class="p">,</span> <span class="n">other_id</span><span class="p">})</span> <span class="o">!=</span> <span class="n">group_edges</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> </span></span><span class="line"><span class="cl"> <span class="k">continue</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">group_edges</span><span class="p">.</span><span class="n">insert</span><span class="p">({</span><span class="n">vertex_id</span><span class="p">,</span> <span class="n">other_id</span><span class="p">});</span> </span></span><span class="line"><span class="cl"> <span class="n">vertex_data</span><span class="o">-&gt;</span><span class="n">adjacency_list</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">other_id</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">group_data_map</span><span class="p">[</span><span class="n">other_id</span><span class="p">]</span><span class="o">-&gt;</span><span class="n">indegree</span><span class="o">++</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Finally, we apply Kahn&rsquo;s algorithm to create a topological order in <code>generate_order</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/graph.cpp" data-first-line="65" data-last-line="90"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/graph.cpp#L65-L90">graph.cpp</a>, lines 65 through 90</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">65 </span><span class="lnt">66 </span><span class="lnt">67 </span><span class="lnt">68 </span><span class="lnt">69 </span><span class="lnt">70 </span><span class="lnt">71 </span><span class="lnt">72 </span><span class="lnt">73 </span><span class="lnt">74 </span><span class="lnt">75 </span><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span><span class="lnt">79 </span><span class="lnt">80 </span><span class="lnt">81 </span><span class="lnt">82 </span><span class="lnt">83 </span><span class="lnt">84 </span><span class="lnt">85 </span><span class="lnt">86 </span><span class="lnt">87 </span><span class="lnt">88 </span><span class="lnt">89 </span><span class="lnt">90 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">group_ptr</span><span class="o">&gt;</span> <span class="n">function_graph</span><span class="o">::</span><span class="n">generate_order</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">function</span><span class="p">,</span> <span class="n">group_id</span><span class="o">&gt;&amp;</span> <span class="n">group_ids</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">group_id</span><span class="p">,</span> <span class="n">data_ptr</span><span class="o">&gt;&amp;</span> <span class="n">group_data_map</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">queue</span><span class="o">&lt;</span><span class="n">group_id</span><span class="o">&gt;</span> <span class="n">id_queue</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">group_ptr</span><span class="o">&gt;</span> <span class="n">output</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">group</span> <span class="p">:</span> <span class="n">group_data_map</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">group</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">indegree</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="n">id_queue</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">group</span><span class="p">.</span><span class="n">first</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">while</span><span class="p">(</span><span class="o">!</span><span class="n">id_queue</span><span class="p">.</span><span class="n">empty</span><span class="p">())</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">new_id</span> <span class="o">=</span> <span class="n">id_queue</span><span class="p">.</span><span class="n">front</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span><span class="o">&amp;</span> <span class="n">group_data</span> <span class="o">=</span> <span class="n">group_data_map</span><span class="p">[</span><span class="n">new_id</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="n">group_ptr</span> <span class="nf">output_group</span><span class="p">(</span><span class="k">new</span> <span class="n">group</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">output_group</span><span class="o">-&gt;</span><span class="n">members</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">group_data</span><span class="o">-&gt;</span><span class="n">functions</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">id_queue</span><span class="p">.</span><span class="n">pop</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">adjacent_group</span> <span class="p">:</span> <span class="n">group_data</span><span class="o">-&gt;</span><span class="n">adjacency_list</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="o">--</span><span class="n">group_data_map</span><span class="p">[</span><span class="n">adjacent_group</span><span class="p">]</span><span class="o">-&gt;</span><span class="n">indegree</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">id_queue</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">adjacent_group</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">output</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">output_group</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">output</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>These four steps are used in <code>compute_order</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/graph.cpp" data-first-line="106" data-last-line="114"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/graph.cpp#L106-L114">graph.cpp</a>, lines 106 through 114</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">106 </span><span class="lnt">107 </span><span class="lnt">108 </span><span class="lnt">109 </span><span class="lnt">110 </span><span class="lnt">111 </span><span class="lnt">112 </span><span class="lnt">113 </span><span class="lnt">114 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">group_ptr</span><span class="o">&gt;</span> <span class="n">function_graph</span><span class="o">::</span><span class="n">compute_order</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">edge</span><span class="o">&gt;</span> <span class="n">transitive_edges</span> <span class="o">=</span> <span class="n">compute_transitive_edges</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">function</span><span class="p">,</span> <span class="n">group_id</span><span class="o">&gt;</span> <span class="n">group_ids</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">group_id</span><span class="p">,</span> <span class="n">data_ptr</span><span class="o">&gt;</span> <span class="n">group_data_map</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">create_groups</span><span class="p">(</span><span class="n">transitive_edges</span><span class="p">,</span> <span class="n">group_ids</span><span class="p">,</span> <span class="n">group_data_map</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">create_edges</span><span class="p">(</span><span class="n">group_ids</span><span class="p">,</span> <span class="n">group_data_map</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nf">generate_order</span><span class="p">(</span><span class="n">group_ids</span><span class="p">,</span> <span class="n">group_data_map</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Let&rsquo;s now look at the remaining two public definitions. First comes <code>add_function</code>, which creates an adjacency list for the function to be inserted (if one does not already exist), and returns a reference to the resulting list:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/graph.cpp" data-first-line="92" data-last-line="99"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/graph.cpp#L92-L99">graph.cpp</a>, lines 92 through 99</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">92 </span><span class="lnt">93 </span><span class="lnt">94 </span><span class="lnt">95 </span><span class="lnt">96 </span><span class="lnt">97 </span><span class="lnt">98 </span><span class="lnt">99 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">function</span><span class="o">&gt;&amp;</span> <span class="n">function_graph</span><span class="o">::</span><span class="n">add_function</span><span class="p">(</span><span class="k">const</span> <span class="n">function</span><span class="o">&amp;</span> <span class="n">f</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">adjacency_list_it</span> <span class="o">=</span> <span class="n">adjacency_lists</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">f</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">adjacency_list_it</span> <span class="o">!=</span> <span class="n">adjacency_lists</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">adjacency_list_it</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">adjacency_lists</span><span class="p">[</span><span class="n">f</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="p">};</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We use this in <code>add_edge</code>, which straightforwardly creates an edge between two functions:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/graph.cpp" data-first-line="101" data-last-line="104"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/graph.cpp#L101-L104">graph.cpp</a>, lines 101 through 104</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">101 </span><span class="lnt">102 </span><span class="lnt">103 </span><span class="lnt">104 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">function_graph</span><span class="o">::</span><span class="n">add_edge</span><span class="p">(</span><span class="k">const</span> <span class="n">function</span><span class="o">&amp;</span> <span class="n">from</span><span class="p">,</span> <span class="k">const</span> <span class="n">function</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">add_function</span><span class="p">(</span><span class="n">from</span><span class="p">).</span><span class="n">insert</span><span class="p">(</span><span class="n">to</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">edges</span><span class="p">.</span><span class="n">insert</span><span class="p">({</span> <span class="n">from</span><span class="p">,</span> <span class="n">to</span> <span class="p">});</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>With this, we can now properly order our typechecking. However, we are just getting started: there are still numerous changes we need to make to get our compiler to behave as we desire.</p> <p>The first change is the least relevant, but will help clean up our code base in the presence of polymorphism: we will get rid of <code>resolve</code>, in both definitions and AST nodes. The reasons for this are twofold. First, <span class="sidenote"> <label class="sidenote-label" for="case-type-note">only the case expression node actually uses the type it stores.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="case-type-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Recall that <code>ast_case</code> needs this information to properly account for the changes to the stack from when data is unpacked. <span class="sidenote-delimiter">]</span> </span> </span> This means that all the rest of the infrastructure we&rsquo;ve written around preserving types is somewhat pointless. Second, when we call <code>resolve</code>, we&rsquo;d now have to distinguish between type variables captured by &ldquo;forall&rdquo; and actual, undefined variables. That&rsquo;s a lot of wasted work! To replace the now-removed <code>type</code> field, we make <code>ast_case</code> include a new member, <code>input_type</code>, which stores the type of the thing between <code>case</code> and <code>of</code>. Since <code>ast_case</code> requires its type to be a data type at the time of typechecking, we no longer need to resolve anything.</p> <p>Next, we need to work in a step geared towards finding function calls (to determine dependencies). As we have noted in <a href="https://danilafe.com/blog/06_compiler_compilation/">part 6</a>, it&rsquo;s pretty easy to tell apart calls to global functions from &ldquo;local&rdquo; ones. If we see that a variable was previously bound (perhaps as a function argument, or by a pattern in a case expression), we know for sure that it is not a global function call. Otherwise, if the variable isn&rsquo;t bound anywhere in the function definition (it&rsquo;s a <strong>free variable</strong>), it must refer to a global function. Then, we can traverse the function body, storing variables that are bound (but only within their scope), and noting references to variables we haven&rsquo;t yet seen. To implement this, we can use a linked list, where each node refers to a particular scope, points to the scope enclosing it, and contains a list of variables&hellip;</p> <p>Wait a minute, this is identical to <code>type_env</code>! There&rsquo;s no reason to reimplement all this. But then, another question arises: do we throw away the <code>type_env</code> generated by the dependency-searching step? It seems wasteful, since we will eventually repeat this same work. Rather, we&rsquo;ll re-use the same <code>type_env</code> instances in both this new step and <code>typecheck</code>. To do this, we will now store a pointer to a <code>type_env</code> in every AST node, and set this pointer during our first traversal of the tree. Indeed, this makes our <code>type_env</code> more like a <a href="https://en.wikipedia.org/wiki/Symbol_table"class="external-link">symbol table<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. With this change, our new dependency-finding step will be implemented by the <code>find_free</code> function with the following signature:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast</span><span class="o">::</span><span class="n">find_free</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">);</span> </span></span></code></pre></div><p>Let&rsquo;s take a look at how this will be implemented. The simplest case (as usual) is <code>ast_int</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/ast.cpp" data-first-line="16" data-last-line="18"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/ast.cpp#L16-L18">ast.cpp</a>, lines 16 through 18</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast_int</span><span class="o">::</span><span class="n">find_free</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="o">-&gt;</span><span class="n">env</span> <span class="o">=</span> <span class="n">env</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>In this case, we associate the <code>type_env</code> with the node, but don&rsquo;t do anything else: a number is not a variable. A more interesting case is <code>ast_lid</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/ast.cpp" data-first-line="33" data-last-line="36"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/ast.cpp#L33-L36">ast.cpp</a>, lines 33 through 36</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast_lid</span><span class="o">::</span><span class="n">find_free</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="o">-&gt;</span><span class="n">env</span> <span class="o">=</span> <span class="n">env</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">env</span><span class="o">-&gt;</span><span class="n">lookup</span><span class="p">(</span><span class="n">id</span><span class="p">)</span> <span class="o">==</span> <span class="k">nullptr</span><span class="p">)</span> <span class="n">into</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">id</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>If a lowercase variable has not yet been bound to something, it&rsquo;s free, and we store it. Somewhat counterintuitively, <code>ast_uid</code> behaves differently:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/ast.cpp" data-first-line="54" data-last-line="56"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/ast.cpp#L54-L56">ast.cpp</a>, lines 54 through 56</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast_uid</span><span class="o">::</span><span class="n">find_free</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="o">-&gt;</span><span class="n">env</span> <span class="o">=</span> <span class="n">env</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We don&rsquo;t allow uppercase variables to be bound to anything outside of data type declarations, so we don&rsquo;t care about uppercase free variables. Next up is <code>ast_binop</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/ast.cpp" data-first-line="73" data-last-line="77"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/ast.cpp#L73-L77">ast.cpp</a>, lines 73 through 77</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">73 </span><span class="lnt">74 </span><span class="lnt">75 </span><span class="lnt">76 </span><span class="lnt">77 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast_binop</span><span class="o">::</span><span class="n">find_free</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="o">-&gt;</span><span class="n">env</span> <span class="o">=</span> <span class="n">env</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">left</span><span class="o">-&gt;</span><span class="n">find_free</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">env</span><span class="p">,</span> <span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">right</span><span class="o">-&gt;</span><span class="n">find_free</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">env</span><span class="p">,</span> <span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>A binary operator can have free variables in the subexpressions on the left and on the right, and the above implementation reflects that. This is identical to the implementation of <code>ast_app</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/ast.cpp" data-first-line="109" data-last-line="113"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/ast.cpp#L109-L113">ast.cpp</a>, lines 109 through 113</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">109 </span><span class="lnt">110 </span><span class="lnt">111 </span><span class="lnt">112 </span><span class="lnt">113 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast_app</span><span class="o">::</span><span class="n">find_free</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="o">-&gt;</span><span class="n">env</span> <span class="o">=</span> <span class="n">env</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">left</span><span class="o">-&gt;</span><span class="n">find_free</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">env</span><span class="p">,</span> <span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">right</span><span class="o">-&gt;</span><span class="n">find_free</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">env</span><span class="p">,</span> <span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Finally, <code>ast_case</code> requires the most complicated function (as usual):</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/ast.cpp" data-first-line="142" data-last-line="150"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/ast.cpp#L142-L150">ast.cpp</a>, lines 142 through 150</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">142 </span><span class="lnt">143 </span><span class="lnt">144 </span><span class="lnt">145 </span><span class="lnt">146 </span><span class="lnt">147 </span><span class="lnt">148 </span><span class="lnt">149 </span><span class="lnt">150 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast_case</span><span class="o">::</span><span class="n">find_free</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="o">-&gt;</span><span class="n">env</span> <span class="o">=</span> <span class="n">env</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">of</span><span class="o">-&gt;</span><span class="n">find_free</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">env</span><span class="p">,</span> <span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">branch</span> <span class="p">:</span> <span class="n">branches</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_env_ptr</span> <span class="n">new_env</span> <span class="o">=</span> <span class="n">type_scope</span><span class="p">(</span><span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">branch</span><span class="o">-&gt;</span><span class="n">pat</span><span class="o">-&gt;</span><span class="n">insert_bindings</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">new_env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">branch</span><span class="o">-&gt;</span><span class="n">expr</span><span class="o">-&gt;</span><span class="n">find_free</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">new_env</span><span class="p">,</span> <span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The <code>type_scope</code> function replaces the <code>type_env::scope</code> method, which cannot (without significant effort) operate on smart pointers. Importantly, we are using a new <code>pattern</code> method here, <code>insert_bindings</code>. This is because we split &ldquo;introducing variables&rdquo; and &ldquo;typechecking variables&rdquo; into two steps for patterns, as well. The implementation of <code>insert_bindings</code> for <code>pattern_var</code> is as follows:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/ast.cpp" data-first-line="230" data-last-line="232"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/ast.cpp#L230-L232">ast.cpp</a>, lines 230 through 232</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">230 </span><span class="lnt">231 </span><span class="lnt">232 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">pattern_var</span><span class="o">::</span><span class="n">insert_bindings</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">env</span><span class="o">-&gt;</span><span class="n">bind</span><span class="p">(</span><span class="n">var</span><span class="p">,</span> <span class="n">mgr</span><span class="p">.</span><span class="n">new_type</span><span class="p">());</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>A variable pattern always introduces the variable it is made up of. On the other hand, the implementation for <code>pattern_constr</code> is as follows:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/ast.cpp" data-first-line="245" data-last-line="249"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/ast.cpp#L245-L249">ast.cpp</a>, lines 245 through 249</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">245 </span><span class="lnt">246 </span><span class="lnt">247 </span><span class="lnt">248 </span><span class="lnt">249 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">pattern_constr</span><span class="o">::</span><span class="n">insert_bindings</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">param</span> <span class="p">:</span> <span class="n">params</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">env</span><span class="o">-&gt;</span><span class="n">bind</span><span class="p">(</span><span class="n">param</span><span class="p">,</span> <span class="n">mgr</span><span class="p">.</span><span class="n">new_type</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>All the variables of the pattern are placed into the environment. For now, we don&rsquo;t worry about arity; this is the job of typechecking.</p> <p>These changes are reflected in all instances of our <code>typecheck</code> function. First of all, <code>typecheck</code> no longer needs to receive a <code>type_env</code> parameter, since each tree node has a <code>type_env_ptr</code>. Furthermore, <code>typecheck</code> should no longer call <code>bind</code>, since this was already done by <code>find_free</code>. For example, <code>ast_lid::typecheck</code> will now use <code>env::lookup</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/ast.cpp" data-first-line="38" data-last-line="40"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/ast.cpp#L38-L40">ast.cpp</a>, lines 38 through 40</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">type_ptr</span> <span class="n">ast_lid</span><span class="o">::</span><span class="n">typecheck</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">env</span><span class="o">-&gt;</span><span class="n">lookup</span><span class="p">(</span><span class="n">id</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">instantiate</span><span class="p">(</span><span class="n">mgr</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Don&rsquo;t worry about <code>instantiate</code> for now; that&rsquo;s coming up. Similarly to <code>ast_lid</code>, <code>ast_case::typecheck</code> will no longer introduce new bindings, but unify existing types via the <code>pattern</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/ast.cpp" data-first-line="152" data-last-line="169"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/ast.cpp#L152-L169">ast.cpp</a>, lines 152 through 169</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">152 </span><span class="lnt">153 </span><span class="lnt">154 </span><span class="lnt">155 </span><span class="lnt">156 </span><span class="lnt">157 </span><span class="lnt">158 </span><span class="lnt">159 </span><span class="lnt">160 </span><span class="lnt">161 </span><span class="lnt">162 </span><span class="lnt">163 </span><span class="lnt">164 </span><span class="lnt">165 </span><span class="lnt">166 </span><span class="lnt">167 </span><span class="lnt">168 </span><span class="lnt">169 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">type_ptr</span> <span class="n">ast_case</span><span class="o">::</span><span class="n">typecheck</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_var</span><span class="o">*</span> <span class="n">var</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">case_type</span> <span class="o">=</span> <span class="n">mgr</span><span class="p">.</span><span class="n">resolve</span><span class="p">(</span><span class="n">of</span><span class="o">-&gt;</span><span class="n">typecheck</span><span class="p">(</span><span class="n">mgr</span><span class="p">),</span> <span class="n">var</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">branch_type</span> <span class="o">=</span> <span class="n">mgr</span><span class="p">.</span><span class="n">new_type</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">branch</span> <span class="p">:</span> <span class="n">branches</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">branch</span><span class="o">-&gt;</span><span class="n">pat</span><span class="o">-&gt;</span><span class="n">typecheck</span><span class="p">(</span><span class="n">case_type</span><span class="p">,</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">branch</span><span class="o">-&gt;</span><span class="n">expr</span><span class="o">-&gt;</span><span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">curr_branch_type</span> <span class="o">=</span> <span class="n">branch</span><span class="o">-&gt;</span><span class="n">expr</span><span class="o">-&gt;</span><span class="n">typecheck</span><span class="p">(</span><span class="n">mgr</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">mgr</span><span class="p">.</span><span class="n">unify</span><span class="p">(</span><span class="n">branch_type</span><span class="p">,</span> <span class="n">curr_branch_type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">input_type</span> <span class="o">=</span> <span class="n">mgr</span><span class="p">.</span><span class="n">resolve</span><span class="p">(</span><span class="n">case_type</span><span class="p">,</span> <span class="n">var</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">type_data</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">input_type</span><span class="p">.</span><span class="n">get</span><span class="p">()))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="nf">type_error</span><span class="p">(</span><span class="s">&#34;attempting case analysis of non-data type&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">branch_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The above implementation uses another new <code>pattern</code> method, <code>typecheck</code>. This method inherits the type checking functionality previously contained in <code>pattern::match</code>. Here&rsquo;s the implementation for <code>pattern_var</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/ast.cpp" data-first-line="234" data-last-line="236"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/ast.cpp#L234-L236">ast.cpp</a>, lines 234 through 236</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">234 </span><span class="lnt">235 </span><span class="lnt">236 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">pattern_var</span><span class="o">::</span><span class="n">typecheck</span><span class="p">(</span><span class="n">type_ptr</span> <span class="n">t</span><span class="p">,</span> <span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">mgr</span><span class="p">.</span><span class="n">unify</span><span class="p">(</span><span class="n">env</span><span class="o">-&gt;</span><span class="n">lookup</span><span class="p">(</span><span class="n">var</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">instantiate</span><span class="p">(</span><span class="n">mgr</span><span class="p">),</span> <span class="n">t</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>And here&rsquo;s the implementation for <code>pattern_constr</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/ast.cpp" data-first-line="251" data-last-line="266"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/ast.cpp#L251-L266">ast.cpp</a>, lines 251 through 266</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">251 </span><span class="lnt">252 </span><span class="lnt">253 </span><span class="lnt">254 </span><span class="lnt">255 </span><span class="lnt">256 </span><span class="lnt">257 </span><span class="lnt">258 </span><span class="lnt">259 </span><span class="lnt">260 </span><span class="lnt">261 </span><span class="lnt">262 </span><span class="lnt">263 </span><span class="lnt">264 </span><span class="lnt">265 </span><span class="lnt">266 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">pattern_constr</span><span class="o">::</span><span class="n">typecheck</span><span class="p">(</span><span class="n">type_ptr</span> <span class="n">t</span><span class="p">,</span> <span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">constructor_type</span> <span class="o">=</span> <span class="n">env</span><span class="o">-&gt;</span><span class="n">lookup</span><span class="p">(</span><span class="n">constr</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">instantiate</span><span class="p">(</span><span class="n">mgr</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="n">constructor_type</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="nf">type_error</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">(</span><span class="s">&#34;pattern using unknown constructor &#34;</span><span class="p">)</span> <span class="o">+</span> <span class="n">constr</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">param</span> <span class="p">:</span> <span class="n">params</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_arr</span><span class="o">*</span> <span class="n">arr</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">type_arr</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">constructor_type</span><span class="p">.</span><span class="n">get</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="n">arr</span><span class="p">)</span> <span class="k">throw</span> <span class="n">type_error</span><span class="p">(</span><span class="s">&#34;too many parameters in constructor pattern&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">mgr</span><span class="p">.</span><span class="n">unify</span><span class="p">(</span><span class="n">env</span><span class="o">-&gt;</span><span class="n">lookup</span><span class="p">(</span><span class="n">param</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">instantiate</span><span class="p">(</span><span class="n">mgr</span><span class="p">),</span> <span class="n">arr</span><span class="o">-&gt;</span><span class="n">left</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">constructor_type</span> <span class="o">=</span> <span class="n">arr</span><span class="o">-&gt;</span><span class="n">right</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">mgr</span><span class="p">.</span><span class="n">unify</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">constructor_type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>So far, so good. However, for all of this to reach the main typechecking code, not only <code>ast</code> subclasses need to be updated, but also the <code>definition</code>s. Here things get more complicated, because <code>definition_data</code> and <code>definition_defn</code> are growing more and more apart. Previously, we had two typechecking steps: <code>typecheck_first</code> (which registered function names into the environment) and <code>typecheck_second</code> (which performed the actual typechecking). However, not only are these names not informative, but the algorithms for typechecking the two types of definition will soon have different numbers of &ldquo;major&rdquo; steps.</p> <p>Let&rsquo;s take a look at how we would typecheck data types. I propose the following steps:</p> <ol> <li>Iterate all declared data types, storing them into some kind of &ldquo;known&rdquo; list.</li> <li>Iterate again, and for each constructor of a type, verify that it refers to &ldquo;known&rdquo; types. Add valid constructors to the global environment as functions.</li> </ol> <p>We don&rsquo;t currently verify that types are &ldquo;known&rdquo;; A user could declare a list of <code>Floobs</code>, and never say what a <code>Floob</code> is. <span class="sidenote"> <label class="sidenote-label" for="known-type-note">This isn&rsquo;t too big of an issue</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="known-type-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Curiously, this flaw did lead to some valid programs being rejected. Since we had no notion of a "known" type, whenever data type constructors were created, every argument type was marked a "base" type; see <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/compiler/09/definition.cpp#L82"> this line</a> if you're curious. This would cause pattern matching to fail on the tail of a list, with the "attempt to pattern match on non-data argument" error. <span class="sidenote-delimiter">]</span> </span> </span> (good luck constructing a value of a non-existent type), but a mature compiler should prevent this from happening.</p> <p>On the other hand, here are the steps for function definitions:</p> <ol> <li>Find the free variables of each function to create the ordered list of groups as described above.</li> <li>Within each group, insert a general function type (like \(a \rightarrow b \rightarrow c\)) into the environment for each function.</li> <li>Within each group (in the same pass) run typechecking (including polymorphism, using the rules as described above).</li> </ol> <p>The two types of definitions further diverge when generating LLVM and compiling to G-machine instructions: data types immediately construct and insert their functions, and do not emit G-machine instructions, while functions generate G-machine instructions, declare prototypes, and emit LLVM in three distinct phases. Overall, there are virtually no similarities between the two data type declarations, and any inheritance of common functions starts to appear somewhat forced. To address this, we remove the <code>definition</code> class altogether, and sever the relationship between <code>definition_data</code> and <code>definition_defn</code>. The two now look as follows:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/definition.hpp" data-first-line="23" data-last-line="67"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/definition.hpp#L23-L67">definition.hpp</a>, lines 23 through 67</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span><span class="lnt">65 </span><span class="lnt">66 </span><span class="lnt">67 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">definition_defn</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">params</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">ast_ptr</span> <span class="n">body</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">type_env_ptr</span> <span class="n">env</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_env_ptr</span> <span class="n">var_env</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">free_variables</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">full_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">return_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">instruction_ptr</span><span class="o">&gt;</span> <span class="n">instructions</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">Function</span><span class="o">*</span> <span class="n">generated_function</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">definition_defn</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">n</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">p</span><span class="p">,</span> <span class="n">ast_ptr</span> <span class="n">b</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">name</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">n</span><span class="p">)),</span> <span class="n">params</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">p</span><span class="p">)),</span> <span class="n">body</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">b</span><span class="p">))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">find_free</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">insert_types</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">typecheck</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">compile</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">declare_llvm</span><span class="p">(</span><span class="n">llvm_context</span><span class="o">&amp;</span> <span class="n">ctx</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">generate_llvm</span><span class="p">(</span><span class="n">llvm_context</span><span class="o">&amp;</span> <span class="n">ctx</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="n">definition_defn_ptr</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">unique_ptr</span><span class="o">&lt;</span><span class="n">definition_defn</span><span class="o">&gt;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">definition_data</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">constructor_ptr</span><span class="o">&gt;</span> <span class="n">constructors</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">type_env_ptr</span> <span class="n">env</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">definition_data</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">n</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">constructor_ptr</span><span class="o">&gt;</span> <span class="n">cs</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">name</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">n</span><span class="p">)),</span> <span class="n">constructors</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">cs</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">insert_types</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">insert_constructors</span><span class="p">()</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">generate_llvm</span><span class="p">(</span><span class="n">llvm_context</span><span class="o">&amp;</span> <span class="n">ctx</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="n">definition_data_ptr</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">unique_ptr</span><span class="o">&lt;</span><span class="n">definition_data</span><span class="o">&gt;</span><span class="p">;</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>In <code>definition_defn</code>, the functions are arranged as follows:</p> <ul> <li><code>find_free</code> locates the free variables in the definition, populating the <code>free_variables</code> field and thereby finding edges for the function graph.</li> <li><code>insert_types</code> stores the type of the function into the global environment (a pointer to which is now stored as a field).</li> <li><code>typecheck</code> runs the standard typechecking steps.</li> <li><code>compile</code> generates G-machine instructions.</li> <li><code>declare_llvm</code> inserts LLVM function prototypes into the <code>llvm_context</code>.</li> <li><code>generate_llvm</code> converts G-machine instructions into LLVM IR.</li> </ul> <p>In <code>definition_data</code>, the steps are significantly simpler:</p> <ul> <li><code>insert_types</code> registers the type being declared as a &ldquo;known&rdquo; type.</li> <li><code>insert_constructors</code> inserts constructors (which are verified to refer to &ldquo;known&rdquo; types) into the global environment.</li> <li><code>generate_llvm</code> creates the LLVM functions (and their IR).</li> </ul> <p>While the last three methods of <code>definition_defn</code> remain unchanged save for the name, the implementations of the first three see some updates. First is <code>find_free</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/definition.cpp" data-first-line="12" data-last-line="26"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/definition.cpp#L12-L26">definition.cpp</a>, lines 12 through 26</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">definition_defn</span><span class="o">::</span><span class="n">find_free</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="o">-&gt;</span><span class="n">env</span> <span class="o">=</span> <span class="n">env</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">var_env</span> <span class="o">=</span> <span class="n">type_scope</span><span class="p">(</span><span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">return_type</span> <span class="o">=</span> <span class="n">mgr</span><span class="p">.</span><span class="n">new_type</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">full_type</span> <span class="o">=</span> <span class="n">return_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">params</span><span class="p">.</span><span class="n">rbegin</span><span class="p">();</span> <span class="n">it</span> <span class="o">!=</span> <span class="n">params</span><span class="p">.</span><span class="n">rend</span><span class="p">();</span> <span class="n">it</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">param_type</span> <span class="o">=</span> <span class="n">mgr</span><span class="p">.</span><span class="n">new_type</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">full_type</span> <span class="o">=</span> <span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_arr</span><span class="p">(</span><span class="n">param_type</span><span class="p">,</span> <span class="n">full_type</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="n">var_env</span><span class="o">-&gt;</span><span class="n">bind</span><span class="p">(</span><span class="o">*</span><span class="n">it</span><span class="p">,</span> <span class="n">param_type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">body</span><span class="o">-&gt;</span><span class="n">find_free</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">var_env</span><span class="p">,</span> <span class="n">free_variables</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>First, to make sure we don&rsquo;t pollute the global scope with function parameters, <code>find_free</code> creates a new environment <code>var_env</code>. Then, it stores into this new environment the function parameters, ensuring that the parameters of a function aren&rsquo;t marked &ldquo;free&rdquo;. Concurrently, <code>find_free</code> constructs the &ldquo;general&rdquo; function type (used by <code>insert_types</code>). Once all the arguments have been bound, <code>definition_defn::find_free</code> makes a call to <code>ast::find_free</code>, which does the work of actually finding free variables.</p> <p>Since the function type is created by <code>find_free</code>, <code>insert_types</code> has very little to do:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/definition.cpp" data-first-line="28" data-last-line="30"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/definition.cpp#L28-L30">definition.cpp</a>, lines 28 through 30</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">definition_defn</span><span class="o">::</span><span class="n">insert_types</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">env</span><span class="o">-&gt;</span><span class="n">bind</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">full_type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Finally, <code>typecheck</code>, which no longer has to bind the function arguments to new types, is also fairly simple:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/definition.cpp" data-first-line="32" data-last-line="35"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/definition.cpp#L32-L35">definition.cpp</a>, lines 32 through 35</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">definition_defn</span><span class="o">::</span><span class="n">typecheck</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">body_type</span> <span class="o">=</span> <span class="n">body</span><span class="o">-&gt;</span><span class="n">typecheck</span><span class="p">(</span><span class="n">mgr</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">mgr</span><span class="p">.</span><span class="n">unify</span><span class="p">(</span><span class="n">return_type</span><span class="p">,</span> <span class="n">body_type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Let&rsquo;s move on to data types. In order to implement <code>definition_data::insert_types</code>, we need to store somewhere a list of all the valid type names. We do this by adding a new <code>type_names</code> field to <code>type_env</code>, and implementing the corresponding methods <code>lookup_type</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/type_env.cpp" data-first-line="11" data-last-line="16"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/type_env.cpp#L11-L16">type_env.cpp</a>, lines 11 through 16</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">type_ptr</span> <span class="n">type_env</span><span class="o">::</span><span class="n">lookup_type</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">type_names</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">it</span> <span class="o">!=</span> <span class="n">type_names</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> <span class="k">return</span> <span class="n">it</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">parent</span><span class="p">)</span> <span class="k">return</span> <span class="n">parent</span><span class="o">-&gt;</span><span class="n">lookup_type</span><span class="p">(</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="k">nullptr</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>And <code>bind_type</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/type_env.cpp" data-first-line="26" data-last-line="29"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/type_env.cpp#L26-L29">type_env.cpp</a>, lines 26 through 29</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">type_env</span><span class="o">::</span><span class="n">bind_type</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">type_name</span><span class="p">,</span> <span class="n">type_ptr</span> <span class="n">t</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">lookup_type</span><span class="p">(</span><span class="n">type_name</span><span class="p">)</span> <span class="o">!=</span> <span class="k">nullptr</span><span class="p">)</span> <span class="k">throw</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_names</span><span class="p">[</span><span class="n">type_name</span><span class="p">]</span> <span class="o">=</span> <span class="n">t</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Note in the above snippets that we disallow redeclaring type names; declaring two data types (or other types) with the same name in our language will not be valid. In <code>insert_types</code>, we create a new data type and store it in the environment:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/definition.cpp" data-first-line="59" data-last-line="62"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/definition.cpp#L59-L62">definition.cpp</a>, lines 59 through 62</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">definition_data</span><span class="o">::</span><span class="n">insert_types</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">this</span><span class="o">-&gt;</span><span class="n">env</span> <span class="o">=</span> <span class="n">env</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">env</span><span class="o">-&gt;</span><span class="n">bind_type</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_data</span><span class="p">(</span><span class="n">name</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We then update <code>insert_constructors</code> to query the environment when creating constructor types, rather than blindly using <code>new type_base(...)</code> like before:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/definition.cpp" data-first-line="64" data-last-line="82"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/definition.cpp#L64-L82">definition.cpp</a>, lines 64 through 82</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">64 </span><span class="lnt">65 </span><span class="lnt">66 </span><span class="lnt">67 </span><span class="lnt">68 </span><span class="lnt">69 </span><span class="lnt">70 </span><span class="lnt">71 </span><span class="lnt">72 </span><span class="lnt">73 </span><span class="lnt">74 </span><span class="lnt">75 </span><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span><span class="lnt">79 </span><span class="lnt">80 </span><span class="lnt">81 </span><span class="lnt">82 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">definition_data</span><span class="o">::</span><span class="n">insert_constructors</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">return_type</span> <span class="o">=</span> <span class="n">env</span><span class="o">-&gt;</span><span class="n">lookup_type</span><span class="p">(</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">type_data</span><span class="o">*</span> <span class="n">this_type</span> <span class="o">=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="n">type_data</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">return_type</span><span class="p">.</span><span class="n">get</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">next_tag</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">constructor</span> <span class="p">:</span> <span class="n">constructors</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">constructor</span><span class="o">-&gt;</span><span class="n">tag</span> <span class="o">=</span> <span class="n">next_tag</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">this_type</span><span class="o">-&gt;</span><span class="n">constructors</span><span class="p">[</span><span class="n">constructor</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="n">next_tag</span><span class="o">++</span> <span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">full_type</span> <span class="o">=</span> <span class="n">return_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">constructor</span><span class="o">-&gt;</span><span class="n">types</span><span class="p">.</span><span class="n">rbegin</span><span class="p">();</span> <span class="n">it</span> <span class="o">!=</span> <span class="n">constructor</span><span class="o">-&gt;</span><span class="n">types</span><span class="p">.</span><span class="n">rend</span><span class="p">();</span> <span class="n">it</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">type</span> <span class="o">=</span> <span class="n">env</span><span class="o">-&gt;</span><span class="n">lookup_type</span><span class="p">(</span><span class="o">*</span><span class="n">it</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="n">type</span><span class="p">)</span> <span class="k">throw</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">full_type</span> <span class="o">=</span> <span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_arr</span><span class="p">(</span><span class="n">type</span><span class="p">,</span> <span class="n">full_type</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">env</span><span class="o">-&gt;</span><span class="n">bind</span><span class="p">(</span><span class="n">constructor</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">,</span> <span class="n">full_type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The separation of data and function definitions must be reconciled with code going back as far as the parser. While previously, we populated a single, global vector of definitions called <code>program</code>, we can no longer do that. Instead, we&rsquo;ll split our program into two maps, one for data types and one for functions. We use maps for convenience: the groups generated by our function graph refer to functions by name, and it would be nice to quickly look up the data the names refer to. Rather than returning such maps, we change our semantic actions to simply insert new data into one of two global maps. Below is a snippet that includes all the changes:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/parser.y" data-first-line="39" data-last-line="65"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/parser.y#L39-L65">parser.y</a>, lines 39 through 65</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span><span class="lnt">65 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-plaintext" data-lang="plaintext"><span class="line"><span class="cl">%type &lt;std::vector&lt;std::string&gt;&gt; lowercaseParams uppercaseParams </span></span><span class="line"><span class="cl">%type &lt;std::vector&lt;branch_ptr&gt;&gt; branches </span></span><span class="line"><span class="cl">%type &lt;std::vector&lt;constructor_ptr&gt;&gt; constructors </span></span><span class="line"><span class="cl">%type &lt;ast_ptr&gt; aAdd aMul case app appBase </span></span><span class="line"><span class="cl">%type &lt;definition_data_ptr&gt; data </span></span><span class="line"><span class="cl">%type &lt;definition_defn_ptr&gt; defn </span></span><span class="line"><span class="cl">%type &lt;branch_ptr&gt; branch </span></span><span class="line"><span class="cl">%type &lt;pattern_ptr&gt; pattern </span></span><span class="line"><span class="cl">%type &lt;constructor_ptr&gt; constructor </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">%start program </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">%% </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">program </span></span><span class="line"><span class="cl"> : definitions { } </span></span><span class="line"><span class="cl"> ; </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">definitions </span></span><span class="line"><span class="cl"> : definitions definition { } </span></span><span class="line"><span class="cl"> | definition { } </span></span><span class="line"><span class="cl"> ; </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">definition </span></span><span class="line"><span class="cl"> : defn { auto name = $1-&gt;name; defs_defn[name] = std::move($1); } </span></span><span class="line"><span class="cl"> | data { auto name = $1-&gt;name; defs_data[name] = std::move($1); } </span></span><span class="line"><span class="cl"> ;</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Note that <code>program</code> and <code>definitions</code> no longer have a type, and that <code>data</code> and <code>defn</code> have been changed to return <code>definition_data_ptr</code> and <code>definition_defn_ptr</code>, respectively. This necessitates changes to our main file. First of all, we declare the two new maps we hope to receive from Bison:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/main.cpp" data-first-line="24" data-last-line="25"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/main.cpp#L24-L25">main.cpp</a>, lines 24 through 25</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">24 </span><span class="lnt">25 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">extern</span> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">,</span> <span class="n">definition_data_ptr</span><span class="o">&gt;</span> <span class="n">defs_data</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="k">extern</span> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">,</span> <span class="n">definition_defn_ptr</span><span class="o">&gt;</span> <span class="n">defs_defn</span><span class="p">;</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We then change all affected functions, which in many cases amounts to splitting the <code>program</code> parameter into <code>defs_data</code> and <code>defs_defn</code> parameters. We also make other, largely mechanical changes: code iterating over definitions now requires the use of <code>second</code> to refer to the value stored in the map, and LLVM generation now needs to separately process the two different types of definitions. The biggest change occurs in <code>typecheck_program</code>, which not only undergoes all the aforementioned modifications, but is also updated to use topological ordering:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/main.cpp" data-first-line="27" data-last-line="84"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/main.cpp#L27-L84">main.cpp</a>, lines 27 through 84</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span><span class="lnt">65 </span><span class="lnt">66 </span><span class="lnt">67 </span><span class="lnt">68 </span><span class="lnt">69 </span><span class="lnt">70 </span><span class="lnt">71 </span><span class="lnt">72 </span><span class="lnt">73 </span><span class="lnt">74 </span><span class="lnt">75 </span><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span><span class="lnt">79 </span><span class="lnt">80 </span><span class="lnt">81 </span><span class="lnt">82 </span><span class="lnt">83 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">typecheck_program</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">,</span> <span class="n">definition_data_ptr</span><span class="o">&gt;&amp;</span> <span class="n">defs_data</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">,</span> <span class="n">definition_defn_ptr</span><span class="o">&gt;&amp;</span> <span class="n">defs_defn</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">int_type</span> <span class="o">=</span> <span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_base</span><span class="p">(</span><span class="s">&#34;Int&#34;</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="n">env</span><span class="o">-&gt;</span><span class="n">bind_type</span><span class="p">(</span><span class="s">&#34;Int&#34;</span><span class="p">,</span> <span class="n">int_type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">binop_type</span> <span class="o">=</span> <span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_arr</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="n">int_type</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_arr</span><span class="p">(</span><span class="n">int_type</span><span class="p">,</span> <span class="n">int_type</span><span class="p">))));</span> </span></span><span class="line"><span class="cl"> <span class="n">env</span><span class="o">-&gt;</span><span class="n">bind</span><span class="p">(</span><span class="s">&#34;+&#34;</span><span class="p">,</span> <span class="n">binop_type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">env</span><span class="o">-&gt;</span><span class="n">bind</span><span class="p">(</span><span class="s">&#34;-&#34;</span><span class="p">,</span> <span class="n">binop_type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">env</span><span class="o">-&gt;</span><span class="n">bind</span><span class="p">(</span><span class="s">&#34;*&#34;</span><span class="p">,</span> <span class="n">binop_type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">env</span><span class="o">-&gt;</span><span class="n">bind</span><span class="p">(</span><span class="s">&#34;/&#34;</span><span class="p">,</span> <span class="n">binop_type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">def_data</span> <span class="p">:</span> <span class="n">defs_data</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">def_data</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">insert_types</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">def_data</span> <span class="p">:</span> <span class="n">defs_data</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">def_data</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">insert_constructors</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">function_graph</span> <span class="n">dependency_graph</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">def_defn</span> <span class="p">:</span> <span class="n">defs_defn</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">def_defn</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">find_free</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">dependency_graph</span><span class="p">.</span><span class="n">add_function</span><span class="p">(</span><span class="n">def_defn</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">dependency</span> <span class="p">:</span> <span class="n">def_defn</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">free_variables</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">defs_defn</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">dependency</span><span class="p">)</span> <span class="o">==</span> <span class="n">defs_defn</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> </span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">dependency_graph</span><span class="p">.</span><span class="n">add_edge</span><span class="p">(</span><span class="n">def_defn</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">,</span> <span class="n">dependency</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">group_ptr</span><span class="o">&gt;</span> <span class="n">groups</span> <span class="o">=</span> <span class="n">dependency_graph</span><span class="p">.</span><span class="n">compute_order</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">groups</span><span class="p">.</span><span class="n">rbegin</span><span class="p">();</span> <span class="n">it</span> <span class="o">!=</span> <span class="n">groups</span><span class="p">.</span><span class="n">rend</span><span class="p">();</span> <span class="n">it</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span><span class="o">&amp;</span> <span class="n">group</span> <span class="o">=</span> <span class="o">*</span><span class="n">it</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">def_defnn_name</span> <span class="p">:</span> <span class="n">group</span><span class="o">-&gt;</span><span class="n">members</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span><span class="o">&amp;</span> <span class="n">def_defn</span> <span class="o">=</span> <span class="n">defs_defn</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">def_defnn_name</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">def_defn</span><span class="o">-&gt;</span><span class="n">insert_types</span><span class="p">(</span><span class="n">mgr</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">def_defnn_name</span> <span class="p">:</span> <span class="n">group</span><span class="o">-&gt;</span><span class="n">members</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span><span class="o">&amp;</span> <span class="n">def_defn</span> <span class="o">=</span> <span class="n">defs_defn</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">def_defnn_name</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">def_defn</span><span class="o">-&gt;</span><span class="n">typecheck</span><span class="p">(</span><span class="n">mgr</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">def_defnn_name</span> <span class="p">:</span> <span class="n">group</span><span class="o">-&gt;</span><span class="n">members</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">env</span><span class="o">-&gt;</span><span class="n">generalize</span><span class="p">(</span><span class="n">def_defnn_name</span><span class="p">,</span> <span class="n">mgr</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">pair</span> <span class="p">:</span> <span class="n">env</span><span class="o">-&gt;</span><span class="n">names</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">pair</span><span class="p">.</span><span class="n">first</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;: &#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">pair</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">print</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div> </div> <p>The above code uses the yet-unexplained <code>generalize</code> method. What&rsquo;s going on?</p> <p>Observe that the <strong>Var</strong> rule of the Hindley-Milner type system says that a variable \(x\) can have a <strong>polytype</strong> in the environment \(\Gamma\). Our <code>type_ptr</code> can only represent monotypes, so we must change what <code>type_env</code> associates with names to a new struct for representing polytypes, which we will call <code>type_scheme</code>. The <code>type_scheme</code> struct, just like the formal definition of a polytype, contains zero or more &ldquo;forall&rdquo;-quantified type variables, followed by a monotype which may use these variables:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/type.hpp" data-first-line="17" data-last-line="27"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/type.hpp#L17-L27">type.hpp</a>, lines 17 through 27</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">type_scheme</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">forall</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">monotype</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">type_scheme</span><span class="p">(</span><span class="n">type_ptr</span> <span class="n">type</span><span class="p">)</span> <span class="o">:</span> <span class="n">forall</span><span class="p">(),</span> <span class="n">monotype</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">type</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">print</span><span class="p">(</span><span class="k">const</span> <span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="nf">instantiate</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span></code></pre></td></tr></table> </div> </div> </div> <p>The <code>type_scheme::instantiate</code> method is effectively an implementation of the special case of the <strong>Inst</strong> rule, in which a polytype is specialized to a monotype. Since the <strong>App</strong> and <strong>Case</strong> rules only use monotypes, we&rsquo;ll be using this special case a lot. We implement this method as follows:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/type.cpp" data-first-line="34" data-last-line="41"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/type.cpp#L34-L41">type.cpp</a>, lines 34 through 41</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">type_ptr</span> <span class="n">type_scheme</span><span class="o">::</span><span class="n">instantiate</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">forall</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="n">monotype</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">,</span> <span class="n">type_ptr</span><span class="o">&gt;</span> <span class="n">subst</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">var</span> <span class="p">:</span> <span class="n">forall</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">subst</span><span class="p">[</span><span class="n">var</span><span class="p">]</span> <span class="o">=</span> <span class="n">mgr</span><span class="p">.</span><span class="n">new_type</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nf">substitute</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">subst</span><span class="p">,</span> <span class="n">monotype</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>In the above code, if the type scheme represents a monotype (i.e., it has no quantified variables), we simply return that monotype. Otherwise, we must perform a substitution, replacing &ldquo;forall&rdquo;-quantified variables with fresh type parameters to be determined (we will never determine a single type for any of the quantified variables, since they are specifically meant to represent any type). We build a substitution map, which assigns to each quantified type variable a corresponding &ldquo;fresh&rdquo; type, and then create a new type with with the substitution applied using <code>substitute</code>, which is implemented as follows:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/type.cpp" data-first-line="18" data-last-line="32"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/type.cpp#L18-L32">type.cpp</a>, lines 18 through 32</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">type_ptr</span> <span class="nf">substitute</span><span class="p">(</span><span class="k">const</span> <span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">,</span> <span class="n">type_ptr</span><span class="o">&gt;&amp;</span> <span class="n">subst</span><span class="p">,</span> <span class="k">const</span> <span class="n">type_ptr</span><span class="o">&amp;</span> <span class="n">t</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_var</span><span class="o">*</span> <span class="n">var</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">resolved</span> <span class="o">=</span> <span class="n">mgr</span><span class="p">.</span><span class="n">resolve</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">var</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">var</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">subst_it</span> <span class="o">=</span> <span class="n">subst</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">var</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">subst_it</span> <span class="o">==</span> <span class="n">subst</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> <span class="k">return</span> <span class="n">resolved</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">subst_it</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="n">type_arr</span><span class="o">*</span> <span class="n">arr</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">type_arr</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">t</span><span class="p">.</span><span class="n">get</span><span class="p">()))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">left_result</span> <span class="o">=</span> <span class="n">substitute</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">subst</span><span class="p">,</span> <span class="n">arr</span><span class="o">-&gt;</span><span class="n">left</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">right_result</span> <span class="o">=</span> <span class="n">substitute</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">subst</span><span class="p">,</span> <span class="n">arr</span><span class="o">-&gt;</span><span class="n">right</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">left_result</span> <span class="o">==</span> <span class="n">arr</span><span class="o">-&gt;</span><span class="n">left</span> <span class="o">&amp;&amp;</span> <span class="n">right_result</span> <span class="o">==</span> <span class="n">arr</span><span class="o">-&gt;</span><span class="n">right</span><span class="p">)</span> <span class="k">return</span> <span class="n">t</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_arr</span><span class="p">(</span><span class="n">left_result</span><span class="p">,</span> <span class="n">right_result</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">t</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>In principle, the function is fairly simple: if the current type is equivalent to a quantified type, we return the corresponding &ldquo;fresh&rdquo; type. If, on the other hand, the type represents a function, we perform a substitution in the function&rsquo;s input and output types. This method avoids creating new types where possible; a new type is only created if a function&rsquo;s input or output type is changed by a substitution (in which case, the function itself is changed by the substitution). In all other cases, substitution won&rsquo;t do anything, so we just return the original type.</p> <p>Now it is a bit more clear why we saw <code>instantiate</code> in a code snippet some time ago; to compute a monotype for a variable reference, we must take into account the possibility that the variable has a polymorphic type, which needs to be specialized (potentially differently in every occurrence of the variable).</p> <p>When talking about our new typechecking algorithm, we mentioned using <strong>Gen</strong> to sprinkle polymorphism into our program. If it can, <strong>Gen</strong> will add free variables in a type to the &ldquo;forall&rdquo; quantifier at the front, making that type polymorphic. We implement this using a new <code>generalize</code> method added to the <code>type_env</code>, which (as per convention) generalizes the type of a given variable as much as possible:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/type_env.cpp" data-first-line="31" data-last-line="41"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/type_env.cpp#L31-L41">type_env.cpp</a>, lines 31 through 41</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">type_env</span><span class="o">::</span><span class="n">generalize</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">,</span> <span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">names_it</span> <span class="o">=</span> <span class="n">names</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">names_it</span> <span class="o">==</span> <span class="n">names</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> <span class="k">throw</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">names_it</span><span class="o">-&gt;</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">forall</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="k">throw</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">free_variables</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">mgr</span><span class="p">.</span><span class="n">find_free</span><span class="p">(</span><span class="n">names_it</span><span class="o">-&gt;</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">monotype</span><span class="p">,</span> <span class="n">free_variables</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">free</span> <span class="p">:</span> <span class="n">free_variables</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">names_it</span><span class="o">-&gt;</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">forall</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">free</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>For now, we disallow types to be generalized twice, and we naturally disallow generalizing types of nonexistent variables. If neither of those things occurs, we find all the free variables in the variable&rsquo;s current type using a new method called <code>type_mgr::find_free</code>, and put them into the &ldquo;forall&rdquo; quantifier. <code>type_mgr::find_free</code> is implemented as follows:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/10/type.cpp" data-first-line="138" data-last-line="148"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/type.cpp#L138-L148">type.cpp</a>, lines 138 through 148</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">138 </span><span class="lnt">139 </span><span class="lnt">140 </span><span class="lnt">141 </span><span class="lnt">142 </span><span class="lnt">143 </span><span class="lnt">144 </span><span class="lnt">145 </span><span class="lnt">146 </span><span class="lnt">147 </span><span class="lnt">148 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">type_mgr</span><span class="o">::</span><span class="n">find_free</span><span class="p">(</span><span class="k">const</span> <span class="n">type_ptr</span><span class="o">&amp;</span> <span class="n">t</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_var</span><span class="o">*</span> <span class="n">var</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">resolved</span> <span class="o">=</span> <span class="n">resolve</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">var</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">var</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">into</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">var</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="nf">if</span><span class="p">(</span><span class="n">type_arr</span><span class="o">*</span> <span class="n">arr</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">type_arr</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">resolved</span><span class="p">.</span><span class="n">get</span><span class="p">()))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">find_free</span><span class="p">(</span><span class="n">arr</span><span class="o">-&gt;</span><span class="n">left</span><span class="p">,</span> <span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">find_free</span><span class="p">(</span><span class="n">arr</span><span class="o">-&gt;</span><span class="n">right</span><span class="p">,</span> <span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The above code is fairly straightforward; if a type is a variable that is not yet bound to anything, it is free; if the type is a function, we search for free variables in its input and output types; otherwise, the type has no free variables.</p> <p>Finally, we have made the necessary changes. Let&rsquo;s test it out with the example from the beginning:</p> <div class="highlight-group" > <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/10/examples/if.txt">if.txt</a>, entire file</div><pre><code>data Bool = { True, False } defn if c t e = { case c of { True -&gt; { t } False -&gt; { e } } } defn main = { if (if True False True) 11 3 } </code></pre> </div> <p>Running it, we get the output:</p> <pre tabindex="0"><code>3 </code></pre><p>Hooray!</p> <p>While this is a major success, we are not yet done. Although our functions can now have polymorphic types, the same cannot be said for our data types! We want to have lists of integers <strong>and</strong> lists of booleans, without having to duplicate any code! While this also falls into the category of polymorphism, this post has already gotten very long, and we will return to it in <a href="https://danilafe.com/blog/11_compiler_polymorphic_data_types/">part 11</a>. Once we&rsquo;re done with that, I still intend to go over <code>let/in</code> expressions, <strong>lambda functions</strong>, and <strong>Input/Output</strong> together with <strong>strings</strong>.</p> Math Rendering is Wrong https://danilafe.com/blog/math_rendering_is_wrong/ Tue, 24 Mar 2020 16:40:27 -0700 https://danilafe.com/blog/math_rendering_is_wrong/ <p>Since I first started working on my website at age fourteen, the site has gone through many revisions, and hopefully changed for the better. This blog was originally dynamically served using a Python/Flask backend, having a custom login system and post &ldquo;editor&rdquo; (just an input box). One of the more strange things about my website, though, was how I displayed content.</p> <p>It was clear to me, even at my young age, that writing raw HTML was suboptimal. Somehow (perhaps through GitHub) I heard about Markdown, and realized that a human-readable markup language was probably a much better way to go. What remained was, of course, rendering the content. The easiest way I found was to just stick a JavaScript script, calling out to <a href="https://github.com/markedjs/marked"class="external-link">marked<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, to run on page load and convert all the markup into pretty HTML.</p> <p>This rendering would happen on every page load. Every time I navigated between pages on my site, for a second or two, I&rsquo;d see the raw, unrendered Markdown that I had written, which would then disappear and be replaced with a proper view of the page&rsquo;s content. The rendering wasn&rsquo;t error-proof, either. If my connection was particularly slow (which it was, thanks, Comcast), or if I forgot to disable uMatrix, I would be left having to sift through the frequent occurences of <code>#</code>, <code>_</code>, <code>*</code>&hellip; Eventually I realized my mistake, and switched to rendering Markdown on the backend. Now, my content would appear to the user already formatted, and they wouldn&rsquo;t have to wait for a JavaScript program to finish to read what I had written. All was well.</p> <p>Sometimes, I look back on the early iterations of my site, and smile at the silly mistakes I&rsquo;d made. Yet I can&rsquo;t innocently make fun of my original Markdown rendering solution, because in a way, it lives on.</p> <a href="#the-state-of-mathematics-on-the-web"> <h3 id="the-state-of-mathematics-on-the-web">The State of Mathematics on the Web</h3> </a> <p>When I search for &ldquo;render math on website&rdquo; on Google, the following two links are at the top of my search:</p> <ul> <li><a href="https://www.mathjax.org"class="external-link">MathJax | Beautiful math in all browsers<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a></li> <li><a href="https://katex.org"class="external-link">KaTeX - the fastests math typesetting library for the web<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a></li> </ul> <p>Indeed, these are the two most popular math rendering solutions (in my experience). Yet both of these solutiosn share something something in common with each other and with the early iterations of my website - they use client-side rendering for static content. In my opinion, this is absurd. Every time that you visit a website that uses MathJax or KaTeX, any mathematical notation arrives to your machine in the form of LaTex markup. For instance, \(e^{-\frac{x}{2}}\) looks like <code>e^{-\frac{x}{2}}</code>. The rendering software (MathJax or KaTeX) then takes this markup and converts it into HTML and CSS that your browser can display. Just like my old website, all of this happens <strong>every time the page is loaded</strong>. This isn&rsquo;t an uncommon thing, either: websites like <a href="https://math.stackexchange.com"class="external-link">Mathematics StackExchange<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> and <a href="https://www.chegg.com"class="external-link">Chegg<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> use MathJax, and many more can be found on <a href="https://docs.mathjax.org/en/v2.7-latest/misc/mathjax-in-use.html"class="external-link">this list<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. According to <a href="https://katex.org/users.html"class="external-link">this page<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, Facebook Messenger, <a href="https://www.khanacademy.org/"class="external-link">Khan Academy<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, <a href="https://gitter.im/"class="external-link">Gitter<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> and <a href="https://about.gitlab.com/"class="external-link">GitLab<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> use KaTeX. Some of these websites don&rsquo;t even load their content with JavaScript enabled, much less render math.</p> <p>A skeptic might say that it is not possible to render LaTeX to HTML and CSS ahead of time. This might even have been true in the past. A user on the <a href="https://meta.mathoverflow.net/questions/2360/can-we-replace-client-side-mathjax-with-server-side-mathjax"class="external-link">&ldquo;can we replace client-side MathJax with server-side MathJax&rdquo;<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> thread on MathOverflow Meta in 2015 points out that MathJax doesn&rsquo;t support server-side HTML output:</p> <blockquote> <p>I found <a href="https://math.meta.stackexchange.com/questions/16809/a-mathjax-alternative-from-khan-academy#comment62132_16817"class="external-link">[the comment]<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> from a MathJax developer confirming that HTML+CSS doesn&rsquo;t work yet with the server-side version . . .</p> </blockquote> <p>The comment is even older, from 2014:</p> <blockquote> <p>. . . [MathJax does not support] HTML-CSS at the moment . . .</p> </blockquote> <p>It&rsquo;s over, go home everyone. We are asking for the impossible.</p> <p>Or are we? Version 2.6 of MathJax has the following comment in its change log:</p> <blockquote> <p><em>Improved CommonHTML output</em>. The CommonHTML output now provides the same layout quality and MathML support as the HTML-CSS and SVG output. It is on average 40% faster than the other outputs and the markup it produces are identical on all browsers and thus can also be pre-generated on the server via MathJax-node.</p> </blockquote> <p>Further, the <a href="http://docs.mathjax.org/en/latest/output/html.html"class="external-link">HTML Support<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> page from MathJax&rsquo;s docs states:</p> <blockquote> <p>[CommonHTML] is MathJax’s primary output mode since MathJax version 2.6. Its major advantage is its quality, consistency, and the fact that its output is independent of the browser, operating system, and user environment</p> </blockquote> <p>So not only is it explicitly possible to have a server-generated math output, but the algorithm that would be used for generating such output is already in use on the client-side! If you look hard enough, you may even find a few resources for using this algorithm server-side, but many of those suffer from another problem&hellip;</p> <a href="#images-wont-cut-it"> <h3 id="images-wont-cut-it">Images Won&rsquo;t Cut It</h3> </a> <p>It&rsquo;s tempting to convert mathematics to an image, such as a PNG or an SVG file. This approach is taken on <a href="https://blog.oniuo.com/post/math-jax-ssr-example/"class="external-link">this blog<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> and this <a href="https://advancedweb.hu/mathjax-processing-on-the-server-side/"class="external-link">Advanced Web Machinery<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> article. <a href="http://mathworld.wolfram.com/Convergent.html"class="external-link">Wolfram MathWorld<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> also render their mathematics to images. However, in my opion, this is <strong>not the right approach</strong>. It is inconvenient for me as a user, and, I suspect, for those in need of assistive technologies. Here are the issues I see with rendering mathematics to images:</p> <ul> <li>Images are impossible to use with copy/paste. I am unable to select a word, number, or symbol in a rendered image. I do this on occasion, and this is not a contrived issue.</li> <li>Images are not nearly as responsive, and are difficult to style. Line breaking, fonts, and even colors are difficult to change when using images. They stick out like a sore thumb when used for inline math, and can look very strange (or disappear) if a user extension manipulates colors on the page.</li> <li>Images are completely opaque to users in need of screen readers. While some readers support <span class="sidenote"> <label class="sidenote-label" for="mathml-note">MathML,</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="mathml-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> MathML is an XML-based markup for mathematics, meant to serve as a low-level ouput target for other math processors. While it is supported in Firefox, it requires a Polyfill in Chrome, and brings us back to front-end JavaScript. <span class="sidenote-delimiter">]</span> </span> </span> images are much harder to work with. The sites that I linked that use images do not have captions with their math, making it completely inaccessible.</li> <li>Using images instead of a JavaScript-based renderer reminds me of the <a href="https://www.logicallyfallacious.com/cgi-bin/uy/webpages.cgi?/logicalfallacies/False-Dilemma"class="external-link">false dichotomy fallacy<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. Why can&rsquo;t we do what we do, but using the server?</li> </ul> <a href="#where-have-the-resources-gone"> <h3 id="where-have-the-resources-gone">Where Have the Resources Gone?</h3> </a> <p>If you look up &ldquo;mathjax setup&rdquo; on Google, you are greeted with dozens of links telling you how to get the rendering working client-side. It&rsquo;s convenient: a single JavaScript <code>&lt;src&gt;</code> tag, and maybe a bit of code at the bottom of the page with some settings.</p> <p>If you look up &ldquo;mathjax render server-side&rdquo;, the resources are far more scarce. In fact, the first two image-based solutions I presented to you came up as results after such a search. One more website looks promising: <a href="https://a3nm.net/blog/selfhost_mathjax.html"class="external-link">Antoine Amarilli&rsquo;s blog post about server-side rendering<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. The page promises a self-hosted way of generating HTML using <code>mathjax-node</code>, and even provides a very compelling sample output. I decided to try out this approach, but to no avail. First of all, the MathJax infrastructure has changed:</p> <blockquote> <p>As mathjax has reorganized their repositories, to make the following work, you will probably need to install manually mathjax-node-cli, as well as maybe installing mathjax-node and possibly mathjax-node-page. Again, I haven&rsquo;t tried it. Thanks again to Ted for pointing this out!</p> </blockquote> <p>This was indeed the case. The bigger issue, though, was that the <code>page2html</code> program, which rendered all the mathematics in a single HTML page, was gone. I found <code>tex2html</code> and <code>text2htmlcss</code>, which could only render equations without the surrounding HTML. I also found <code>mjpage</code>, which replaced mathematical expressions in a page with their SVG forms. This actually looked quite good - the SVGs even had labels with their original LaTeX code. However, those labels were hard to read (likely especially so for people using screen readers), and the SVG images otherwise maintained most of the issues I described above. Additionally, <span class="sidenote"> <label class="sidenote-label" for="sluggish-note">the page behaved significantly more sluggishly after the initial render than the JavaScript-based alternative.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="sluggish-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> This is purely anecdotal, and would require a more thorough analysis to generalize. <span class="sidenote-delimiter">]</span> </span> </span> </p> <p>In short, it&rsquo;s much harder to find resources for server-side LaTeX rendering. It is especially hard to find such resources that work with more than static pages. I could probably use a regular expression to extract math I need to render from HTML, call <code>tex2htmlcss</code> on it, and splice it back into the page. But how could a WordPress user render their math? What about someone writing their blog engine like I did in my youth? Somehow, those of us wanting to give our users a better experience are left fumbling for an alternative, more or less without outside help&hellip;</p> <a href="#conclusion"> <h3 id="conclusion">Conclusion</h3> </a> <p>The majority of websites today use client-side, JavaScript-based rendering techniques for mathematics. The work that could be done by a server is outsorced to thousands of browsers, who have to run the same code to get <strong>identical results</strong>. Somehow, this is the best solution - the most accessible alternatives use images, which are a downgrade to the user experience. I wish for server-side rendering to become more common, and better documented.</p> Creating Recursive Functions in a Stack Based Language https://danilafe.com/blog/stack_recursion/ Fri, 06 Mar 2020 17:56:55 -0800 https://danilafe.com/blog/stack_recursion/ <link rel="stylesheet" href="https://danilafe.com/scss/stack.min.css"> <p>In CS 381, Programming Language Fundamentals, many students chose to implement a stack based language. Such languages are very neat, but two of the requirements for such languages may, at first, seem somewhat hard to satisfy:</p> <blockquote> <p>Recursion/loops, . . . [and] . . . Procedures/functions with arguments (or some other abstraction mechanism)</p> </blockquote> <p>A while-loop makes enough sense. The most straightforward way to implement such a loop is to keep reading a boolean from the stack, and, if that boolean is true, running some sequence of instructions. But while loops do not give you procedures - they are not a sufficiently powerful abstraction mechanism for this assignment. So, we turn to functions.</p> <p>The first instinct in implementing functions is to fall back to the tried-and-true method of introducing more global state: we have a stack, but why don&rsquo;t we also add a mapping from function names to their definitions (an environment)? This works, but I feel like it goes somewhat against the whole idea of a stack-based language. We can do everything we need to do, entirely on the stack!</p> <a href="#a-toy-language"> <h3 id="a-toy-language">A Toy Language</h3> </a> <p>To make this post more concrete, let&rsquo;s define a small language. Small enough that it&rsquo;s easy to reason about, but complex enough to support functions. I won&rsquo;t be giving a Haskell-encoded abstract syntax definition - rather, let&rsquo;s work from concrete syntax. How about something like:</p> $$ \begin{aligned} \textit{cmd} ::= \; &amp;amp; \text{Pop} \; n\\ | \; &amp;amp; \text{Slide} \; n \\ | \; &amp;amp; \text{Offset} \; n \\ | \; &amp;amp; \text{Eq} \\ | \; &amp;amp; \text{PushI} \; i \\ | \; &amp;amp; \text{Add} \\ | \; &amp;amp; \text{Mul} \\ | \; &amp;amp; \textbf{if} \; \{ \textit{cmd}* \} \; \textbf{else} \; \{ \textit{cmd}* \} \\ | \; &amp;amp; \textbf{func} \; \{ \textit{cmd}* \} \\ \ \; &amp;amp; \textbf{Call} \end{aligned} $$ <p>Let&rsquo;s informally define the meanings of each of the described commands:</p> <ol> <li>\(\text{Pop} \; n\): Removes the top \(n\) elements from the stack.</li> <li>\(\text{Slide} \; n \): Removes the top \(n\) elements <strong>after the first element on the stack</strong>. The first element is not removed.</li> <li>\(\text{Offset} \; n \): Pushes an element from the stack onto the stack, again. When \(n=0\), the top element is pushed, when \(n=1\), the second element is pushed, and so on.</li> <li>\(\text{Eq}\): Compares two numbers on top of the stack for equality. The numbers are removed, and replaced with a boolean indicating whether or not they are equal.</li> <li>\(\text{PushI} \; i \): Pushes an integer \(i\) onto the stack.</li> <li>\(\text{Add}\): Adds two numbers on top of the stack. The two numbers are removed, and replaced with their sum.</li> <li>\(\text{Mul}\): Multiplies two numbers on top of the stack. The two numbers are removed, and replaced with their product.</li> <li>\(\textbf{if}\)/\(\textbf{else}\): Runs the first list of commands if the boolean &ldquo;true&rdquo; is on top of the stack, and the second list of commands if the boolean is &ldquo;false&rdquo;.</li> <li>\(\textbf{func}\): pushes a function with the given commands onto the stack.</li> <li>\(\text{Call}\): calls the function at the top of the stack. The function is removed, and its body is then executed.</li> </ol> <p>Great! Let&rsquo;s now write some dummy programs in our language (and switch to code blocks from LaTeX). How about a program that multiplies 4 and 5?</p> <pre tabindex="0"><code>PushI 5 PushI 4 Mul </code></pre><p>Next, let&rsquo;s try something more complicated. <span class="sidenote"> <label class="sidenote-label" for="contrived-note">How about a program that checks if 3 is equal to 4, and returns 999 if they are equal, and 1 if they are not?</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="contrived-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> I'm aware that this example is contrived. To minimize the cognitive load of working with our language, I've stripped it of many useful features, including inequalities. This is why the example may seem strange: I had to pose a question I could answer! <span class="sidenote-delimiter">]</span> </span> </span> </p> <pre tabindex="0"><code>PushI 4 PushI 3 Eq if { PushI 999 } else { PushI 1 } </code></pre><p>Now, it&rsquo;s time for the actual meat: can our language do recursion? I claim that it does, but before we start hacking away, there&rsquo;s one more thing we need to do: establish a calling convention.</p> <a href="#be-conventional"> <h3 id="be-conventional">Be Conventional!</h3> </a> <p>Our language does not enforce any etiquette. You can easily create a function that pops every value off the stack, continuing until the stack is empty. You can equally easily make a function that fills your stack with random junk. With such potential for disorder, a programmer &mdash; maybe yourself &mdash; may experience some <span class="sidenote"> <label class="sidenote-label" for="anomie-note">anomie.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="anomie-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Anomie is defined as "lack of the usual social or ethical standards in an individual or group" according to the Oxford dictionary. <span class="sidenote-delimiter">]</span> </span> </span> To deal with this, we try to maintain a little bit of order in the midst of all the computational chaos. We will adopt calling conventions.</p> <p>When I say calling convention, I mean that every time we call a function, we do it in a methodical way. There are many possible such methods, but I propose the following:</p> <ol> <li>Since \(\text{Call}\) requires that the function you&rsquo;re calling is at the top of the stack, we stick with that.</li> <li>If the function expects arguments, we push them on the stack right before the function. The first argument of the function should be second from the top of the stack (i.e., <span class="sidenote"> <label class="sidenote-label" for="offset-note">accessible from the function via \(\text{Offset} \; 0\)).</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="offset-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Note that \(\text{Call}\) removes the function from the stack, which is why the first argument ends up at the very top. <span class="sidenote-delimiter">]</span> </span> </span> The second argument should follow, then the third, and so on.</li> <li>When a function returns, it should not leave its arguments on the stack. Instead of them, the function should leave its resulting value.</li> <li>A function does not modify the stack below the arguments it receives.</li> </ol> <p>Let&rsquo;s try this out with a basic function definition and call. How about a function that always returns 0, no matter what argument you give it? The function itself would look something like this:</p> <pre tabindex="0"><code>PushI 0 Slide 1 </code></pre><p>Here&rsquo;s how things will play out. When the function is called &mdash; and we assume that it is called correctly, of course &ndash; it will receive an integer on top of the stack. That may not, and likely will not, be the only thing on the stack. However, to stick by convention 4, we pretend that the stack is empty, and that trying to manipulate it will result in an error. So, we can start by imagining an empty stack, with an integer \(x\) on top:</p> <div class="stack"> <div class="stack-element"> </div> <div class="stack-element"> </div> <div class="stack-element"> </div> <div class="stack-element"> \(x\) </div> </div> <p>Then, \(\text{PushI} \; 0\) will push 0 onto the stack:</p> <div class="stack"> <div class="stack-element"> </div> <div class="stack-element"> </div> <div class="stack-element"> 0 </div> <div class="stack-element"> \(x\) </div> </div> <p>\(\text{Slide} \; 1\) will then remove the 1 element after the top element: \(x\). We end up with the following stack:</p> <div class="stack"> <div class="stack-element"> </div> <div class="stack-element"> </div> <div class="stack-element"> </div> <div class="stack-element"> 0 </div> </div> <p>The function has finished running, and we maintain convention 3: the function&rsquo;s return value is in place of its argument on the stack.</p> <p>All that&rsquo;s left is to call this function. Let&rsquo;s try calling the function with the number 15. We do this like so:</p> <pre tabindex="0"><code>PushI 15 func { PushI 0; Slide 1 } Call </code></pre><p>The function must be on top of the stack, as per the semantics of our language (and, I suppose, convention 1). Because of this, we have to push it last. It only takes one argument, which we push on the stack first (so that it ends up below the function, as per convention 2). When both are pushed, we use \(\text{Call}\) to execute the function, which will proceed as we&rsquo;ve seen above.</p> <a href="#get-ahold-of-yourself"> <h3 id="get-ahold-of-yourself">Get Ahold of Yourself!</h3> </a> <p>How should a function call itself? The fact that functions reside on the stack, and can therefore be manipulated in the same way as any stack elements. This opens up an opportunity for us: we can pass the function as an argument to itself! Then, when it needs to make a recursive call, all it must do is \(\text{Offset}\) itself onto the top of the stack, then \(\text{Call}\), and voila!</p> <p>Talk is great, of course, but talking doesn&rsquo;t give us any examples. Let&rsquo;s walk through an example of writing a recursive function this way. Let&rsquo;s try <a href="https://en.wikipedia.org/wiki/Factorial"class="external-link">factorial<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>!</p> <p>The &ldquo;easy&rdquo; implementation of factorial is split into two cases: the base case, when \(0! = 1\) is computed, and the recursive case, in which we multiply the input number \(n\) by the result of computing factorial for \(n-1\). Accordingly, we will use the \(\textbf{if}\)/\(\text{else}\) command. We will make our function take two arguments, with the number input as the first (&ldquo;top&rdquo;) argument, and the function itself as the second argument. Importantly, we do not want to destroy the input number by running \(\text{Eq}\) directly on it. Instead, we first copy it using \(\text{Offset} \; 0\), then compare it to 0:</p> <pre tabindex="0"><code>Offset 0 PushI 0 Eq </code></pre><p>Let&rsquo;s walk through this. We start with only the arguments on the stack:</p> <div class="stack"> <div class="stack-element"> </div> <div class="stack-element"> </div> <div class="stack-element"> \(n\) </div> <div class="stack-element"> factorial </div> </div> <p>Then, \(\text{Offset} \; 0\) duplicates the first argument (the number):</p> <div class="stack"> <div class="stack-element"> </div> <div class="stack-element"> \(n\) </div> <div class="stack-element"> \(n\) </div> <div class="stack-element"> factorial </div> </div> <p>Next, 0 is pushed onto the stack:</p> <div class="stack"> <div class="stack-element"> 0 </div> <div class="stack-element"> \(n\) </div> <div class="stack-element"> \(n\) </div> <div class="stack-element"> factorial </div> </div> <p>Finally, \(\text{Eq}\) performs the equality check:</p> <div class="stack"> <div class="stack-element"> </div> <div class="stack-element"> true/false </div> <div class="stack-element"> \(n\) </div> <div class="stack-element"> factorial </div> </div> <p>Great! Now, it&rsquo;s time to branch. What happens if &ldquo;true&rdquo; is on top of the stack? In that case, we no longer need any more information. We always return 1 in this case. So, just like the function I described earlier, we can do the following:</p> <pre tabindex="0"><code>PushI 1 Slide 2 </code></pre><p>As before, we push the desired answer onto the stack:</p> <div class="stack"> <div class="stack-element"> </div> <div class="stack-element"> 1 </div> <div class="stack-element"> \(n\) </div> <div class="stack-element"> factorial </div> </div> <p>Then, to follow convention 3, we must get rid of the arguments. We do this by using \(\text{Slide}\):</p> <div class="stack"> <div class="stack-element"> </div> <div class="stack-element"> </div> <div class="stack-element"> </div> <div class="stack-element"> 1 </div> </div> <p>Great! The \(\textbf{if}\) branch is now done, and we&rsquo;re left with the correct answer on the stack. Excellent!</p> <p>It&rsquo;s the recursive case that&rsquo;s more interesting. To make the recursive call, we must carefully set up our stack. Just like before, the function must be an argument to itself, and it&rsquo;s found lower on the stack, so we push it first:</p> <pre tabindex="0"><code>Offset 1 </code></pre><p>The result is as follows:</p> <div class="stack"> <div class="stack-element"> </div> <div class="stack-element"> factorial </div> <div class="stack-element"> \(n\) </div> <div class="stack-element"> factorial </div> </div> <p>Next, we must compute \(n-1\). This is pretty standard stuff:</p> <pre tabindex="0"><code>Offset 1 PushI -1 Add </code></pre><p>Why these three instructions? Well, with the function now on the top of the stack, the number argument is somewhat buried, and thus, we need to use \(\text{Offset} \; 1\) to get to it:</p> <div class="stack"> <div class="stack-element"> \(n\) </div> <div class="stack-element"> factorial </div> <div class="stack-element"> \(n\) </div> <div class="stack-element"> factorial </div> </div> <p>Then, we push a negative number, and add it to to the number on top. We end up with:</p> <div class="stack"> <div class="stack-element"> \(n-1\) </div> <div class="stack-element"> factorial </div> <div class="stack-element"> \(n\) </div> <div class="stack-element"> factorial </div> </div> <p>Finally, we have our arguments in order as per convention 2. To follow convention 1, we must now push the function onto the top of the stack:</p> <pre tabindex="0"><code>Offset 1 </code></pre><p>The stack is now as follows:</p> <div class="stack"> <div class="stack-element"> factorial </div> <div class="stack-element"> \(n-1\) </div> <div class="stack-element"> factorial </div> <div class="stack-element"> \(n\) </div> <div class="stack-element"> factorial </div> </div> <p>Good! With the preparations for the function call now complete, we take the leap:</p> <pre tabindex="0"><code>Call </code></pre><p>If the function behaves as promised, this will remove the top 3 elements from the stack. The top element, which is the function itself, will be removed by the \(\text{Call}\) operator. The two next two elements will be removed from the stack and replaced with the result of the function as per convention 2. The rest of the stack will remain untouched as per convention 4. We thus expect the stack to look as follows:</p> <div class="stack"> <div class="stack-element"> </div> <div class="stack-element"> \((n-1)!\) </div> <div class="stack-element"> \(n\) </div> <div class="stack-element"> factorial </div> </div> <p>We&rsquo;re almost there! What&rsquo;s left is to perform the multiplication (we&rsquo;re safe to destroy the argument now, since we will not be needing it after this), and clean up the stack:</p> <pre tabindex="0"><code>Mul Slide 1 </code></pre><p>The multiplication leaves us with \(n(n-1)! = n!\) on top of the stack, and the function argument below it:</p> <div class="stack"> <div class="stack-element"> </div> <div class="stack-element"> </div> <div class="stack-element"> \(n!\) </div> <div class="stack-element"> factorial </div> </div> <p>We then use \(\text{Slide}\) so that only the factorial is on the stack, satisfying convention 3:</p> <div class="stack"> <div class="stack-element"> </div> <div class="stack-element"> </div> <div class="stack-element"> </div> <div class="stack-element"> \(n!\) </div> </div> <p>That&rsquo;s it! We have successfully executed the recursive case. The whole function is now as follows:</p> <pre tabindex="0"><code>Offset 0 PushI 0 Eq if { PushI 1 Slide 2 } else { Offset 1 Offset 1 PushI -1 Add Offset 1 Call Mul Slide 1 } </code></pre><p>We can now invoke this function to compute \(5!\) as follows:</p> <pre tabindex="0"><code>func { ... } PushI 5 Offset 1 Call </code></pre><p>Awesome! That&rsquo;s about it. We have made a stack-based language with full support for recursion and procedures. I hope this was helpful.</p> Meaningfully Typechecking a Language in Idris https://danilafe.com/blog/typesafe_interpreter/ Thu, 27 Feb 2020 21:58:55 -0800 https://danilafe.com/blog/typesafe_interpreter/ <p>This term, I&rsquo;m a TA for Oregon State University&rsquo;s Programming Languages course. The students in the course are tasked with using Haskell to implement a programming language of their own design. One of the things they can do to gain points for the project is implement type checking, rejecting <span class="sidenote"> <label class="sidenote-label" for="ill-typed-note">ill-typed programs or expressions</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="ill-typed-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Whether or not the below example is ill-typed actually depends on your language. Many languages (even those with a static type system, like C++ or Crystal) have a notion of "truthy" and "falsy" values. These values can be used in the condition of an if-expression, and will be equivalent to "true" or "false", respectively. However, for simplicity, I will avoid including truthy and falsy values into the languages in this post. For the same reason, I will avoid reasoning about <a href="https://developer.mozilla.org/en-US/docs/Glossary/Type_coercion">type coercions</a>, which make expressions like <code>"Hello"+3</code> valid. <span class="sidenote-delimiter">]</span> </span> </span> such as:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kr">if</span> <span class="s">&#34;Hello&#34;</span> <span class="kr">then</span> <span class="mi">0</span> <span class="kr">else</span> <span class="mi">1</span> </span></span></code></pre></div><p>For instance, a student may have a function <code>typecheck</code>, with the following signature (in Haskell):</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">typecheck</span> <span class="ow">::</span> <span class="kt">Expr</span> <span class="ow">-&gt;</span> <span class="kt">Either</span> <span class="kt">TypeError</span> <span class="kt">ExprType</span> </span></span></code></pre></div><p>The function will return an error if something goes wrong, or, if everything goes well, the type of the given expression. So far, so good.</p> <p>A student asked, however:</p> <blockquote> <p>Now that I ran type checking on my program, surely I don&rsquo;t need to include errors in my <span class="sidenote"> <label class="sidenote-label" for="valuation-function-note">valuation function!</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="valuation-function-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> I'm using "valuation function" here in the context of <a href="https://en.wikibooks.org/wiki/Haskell/Denotational_semantics">denotational semantics</a>. In short, a <a href="http://www.inf.ed.ac.uk/teaching/courses/inf2a/readings/semantics-note.pdf">valuation function</a> takes an expression and assigns to it some representation of its meaning. For a language of arithmetic expression, the "meaning" of an expression is just a number (the result of simplifying the expression). For a language of booleans, <code>and</code>, and <code>or</code>, the "meaning" is a boolean for the same reason. Since an expression in the language can be ill-formed (like <code>list(5)</code> in Python), the "meaning" (<em>semantic domain</em>) of a complicated language tends to include the possibility of errors. <span class="sidenote-delimiter">]</span> </span> </span> I should be able to make my function be of type <code>Expr -&gt; Val</code>, and not <code>Expr -&gt; Maybe Val</code>!</p> </blockquote> <p>Unfortunately, this is not quite true. It is true that if the student&rsquo;s type checking function is correct, then there will be no way for a type error to occur during the evaluation of an expression &ldquo;validated&rdquo; by said function. The issue is, though, that <strong>the type system does not know about the expression&rsquo;s type-correctness</strong>. Haskell doesn&rsquo;t know that an expression has been type checked; worse, since the function&rsquo;s type indicates that it accepts <code>Expr</code>, it must handle invalid expressions to avoid being <a href="https://wiki.haskell.org/Partial_functions"class="external-link">partial<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. In short, even if we <strong>know</strong> that the expressions we give to a function are type safe, we have no way of enforcing this.</p> <p>A potential solution offered in class was to separate the expressions into several data types, <code>BoolExpr</code>, <code>ArithExpr</code>, and finally, a more general <code>Expr'</code> that can be constructed from the first two. Operations such as <code>and</code> and <code>or</code> will then only be applicable to boolean expressions:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kr">data</span> <span class="kt">BoolExpr</span> <span class="ow">=</span> <span class="kt">BoolLit</span> <span class="kt">Bool</span> <span class="o">|</span> <span class="kt">And</span> <span class="kt">BoolExpr</span> <span class="kt">BoolExpr</span> <span class="o">|</span> <span class="kt">Or</span> <span class="kt">BoolExpr</span> <span class="kt">BoolExpr</span> </span></span></code></pre></div><p>It will be a type error to represent an expression such as <code>true or 5</code>. Then, <code>Expr'</code> may have a constructor such as <code>IfElse</code> that only accepts a boolean expression as the first argument:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kr">data</span> <span class="kt">Expr&#39;</span> <span class="ow">=</span> <span class="kt">IfElse</span> <span class="kt">BoolExpr</span> <span class="kt">Expr&#39;</span> <span class="kt">Expr&#39;</span> <span class="o">|</span> <span class="o">...</span> </span></span></code></pre></div><p>All seems well. Now, it&rsquo;s impossible to have a non-boolean condition, and thus, this error has been eliminated from the evaluator. Maybe we can even have our type checking function translate an unsafe, potentially incorrect <code>Expr</code> into a more safe <code>Expr'</code>:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">typecheck</span> <span class="ow">::</span> <span class="kt">Expr</span> <span class="ow">-&gt;</span> <span class="kt">Either</span> <span class="kt">TypeError</span> <span class="p">(</span><span class="kt">Expr&#39;</span><span class="p">,</span> <span class="kt">ExprType</span><span class="p">)</span> </span></span></code></pre></div><p>However, we typically also want the branches of an if-expression to both have the same type - <code>if x then 3 else False</code> may work sometimes, but not always, depending of the value of <code>x</code>. How do we encode this? Do we have two constructors, <code>IfElseBool</code> and <code>IfElseInt</code>, with one in <code>BoolExpr</code> and the other in <code>ArithExpr</code>? What if we add strings? We&rsquo;ll be copying functionality back and forth, and our code will suffer. Wouldn&rsquo;t it be nice if we could somehow tag our expressions with the type they produce? Instead of <code>BoolExpr</code> and <code>ArithExpr</code>, we would be able to have <code>Expr BoolType</code> and <code>Expr IntType</code>, which would share the <code>IfElse</code> constructor&hellip;</p> <p>It&rsquo;s not easy to do this in canonical Haskell, but it can be done in Idris!</p> <a href="#enter-dependent-types"> <h3 id="enter-dependent-types">Enter Dependent Types</h3> </a> <p>Idris is a language with support for <a href="https://en.wikipedia.org/wiki/Dependent_type"class="external-link">dependent types<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. Wikipedia gives the following definition for &ldquo;dependent type&rdquo;:</p> <blockquote> <p>In computer science and logic, a dependent type is a type whose definition depends on a value.</p> </blockquote> <p>This is exactly what we want. In Idris, we can define the possible set of types in our language:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-interpreter/TypesafeIntr.idr" data-first-line="1" data-last-line="4"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntr.idr#L1-L4">TypesafeIntr.idr</a>, lines 1 through 4</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="kr">data</span> <span class="kt">ExprType</span> </span></span><span class="line"><span class="cl"> <span class="ow">=</span> <span class="kt">IntType</span> </span></span><span class="line"><span class="cl"> <span class="ow">|</span> <span class="kt">BoolType</span> </span></span><span class="line"><span class="cl"> <span class="ow">|</span> <span class="kt">StringType</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Then, we can define a <code>SafeExpr</code> type family, which is indexed by <code>ExprType</code>. Here&rsquo;s the <span class="sidenote"> <label class="sidenote-label" for="gadt-note">code,</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="gadt-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> I should probably note that the definition of <code>SafeExpr</code> is that of a <a href="https://en.wikipedia.org/wiki/Generalized_algebraic_data_type">Generalized Algebraic Data Type</a>, or GADT for short. This is what allows each of our constructors to produce values of a different type: <code>IntLiteral</code> builds <code>SafeExpr IntType</code>, while <code>BoolLiteral</code> builds <code>SafeExpr BoolType</code>. <span class="sidenote-delimiter">]</span> </span> </span> which we will discuss below:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-interpreter/TypesafeIntr.idr" data-first-line="23" data-last-line="27"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntr.idr#L23-L27">TypesafeIntr.idr</a>, lines 23 through 27</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="kr">data</span> <span class="kt">SafeExpr</span> <span class="ow">:</span> <span class="kt">ExprType</span> <span class="ow">-&gt;</span> <span class="kt">Type</span> <span class="kr">where</span> </span></span><span class="line"><span class="cl"> <span class="nf">IntLiteral</span> <span class="ow">:</span> <span class="kt">Int</span> <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> <span class="kt">IntType</span> </span></span><span class="line"><span class="cl"> <span class="nf">BoolLiteral</span> <span class="ow">:</span> <span class="kt">Bool</span> <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> <span class="kt">BoolType</span> </span></span><span class="line"><span class="cl"> <span class="nf">StringLiteral</span> <span class="ow">:</span> <span class="kt">String</span> <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> <span class="kt">StringType</span> </span></span><span class="line"><span class="cl"> <span class="nf">BinOperation</span> <span class="ow">:</span> <span class="ow">(</span>repr a <span class="ow">-&gt;</span> repr b <span class="ow">-&gt;</span> repr c<span class="ow">)</span> <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> a <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> b <span class="ow">-&gt;</span> <span class="kt">SafeExpr</span> c</span></span></code></pre></td></tr></table> </div> </div> </div> <p>The first line of the above snippet says, &ldquo;<code>SafeExpr</code> is a type constructor that requires a value of type <code>ExprType</code>&rdquo;. For example, we can have <code>SafeExpr IntType</code>, or <code>SafeExpr BoolType</code>. Next, we have to define constructors for <code>SafeExpr</code>. One such constructor is <code>IntLiteral</code>, which takes a value of type <code>Int</code> (which represents the value of the integer literal), and builds a value of <code>SafeExpr IntType</code>, that is, an expression that <strong>we know evaluates to an integer</strong>.</p> <p>The same is the case for <code>BoolLiteral</code> and <code>StringLiteral</code>, only they build values of type <code>SafeExpr BoolType</code> and <code>SafeExpr StringType</code>, respectively.</p> <p>The more complicated case is that of <code>BinOperation</code>. Put simply, it takes a binary function of type <code>a-&gt;b-&gt;c</code> (kind of), two <code>SafeExpr</code>s producing <code>a</code> and <code>b</code>, and combines the values of those expressions using the function to generate a value of type <code>c</code>. Since the whole expression returns <code>c</code>, <code>BinOperation</code> builds a value of type <code>SafeExpr c</code>.</p> <p>That&rsquo;s almost it. Except, what&rsquo;s up with <code>repr</code>? We need it because <code>SafeExpr</code> is parameterized by a <strong>value</strong> of type <code>ExprType</code>. Thus, <code>a</code>, <code>b</code>, and <code>c</code> are all values in the definition of <code>BinOperation</code>. However, in a function <code>input-&gt;output</code>, both <code>input</code> and <code>output</code> have to be <strong>types</strong>, not values. Thus, we define a function <code>repr</code> which converts values such as <code>IntType</code> into the actual type that <code>eval</code> would yield when running our expression:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-interpreter/TypesafeIntr.idr" data-first-line="6" data-last-line="9"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntr.idr#L6-L9">TypesafeIntr.idr</a>, lines 6 through 9</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span><span class="lnt">9 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">repr</span> <span class="ow">:</span> <span class="kt">ExprType</span> <span class="ow">-&gt;</span> <span class="kt">Type</span> </span></span><span class="line"><span class="cl">repr <span class="kt">IntType</span> <span class="ow">=</span> <span class="kt">Int</span> </span></span><span class="line"><span class="cl">repr <span class="kt">BoolType</span> <span class="ow">=</span> <span class="kt">Bool</span> </span></span><span class="line"><span class="cl">repr <span class="kt">StringType</span> <span class="ow">=</span> <span class="kt">String</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The power of dependent types allows us to run <code>repr</code> inside the type of <code>BinOp</code> to compute the type of the function it must accept.</p> <p>Now, we have a way to represent expressions that are guaranteed to be type safe. With this, we can make our <code>typeheck</code> function convert an <code>Expr</code> to a <code>SafeExpr</code>. Wait a minute, though! We can&rsquo;t <em>just</em> return <code>SafeExpr</code>: it&rsquo;s a type constructor! We need to somehow return <code>SafeExpr a</code>, where <code>a</code> is a value of type <code>ExprType</code>. But it doesn&rsquo;t make sense for the return type to have a new type variable that didn&rsquo;t occur in the rest of the type signature. It would be ideal if we could return both the type of the expression, and a <code>SafeExpr</code> of that type.</p> <p>In fact, we can!</p> <p>Idris has something called <em>dependent pairs</em>, which are like normal pairs, but in which the type of the second element depends on the value of the first element. The canonical example of this is a pair of (list length, list of that many elements). For instance, in Idris, we can write:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">listPair</span> <span class="ow">:</span> <span class="ow">(</span>n <span class="ow">:</span> <span class="kt">Nat</span> <span class="ow">**</span> <span class="kt">Vec</span> n <span class="kt">Int</span><span class="ow">)</span> </span></span></code></pre></div><p>In the above snippet, we declare the type for a pair of a natural number (<code>n : Nat</code>) and a list of integers (<code>Vect n Int</code>), where the number of elements in the list is equal to the natural number. Let&rsquo;s try applying this to our problem. We want to return an <code>ExprType</code>, and a <code>SafeExpr</code> which depends on that <code>ExprType</code>. How about this:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-interpreter/TypesafeIntr.idr" data-first-line="36" data-last-line="36"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntr.idr#L36-L36">TypesafeIntr.idr</a>, line 36</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">36 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">typecheck</span> <span class="ow">:</span> <span class="kt">Expr</span> <span class="ow">-&gt;</span> <span class="kt">Either</span> <span class="kt">String</span> <span class="ow">(</span>n <span class="ow">:</span> <span class="kt">ExprType</span> <span class="ow">**</span> <span class="kt">SafeExpr</span> n<span class="ow">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Given an expression, we return either an error (<code>String</code>) or a dependent pair, which contains some <code>ExprType</code> <code>n</code> and a <code>SafeExpr</code> that evaluates to a value of type <code>n</code>. We can even start implementing this function, starting with literals:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-interpreter/TypesafeIntr.idr" data-first-line="37" data-last-line="39"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntr.idr#L37-L39">TypesafeIntr.idr</a>, lines 37 through 39</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl">typecheck <span class="ow">(</span><span class="kt">IntLit</span> i<span class="ow">)</span> <span class="ow">=</span> <span class="kt">Right</span> <span class="ow">(</span><span class="kr">_</span> <span class="ow">**</span> <span class="kt">IntLiteral</span> i<span class="ow">)</span> </span></span><span class="line"><span class="cl">typecheck <span class="ow">(</span><span class="kt">BoolLit</span> b<span class="ow">)</span> <span class="ow">=</span> <span class="kt">Right</span> <span class="ow">(</span><span class="kr">_</span> <span class="ow">**</span> <span class="kt">BoolLiteral</span> b<span class="ow">)</span> </span></span><span class="line"><span class="cl">typecheck <span class="ow">(</span><span class="kt">StringLit</span> s<span class="ow">)</span> <span class="ow">=</span> <span class="kt">Right</span> <span class="ow">(</span><span class="kr">_</span> <span class="ow">**</span> <span class="kt">StringLiteral</span> s<span class="ow">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Note the use of <code>_</code>. Since <code>IntLiteral</code> always produces <code>SafeExpr IntType</code>, we allow Idris to infer that <code>IntType</code> must be the first element of the tuple. This is easy enough, because a boolean, integer, or string literal can never be type-incorrect.</p> <p>The interesting case is that of a binary operation. Is <code>&quot;hello&quot; * 3</code> invalid? It might be, but some languages evaluate the multiplication of a string by a number as repeating the string that many times: <code>&quot;hellohellohello&quot;</code>. It is up to us, the language designers, to specify the set of valid operations. Furthermore, observe that <code>BinOperation</code> takes a <em>function</em> as its first argument, not an <code>Op</code>. To guarantee that we can, in fact, evaluate a <code>BinOperation</code> to the promised type, we require that the means of performing the evaluation is included in the expression. Thus, when we convert <code>Expr</code> to <code>SafeExpr</code>, we need to convert an <code>Op</code> to a corresponding function. As we can see with <code>&quot;hello&quot;*3</code> and <code>163*2</code>, an <code>Op</code> can correspond to a different function depending on the types of its inputs. To deal with this, we define a new function <code>typecheckOp</code>, which takes an <code>Op</code> and two expression types, and returns either an error (if the <code>Op</code> can&rsquo;t be applied to those types) or a dependent pair containing the output type of the operation and a function that performs the required computation. That&rsquo;s a mouthful; let&rsquo;s look at the code:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-interpreter/TypesafeIntr.idr" data-first-line="29" data-last-line="34"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntr.idr#L29-L34">TypesafeIntr.idr</a>, lines 29 through 34</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">typecheckOp</span> <span class="ow">:</span> <span class="kt">Op</span> <span class="ow">-&gt;</span> <span class="ow">(</span>a <span class="ow">:</span> <span class="kt">ExprType</span><span class="ow">)</span> <span class="ow">-&gt;</span> <span class="ow">(</span>b <span class="ow">:</span> <span class="kt">ExprType</span><span class="ow">)</span> <span class="ow">-&gt;</span> <span class="kt">Either</span> <span class="kt">String</span> <span class="ow">(</span>c <span class="ow">:</span> <span class="kt">ExprType</span> <span class="ow">**</span> repr a <span class="ow">-&gt;</span> repr b <span class="ow">-&gt;</span> repr c<span class="ow">)</span> </span></span><span class="line"><span class="cl">typecheckOp <span class="kt">Add</span> <span class="kt">IntType</span> <span class="kt">IntType</span> <span class="ow">=</span> <span class="kt">Right</span> <span class="ow">(</span><span class="kt">IntType</span> <span class="ow">**</span> <span class="ow">(+))</span> </span></span><span class="line"><span class="cl">typecheckOp <span class="kt">Subtract</span> <span class="kt">IntType</span> <span class="kt">IntType</span> <span class="ow">=</span> <span class="kt">Right</span> <span class="ow">(</span><span class="kt">IntType</span> <span class="ow">**</span> <span class="ow">(-))</span> </span></span><span class="line"><span class="cl">typecheckOp <span class="kt">Multiply</span> <span class="kt">IntType</span> <span class="kt">IntType</span> <span class="ow">=</span> <span class="kt">Right</span> <span class="ow">(</span><span class="kt">IntType</span> <span class="ow">**</span> <span class="ow">(*))</span> </span></span><span class="line"><span class="cl">typecheckOp <span class="kt">Divide</span> <span class="kt">IntType</span> <span class="kt">IntType</span> <span class="ow">=</span> <span class="kt">Right</span> <span class="ow">(</span><span class="kt">IntType</span> <span class="ow">**</span> div<span class="ow">)</span> </span></span><span class="line"><span class="cl">typecheckOp <span class="kr">_</span> <span class="kr">_</span> <span class="kr">_</span> <span class="ow">=</span> <span class="kt">Left</span> <span class="s">&#34;Invalid binary operator application&#34;</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>When <code>(+)</code> is applied to two integers, this is not an error, and the result is also an integer. To perform addition, we use Idris&rsquo; built-in function <code>(+)</code>. The same is true for all other arithmetic operations in this example. In all other cases, we simply return an error. We can now use <code>typecheckOp</code> in our <code>typecheck</code> function:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-interpreter/TypesafeIntr.idr" data-first-line="40" data-last-line="44"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntr.idr#L40-L44">TypesafeIntr.idr</a>, lines 40 through 44</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl">typecheck <span class="ow">(</span><span class="kt">BinOp</span> o l r<span class="ow">)</span> <span class="ow">=</span> <span class="kr">do</span> </span></span><span class="line"><span class="cl"> <span class="ow">(</span>lt <span class="ow">**</span> le<span class="ow">)</span> <span class="ow">&lt;-</span> typecheck l </span></span><span class="line"><span class="cl"> <span class="ow">(</span>rt <span class="ow">**</span> re<span class="ow">)</span> <span class="ow">&lt;-</span> typecheck r </span></span><span class="line"><span class="cl"> <span class="ow">(</span>ot <span class="ow">**</span> f<span class="ow">)</span> <span class="ow">&lt;-</span> typecheckOp o lt rt </span></span><span class="line"><span class="cl"> pure <span class="ow">(</span><span class="kr">_</span> <span class="ow">**</span> <span class="kt">BinOperation</span> f le re<span class="ow">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Here, we use do-notation to first type check first the left, then the right subexpression. Since the result of type checking the subexpressions gives us their output types, we can feed these types, together with <code>o</code>, to <code>typecheckOp</code> to determine the output type and the applicable evaluation function. Finally, we assemble the new <code>SafeExpr</code> from the function and the two translated subexpressions.</p> <p>Alright, we&rsquo;ve done all this work. Is it worth it? Let&rsquo;s try implementing <code>eval</code>:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-interpreter/TypesafeIntr.idr" data-first-line="46" data-last-line="50"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntr.idr#L46-L50">TypesafeIntr.idr</a>, lines 46 through 50</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">eval</span> <span class="ow">:</span> <span class="kt">SafeExpr</span> t <span class="ow">-&gt;</span> repr t </span></span><span class="line"><span class="cl">eval <span class="ow">(</span><span class="kt">IntLiteral</span> i<span class="ow">)</span> <span class="ow">=</span> i </span></span><span class="line"><span class="cl">eval <span class="ow">(</span><span class="kt">BoolLiteral</span> b<span class="ow">)</span> <span class="ow">=</span> b </span></span><span class="line"><span class="cl">eval <span class="ow">(</span><span class="kt">StringLiteral</span> s<span class="ow">)</span> <span class="ow">=</span> s </span></span><span class="line"><span class="cl">eval <span class="ow">(</span><span class="kt">BinOperation</span> f l r<span class="ow">)</span> <span class="ow">=</span> f <span class="ow">(</span>eval l<span class="ow">)</span> <span class="ow">(</span>eval r<span class="ow">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>That&rsquo;s it! No <code>Maybe</code>, no error cases. <code>eval</code> is completely total, but doesn&rsquo;t require error handling because it <strong>knows that the expression it is evaluating is type-correct</strong>!</p> <p>Let&rsquo;s run all of this. We&rsquo;ll need some code to print the result of evaluating an expression. Here&rsquo;s all that:</p> <div class="highlight-group" data-base-path="" data-file-path="typesafe-interpreter/TypesafeIntr.idr" data-first-line="52" data-last-line="64"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntr.idr#L52-L64">TypesafeIntr.idr</a>, lines 52 through 64</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="nf">resultStr</span> <span class="ow">:</span> <span class="ow">{</span>t <span class="ow">:</span> <span class="kt">ExprType</span><span class="ow">}</span> <span class="ow">-&gt;</span> repr t <span class="ow">-&gt;</span> <span class="kt">String</span> </span></span><span class="line"><span class="cl">resultStr <span class="ow">{</span>t<span class="ow">=</span><span class="kt">IntType</span><span class="ow">}</span> i <span class="ow">=</span> show i </span></span><span class="line"><span class="cl">resultStr <span class="ow">{</span>t<span class="ow">=</span><span class="kt">BoolType</span><span class="ow">}</span> b <span class="ow">=</span> show b </span></span><span class="line"><span class="cl">resultStr <span class="ow">{</span>t<span class="ow">=</span><span class="kt">StringType</span><span class="ow">}</span> s <span class="ow">=</span> show s </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="nf">tryEval</span> <span class="ow">:</span> <span class="kt">Expr</span> <span class="ow">-&gt;</span> <span class="kt">String</span> </span></span><span class="line"><span class="cl">tryEval ex <span class="ow">=</span> </span></span><span class="line"><span class="cl"> <span class="kr">case</span> typecheck ex <span class="kr">of</span> </span></span><span class="line"><span class="cl"> <span class="kt">Left</span> err <span class="ow">=&gt;</span> <span class="s">&#34;Type error: &#34;</span> <span class="ow">++</span> err </span></span><span class="line"><span class="cl"> <span class="kt">Right</span> <span class="ow">(</span>t <span class="ow">**</span> e<span class="ow">)</span> <span class="ow">=&gt;</span> resultStr <span class="ow">$</span> eval <span class="ow">{</span>t<span class="ow">}</span> e </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="nf">main</span> <span class="ow">:</span> <span class="kt">IO</span> <span class="ow">()</span> </span></span><span class="line"><span class="cl">main <span class="ow">=</span> putStrLn <span class="ow">$</span> tryEval <span class="ow">$</span> <span class="kt">BinOp</span> <span class="kt">Add</span> <span class="ow">(</span><span class="kt">IntLit</span> <span class="mi">6</span><span class="ow">)</span> <span class="ow">(</span><span class="kt">BinOp</span> <span class="kt">Multiply</span> <span class="ow">(</span><span class="kt">IntLit</span> <span class="mi">160</span><span class="ow">)</span> <span class="ow">(</span><span class="kt">IntLit</span> <span class="mi">2</span><span class="ow">))</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>And the output is:</p> <pre tabindex="0"><code>&gt;&gt;&gt; idris TypesafeIntr.idr -o typesafe &gt;&gt;&gt; ./typesafe 326 </code></pre><p>That&rsquo;s right! What about a type-incorrect example?</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Idris" data-lang="Idris"><span class="line"><span class="cl"><span class="kt">BinOp</span> <span class="kt">Add</span> <span class="ow">(</span><span class="kt">IntLit</span> <span class="mi">6</span><span class="ow">)</span> <span class="ow">(</span><span class="kt">BinOp</span> <span class="kt">Multiply</span> <span class="ow">(</span><span class="kt">IntLit</span> <span class="mi">160</span><span class="ow">)</span> <span class="ow">(</span><span class="kt">StringLit</span> <span class="s">&#34;hi&#34;</span><span class="ow">))</span> </span></span></code></pre></div><p>The program reports:</p> <pre tabindex="0"><code>Type error: Invalid binary operator application </code></pre><p>Awesome!</p> <a href="#wrapping-up"> <h3 id="wrapping-up">Wrapping Up</h3> </a> <p>In this post, we learned that type checking can be used to translate an expression into a more strongly-typed data type, which can be (more) safe to evaluate. To help strengthen the types of <span class="sidenote"> <label class="sidenote-label" for="if-else-note">our expression language,</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="if-else-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> You may be thinking, "but where did the if-expressions go?". It turns out that making sure that the branches of an if-expression are of the same type is actually a fairly difficult task; the best way I found was enumerating all the possible "valid" combinations of types in a case-expression. Since this is obviously not the right solution, I decided to publish what I have, and look for an alternative. If I find a better solution, I will write a follow-up post. <span class="sidenote-delimiter">]</span> </span> </span> we used the Idris language and its support for dependent types and Generalized Algebraic Data Types (GADTs). I hope this was interesting!</p> <p>As usual, you can find the code for this post in this website&rsquo;s Git repository. The source file we went through today is found <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/typesafe-interpreter/TypesafeIntr.idr"class="external-link">here<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>.</p> Building a Basic Crystal Project with Nix https://danilafe.com/blog/crystal_nix/ Sun, 16 Feb 2020 14:31:42 -0800 https://danilafe.com/blog/crystal_nix/ <p>I really like the idea of Nix: you can have reproducible builds, written more or less declaratively. I also really like the programming language <a href="https://crystal-lang.org/"class="external-link">Crystal<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, which is a compiled Ruby derivative. Recently, I decided to try learn NixOS as a package author, and decided to make a Crystal project of mine, <a href="https://github.com/DanilaFe/pegasus"class="external-link">pegasus<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, my guinea pig. In this post, I will document my experience setting up Nix with Crystal.</p> <a href="#getting-started"> <h3 id="getting-started">Getting Started</h3> </a> <p>Pegasus is a rather simple package in terms of the build process - it has no dependencies, and can be built with nothing but a Crystal compiler. Thus, I didn&rsquo;t have to worry about dependencies. However, the <code>nixpkgs</code> repository does have a way to specify build dependencies for a Nix project: <a href="https://github.com/NixOS/nixpkgs/blob/master/pkgs/development/compilers/crystal/crystal2nix.nix"class="external-link"><code>crystal2nix</code><svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>.</p> <p><code>crystal2nix</code> is another Nix package, which consists of a single Crystal binary program of the same name. It translates a <code>shards.lock</code> file, generated by Crystal&rsquo;s <code>shards</code> package manager, into a <code>shards.nix</code> file, which allows Nix to properly build the dependencies of a Crystal package. If you have a project with a <code>shards.lock</code> file, you can use <code>shards2nix</code> inside a <code>nix-shell</code> as follows:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">nix-shell -p crystal2nix --run crystal2nix </span></span></code></pre></div><p>The above command says, create an environment with the <code>crystal2nix</code> package, and run the program. Note that you should run this <a href="https://github.com/NixOS/nixpkgs/blob/21bfc57dd9eb5c7c58b6ab0bfa707cbc7cf04e98/pkgs/development/compilers/crystal/build-package.nix#L2"class="external-link">inside the project&rsquo;s root<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. Also note that if you don&rsquo;t depend on other Crystal packages, you will not have a <code>shards.lock</code>, and running <code>crystal2nix</code> is unnecessary.</p> <p>The Crystal folder in the <code>nixpkgs</code> repository contains one more handy utility: <code>buildCrystalPackage</code>. This is a function exported by the <code>crystal</code> Nix package, which significantly simplifies the process of building a Crystal binary package. We can look to <code>crystal2nix.nix</code> (linked above) for a concrete example. We can observe the following attributes:</p> <ul> <li><code>pname</code> - the name of the package.</li> <li><code>version</code> - the <span class="sidenote"> <label class="sidenote-label" for="version-note">version</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="version-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> In my example code, I set the Nix package version to the commit hash. Doing this alone is probably not the best idea, since it will prevent version numbers from being ordered. However, version <code>0.1.0</code> didn't make sense either, since the project technically doesn't have a release yet. You should set this to an actual package version if you have one. <span class="sidenote-delimiter">]</span> </span> </span> of the package, as usual.</li> <li><code>crystalBinaries.&lt;xxx&gt;.src</code> - the source Crystal file for binary <code>xxx</code>.</li> </ul> <p>Using these attributes, I concocted the following expression for pegasus and all of its included programs:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="cl"><span class="p">{</span> <span class="n">stdenv</span><span class="o">,</span> <span class="n">crystal</span><span class="o">,</span> <span class="n">fetchFromGitHub</span> <span class="p">}:</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">let</span> </span></span><span class="line"><span class="cl"> <span class="n">version</span> <span class="o">=</span> <span class="s2">&#34;0489d47b191ecf8501787355b948801506e7c70f&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">src</span> <span class="o">=</span> <span class="n">fetchFromGitHub</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">owner</span> <span class="o">=</span> <span class="s2">&#34;DanilaFe&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">repo</span> <span class="o">=</span> <span class="s2">&#34;pegasus&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">rev</span> <span class="o">=</span> <span class="n">version</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">sha256</span> <span class="o">=</span> <span class="s2">&#34;097m7l16byis07xlg97wn5hdsz9k6c3h1ybzd2i7xhkj24kx230s&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span> </span></span><span class="line"><span class="cl"><span class="k">in</span> </span></span><span class="line"><span class="cl"> <span class="n">crystal</span><span class="o">.</span><span class="n">buildCrystalPackage</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">pname</span> <span class="o">=</span> <span class="s2">&#34;pegasus&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">inherit</span> <span class="n">version</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">inherit</span> <span class="n">src</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">crystalBinaries</span><span class="o">.</span><span class="n">pegasus</span><span class="o">.</span><span class="n">src</span> <span class="o">=</span> <span class="s2">&#34;src/pegasus.cr&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">crystalBinaries</span><span class="o">.</span><span class="n">pegasus-dot</span><span class="o">.</span><span class="n">src</span> <span class="o">=</span> <span class="s2">&#34;src/tools/dot/pegasus_dot.cr&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">crystalBinaries</span><span class="o">.</span><span class="n">pegasus-sim</span><span class="o">.</span><span class="n">src</span> <span class="o">=</span> <span class="s2">&#34;src/tools/sim/pegasus_sim.cr&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">crystalBinaries</span><span class="o">.</span><span class="n">pegasus-c</span><span class="o">.</span><span class="n">src</span> <span class="o">=</span> <span class="s2">&#34;src/generators/c/pegasus_c.cr&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">crystalBinaries</span><span class="o">.</span><span class="n">pegasus-csem</span><span class="o">.</span><span class="n">src</span> <span class="o">=</span> <span class="s2">&#34;src/generators/csem/pegasus_csem.cr&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">crystalBinaries</span><span class="o">.</span><span class="n">pegasus-crystal</span><span class="o">.</span><span class="n">src</span> <span class="o">=</span> <span class="s2">&#34;src/generators/crystal/pegasus_crystal.cr&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">crystalBinaries</span><span class="o">.</span><span class="n">pegasus-crystalsem</span><span class="o">.</span><span class="n">src</span> <span class="o">=</span> <span class="s2">&#34;src/generators/crystalsem/pegasus_crystalsem.cr&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span></code></pre></div><p>Here, I used Nix&rsquo;s <code>fetchFromGitHub</code> helper function. It clones a Git repository from <code>https://github.com/&lt;owner&gt;/&lt;repo&gt;</code>, checks out the <code>rev</code> commit or branch, and makes sure that it matches the <code>sha256</code> hash. The hash check is required so that Nix can maintain the reproducibility of the build: if the commit is changed, the code to compile may not be the same, and thus, the package would be different. The hash helps detect such changes. To generate the hash, I used <code>nix-prefetch-git</code>, which tries to clone the repository and compute its hash.</p> <p>In the case that your project has a <code>shards.nix</code> file generated as above, you will also need to add the following line inside your <code>buildCrystalPackage</code> call:</p> <pre tabindex="0"><code>shardsFile = ./shards.nix; </code></pre><p>The <code>shards.nix</code> file will contain all the dependency Git repositories, and the <code>shardsFile</code> attribute will forward this list to <code>buildCrystalPackage</code>, which will handle their inclusion in the package build.</p> <p>That&rsquo;s pretty much it! The <code>buildCrystalPackage</code> Nix function does most of the heavy lifting for Crystal binary packages. Please also check out <a href="https://edef.eu/~qyliss/nixlib/file/nixpkgs/doc/languages-frameworks/crystal.section.md.html"class="external-link">this web page<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>: I found out from it that <code>pname</code> had to be used instead of <code>name</code>, and it also has some information regarding additional compiler options and build inputs.</p> <a href="#appendix-a-small-caveat"> <h3 id="appendix-a-small-caveat">Appendix: A Small Caveat</h3> </a> <p>I was running the <code>crystal2nix</code> (and doing all of my Nix-related work) in a NixOS virtual machine. However, my version of NixOS was somewhat out of date (<code>19.04</code>), and I could not retrieve <code>crystal2nix</code>. I had to switch channels to <code>nixos-19.09</code>, which is the current stable version of NixOS.</p> <p>There was one more difficulty involved in <a href="https://nixos.wiki/wiki/Nix_channels"class="external-link">switching channels<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>: I had to do it as root. It so happens that if you add a channel as non-root user, your system will still use the channel specified by root, and thus, you will experience the update. You can spot this issue in the output of <code>nix-env -u</code>; it will complain of duplicate packages.</p> Compiling a Functional Language Using C++, Part 9 - Garbage Collection https://danilafe.com/blog/09_compiler_garbage_collection/ Mon, 10 Feb 2020 19:22:41 -0800 https://danilafe.com/blog/09_compiler_garbage_collection/ <blockquote> <p>&ldquo;When will you learn? When will you learn that <strong>your actions have consequences?</strong>&rdquo;</p> </blockquote> <p>So far, we&rsquo;ve entirely ignored the problem of memory management. Every time that we need a new node for our growing graph, we simply ask for more memory from the runtime with <code>malloc</code>. But selfishly, even when we no longer require the memory allocated for a particular node, when that node is no longer in use, we do not <code>free</code> it. In fact, our runtime currently has no idea about which nodes are needed and which ones are ready to be discarded.</p> <p>To convince ourselves that this is a problem, let&rsquo;s first assess the extent of the damage. Consider the program from <code>works3.txt</code>:</p> <div class="highlight-group" > <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/09/examples/works3.txt">works3.txt</a>, entire file</div><pre><code>data List = { Nil, Cons Int List } defn length l = { case l of { Nil -&gt; { 0 } Cons x xs -&gt; { 1 + length xs } } } defn main = { length (Cons 1 (Cons 2 (Cons 3 Nil))) } </code></pre> </div> <p>Compiling and running this program through <code>valgrind</code>, we get the following output:</p> <pre tabindex="0"><code>==XXXX== LEAK SUMMARY: ==XXXX== definitely lost: 288 bytes in 12 blocks ==XXXX== indirectly lost: 768 bytes in 34 blocks ==XXXX== possibly lost: 0 bytes in 0 blocks ==XXXX== still reachable: 0 bytes in 0 blocks ==XXXX== suppressed: 0 bytes in 0 blocks </code></pre><p>We lost 1056 bytes of memory, just to return the length of a list with 3 elements. The problem of leaking memory is very real.</p> <p>How do we solve this issue? We can&rsquo;t embed memory management into our language; We want to keep it pure, and managing memory is typically pretty far from that goal. Instead, we will make our runtime do the work of freeing memory. Even then, this is a nontrivial goal: our runtime manipulates graphs, each of which can be combined with others in arbitrary ways. In general, there will not always be a <em>single</em> node that, when freed, will guarantee that another node can be freed as well. Instead, it&rsquo;s very possible in our graphs that two parent nodes both refer to a third, and only when both parents are freed can we free that third node itself. Consider, for instance, the function <code>square</code> as follows:</p> <pre tabindex="0"><code>defn square x = { x * x } </code></pre><p>This function will receive, on top of the stack, a single graph representing <code>x</code>. It will then create two applications of a global <code>(+)</code> function, each time to the graph of <code>x</code>. Thus, it will construct a tree with two <code>App</code> nodes, both of which <span class="sidenote"> <label class="sidenote-label" for="lazy-note">must keep track of a reference to x.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="lazy-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> We later take advantage of this, by replacing the graph of <code>x</code> with the result of evaluating it. Since both <code>App</code> nodes point to the same graph, when we evaluate it once, each node observes this update, and is not required to evaluate <code>x</code> again. With this, we achieve lazy evaluation. <span class="sidenote-delimiter">]</span> </span> </span> The runtime will have to wait until both <code>App</code> nodes are freed before it can free the graph of <code>x</code>.</p> <p>This seems simple enough! If there are multiple things that may reference a node in the graph, why don&rsquo;t we just keep track of how many there are? Once we know that no more things are still referencing a node, we can free it. This is called <a href="https://en.wikipedia.org/wiki/Reference_counting"class="external-link">reference counting<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. Reference counting is a valid technique, but unfortunately, it will not suit us. The reason for this is that our language may produce <a href="https://en.wikipedia.org/wiki/Cycle_%28graph_theory%29"class="external-link">cyclic graphs<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. Consider, for example, this definition of an infinite list of the number 1:</p> <pre tabindex="0"><code>defn ones = { Cons 1 ones } </code></pre><p>Envisioning the graph of the tree, we can see <code>ones</code> as an application of the constructor <code>Cons</code> to two arguments, one of which is <code>ones</code> again. <span class="sidenote"> <label class="sidenote-label" for="recursive-note">It refers to itself!</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="recursive-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Things are actually more complicated than this. In our current language, recursive definitions are only possible in function definitions (like <code>ones</code>). In our runtime, each time there is a reference to a function, this is done through a <em>new node</em>, which means that functions with recursive definitions are <em>not</em> represented cyclically. Therefore, reference counting would work. However, in the future, our language will have more ways of creating circular definitions, some of which will indeed create cycles in our graphs. So, to prepare for this, we will avoid the use of reference counting. <span class="sidenote-delimiter">]</span> </span> </span> In this case, when we compute the number of nodes that require <code>ones</code>, we will always find the number to be at least 1: <code>ones</code> needs <code>ones</code>, which needs <code>ones</code>, and so on. It will not be possible for us to free <code>ones</code>, then, by simply counting the number of references to it.</p> <p>There&rsquo;s a more powerful technique than reference counting for freeing unused memory: <strong>mark-and-sweep garbage collection</strong>. This technique is conceptually pretty simple to grasp, yet will allow us to handle cycles in our graphs. Unsurprisingly, we implement this type of garbage collection in two stages:</p> <ol> <li><strong>Mark</strong>: We go through every node that is still needed by the runtime, and recursively mark it, its children, and so on as &ldquo;to keep&rdquo;.</li> <li><strong>Sweep</strong>: We go through every node we haven&rsquo;t yet freed, and, if it hasn&rsquo;t been marked as &ldquo;to keep&rdquo;, we free it.</li> </ol> <p>This also seems simple enough. There are two main things for us to figure out:</p> <ol> <li>For <strong>Mark</strong>, what are the &ldquo;nodes still needed by the runtime&rdquo;? These are just the nodes on the various G-machine stacks. If a node is not on the stack, nor is it a child of a node that is on the stack, why should we keep it around?</li> <li>For <strong>Sweep</strong>, how do we keep track of all the nodes we haven&rsquo;t yet freed? In our case, the solution is a global list of allocated nodes, which is updated every time that a node is allocated.</li> </ol> <p>Wait a minute, though. Inside of <code>unwind</code> in C, we only have a reference to the most recent stack. Our execution model allows for an arbitrary number of stacks: we can keep using <code>Eval</code>, placing the current stack on the dump, and starting a new stack from scratch to evaluate a node. How can we traverse these stacks from inside unwind? One solution could be to have each stack point to the &ldquo;parent&rdquo; stack. To find all the nodes on the stack, then, we&rsquo;d start with the current stack, mark all the nodes on it as &ldquo;required&rdquo;, then move on to the parent stack, rinse and repeat. This is plausible and pretty simple, but there&rsquo;s another way.</p> <p>We clean up after ourselves.</p> <a href="#towards-a-cleaner-stack"> <h3 id="towards-a-cleaner-stack">Towards a Cleaner Stack</h3> </a> <p>The G-machine compilation rules Simon Peyton Jones presents are written in a particular way. Every time that a function is called, all it leaves behind on the stack is the graph node that represents the function&rsquo;s output. Our own internal functions, however, have been less careful. Consider, for instance, the &ldquo;binary operator&rdquo; function I showed you. Its body is given by the following G-machine instructions:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">instructions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_push</span><span class="p">(</span><span class="mi">1</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"><span class="n">instructions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_eval</span><span class="p">()));</span> </span></span><span class="line"><span class="cl"><span class="n">instructions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_push</span><span class="p">(</span><span class="mi">1</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"><span class="n">instructions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_eval</span><span class="p">()));</span> </span></span><span class="line"><span class="cl"><span class="n">instructions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_binop</span><span class="p">(</span><span class="n">op</span><span class="p">)));</span> </span></span></code></pre></div><p>When the function is called, there are at least 3 things on the stack:</p> <ol> <li>The &ldquo;outermost&rdquo; application node, to be replaced with an indirection (to enable laziness).</li> <li>The second argument to the binary operator.</li> <li>The first argument to the binary operator.</li> </ol> <p>Then, <strong>Push</strong> adds another node to the stack, an <strong>Eval</strong> forces its evaluation (and leaves it on the stack). This happens again with the second argument. Finally, we call <strong>BinOp</strong>, popping two values off the stack and combining them according to the binary operator. This leaves the stack with 4 things: the 3 I described above, and thew newly computed value. This is fine as far as <code>eval</code> is concerned: its implementation only asks for the top value on the stack after <code>unwind</code> finishes. But for anything more complicated, this is a very bad side effect. We want to leave the stack as clean as we found it - with one node and no garbage.</p> <p>Fortunately, the way we compile functions is a good guide for how we should compile internal operators and constructors. The idea is captured by the two instructions we insert at the end of a user-defined function:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/09/definition.cpp" data-first-line="56" data-last-line="57"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/09/definition.cpp#L56-L57">definition.cpp</a>, lines 56 through 57</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">56 </span><span class="lnt">57 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="n">instructions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_update</span><span class="p">(</span><span class="n">params</span><span class="p">.</span><span class="n">size</span><span class="p">())));</span> </span></span><span class="line"><span class="cl"> <span class="n">instructions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_pop</span><span class="p">(</span><span class="n">params</span><span class="p">.</span><span class="n">size</span><span class="p">())));</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Once a result is computed, we turn the node that represented the application into an indirection, and point it to the computed result (as I said before, this enables lazy evaluation). We also pop the arguments given to the function off the stack. Let&rsquo;s add these two things to the <code>gen_llvm_internal_op</code> function:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/09/main.cpp" data-first-line="70" data-last-line="85"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/09/main.cpp#L70-L85">main.cpp</a>, lines 70 through 85</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">70 </span><span class="lnt">71 </span><span class="lnt">72 </span><span class="lnt">73 </span><span class="lnt">74 </span><span class="lnt">75 </span><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span><span class="lnt">79 </span><span class="lnt">80 </span><span class="lnt">81 </span><span class="lnt">82 </span><span class="lnt">83 </span><span class="lnt">84 </span><span class="lnt">85 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">gen_llvm_internal_op</span><span class="p">(</span><span class="n">llvm_context</span><span class="o">&amp;</span> <span class="n">ctx</span><span class="p">,</span> <span class="n">binop</span> <span class="n">op</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">new_function</span> <span class="o">=</span> <span class="n">ctx</span><span class="p">.</span><span class="n">create_custom_function</span><span class="p">(</span><span class="n">op_action</span><span class="p">(</span><span class="n">op</span><span class="p">),</span> <span class="mi">2</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">instruction_ptr</span><span class="o">&gt;</span> <span class="n">instructions</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">instructions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_push</span><span class="p">(</span><span class="mi">1</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"> <span class="n">instructions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_eval</span><span class="p">()));</span> </span></span><span class="line"><span class="cl"> <span class="n">instructions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_push</span><span class="p">(</span><span class="mi">1</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"> <span class="n">instructions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_eval</span><span class="p">()));</span> </span></span><span class="line"><span class="cl"> <span class="n">instructions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_binop</span><span class="p">(</span><span class="n">op</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"> <span class="n">instructions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_update</span><span class="p">(</span><span class="mi">2</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"> <span class="n">instructions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_pop</span><span class="p">(</span><span class="mi">2</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">builder</span><span class="p">.</span><span class="n">SetInsertPoint</span><span class="p">(</span><span class="o">&amp;</span><span class="n">new_function</span><span class="o">-&gt;</span><span class="n">getEntryBlock</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">instruction</span> <span class="p">:</span> <span class="n">instructions</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">instruction</span><span class="o">-&gt;</span><span class="n">gen_llvm</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">new_function</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">builder</span><span class="p">.</span><span class="n">CreateRetVoid</span><span class="p">();</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Notice, in particular, the <code>instruction_update(2)</code> and <code>instruction_pop(2)</code> instructions that were recently added. A similar thing has to be done for data type constructors. The difference, though, is that <strong>Pack</strong> removes the data it packs from the stack, and thus, <strong>Pop</strong> is not needed:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/09/definition.cpp" data-first-line="102" data-last-line="117"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/09/definition.cpp#L102-L117">definition.cpp</a>, lines 102 through 117</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">102 </span><span class="lnt">103 </span><span class="lnt">104 </span><span class="lnt">105 </span><span class="lnt">106 </span><span class="lnt">107 </span><span class="lnt">108 </span><span class="lnt">109 </span><span class="lnt">110 </span><span class="lnt">111 </span><span class="lnt">112 </span><span class="lnt">113 </span><span class="lnt">114 </span><span class="lnt">115 </span><span class="lnt">116 </span><span class="lnt">117 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">definition_data</span><span class="o">::</span><span class="n">gen_llvm_first</span><span class="p">(</span><span class="n">llvm_context</span><span class="o">&amp;</span> <span class="n">ctx</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">constructor</span> <span class="p">:</span> <span class="n">constructors</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">new_function</span> <span class="o">=</span> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">create_custom_function</span><span class="p">(</span><span class="n">constructor</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">,</span> <span class="n">constructor</span><span class="o">-&gt;</span><span class="n">types</span><span class="p">.</span><span class="n">size</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">instruction_ptr</span><span class="o">&gt;</span> <span class="n">instructions</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">instructions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="k">new</span> <span class="n">instruction_pack</span><span class="p">(</span><span class="n">constructor</span><span class="o">-&gt;</span><span class="n">tag</span><span class="p">,</span> <span class="n">constructor</span><span class="o">-&gt;</span><span class="n">types</span><span class="p">.</span><span class="n">size</span><span class="p">())</span> </span></span><span class="line"><span class="cl"> <span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="n">instructions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_update</span><span class="p">(</span><span class="mi">0</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">builder</span><span class="p">.</span><span class="n">SetInsertPoint</span><span class="p">(</span><span class="o">&amp;</span><span class="n">new_function</span><span class="o">-&gt;</span><span class="n">getEntryBlock</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">instruction</span> <span class="p">:</span> <span class="n">instructions</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">instruction</span><span class="o">-&gt;</span><span class="n">gen_llvm</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">new_function</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">builder</span><span class="p">.</span><span class="n">CreateRetVoid</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>With this done, let&rsquo;s run a quick test: let&rsquo;s print the number of things on the stack at the end of an <code>eval</code> call (before the stack is freed, of course). We can compare the output of runtime without the fix (<code>old</code>) and with the fix (<code>current</code>):</p> <pre tabindex="0"><code> current old Current stack size is 0 | Current stack size: 1 Current stack size is 0 | Current stack size: 1 Current stack size is 0 | Current stack size: 1 Current stack size is 0 | Current stack size: 1 Current stack size is 0 | Current stack size: 0 Current stack size is 0 | Current stack size: 0 Current stack size is 0 | Current stack size: 3 Current stack size is 0 | Current stack size: 0 Current stack size is 0 | Current stack size: 3 Current stack size is 0 | Current stack size: 0 Current stack size is 0 | Current stack size: 3 Result: 3 | Result: 3 </code></pre><p>The stack is now much cleaner! Every time <code>eval</code> is called, it starts with one node, and ends with one node (which is then popped).</p> <a href="#one-stack-to-rule-them-all"> <h3 id="one-stack-to-rule-them-all">One Stack to Rule Them All</h3> </a> <p>Wait a minute. If the stack is really always empty at the end, do we really need to construct a new stack every time? <span class="sidenote"> <label class="sidenote-label" for="arity-note">I think not</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="arity-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> There's some nuance to this. While it is true that for the most part, we can get rid of the new stacks in favor of a single one, our runtime will experience a change. The change lies in the Unwind-Global rule, which <em>requires that the stack has as many children as the function needs arguments</em>. Until now, there was no way for this condition to be accidentally satisfied: the function we were unwinding was the only thing on the stack. Now, though, things are different: the function being unwound may share a stack with something else, and just checking the stack size will not be sufficient. <em>I believe</em> that this is not a problem for us, since the compiler will only emit <strong>Eval</strong> instructions for things it knows are data types or numbers, meaning their type is not a partially applied function that is missing arguments. However, this is a nontrivial observation. <span class="sidenote-delimiter">]</span> </span> </span> , and Simon Peyton Jones seems to agree. In <em>Implementing Functional Languages: a tutorial</em>, he mentions that the dump does not need to be implemented as a real stack of stacks. So let&rsquo;s try this out: instead of starting a new stack using <code>eval</code>, let&rsquo;s use an existing one, by just calling <code>unwind</code> again. To do so, all we have to do is change our <code>instruction_eval</code> instruction. When the G-machine wants something evaluated now, it should just call <code>unwind</code> directly!</p> <p>To make this change, we have to make <code>unwind</code> available to the compiler. We thus declare it in the <code>llvm_context.cpp</code> file:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/09/llvm_context.cpp" data-first-line="158" data-last-line="163"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/09/llvm_context.cpp#L158-L163">llvm_context.cpp</a>, lines 158 through 163</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">158 </span><span class="lnt">159 </span><span class="lnt">160 </span><span class="lnt">161 </span><span class="lnt">162 </span><span class="lnt">163 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="n">functions</span><span class="p">[</span><span class="s">&#34;unwind&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="n">Function</span><span class="o">::</span><span class="n">Create</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="n">FunctionType</span><span class="o">::</span><span class="n">get</span><span class="p">(</span><span class="n">void_type</span><span class="p">,</span> <span class="p">{</span> <span class="n">gmachine_ptr_type</span> <span class="p">},</span> <span class="nb">false</span><span class="p">),</span> </span></span><span class="line"><span class="cl"> <span class="n">Function</span><span class="o">::</span><span class="n">LinkageTypes</span><span class="o">::</span><span class="n">ExternalLinkage</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;unwind&#34;</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="o">&amp;</span><span class="n">module</span> </span></span><span class="line"><span class="cl"> <span class="p">);</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>And even create a function to construct a call to <code>unwind</code> with the following signature:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/09/llvm_context.hpp" data-first-line="58" data-last-line="58"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/09/llvm_context.hpp#L58-L58">llvm_context.hpp</a>, line 58</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">58 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">create_unwind</span><span class="p">(</span><span class="n">llvm</span><span class="o">::</span><span class="n">Function</span><span class="o">*</span><span class="p">);</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We implement it like so:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/09/llvm_context.cpp" data-first-line="217" data-last-line="220"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/09/llvm_context.cpp#L217-L220">llvm_context.cpp</a>, lines 217 through 220</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">217 </span><span class="lnt">218 </span><span class="lnt">219 </span><span class="lnt">220 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">llvm_context</span><span class="o">::</span><span class="n">create_unwind</span><span class="p">(</span><span class="n">Function</span><span class="o">*</span> <span class="n">f</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">unwind_f</span> <span class="o">=</span> <span class="n">functions</span><span class="p">.</span><span class="n">at</span><span class="p">(</span><span class="s">&#34;unwind&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">builder</span><span class="p">.</span><span class="n">CreateCall</span><span class="p">(</span><span class="n">unwind_f</span><span class="p">,</span> <span class="p">{</span> <span class="n">f</span><span class="o">-&gt;</span><span class="n">args</span><span class="p">().</span><span class="n">begin</span><span class="p">()</span> <span class="p">});</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Finally, the <code>instruction_eval::gen_llvm</code> method simply calls <code>unwind</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/09/instruction.cpp" data-first-line="157" data-last-line="159"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/09/instruction.cpp#L157-L159">instruction.cpp</a>, lines 157 through 159</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">157 </span><span class="lnt">158 </span><span class="lnt">159 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">instruction_eval</span><span class="o">::</span><span class="n">gen_llvm</span><span class="p">(</span><span class="n">llvm_context</span><span class="o">&amp;</span> <span class="n">ctx</span><span class="p">,</span> <span class="n">Function</span><span class="o">*</span> <span class="n">f</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">create_unwind</span><span class="p">(</span><span class="n">f</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>After this change, we only call <code>eval</code> from <code>main</code>. Furthermore, since <code>eval</code> releases all the resources it allocates before returning, we won&rsquo;t be able to <span class="sidenote"> <label class="sidenote-label" for="retrieve-note">easily retrieve</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="retrieve-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> We were able to do this before, but that's because our runtime didn't free the nodes, <em>ever</em>. Now that it does, returning a node violates that node's lifetime. <span class="sidenote-delimiter">]</span> </span> </span> the result of the evaluation from it. Thus, we simply merge <code>eval</code> with <code>main</code> - combining the printing and the initialization / freeing code.</p> <p>With this, only one stack will be allocated for the entirety of program execution. This doesn&rsquo;t just help us save on memory allocations, but also <strong>solves the problem of marking valid nodes during garbage collection</strong>! Instead of traversing a dump of stacks, we can now simply traverse a single stack; all that we need is in one place.</p> <p>So this takes care, more or less, of the &ldquo;mark&rdquo; portion of mark-and-sweep. Using the stack, we can recursively mark the nodes that we need. But what about &ldquo;sweeping&rdquo;? How can we possibly know of every node that we&rsquo;ve allocated? There&rsquo;s some more bookkeping for us to do.</p> <a href="#its-all-connected"> <h3 id="its-all-connected">It&rsquo;s All Connected</h3> </a> <p>There exists a simple technique I&rsquo;ve previously seen (and used) for keeping track of all the allocated memory. The technique is to <strong>turn all the allocated nodes into elements of a linked list</strong>. The general process of implementing this proceeds as follows:</p> <ol> <li>To each node, add a &ldquo;next&rdquo; pointer.</li> <li>Keep a handle to the whole node chain somewhere.</li> <li>Add each newly allocated node to the front of the whole chain.</li> </ol> <p>This &ldquo;somewhere&rdquo; could be a global variable. However, since we already pass a stack to almost all of our functions, it makes more sense to make the list handle a part of some data structure that will also contain the stack, and pass that around, instead. This keeps all of the G-machine data in one place, and in principle could allow for concurrent execution of more than one G-machine in a single program. Let&rsquo;s call our new data structure <code>gmachine</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/09/runtime.h" data-first-line="69" data-last-line="74"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/09/runtime.h#L69-L74">runtime.h</a>, lines 69 through 74</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">69 </span><span class="lnt">70 </span><span class="lnt">71 </span><span class="lnt">72 </span><span class="lnt">73 </span><span class="lnt">74 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">gmachine</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="nc">stack</span> <span class="n">stack</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="nc">node_base</span><span class="o">*</span> <span class="n">gc_nodes</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int64_t</span> <span class="n">gc_node_count</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int64_t</span> <span class="n">gc_node_threshold</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Here, the <code>stack</code> field holds the G-machine stack, and the <code>gc_nodes</code> is the handle to the list of all the nodes we&rsquo;ve allocated and not yet freed. Don&rsquo;t worry about the <code>gc_node_count</code> and <code>gc_threshold</code> fields - we&rsquo;ll get to them a little later.</p> <p>This is going to be a significant change. First of all, since the handle won&rsquo;t be global, it can&rsquo;t be accessed from inside the <code>alloc_*</code> functions. Instead, we have to make sure to add nodes allocated through <code>alloc_*</code> to a G-machine somewhere wherever we call the allocators. To make it easier to add nodes to a G-machine GC handle, let&rsquo;s make a new function, <code>track</code>:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span> <span class="nf">gmachine_track</span><span class="p">(</span><span class="k">struct</span> <span class="n">gmachine</span><span class="o">*</span><span class="p">,</span> <span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span><span class="p">);</span> </span></span></code></pre></div><p>This function will add the given node to the G-machine&rsquo;s handle, and return that same node. This way, we can wrap nodes in a call to <code>gmachine_track</code>. We will talk about this function&rsquo;s implementation later in the post.</p> <p>This doesn&rsquo;t get us all the way to a working runtime, though: right now, we still pass around <code>struct stack*</code> instead of <code>struct gmachine*</code> everywhere. However, the whole point of adding the <code>gmachine</code> struct was to store more data in it! Surely we need that new data somewhere, and thus, we need to use the <code>gmachine</code> struct for <em>some</em> functions. What functions <em>do</em> need a whole <code>gmachine*</code>, and which ones only need a <code>stack*</code>?</p> <ol> <li><span class="sidenote"> <label class="sidenote-label" for="ownership-note">Clearly,</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="ownership-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> This might not be clear. Maybe <em>pushing</em> onto a stack will add a node to our GC handle, and so, we need to have access to the handle in <code>stack_push</code>. The underlying question is that of <em>ownership</em>: when we allocate a node, which part of the program does it "belong" to? The "owner" of the node should do the work of managing when to free it or keep it. Since we already agreed to create a <code>gmachine</code> struct to house the GC handle, it makes sense that nodes are owned by the G-machine. Thus, the assumption in functions like <code>stack_push</code> is that the "owner" of the node already took care of allocating and tracking it, and <code>stack_push</code> itself shouldn't bother. <span class="sidenote-delimiter">]</span> </span> </span> <code>stack_push</code>, <code>stack_pop</code>, and similar functions do not require a G-machine.</li> <li><code>stack_alloc</code> and <code>stack_pack</code> <strong>do</strong> need a G-machine, because they must allocate new nodes. Where the nodes are allocated, we should add them to the GC handle.</li> <li>Since they use <code>stack_alloc</code> and <code>stack_pack</code>, generated functions also need a G-machine.</li> <li>Since <code>unwind</code> calls the generated functions, it must also receive a G-machine.</li> </ol> <p>As far as stack functions go, we only <em>need</em> to update <code>stack_alloc</code> and <code>stack_pack</code>. Everything else doesn&rsquo;t require new node allocations, and thus, does not require the GC handle. However, this makes our code rather ugly: we have a set of mostly <code>stack_*</code> functions, followed suddenly by two <code>gmachine_*</code> functions. In the interest of cleanliness, let&rsquo;s instead do the following:</p> <ol> <li>Make all functions associated with G-machine rules (like <strong>Alloc</strong>, <strong>Update</strong>, and so on) require a <code>gmachine*</code>. This way, theres a correspondence between our code and the theory.</li> <li>Leave the rest of the functions (<code>stack_push</code>, <code>stack_pop</code>, etc.) as is. They are not G-machine specific, and don&rsquo;t require a GC handle, so there&rsquo;s no need to touch them.</li> </ol> <p>Let&rsquo;s make this change. We end up with the following functions:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/09/runtime.h" data-first-line="56" data-last-line="84"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/09/runtime.h#L56-L84">runtime.h</a>, lines 56 through 84</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span><span class="lnt">65 </span><span class="lnt">66 </span><span class="lnt">67 </span><span class="lnt">68 </span><span class="lnt">69 </span><span class="lnt">70 </span><span class="lnt">71 </span><span class="lnt">72 </span><span class="lnt">73 </span><span class="lnt">74 </span><span class="lnt">75 </span><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span><span class="lnt">79 </span><span class="lnt">80 </span><span class="lnt">81 </span><span class="lnt">82 </span><span class="lnt">83 </span><span class="lnt">84 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">stack</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">size_t</span> <span class="n">size</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">size_t</span> <span class="n">count</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_base</span><span class="o">**</span> <span class="n">data</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">stack_init</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">stack_free</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">stack_push</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span> <span class="n">n</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span> <span class="nf">stack_pop</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span> <span class="nf">stack_peek</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">o</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">stack_popn</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">n</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">gmachine</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">stack</span> <span class="n">stack</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span> <span class="n">gc_nodes</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int64_t</span> <span class="n">gc_node_count</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int64_t</span> <span class="n">gc_node_threshold</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">gmachine_init</span><span class="p">(</span><span class="k">struct</span> <span class="n">gmachine</span><span class="o">*</span> <span class="n">g</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">gmachine_free</span><span class="p">(</span><span class="k">struct</span> <span class="n">gmachine</span><span class="o">*</span> <span class="n">g</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">gmachine_slide</span><span class="p">(</span><span class="k">struct</span> <span class="n">gmachine</span><span class="o">*</span> <span class="n">g</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">n</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">gmachine_update</span><span class="p">(</span><span class="k">struct</span> <span class="n">gmachine</span><span class="o">*</span> <span class="n">g</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">o</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">gmachine_alloc</span><span class="p">(</span><span class="k">struct</span> <span class="n">gmachine</span><span class="o">*</span> <span class="n">g</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">o</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">gmachine_pack</span><span class="p">(</span><span class="k">struct</span> <span class="n">gmachine</span><span class="o">*</span> <span class="n">g</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">n</span><span class="p">,</span> <span class="kt">int8_t</span> <span class="n">t</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">gmachine_split</span><span class="p">(</span><span class="k">struct</span> <span class="n">gmachine</span><span class="o">*</span> <span class="n">g</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">n</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span> <span class="nf">gmachine_track</span><span class="p">(</span><span class="k">struct</span> <span class="n">gmachine</span><span class="o">*</span> <span class="n">g</span><span class="p">,</span> <span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span> <span class="n">b</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">gmachine_gc</span><span class="p">(</span><span class="k">struct</span> <span class="n">gmachine</span><span class="o">*</span> <span class="n">g</span><span class="p">);</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>For the majority of the changed functions, the updates are <span class="sidenote"> <label class="sidenote-label" for="cosmetic-note">cosmetic.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="cosmetic-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> We must also update the LLVM/C++ declarations of the affected functions: many of them now take a <code>gmachine_ptr_type</code> instead of <code>stack_ptr_type</code>. This change is not shown explicitly here (it is hard to do with our growing code base), but it is nonetheless significant. <span class="sidenote-delimiter">]</span> </span> </span> The functions that require more significant modifications are <code>gmachine_alloc</code> and <code>gmachine_pack</code>. In both, we must now make a call to <code>gmachine_track</code> to ensure that a newly allocated node will be garbage collected in the future. The updated code for <code>gmachine_alloc</code> is:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/09/runtime.c" data-first-line="140" data-last-line="145"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/09/runtime.c#L140-L145">runtime.c</a>, lines 140 through 145</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">140 </span><span class="lnt">141 </span><span class="lnt">142 </span><span class="lnt">143 </span><span class="lnt">144 </span><span class="lnt">145 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">gmachine_alloc</span><span class="p">(</span><span class="k">struct</span> <span class="n">gmachine</span><span class="o">*</span> <span class="n">g</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">o</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span><span class="p">(</span><span class="n">o</span><span class="o">--</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">stack_push</span><span class="p">(</span><span class="o">&amp;</span><span class="n">g</span><span class="o">-&gt;</span><span class="n">stack</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nf">gmachine_track</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="p">(</span><span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span><span class="p">)</span> <span class="nf">alloc_ind</span><span class="p">(</span><span class="nb">NULL</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Correspondingly, the updated code for <code>gmachine_pack</code> is:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/09/runtime.c" data-first-line="147" data-last-line="162"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/09/runtime.c#L147-L162">runtime.c</a>, lines 147 through 162</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">147 </span><span class="lnt">148 </span><span class="lnt">149 </span><span class="lnt">150 </span><span class="lnt">151 </span><span class="lnt">152 </span><span class="lnt">153 </span><span class="lnt">154 </span><span class="lnt">155 </span><span class="lnt">156 </span><span class="lnt">157 </span><span class="lnt">158 </span><span class="lnt">159 </span><span class="lnt">160 </span><span class="lnt">161 </span><span class="lnt">162 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">gmachine_pack</span><span class="p">(</span><span class="k">struct</span> <span class="n">gmachine</span><span class="o">*</span> <span class="n">g</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">n</span><span class="p">,</span> <span class="kt">int8_t</span> <span class="n">t</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">assert</span><span class="p">(</span><span class="n">g</span><span class="o">-&gt;</span><span class="n">stack</span><span class="p">.</span><span class="n">count</span> <span class="o">&gt;=</span> <span class="n">n</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_base</span><span class="o">**</span> <span class="n">data</span> <span class="o">=</span> <span class="nf">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="o">*</span><span class="n">data</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">n</span> <span class="o">+</span> <span class="mi">1</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="nf">assert</span><span class="p">(</span><span class="n">data</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">memcpy</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">g</span><span class="o">-&gt;</span><span class="n">stack</span><span class="p">.</span><span class="n">data</span><span class="p">[</span><span class="n">g</span><span class="o">-&gt;</span><span class="n">stack</span><span class="p">.</span><span class="n">count</span> <span class="o">-</span> <span class="n">n</span><span class="p">],</span> <span class="n">n</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="o">*</span><span class="n">data</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="n">data</span><span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_data</span><span class="o">*</span> <span class="n">new_node</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">node_data</span><span class="o">*</span><span class="p">)</span> <span class="nf">alloc_node</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">new_node</span><span class="o">-&gt;</span><span class="n">array</span> <span class="o">=</span> <span class="n">data</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">new_node</span><span class="o">-&gt;</span><span class="n">base</span><span class="p">.</span><span class="n">tag</span> <span class="o">=</span> <span class="n">NODE_DATA</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">new_node</span><span class="o">-&gt;</span><span class="n">tag</span> <span class="o">=</span> <span class="n">t</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="nf">stack_popn</span><span class="p">(</span><span class="o">&amp;</span><span class="n">g</span><span class="o">-&gt;</span><span class="n">stack</span><span class="p">,</span> <span class="n">n</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">stack_push</span><span class="p">(</span><span class="o">&amp;</span><span class="n">g</span><span class="o">-&gt;</span><span class="n">stack</span><span class="p">,</span> <span class="nf">gmachine_track</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="p">(</span><span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span><span class="p">)</span> <span class="n">new_node</span><span class="p">));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Note that we&rsquo;ve secretly made one more change. Instead of allocating <code>sizeof(*data) * n</code> bytes of memory for the array of packed nodes, we allocate <code>sizeof(*data) * (n + 1)</code>, and set the last element to <code>NULL</code>. This will allow other functions (which we will soon write) to know how many elements are packed inside a <code>node_data</code> (effectively, we&rsquo;ve added a <code>NULL</code> terminator).</p> <p>We must change our compiler to keep it up to date with this change. Importantly, it must know that a G-machine struct exists. To give it this information, we add a new <code>llvm::StructType*</code> called <code>gmachine_type</code> to the <code>llvm_context</code> class, initialize it in the constructor, and set its body as follows:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/09/llvm_context.cpp" data-first-line="21" data-last-line="26"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/09/llvm_context.cpp#L21-L26">llvm_context.cpp</a>, lines 21 through 26</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="n">gmachine_type</span><span class="o">-&gt;</span><span class="n">setBody</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="n">stack_ptr_type</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">node_ptr_type</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">IntegerType</span><span class="o">::</span><span class="n">getInt64Ty</span><span class="p">(</span><span class="n">ctx</span><span class="p">),</span> </span></span><span class="line"><span class="cl"> <span class="n">IntegerType</span><span class="o">::</span><span class="n">getInt64Ty</span><span class="p">(</span><span class="n">ctx</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">);</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The compiler must also know that generated functions now use the G-machine struct rather than a stack struct:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/09/llvm_context.cpp" data-first-line="19" data-last-line="19"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/09/llvm_context.cpp#L19-L19">llvm_context.cpp</a>, line 19</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">19 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="n">function_type</span> <span class="o">=</span> <span class="n">FunctionType</span><span class="o">::</span><span class="n">get</span><span class="p">(</span><span class="n">Type</span><span class="o">::</span><span class="n">getVoidTy</span><span class="p">(</span><span class="n">ctx</span><span class="p">),</span> <span class="p">{</span> <span class="n">gmachine_ptr_type</span> <span class="p">},</span> <span class="nb">false</span><span class="p">);</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Since we still use some functions that require a stack and not a G-machine, we must have a way to get the stack from a G-machine. To do this, we create a new <code>unwrap</code> function, which uses LLVM&rsquo;s GEP instruction to get a pointer to the G-machine&rsquo;s stack field:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/09/llvm_context.cpp" data-first-line="222" data-last-line="225"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/09/llvm_context.cpp#L222-L225">llvm_context.cpp</a>, lines 222 through 225</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">222 </span><span class="lnt">223 </span><span class="lnt">224 </span><span class="lnt">225 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">Value</span><span class="o">*</span> <span class="n">llvm_context</span><span class="o">::</span><span class="n">unwrap_gmachine_stack_ptr</span><span class="p">(</span><span class="n">Value</span><span class="o">*</span> <span class="n">g</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">offset_0</span> <span class="o">=</span> <span class="n">create_i32</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">builder</span><span class="p">.</span><span class="n">CreateGEP</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="p">{</span> <span class="n">offset_0</span><span class="p">,</span> <span class="n">offset_0</span> <span class="p">});</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We use this function elsewhere, such <code>llvm_context::create_pop</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/09/llvm_context.cpp" data-first-line="176" data-last-line="179"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/09/llvm_context.cpp#L176-L179">llvm_context.cpp</a>, lines 176 through 179</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">176 </span><span class="lnt">177 </span><span class="lnt">178 </span><span class="lnt">179 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">Value</span><span class="o">*</span> <span class="n">llvm_context</span><span class="o">::</span><span class="n">create_pop</span><span class="p">(</span><span class="n">Function</span><span class="o">*</span> <span class="n">f</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">pop_f</span> <span class="o">=</span> <span class="n">functions</span><span class="p">.</span><span class="n">at</span><span class="p">(</span><span class="s">&#34;stack_pop&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">builder</span><span class="p">.</span><span class="n">CreateCall</span><span class="p">(</span><span class="n">pop_f</span><span class="p">,</span> <span class="p">{</span> <span class="n">unwrap_gmachine_stack_ptr</span><span class="p">(</span><span class="n">f</span><span class="o">-&gt;</span><span class="n">arg_begin</span><span class="p">())</span> <span class="p">});</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Finally, we want to make sure our generated functions don&rsquo;t allocate nodes without tracking them with the G-machine. To do so, we modify all the <code>create_*</code> methods to require the G-machine function argument, and update the functions themselves to call <code>gmachine_track</code>. For example, here&rsquo;s <code>llvm_context::create_num</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/09/llvm_context.cpp" data-first-line="235" data-last-line="239"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/09/llvm_context.cpp#L235-L239">llvm_context.cpp</a>, lines 235 through 239</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">235 </span><span class="lnt">236 </span><span class="lnt">237 </span><span class="lnt">238 </span><span class="lnt">239 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">Value</span><span class="o">*</span> <span class="n">llvm_context</span><span class="o">::</span><span class="n">create_num</span><span class="p">(</span><span class="n">Function</span><span class="o">*</span> <span class="n">f</span><span class="p">,</span> <span class="n">Value</span><span class="o">*</span> <span class="n">v</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">alloc_num_f</span> <span class="o">=</span> <span class="n">functions</span><span class="p">.</span><span class="n">at</span><span class="p">(</span><span class="s">&#34;alloc_num&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">alloc_num_call</span> <span class="o">=</span> <span class="n">builder</span><span class="p">.</span><span class="n">CreateCall</span><span class="p">(</span><span class="n">alloc_num_f</span><span class="p">,</span> <span class="p">{</span> <span class="n">v</span> <span class="p">});</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nf">create_track</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">alloc_num_call</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Of course, this requires us to add a new <code>create_track</code> method to the <code>llvm_context</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/09/llvm_context.cpp" data-first-line="212" data-last-line="215"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/09/llvm_context.cpp#L212-L215">llvm_context.cpp</a>, lines 212 through 215</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">212 </span><span class="lnt">213 </span><span class="lnt">214 </span><span class="lnt">215 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">Value</span><span class="o">*</span> <span class="n">llvm_context</span><span class="o">::</span><span class="n">create_track</span><span class="p">(</span><span class="n">Function</span><span class="o">*</span> <span class="n">f</span><span class="p">,</span> <span class="n">Value</span><span class="o">*</span> <span class="n">v</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">track_f</span> <span class="o">=</span> <span class="n">functions</span><span class="p">.</span><span class="n">at</span><span class="p">(</span><span class="s">&#34;gmachine_track&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">builder</span><span class="p">.</span><span class="n">CreateCall</span><span class="p">(</span><span class="n">track_f</span><span class="p">,</span> <span class="p">{</span> <span class="n">f</span><span class="o">-&gt;</span><span class="n">arg_begin</span><span class="p">(),</span> <span class="n">v</span> <span class="p">});</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This is good. Let&rsquo;s now implement the actual mark-and-sweep algorithm in <code>gmachine_gc</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/09/runtime.c" data-first-line="186" data-last-line="204"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/09/runtime.c#L186-L204">runtime.c</a>, lines 186 through 204</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">186 </span><span class="lnt">187 </span><span class="lnt">188 </span><span class="lnt">189 </span><span class="lnt">190 </span><span class="lnt">191 </span><span class="lnt">192 </span><span class="lnt">193 </span><span class="lnt">194 </span><span class="lnt">195 </span><span class="lnt">196 </span><span class="lnt">197 </span><span class="lnt">198 </span><span class="lnt">199 </span><span class="lnt">200 </span><span class="lnt">201 </span><span class="lnt">202 </span><span class="lnt">203 </span><span class="lnt">204 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">gmachine_gc</span><span class="p">(</span><span class="k">struct</span> <span class="n">gmachine</span><span class="o">*</span> <span class="n">g</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">g</span><span class="o">-&gt;</span><span class="n">stack</span><span class="p">.</span><span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">gc_visit_node</span><span class="p">(</span><span class="n">g</span><span class="o">-&gt;</span><span class="n">stack</span><span class="p">.</span><span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_base</span><span class="o">**</span> <span class="n">head_ptr</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">g</span><span class="o">-&gt;</span><span class="n">gc_nodes</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span><span class="p">(</span><span class="o">*</span><span class="n">head_ptr</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">((</span><span class="o">*</span><span class="n">head_ptr</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">gc_reachable</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="o">*</span><span class="n">head_ptr</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">gc_reachable</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">head_ptr</span> <span class="o">=</span> <span class="o">&amp;</span><span class="p">(</span><span class="o">*</span><span class="n">head_ptr</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">gc_next</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span> <span class="n">to_free</span> <span class="o">=</span> <span class="o">*</span><span class="n">head_ptr</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="o">*</span><span class="n">head_ptr</span> <span class="o">=</span> <span class="n">to_free</span><span class="o">-&gt;</span><span class="n">gc_next</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">free_node_direct</span><span class="p">(</span><span class="n">to_free</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">free</span><span class="p">(</span><span class="n">to_free</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">g</span><span class="o">-&gt;</span><span class="n">gc_node_count</span><span class="o">--</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>In the code above, we first iterate through the stack, calling <code>gc_visit_node</code> on every node that we encounter. The assumption is that once <code>gc_visit_node</code> is done, every node that <em>can</em> be reached has its <code>gc_reachable</code> field set to 1, and all the others have it set to 0.</p> <p>Once we reach the end of the stack, we continue to the &ldquo;sweep&rdquo; phase, iterating through the linked list of nodes (held in the G-machine GC handle <code>gc_nodes</code>). For each node, if its <code>gc_reachable</code> flag is not set, we remove it from the linked list, and call <code>free_node_direct</code> on it. Otherwise (that is, if the flag <strong>is</strong> set), we clear it, so that the node can potentially be garbage collected in a future invocation of <code>gmachine_gc</code>.</p> <p><code>gc_visit_node</code> recursively marks a node and its children as reachable:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/09/runtime.c" data-first-line="51" data-last-line="70"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/09/runtime.c#L51-L70">runtime.c</a>, lines 51 through 70</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span><span class="lnt">65 </span><span class="lnt">66 </span><span class="lnt">67 </span><span class="lnt">68 </span><span class="lnt">69 </span><span class="lnt">70 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">gc_visit_node</span><span class="p">(</span><span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">n</span><span class="o">-&gt;</span><span class="n">gc_reachable</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">n</span><span class="o">-&gt;</span><span class="n">gc_reachable</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">n</span><span class="o">-&gt;</span><span class="n">tag</span> <span class="o">==</span> <span class="n">NODE_APP</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_app</span><span class="o">*</span> <span class="n">app</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">node_app</span><span class="o">*</span><span class="p">)</span> <span class="n">n</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">gc_visit_node</span><span class="p">(</span><span class="n">app</span><span class="o">-&gt;</span><span class="n">left</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">gc_visit_node</span><span class="p">(</span><span class="n">app</span><span class="o">-&gt;</span><span class="n">right</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">if</span><span class="p">(</span><span class="n">n</span><span class="o">-&gt;</span><span class="n">tag</span> <span class="o">==</span> <span class="n">NODE_IND</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_ind</span><span class="o">*</span> <span class="n">ind</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">node_ind</span><span class="o">*</span><span class="p">)</span> <span class="n">n</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">gc_visit_node</span><span class="p">(</span><span class="n">ind</span><span class="o">-&gt;</span><span class="n">next</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">if</span><span class="p">(</span><span class="n">n</span><span class="o">-&gt;</span><span class="n">tag</span> <span class="o">==</span> <span class="n">NODE_DATA</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_data</span><span class="o">*</span> <span class="n">data</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">node_data</span><span class="o">*</span><span class="p">)</span> <span class="n">n</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_base</span><span class="o">**</span> <span class="n">to_visit</span> <span class="o">=</span> <span class="n">data</span><span class="o">-&gt;</span><span class="n">array</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span><span class="p">(</span><span class="o">*</span><span class="n">to_visit</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">gc_visit_node</span><span class="p">(</span><span class="o">*</span><span class="n">to_visit</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">to_visit</span><span class="o">++</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This is possible with the <code>node_data</code> nodes because of the change we made to the <code>gmachine_pack</code> instruction earlier: now, the last element of the &ldquo;packed&rdquo; array is <code>NULL</code>, telling <code>gc_visit_node</code> that it has reached the end of the list of children.</p> <p><code>free_node_direct</code> performs a non-recursive deallocation of all the resources held by a particular node. So far, this is only needed for <code>node_data</code> nodes, since the arrays holding their children are dynamically allocated. Thus, the code for the function is pretty simple:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/09/runtime.c" data-first-line="45" data-last-line="49"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/09/runtime.c#L45-L49">runtime.c</a>, lines 45 through 49</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">free_node_direct</span><span class="p">(</span><span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">n</span><span class="o">-&gt;</span><span class="n">tag</span> <span class="o">==</span> <span class="n">NODE_DATA</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">free</span><span class="p">(((</span><span class="k">struct</span> <span class="n">node_data</span><span class="o">*</span><span class="p">)</span> <span class="n">n</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">array</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#when-to-collect"> <h3 id="when-to-collect">When to Collect</h3> </a> <p>When should we run garbage collection? Initially, I tried running it after every call to <code>unwind</code>. However, this quickly proved impractical: the performance of all the programs in the language decreased by a spectacular amount. Programs like <code>works1.txt</code> and <code>works2.txt</code> would take tens of seconds to complete.</p> <p>Instead of this madness, let&rsquo;s settle for an approach common to many garbage collectors. Let&rsquo;s <strong>perform garbage collection every time the amount of memory we&rsquo;ve allocated doubles</strong>. Tracking when the amount of allocated memory doubles is the purpose of the <code>gc_node_count</code> and <code>gc_threshold</code> fields in the <code>gmachine</code> struct. The former field tracks how many nodes have been tracked by the garbage collector, and the latter holds the number of nodes the G-machine must reach before triggering garbage collection.</p> <p>Since the G-machine is made aware of allocations by a call to the <code>gmachine_track</code> function, this is where we will attempt to perform garbage collection. We end up with the following code:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/09/runtime.c" data-first-line="171" data-last-line="184"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/09/runtime.c#L171-L184">runtime.c</a>, lines 171 through 184</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">171 </span><span class="lnt">172 </span><span class="lnt">173 </span><span class="lnt">174 </span><span class="lnt">175 </span><span class="lnt">176 </span><span class="lnt">177 </span><span class="lnt">178 </span><span class="lnt">179 </span><span class="lnt">180 </span><span class="lnt">181 </span><span class="lnt">182 </span><span class="lnt">183 </span><span class="lnt">184 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">node_base</span><span class="o">*</span> <span class="nf">gmachine_track</span><span class="p">(</span><span class="k">struct</span> <span class="nc">gmachine</span><span class="o">*</span> <span class="n">g</span><span class="p">,</span> <span class="k">struct</span> <span class="nc">node_base</span><span class="o">*</span> <span class="n">b</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">g</span><span class="o">-&gt;</span><span class="n">gc_node_count</span><span class="o">++</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">b</span><span class="o">-&gt;</span><span class="n">gc_next</span> <span class="o">=</span> <span class="n">g</span><span class="o">-&gt;</span><span class="n">gc_nodes</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">g</span><span class="o">-&gt;</span><span class="n">gc_nodes</span> <span class="o">=</span> <span class="n">b</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">g</span><span class="o">-&gt;</span><span class="n">gc_node_count</span> <span class="o">&gt;=</span> <span class="n">g</span><span class="o">-&gt;</span><span class="n">gc_node_threshold</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">uint64_t</span> <span class="n">nodes_before</span> <span class="o">=</span> <span class="n">g</span><span class="o">-&gt;</span><span class="n">gc_node_count</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">gc_visit_node</span><span class="p">(</span><span class="n">b</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">gmachine_gc</span><span class="p">(</span><span class="n">g</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">g</span><span class="o">-&gt;</span><span class="n">gc_node_threshold</span> <span class="o">=</span> <span class="n">g</span><span class="o">-&gt;</span><span class="n">gc_node_count</span> <span class="o">*</span> <span class="mi">2</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">b</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>When a node is added to the GC handle, we increment the <code>gc_node_count</code> field. If the new value of this field exceeds the threshold, we perform garbage collection. There are cases in which this is fairly dangerous: for instance, <code>gmachine_pack</code> first moves all packed nodes into an array, then allocates a <code>node_data</code> node. This means that for a brief moment, the nodes stored into the new data node are inaccessible from the stack, and thus susceptible to garbage collection. To prevent situations like this, we run <code>gc_visit_node</code> on the node being tracked, marking it and its children as &ldquo;reachable&rdquo;. Finally, we set the next &ldquo;free&rdquo; threshold to double the number of currently allocated nodes.</p> <p>This is about as much as we need to do. The change in this post was a major one, and required updating multiple files. As always, you&rsquo;re welcome to check out <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/compiler/09"class="external-link">the compiler source code for this post<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. To wrap up, let&rsquo;s evaluate our change.</p> <p>To especially stress the compiler, I came up with a prime number generator. Since booleans are not in the standard library, and since it isn&rsquo;t possible to pattern match on numbers, my only option was the use Peano encoding. This effectively means that numbers are represented as linked lists, which makes garbage collection all the more important. The program is quite long, but you can <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/compiler/09/examples/primes.txt"class="external-link">find the entire code here<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>.</p> <p>When I ran the <code>primes</code> program compiled using the previous version of the compiler using <code>time</code>, I got the following output:</p> <pre tabindex="0"><code>Maximum resident set size (kbytes): 935764 Minor (reclaiming a frame) page faults: 233642 </code></pre><p>In contrast, here is the output of <code>time</code> when running the same program compiled with the new version of the compiler:</p> <pre tabindex="0"><code>Maximum resident set size (kbytes): 7448 Minor (reclaiming a frame) page faults: 1577 </code></pre><p>We have reduced maximum memory usage by a factor of 125, and the number of page faults by a factor of 148. That seems pretty good!</p> <p>With this success, we end today&rsquo;s post. As I mentioned before, we&rsquo;re not done. The language is still clunky to use, and can benefit from <code>let/in</code> expressions and <strong>lambda functions</strong>. Furthermore, our language is currently monomorphic, and would be much better with <strong>polymorphism</strong>. Finally, to make our language capable of more-than-trivial work, we may want to implement <strong>Input/Output</strong> and <strong>strings</strong>. We tackle the largest of these features, polymorphism, in <a href="https://danilafe.com/blog/10_compiler_polymorphism/">Part 10</a>.</p> Using GHC IDE for Haskell Error Checking and Autocompletion https://danilafe.com/blog/haskell_language_server_again/ Mon, 06 Jan 2020 17:07:25 -0800 https://danilafe.com/blog/haskell_language_server_again/ <p>Last year, when I took Oregon State University&rsquo;s CS 381 class, I ended up setting up my editor with the Haskell IDE engine. This made it possible to detect errors, view types, and have good autocompletion within the editor itself. Recently, I&rsquo;ve found that GHC IDE works better for my projects, so instead of butchering the original article, I&rsquo;ll just quickly write an updated version here, referencing the old one when necessary.</p> <p>By the end of the article, your editor should be able to detect errors and properly autocomplete Haskell code, somewhat like in the below screenshot:</p> <p><img src="https://i.imgur.com/CRMznGL.png" alt="Imgur"></p> <a href="#downloading-and-installing-ghc-ide"> <h3 id="downloading-and-installing-ghc-ide">Downloading and Installing GHC IDE</h3> </a> <p>GHC IDE is a Haskell-based program that uses the <span class="sidenote"> <label class="sidenote-label" for="lsp-note">language server protocol</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="lsp-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> You don't really need to know what the language server protocol (LSP) is to use GHC IDE. If you are nonetheless interested, I wrote a little bit about it <a href="https://danilafe.com/blog/haskell_language_server/#prelude-language-server-protocol">in the previous iteration of this post.</a> If you want more information, check out the <a href="https://microsoft.github.io/language-server-protocol/">official Microsoft page on LSP.</a> <span class="sidenote-delimiter">]</span> </span> </span> to communicate with any editor that supports it. Editors with support the the LSP include Atom, Visual Studio Code, Emacs, and Vim. Thus, You can get a good Haskell development environment without tying yourself to one application or service.</p> <p>We first want to download the GHC IDE. To do this, you need to have <a href="https://git-scm.com/"class="external-link">Git<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> installed. Once you have that, in your Git bash (on Windows) or in your terminal (maxOS, Linux), type the command:</p> <pre tabindex="0"><code>git clone https://github.com/digital-asset/ghcide.git </code></pre><p>To install GHC IDE, you can use either <code>cabal</code> (which is typically the <code>cabal-install</code> package, and is required normally for this class) or <code>stack</code> (a build tool). For <code>cabal</code>:</p> <pre tabindex="0"><code>cabal install </code></pre><p>And for <code>stack</code>:</p> <pre tabindex="0"><code>stack install </code></pre><p>This will create an executable in your <code>~/.local/bin</code> directory. By default, this is not usable from other programs, such as Vim, so you should add this directory to your path. On Linux and macOS, this is done by adding the following line to your <code>.bashrc</code> file (or equivalent):</p> <pre tabindex="0"><code>export PATH=$PATH:/home/&lt;yourusername&gt;/.local/bin </code></pre><p>On Windows, this is done by <span class="sidenote"> <label class="sidenote-label" for="path-note">editing your PATH variable.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="path-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> If you need to know how to change your <code>PATH</code>, I wrote about it briefly in the <a href="https://danilafe.com/blog/haskell_language_server/ #installation-of-v0-5-0-0-windows-systems">previous iteration of this post.</a> <span class="sidenote-delimiter">]</span> </span> </span> I don&rsquo;t run Windows, so I don&rsquo;t know where <code>cabal install</code> will place the executable, but I do know where the executable will appear if you use <code>stack install</code> - in the directory given by:</p> <pre tabindex="0"><code>stack path --local-bin </code></pre><p>Adding that to your path should be sufficient to use GHC IDE.</p> <a href="#setting-up-your-editor"> <h3 id="setting-up-your-editor">Setting up Your Editor</h3> </a> <p>This is where the paths diverge. I personally use (Neo)vim, but for the sake of completeness, I&rsquo;ll go over installation for Atom and VSCode (I&rsquo;m not including Emacs because I know nothing about configuring Emacs).</p> <a href="#atom"> <h4 id="atom">Atom</h4> </a> <p>There appears to be an Atom extension specifically for GHC IDE: <a href="https://atom.io/packages/ide-haskell-ghcide"class="external-link">ide-haskell-ghcide<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. It doesn&rsquo;t have a lot of configuration options, and will certainly require GHC IDE to be in your path. However, since both GHC IDE and the Haskell IDE engine use the Language Server Protocol, the more mature <a href="https://atom.io/packages/ide-haskell-hie"class="external-link">ide-haskell-hie<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> extension may work, as well. In fact, since <code>ide-haskell-ghcide</code> is so young, I&rsquo;d recommend trying <code>ide-haskell-hie</code> first, configuring the settings (found under <em>Settings &gt; Packages &gt; (Search ide-haskell-hie) &gt; Settings</em>) to use the following full path:</p> <pre tabindex="0"><code>&lt;output of stack path --local-bin&gt;/ghcide </code></pre><a href="#vscode"> <h4 id="vscode">VSCode</h4> </a> <p>The team behind GHC IDE maintains an official VSCode extension found <a href="https://marketplace.visualstudio.com/items?itemName=DigitalAssetHoldingsLLC.ghcide"class="external-link">here<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. Installing it, when you have GHC IDE also installed, should be sufficient to get VSCode to autocomplete and error check.</p> <a href="#neovim"> <h4 id="neovim">(Neo)vim</h4> </a> <p>My original recommendations for (neo)vim remain unchanged, with the exception of using <code>ghcide</code> instead of <code>hie</code> in the <code>serverCommands</code> variable. You can find the original instructions <a href="https://danilafe.com/blog/haskell_language_server/#neovim">here</a>.</p> <a href="#conclusion"> <h3 id="conclusion">Conclusion</h3> </a> <p>I hope that using GHC IDE, you&rsquo;ll be able to have a significantly more pleasant Haskell experience in CS 381. Enjoy!</p> A Language for an Assignment - Homework 3 https://danilafe.com/blog/02_cs325_languages_hw3/ Thu, 02 Jan 2020 22:17:43 -0800 https://danilafe.com/blog/02_cs325_languages_hw3/ <p>It rained in Sunriver on New Year&rsquo;s Eve, and it continued to rain for the next couple of days. So, instead of going skiing as planned, to the dismay of my family and friends, I spent the majority of those days working on the third language for homework 3. It was quite the language, too - the homework has three problems, each of which has a solution independent of the others. I invite you to join me in my descent into madness as we construct another language.</p> <a href="#homework-3"> <h3 id="homework-3">Homework 3</h3> </a> <p>Let&rsquo;s take a look at the three homework problems. The first two are related, but are solved using a different technique:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/hws/hw3.txt" data-first-line="18" data-last-line="30"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/hws/hw3.txt#L18-L30">hw3.txt</a>, lines 18 through 30</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">1. Given an array A of n numbers, a query x, and a number k, </span></span><span class="line"><span class="cl"> find the k numbers in A that are closest (in value) to x. </span></span><span class="line"><span class="cl"> For example: </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> find([4,1,3,2,7,4], 5.2, 2) returns [4,4] </span></span><span class="line"><span class="cl"> find([4,1,3,2,7,4], 6.5, 3) returns [4,7,4] </span></span><span class="line"><span class="cl"> find([5,3,4,1,6,3], 3.5, 2) returns [3,4] </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> Filename: closest_unsorted.py </span></span><span class="line"><span class="cl"> Must run in O(n) time. </span></span><span class="line"><span class="cl"> The elements in the returned list must be in the original order. </span></span><span class="line"><span class="cl"> In case two numbers are equally close to x, choose the earlier one.</span></span></code></pre></td></tr></table> </div> </div> </div> <p>This problem requires us to find the <code>k</code> numbers closest to some query (which I will call <code>n</code>) from a list <code>xs</code>. The list isn&rsquo;t sorted, and the problem must run in linear time. Sorting the list would require the standard <span class="sidenote"> <label class="sidenote-label" for="n-note">\(O(n\log n)\) time.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="n-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> The \(n\) in this expression is not the same as the query <code>n</code>, but rather the length of the list. In fact, I have not yet assigned the length of the input <code>xs</code> to any variable. If we say that \(m\) is a number that denotes that length, the proper expression for the complexity is \(O(m \log m)\). <span class="sidenote-delimiter">]</span> </span> </span> Thus, we have to take another route, which should already be familiar: quickselect. Using quickselect, we can find the <code>k</code>th closest number, and then collect all the numbers that are closer than the <code>kth</code> closest number. So, we need a language that:</p> <ul> <li>Supports quickselect (and thus, list partitioning and recursion).</li> <li>Supports iteration, <span class="sidenote"> <label class="sidenote-label" for="iteration-note">multiple times.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="iteration-note"></input><span class="sidenote-content sidenote-left"><span class="sidenote-delimiter">[note:</span> Why would we need to iterate multiple times? Note that we could have a list of numbers that are all the same, <code>[1,1,1,1,1]</code>. Then, we'll need to know how many of the numbers <em>equally close</em> as the <code>k</code>th element we need to include, which will require another pass through the list. <span class="sidenote-delimiter">]</span> </span> </span> </li> </ul> <p>That&rsquo;s a good start. Let&rsquo;s take a look at the second problem:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/hws/hw3.txt" data-first-line="33" data-last-line="47"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/hws/hw3.txt#L33-L47">hw3.txt</a>, lines 33 through 47</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">2. [WILL BE GRADED] </span></span><span class="line"><span class="cl"> Now what if the input array is sorted? Can you do it faster? </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> find([1,2,3,4,4,7], 5.2, 2) returns [4,4] </span></span><span class="line"><span class="cl"> find([1,2,3,4,4,7], 6.5, 3) returns [4,4,7] </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> Filename: closest_sorted.py </span></span><span class="line"><span class="cl"> Must run in O(logn + k) time. </span></span><span class="line"><span class="cl"> The elements in the returned list must be in the original order. </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> Note: in case two numbers are equally close to x, choose the smaller one: </span></span><span class="line"><span class="cl"> find([1,2,3,4,4,6,6], 5, 3) returns [4,4,6] </span></span><span class="line"><span class="cl"> find([1,2,3,4,4,5,6], 4, 5) returns [2,3,4,4,5] </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> Hint: you can use Python&#39;s bisect.bisect for binary search.</span></span></code></pre></td></tr></table> </div> </div> </div> <p>This problem really is easier. We have to find the position of <em>the</em> closest element, and then try expand towards either the left or right, depending on which end is better. This expansion will take several steps, and will likely require a way to &ldquo;look&rdquo; at a given part of the list. So let&rsquo;s add two more rules. We need a language that also:</p> <ul> <li>Supports looping control flow, such as <code>while</code>.</li> <li><span class="sidenote"> <label class="sidenote-label" for="view-note">Allows for a &ldquo;view&rdquo; into the list</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="view-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> We could, of course, simply use list indexing. But then, we'd just be making a simple imperative language, and that's boring. So let's play around with our design a little, and experimentally add such a "list view" component. <span class="sidenote-delimiter">]</span> </span> </span> (like an abstraction over indexing).</li> </ul> <p>This is shaping up to be a fun language. Let&rsquo;s take a look at the last problem: <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/hws/hw3.txt" data-first-line="50" data-last-line="64"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/hws/hw3.txt#L50-L64">hw3.txt</a>, lines 50 through 64</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">3. For a given array A of n *distinct* numbers, find all triples (x,y,z) </span></span><span class="line"><span class="cl"> s.t. x + y = z. (x, y, z are distinct numbers) </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> e.g., </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> find([1, 4, 2, 3, 5]) returns [(1,3,4), (1,2,3), (1,4,5), (2,3,5)] </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> Note that: </span></span><span class="line"><span class="cl"> 1) no duplicates in the input array </span></span><span class="line"><span class="cl"> 2) you can choose any arbitrary order for triples in the returned list. </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> Filename: xyz.py </span></span><span class="line"><span class="cl"> Must run in O(n^2) time. </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> Hint: you can use any built-in sort in Python.</span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>This problem requires more iterations of a list. We have several <span class="sidenote"> <label class="sidenote-label" for="cursor-note">&ldquo;cursors&rdquo;</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="cursor-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> I always make the language before I write the post, since a lot of design decisions change mid-implementation. I realize now that "cursors" would've been a better name for this language feature, but alas, it is too late. <span class="sidenote-delimiter">]</span> </span> </span> looking into the list, and depending if the values at each of the cursors add up, we do or do not add a new tuple to a list. So, two more requirements:</p> <ul> <li>The &ldquo;cursors&rdquo; must be able to interact.</li> <li>The language can represent <span class="sidenote"> <label class="sidenote-label" for="tuple-note">tuples.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="tuple-note"></input><span class="sidenote-content sidenote-left"><span class="sidenote-delimiter">[note:</span> We could, of course, hack some other way to return a list of tuples, but it turns out tuples are pretty simple to implement, and help make for nicer programming in our language. <span class="sidenote-delimiter">]</span> </span> </span> </li> </ul> <p>I think we&rsquo;ve gathered what we want from the homework. Let&rsquo;s move on to the language!</p> <a href="#a-language"> <h3 id="a-language">A Language</h3> </a> <p>As is now usual, let&rsquo;s envision a solution to the problems in our language. There are actually quite a lot of functions to look at, so let&rsquo;s see them one by one. First, let&rsquo;s look at <code>qselect</code>.</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/sols/hw3.lang" data-first-line="1" data-last-line="19"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/sols/hw3.lang#L1-L19">hw3.lang</a>, lines 1 through 19</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">function qselect(xs, k, c) { </span></span><span class="line"><span class="cl"> if xs == [] { </span></span><span class="line"><span class="cl"> return 0; </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> traverser bisector(list: xs, span: (0,len(xs))); </span></span><span class="line"><span class="cl"> traverser pivot(list: xs, random: true); </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> let pivotE = pop!(pivot); </span></span><span class="line"><span class="cl"> let (leftList, rightList) = bisect!(bisector, (x) -&gt; c(x) &lt; c(pivotE)); </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> if k &gt; len(leftList) + 1 { </span></span><span class="line"><span class="cl"> return qselect(rightList, k - len(leftList) - 1, c); </span></span><span class="line"><span class="cl"> } elsif k == len(leftList) + 1 { </span></span><span class="line"><span class="cl"> return pivotE; </span></span><span class="line"><span class="cl"> } else { </span></span><span class="line"><span class="cl"> return qselect(leftList, k, c); </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl">}</span></span></code></pre></td></tr></table> </div> </div> </div> <p>After the early return, the first interesting part of the language is the use of what I have decided to call a <strong>list traverser</strong>. The list traverser is a <strong>generalization of a list index</strong>. Whenever we use a list index variable, we generally use the following operations:</p> <ul> <li><strong>Initialize</strong>: we set the list index to some initial value, such as 0.</li> <li><strong>Step</strong>: If we&rsquo;re walking the list from left to right, we increment the index. If we&rsquo;re walking the list from right to left, we decrement the index.</li> <li><strong>Validity Check</strong>: We check if the index is still valid (that is, we haven&rsquo;t gone past the edge of the list).</li> <li><strong>Access</strong>: Get the element the cursor is pointing to.</li> </ul> <p>A <span class="sidenote"> <label class="sidenote-label" for="cpp-note">traverser declaration</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="cpp-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> A fun fact is that we've just rediscovered C++ <a href="http://www.cplusplus.com/reference/iterator/">iterators</a>. C++ containers and their iterators provide us with the operations I described: We can initialize an iterator like <code>auto it = list.begin()</code>. We can step the iterator using <code>it++</code>. We can check its validity using <code>it != list.end()</code>, and access what it's pointing to using <code>*it</code>. While C++ uses templates and inheritance for this, we define a language feature specifically for lists. <span class="sidenote-delimiter">]</span> </span> </span> describes these operations. The declartion for the <code>bisector</code> traverser creates a &ldquo;cursor&rdquo; over the list <code>xs</code>, that goes between the 0th and last elements of <code>xs</code>. The declaration for the <code>pivot</code> traverser creates a &ldquo;cursor&rdquo; over the list <code>xs</code> that jumps around random locations in the list.</p> <p>The next interesting part of the language is a <strong>traverser macro</strong>. This thing, that looks like a function call (but isn&rsquo;t), performs an operation on the cursor. For instance, <code>pop!</code> removes the element at the cursor from the list, whereas <code>bisect!</code> categorizes the remaining elements in the cursor&rsquo;s list into two lists, using a boolean-returning lambda (written in Java syntax).</p> <p>Note that this implementation of <code>qselect</code> takes a function <code>c</code>, which it uses to judge the actual value of the number. This is because our <code>qselect</code> won&rsquo;t be finding <em>the</em> smallest number, but the number with the smallest difference with <code>n</code>. <code>n</code> will be factored in via the function.</p> <p>Next up, let&rsquo;s take a look at the function that uses <code>qselect</code>, <code>closestUnsorted</code>:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/sols/hw3.lang" data-first-line="21" data-last-line="46"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/sols/hw3.lang#L21-L46">hw3.lang</a>, lines 21 through 46</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">function closestUnsorted(xs, k, n) { </span></span><span class="line"><span class="cl"> let min = qselect(list(xs), k, (x) -&gt; abs(x - n)); </span></span><span class="line"><span class="cl"> let out = []; </span></span><span class="line"><span class="cl"> let countEqual = k; </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> traverser iter(list: xs, span: (0, len(xs))); </span></span><span class="line"><span class="cl"> while valid!(iter) { </span></span><span class="line"><span class="cl"> if abs(at!(iter)-n) &lt; abs(min-n) { </span></span><span class="line"><span class="cl"> let countEqual = countEqual - 1; </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl"> step!(iter); </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> traverser iter(list: xs, span: (0, len(xs))); </span></span><span class="line"><span class="cl"> while valid!(iter) { </span></span><span class="line"><span class="cl"> if abs(at!(iter)-n) == abs(min-n) and countEqual &gt; 0 { </span></span><span class="line"><span class="cl"> let countEqual = countEqual - 1; </span></span><span class="line"><span class="cl"> let out = out + [at!(iter)]; </span></span><span class="line"><span class="cl"> } elsif abs(at!(iter)-n) &lt; abs(min-n) { </span></span><span class="line"><span class="cl"> let out = out + [at!(iter)]; </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl"> step!(iter); </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> return out; </span></span><span class="line"><span class="cl">}</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Like we discussed, it finds the <code>k</code>th closest element (calling it <code>min</code>), and counts how many elements that are <strong>equal</strong> need to be included, by setting the number to <code>k</code> at first, and subtracting 1 for every number it encounters that&rsquo;s closer than <code>min</code>. Notice that we use the <code>valid!</code> and <code>step!</code> macros, which implement the operations we described above. Notice that the user doesn&rsquo;t deal with adding and subtracting numbers, and doing comparisons. All they have to do is ask &ldquo;am I still good to iterate?&rdquo;</p> <p>Next, let&rsquo;s take a look at <code>closestSorted</code>, which will require more traverser macros.</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/sols/hw3.lang" data-first-line="48" data-last-line="70"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/sols/hw3.lang#L48-L70">hw3.lang</a>, lines 48 through 70</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span><span class="lnt">65 </span><span class="lnt">66 </span><span class="lnt">67 </span><span class="lnt">68 </span><span class="lnt">69 </span><span class="lnt">70 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">function closestSorted(xs, k, n) { </span></span><span class="line"><span class="cl"> let start = bisect(xs, n); </span></span><span class="line"><span class="cl"> let counter = 0; </span></span><span class="line"><span class="cl"> traverser left(list: xs, span: (0, start), reverse: true); </span></span><span class="line"><span class="cl"> traverser right(list: xs, span: (start, len(xs))); </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> while counter != k and canstep!(left) and valid!(right) { </span></span><span class="line"><span class="cl"> if abs(at!(left, 1) - n) &lt; abs(at!(right) - n) { </span></span><span class="line"><span class="cl"> step!(left); </span></span><span class="line"><span class="cl"> } else { </span></span><span class="line"><span class="cl"> step!(right); </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl"> let counter = counter + 1; </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> while counter != k and (canstep!(left) or valid!(right)) { </span></span><span class="line"><span class="cl"> if canstep!(left) { step!(left); } </span></span><span class="line"><span class="cl"> else { step!(right); } </span></span><span class="line"><span class="cl"> let counter = counter + 1; </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> return subset!(left, right); </span></span><span class="line"><span class="cl">}</span></span></code></pre></td></tr></table> </div> </div> </div> <p>The first new macro is <code>canstep!</code>. This macro just verifies that the traverser can make another step. We need this for the &ldquo;reverse&rdquo; iterator, which indicates the lower bound of the range of numbers we want to return, because <code>subset!</code> (which itself is just Python&rsquo;s slice, like <code>xs[a:b]</code>), uses an inclusive bottom index, and thus, we can&rsquo;t afford to step it before knowing that we can, and that it&rsquo;s a better choice after the step.</p> <p>Similarly, we have the <code>at!(t, i)</code> macro, which looks at the traverser <code>t</code>, with offset <code>i</code>.</p> <p>We have two loops. The first loop runs as long as we can expand the range in both directions, and picks the better direction at each iteration. The second loop runs as long as we still want more numbers, but have already hit the edge of the list on the left or on the right.</p> <p>Finally, let&rsquo;s look at the solution to <code>xyz</code>:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/sols/hw3.lang" data-first-line="72" data-last-line="95"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/sols/hw3.lang#L72-L95">hw3.lang</a>, lines 72 through 95</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">72 </span><span class="lnt">73 </span><span class="lnt">74 </span><span class="lnt">75 </span><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span><span class="lnt">79 </span><span class="lnt">80 </span><span class="lnt">81 </span><span class="lnt">82 </span><span class="lnt">83 </span><span class="lnt">84 </span><span class="lnt">85 </span><span class="lnt">86 </span><span class="lnt">87 </span><span class="lnt">88 </span><span class="lnt">89 </span><span class="lnt">90 </span><span class="lnt">91 </span><span class="lnt">92 </span><span class="lnt">93 </span><span class="lnt">94 </span><span class="lnt">95 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">sorted function xyz(xs, k) { </span></span><span class="line"><span class="cl"> traverser x(list: xs, span: (0,len(xs))); </span></span><span class="line"><span class="cl"> let dest = []; </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> while valid!(x) { </span></span><span class="line"><span class="cl"> traverser z(list: xs, span: (pos!(x)+2,len(xs))); </span></span><span class="line"><span class="cl"> traverser y(list: xs, span: (pos!(x)+1,pos!(z))); </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> while valid!(y) and valid!(z) { </span></span><span class="line"><span class="cl"> if at!(x) + at!(y) == at!(z) { </span></span><span class="line"><span class="cl"> let dest = dest + [(at!(x), at!(y), at!(z))]; </span></span><span class="line"><span class="cl"> step!(z); </span></span><span class="line"><span class="cl"> } elsif at!(x) + at!(y) &gt; at!(z) { </span></span><span class="line"><span class="cl"> step!(z); </span></span><span class="line"><span class="cl"> } else { </span></span><span class="line"><span class="cl"> step!(y); </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> step!(x); </span></span><span class="line"><span class="cl"> } </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> return dest; </span></span><span class="line"><span class="cl">}</span></span></code></pre></td></tr></table> </div> </div> </div> <p>I won&rsquo;t go in depth, but notice that the expression in the <code>span</code> part of the <code>traverser</code> declaration can access another traverser. We treat as a feature the fact that this expression isn&rsquo;t immediately evaluated at the place of the traverser declaration. Rather, every time that a comparison for a traverser operation is performed, this expression is re-evaluated. This allows us to put dynamic bounds on traversers <code>y</code> and <code>z</code>, one of which must not exceed the other.</p> <p>Note also a new keyword that was just used: <code>sorted</code>. This is a harmless little language feature that automatically calls <code>.sort()</code> on the first argument of the function.</p> <p>This is more than enough to work with. Let&rsquo;s move on to the implementation.</p> <a href="#implementation"> <h4 id="implementation">Implementation</h4> </a> <p>Again, let&rsquo;s not go too far into the details of implementing the language from scratch. Instead, let&rsquo;s take a look into specific parts of the language that deserve attention.</p> <a href="#revenge-of-the-state-monad"> <h5 id="revenge-of-the-state-monad">Revenge of the State Monad</h5> </a> <p>Our previous language was, indeed, a respite from complexity. Translation was straightforward, and the resulting expressions and statements were plugged straight into a handwritten AST. We cannot get away with this here; the language is powerful enough to implement three list-based problems, which comes at the cost of increased complexity.</p> <p>We need, once again, to generate temporary variables. We also need to keep track of which variables are traversers, and the properties of these traversers, throughout each function of the language. We thus fall back to using <code>Control.Monad.State</code>:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/src/LanguageThree.hs" data-first-line="198" data-last-line="198"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/src/LanguageThree.hs#L198-L198">LanguageThree.hs</a>, line 198</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">198 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kr">type</span> <span class="kt">Translator</span> <span class="ow">=</span> <span class="kt">State</span> <span class="p">(</span><span class="kt">Map</span><span class="o">.</span><span class="kt">Map</span> <span class="kt">String</span> <span class="kt">ValidTraverserData</span><span class="p">,</span> <span class="p">[</span><span class="kt">Py</span><span class="o">.</span><span class="kt">PyStmt</span><span class="p">],</span> <span class="kt">Int</span><span class="p">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>There&rsquo;s one part of the state tuple that we haven&rsquo;t yet explained: the list of statements.</p> <a href="#generating-statements"> <h5 id="generating-statements">Generating Statements</h5> </a> <p>Recall that our translation function for expressions in the first homework had the type:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">translateExpr</span> <span class="ow">::</span> <span class="kt">Expr</span> <span class="ow">-&gt;</span> <span class="kt">Translator</span> <span class="p">([</span><span class="kt">Py</span><span class="o">.</span><span class="kt">PyStmt</span><span class="p">],</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">PyExpr</span><span class="p">)</span> </span></span></code></pre></div><p>We then had to use <code>do</code>-notation, and explicitly concatenate lists of emitted statements. In this language, I took an alternative route: I made the statements part of the state. They are thus implicitly generated and stored in the monad, and expression generators don&rsquo;t have to worry about concatenating them. When the program is ready to use the generated statements (say, when an <code>if</code>-statement needs to use the statements emitted by the condition expression), we retrieve them from the monad:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/src/LanguageThree.hs" data-first-line="228" data-last-line="234"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/src/LanguageThree.hs#L228-L234">LanguageThree.hs</a>, lines 228 through 234</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">228 </span><span class="lnt">229 </span><span class="lnt">230 </span><span class="lnt">231 </span><span class="lnt">232 </span><span class="lnt">233 </span><span class="lnt">234 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">collectStatements</span> <span class="ow">::</span> <span class="kt">Translator</span> <span class="n">a</span> <span class="ow">-&gt;</span> <span class="kt">Translator</span> <span class="p">([</span><span class="kt">Py</span><span class="o">.</span><span class="kt">PyStmt</span><span class="p">],</span> <span class="n">a</span><span class="p">)</span> </span></span><span class="line"><span class="cl"><span class="nf">collectStatements</span> <span class="n">t</span> <span class="ow">=</span> <span class="kr">do</span> </span></span><span class="line"><span class="cl"> <span class="n">modify</span> <span class="p">(</span><span class="n">first</span> <span class="o">$</span> <span class="n">const</span> <span class="kt">[]</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">a</span> <span class="ow">&lt;-</span> <span class="n">t</span> </span></span><span class="line"><span class="cl"> <span class="n">ss</span> <span class="ow">&lt;-</span> <span class="n">gets</span> <span class="o">$</span> <span class="nf">\</span><span class="p">(</span><span class="kr">_</span><span class="p">,</span> <span class="n">ss</span><span class="p">,</span> <span class="kr">_</span><span class="p">)</span> <span class="ow">-&gt;</span> <span class="n">ss</span> </span></span><span class="line"><span class="cl"> <span class="n">modify</span> <span class="p">(</span><span class="n">first</span> <span class="o">$</span> <span class="n">const</span> <span class="kt">[]</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">return</span> <span class="p">(</span><span class="n">ss</span><span class="p">,</span> <span class="n">a</span><span class="p">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>I should note, for transparency, that there&rsquo;s a bug in my use of this function. When I compile <code>if</code>-statements, I accidentally place statements generated by the condition into the body of the <code>if</code>. This bug doesn&rsquo;t manifest in the solutions to the homework problems, and so I decided not to spend any more time on fixing it.</p> <a href="#validating-traverser-declarations"> <h5 id="validating-traverser-declarations">Validating Traverser Declarations</h5> </a> <p>We declare two separate types that hold traverser data. The first is a kind of &ldquo;draft&rdquo; type, <code>TraverserData</code>:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/src/LanguageThree.hs" data-first-line="184" data-last-line="190"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/src/LanguageThree.hs#L184-L190">LanguageThree.hs</a>, lines 184 through 190</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">184 </span><span class="lnt">185 </span><span class="lnt">186 </span><span class="lnt">187 </span><span class="lnt">188 </span><span class="lnt">189 </span><span class="lnt">190 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kr">data</span> <span class="kt">TraverserBounds</span> <span class="ow">=</span> <span class="kt">Range</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">PyExpr</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">PyExpr</span> <span class="o">|</span> <span class="kt">Random</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kr">data</span> <span class="kt">TraverserData</span> <span class="ow">=</span> <span class="kt">TraverserData</span> </span></span><span class="line"><span class="cl"> <span class="p">{</span> <span class="n">list</span> <span class="ow">::</span> <span class="kt">Maybe</span> <span class="kt">String</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="n">bounds</span> <span class="ow">::</span> <span class="kt">Maybe</span> <span class="kt">TraverserBounds</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="n">rev</span> <span class="ow">::</span> <span class="kt">Bool</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This record holds all possible configurations of a traverser that occur as the program is iterating through the various <code>key: value</code> pairs in the declaration. For instance, at the very beginning of processing a traverser declaration, our program will use a &ldquo;default&rdquo; <code>TraverserData</code>, with all fields set to <code>Nothing</code> or their default value. This value will then be modified by the first key/value pair, changing, for instance, the list that the traverser operates on. This new modified <code>TraverserData</code> will then be modified by the next key/value pair, and so on. Doing this with every key/value pair (called an option in the below snippet) is effectively a foldl operation.</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/src/LanguageThree.hs" data-first-line="378" data-last-line="387"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/src/LanguageThree.hs#L378-L387">LanguageThree.hs</a>, lines 378 through 387</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">378 </span><span class="lnt">379 </span><span class="lnt">380 </span><span class="lnt">381 </span><span class="lnt">382 </span><span class="lnt">383 </span><span class="lnt">384 </span><span class="lnt">385 </span><span class="lnt">386 </span><span class="lnt">387 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">applyOption</span> <span class="ow">::</span> <span class="kt">TraverserData</span> <span class="ow">-&gt;</span> <span class="p">(</span><span class="kt">String</span><span class="p">,</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">PyExpr</span><span class="p">)</span> <span class="ow">-&gt;</span> <span class="kt">Maybe</span> <span class="kt">TraverserData</span> </span></span><span class="line"><span class="cl"><span class="nf">applyOption</span> <span class="n">td</span> <span class="p">(</span><span class="s">&#34;list&#34;</span><span class="p">,</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="n">s</span><span class="p">)</span> <span class="ow">=</span> </span></span><span class="line"><span class="cl"> <span class="n">return</span> <span class="o">$</span> <span class="n">td</span> <span class="p">{</span> <span class="n">list</span> <span class="ow">=</span> <span class="kt">Just</span> <span class="n">s</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="nf">applyOption</span> <span class="n">td</span> <span class="p">(</span><span class="s">&#34;span&#34;</span><span class="p">,</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">TupleLiteral</span> <span class="p">[</span><span class="n">f</span><span class="p">,</span> <span class="n">t</span><span class="p">])</span> <span class="ow">=</span> </span></span><span class="line"><span class="cl"> <span class="n">return</span> <span class="o">$</span> <span class="n">td</span> <span class="p">{</span> <span class="n">bounds</span> <span class="ow">=</span> <span class="kt">Just</span> <span class="o">$</span> <span class="kt">Range</span> <span class="n">f</span> <span class="n">t</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="nf">applyOption</span> <span class="n">td</span> <span class="p">(</span><span class="s">&#34;random&#34;</span><span class="p">,</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">BoolLiteral</span> <span class="kt">True</span><span class="p">)</span> <span class="ow">=</span> </span></span><span class="line"><span class="cl"> <span class="n">return</span> <span class="o">$</span> <span class="n">td</span> <span class="p">{</span> <span class="n">bounds</span> <span class="ow">=</span> <span class="kt">Just</span> <span class="kt">Random</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="nf">applyOption</span> <span class="n">td</span> <span class="p">(</span><span class="s">&#34;reverse&#34;</span><span class="p">,</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">BoolLiteral</span> <span class="n">b</span><span class="p">)</span> <span class="ow">=</span> </span></span><span class="line"><span class="cl"> <span class="n">return</span> <span class="o">$</span> <span class="n">td</span> <span class="p">{</span> <span class="n">rev</span> <span class="ow">=</span> <span class="n">b</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="nf">applyOption</span> <span class="n">td</span> <span class="kr">_</span> <span class="ow">=</span> <span class="kt">Nothing</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The data may not have all the required fields until the very end, and its type reflects that: <code>Maybe String</code> here, <code>Maybe TraverserBounds</code> there. We don&rsquo;t want to deal with unwrapping the <code>Maybe a</code> values every time we use the traverser, especially if we&rsquo;ve done so before. So, we define a <code>ValidTraverserData</code> record that does not have <code>Maybe</code> arguments, and thus, has all the required data. At the end of a traverser declaration, we attempt to translate a <code>TraverserData</code> into a <code>ValidTraverserData</code>, invoking <code>fail</code> if we can&rsquo;t, and storing the <code>ValidTraverserData</code> into the state otherwise:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/src/LanguageThree.hs" data-first-line="408" data-last-line="420"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/src/LanguageThree.hs#L408-L420">LanguageThree.hs</a>, lines 408 through 420</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">408 </span><span class="lnt">409 </span><span class="lnt">410 </span><span class="lnt">411 </span><span class="lnt">412 </span><span class="lnt">413 </span><span class="lnt">414 </span><span class="lnt">415 </span><span class="lnt">416 </span><span class="lnt">417 </span><span class="lnt">418 </span><span class="lnt">419 </span><span class="lnt">420 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">translateStmt</span> <span class="p">(</span><span class="kt">Traverser</span> <span class="n">s</span> <span class="n">os</span><span class="p">)</span> <span class="ow">=</span> </span></span><span class="line"><span class="cl"> <span class="n">foldlM</span> <span class="n">applyOption</span> <span class="n">defaultTraverser</span> <span class="o">&lt;$&gt;</span> <span class="n">mapM</span> <span class="n">translateOption</span> <span class="n">os</span> <span class="o">&gt;&gt;=</span> <span class="n">saveTraverser</span> </span></span><span class="line"><span class="cl"> <span class="kr">where</span> </span></span><span class="line"><span class="cl"> <span class="n">saveTraverser</span> <span class="ow">::</span> <span class="kt">Maybe</span> <span class="kt">TraverserData</span> <span class="ow">-&gt;</span> <span class="kt">Translator</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">PyStmt</span> </span></span><span class="line"><span class="cl"> <span class="n">saveTraverser</span> <span class="p">(</span><span class="kt">Just</span> <span class="p">(</span><span class="n">td</span><span class="o">@</span><span class="kt">TraverserData</span> <span class="p">{</span> <span class="n">list</span> <span class="ow">=</span> <span class="kt">Just</span> <span class="n">l</span><span class="p">,</span> <span class="n">bounds</span> <span class="ow">=</span> <span class="kt">Just</span> <span class="n">bs</span><span class="p">}))</span> <span class="ow">=</span> </span></span><span class="line"><span class="cl"> <span class="n">putTraverser</span> <span class="n">s</span> <span class="n">vtd</span> <span class="o">$&gt;</span> <span class="n">translateInitialBounds</span> <span class="n">s</span> <span class="n">vtd</span> </span></span><span class="line"><span class="cl"> <span class="kr">where</span> </span></span><span class="line"><span class="cl"> <span class="n">vtd</span> <span class="ow">=</span> <span class="kt">ValidTraverserData</span> </span></span><span class="line"><span class="cl"> <span class="p">{</span> <span class="n">validList</span> <span class="ow">=</span> <span class="n">l</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="n">validBounds</span> <span class="ow">=</span> <span class="n">bs</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="n">validRev</span> <span class="ow">=</span> <span class="n">rev</span> <span class="n">td</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">saveTraverser</span> <span class="kt">Nothing</span> <span class="ow">=</span> <span class="n">fail</span> <span class="s">&#34;Invalid traverser (!)&#34;</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Then, every time we retrieve a traverser from the state, define a lookup monadic operation like this:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/src/LanguageThree.hs" data-first-line="240" data-last-line="244"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/src/LanguageThree.hs#L240-L244">LanguageThree.hs</a>, lines 240 through 244</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">240 </span><span class="lnt">241 </span><span class="lnt">242 </span><span class="lnt">243 </span><span class="lnt">244 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">requireTraverser</span> <span class="ow">::</span> <span class="kt">String</span> <span class="ow">-&gt;</span> <span class="kt">Translator</span> <span class="kt">ValidTraverserData</span> </span></span><span class="line"><span class="cl"><span class="nf">requireTraverser</span> <span class="n">s</span> <span class="ow">=</span> <span class="n">gets</span> <span class="p">(</span><span class="nf">\</span><span class="p">(</span><span class="n">m</span><span class="p">,</span> <span class="kr">_</span><span class="p">,</span> <span class="kr">_</span><span class="p">)</span> <span class="ow">-&gt;</span> <span class="kt">Map</span><span class="o">.</span><span class="n">lookup</span> <span class="n">s</span> <span class="n">m</span><span class="p">)</span> <span class="o">&gt;&gt;=</span> <span class="n">handleMaybe</span> </span></span><span class="line"><span class="cl"> <span class="kr">where</span> </span></span><span class="line"><span class="cl"> <span class="n">handleMaybe</span> <span class="kt">Nothing</span> <span class="ow">=</span> <span class="n">fail</span> <span class="s">&#34;Invalid traverser&#34;</span> </span></span><span class="line"><span class="cl"> <span class="n">handleMaybe</span> <span class="p">(</span><span class="kt">Just</span> <span class="n">vtd</span><span class="p">)</span> <span class="ow">=</span> <span class="n">return</span> <span class="n">vtd</span></span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#compiling-macros"> <h5 id="compiling-macros">Compiling Macros</h5> </a> <p>I didn&rsquo;t call them macros for no reason. Clearly, we don&rsquo;t want to generate code that <span class="sidenote"> <label class="sidenote-label" for="increment-note">calls functions only to increment an index.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="increment-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> In fact, there's no easy way to do this at all. Python's integers (if we choose to represent our traversers using integers), are immutable. Furthermore, unlike C++, where passing by reference allows a function to change its parameters "outside" the call, Python offers no way to reassign a different value to a variable given to a function. <br><br> For an example use of C++'s pass-by-reference mechanic, consider <code>std::swap</code>: it's a function, but it modifies the two variables given to it. There's no way to generically implement such a function in Python. <span class="sidenote-delimiter">]</span> </span> </span> We also can&rsquo;t allow arbitrary expressions to serve as traversers: our translator keeps some context about which variables are traversers, what their bounds are, and how they behave. Thus, <strong>calls to traverser macros are very much macros</strong>: they operate on AST nodes, and <strong>require</strong> that their first argument is a variable, named like the traverser. We use the <code>requireTraverser</code> monadic operation to get the traverser associated with the given variable name, and then perform the operation as intended. The <code>at!(t)</code> operation is straightforward:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/src/LanguageThree.hs" data-first-line="317" data-last-line="319"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/src/LanguageThree.hs#L317-L319">LanguageThree.hs</a>, lines 317 through 319</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">317 </span><span class="lnt">318 </span><span class="lnt">319 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">translateExpr</span> <span class="p">(</span><span class="kt">TraverserCall</span> <span class="s">&#34;at&#34;</span> <span class="p">[</span><span class="kt">Var</span> <span class="n">s</span><span class="p">])</span> <span class="ow">=</span> <span class="kr">do</span> </span></span><span class="line"><span class="cl"> <span class="n">l</span> <span class="ow">&lt;-</span> <span class="n">validList</span> <span class="o">&lt;$&gt;</span> <span class="n">requireTraverser</span> <span class="n">s</span> </span></span><span class="line"><span class="cl"> <span class="n">return</span> <span class="o">$</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Access</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="n">l</span><span class="p">)</span> <span class="p">[</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="n">s</span><span class="p">]</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The <code>at!(t,i)</code> is less so, since it deals with the intricacies of accessing the list at either a positive of negative offset, depending on the direction of the traverser. We implement a function to properly generate an expression for the offset:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/src/LanguageThree.hs" data-first-line="246" data-last-line="249"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/src/LanguageThree.hs#L246-L249">LanguageThree.hs</a>, lines 246 through 249</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">246 </span><span class="lnt">247 </span><span class="lnt">248 </span><span class="lnt">249 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">traverserIncrement</span> <span class="ow">::</span> <span class="kt">Bool</span> <span class="ow">-&gt;</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">PyExpr</span> <span class="ow">-&gt;</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">PyExpr</span> <span class="ow">-&gt;</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">PyExpr</span> </span></span><span class="line"><span class="cl"><span class="nf">traverserIncrement</span> <span class="n">rev</span> <span class="n">by</span> <span class="n">e</span> <span class="ow">=</span> </span></span><span class="line"><span class="cl"> <span class="kt">Py</span><span class="o">.</span><span class="kt">BinOp</span> <span class="n">op</span> <span class="n">e</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">BinOp</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Multiply</span> <span class="n">by</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">IntLiteral</span> <span class="mi">1</span><span class="p">))</span> </span></span><span class="line"><span class="cl"> <span class="kr">where</span> <span class="n">op</span> <span class="ow">=</span> <span class="kr">if</span> <span class="n">rev</span> <span class="kr">then</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Subtract</span> <span class="kr">else</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Add</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We then implement <code>at!(t,i)</code> as follows:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/src/LanguageThree.hs" data-first-line="320" data-last-line="323"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/src/LanguageThree.hs#L320-L323">LanguageThree.hs</a>, lines 320 through 323</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">320 </span><span class="lnt">321 </span><span class="lnt">322 </span><span class="lnt">323 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">translateExpr</span> <span class="p">(</span><span class="kt">TraverserCall</span> <span class="s">&#34;at&#34;</span> <span class="p">[</span><span class="kt">Var</span> <span class="n">s</span><span class="p">,</span> <span class="kt">IntLiteral</span> <span class="n">i</span><span class="p">])</span> <span class="ow">=</span> <span class="kr">do</span> </span></span><span class="line"><span class="cl"> <span class="n">vtd</span> <span class="ow">&lt;-</span> <span class="n">requireTraverser</span> <span class="n">s</span> </span></span><span class="line"><span class="cl"> <span class="n">return</span> <span class="o">$</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Access</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="o">$</span> <span class="n">validList</span> <span class="n">vtd</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="n">traverserIncrement</span> <span class="p">(</span><span class="n">validRev</span> <span class="n">vtd</span><span class="p">)</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">IntLiteral</span> <span class="n">i</span><span class="p">)</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="n">s</span><span class="p">)]</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The most complicated macro is <code>bisect!</code>. It must be able to step the traverser, and also return a tuple of two lists that the bisection yields. We also prefer that it didn&rsquo;t pollute the environment with extra variables. To achieve this, we want <code>bisect!</code> to be a function call. We want this function to implement the iteration and list construction.</p> <p><code>bisect!</code>, by definition, takes a lambda. This lambda, in our language, is declared in the lexical scope in which <code>bisect!</code> is called. Thus, to guarantee correct translation, we must do one of two things:</p> <ol> <li>Translate 1-to-1, and create a lambda, passing it to a fixed <code>bisect</code> function declared elsewhere.</li> <li>Translate to a nested function declaration, <span class="sidenote"> <label class="sidenote-label" for="inline-note">inlining the lambda.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="inline-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Inlining, in this case, means replacing a call to a function with the function's body. We do this to prevent the overhead of calling a function, which typically involves pushing on a stack and other extraneous work. If our function is simple, like a simple comparison, it doesn't make sense to spend the effort calling it. <span class="sidenote-delimiter">]</span> </span> </span> </li> </ol> <p>Since I quite like the idea of inlining a lambda, let&rsquo;s settle for that. To do this, we pull a fresh temporary variable and declare a function, into which we place the traverser iteration code, as well as the body of the lambda, with the variable substituted for the list access expression. <span class="sidenote"> <label class="sidenote-label" for="nonlocal-note">Here&rsquo;s the code:</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="nonlocal-note"></input><span class="sidenote-content sidenote-left"><span class="sidenote-delimiter">[note:</span> Reading the lexical scope is one thing, but modifying it is another. To prevent accidental changes to the variables outside a nested function, Python assumes that variables assigned inside the function body are local to the function. Thus, to make sure changing our variable (the traverser index) has an effect outside the function (as it should) we must include the <code>nonlocal</code> keyword, telling Python that we're not declaring a new, local variable, but mutating the old one. <span class="sidenote-delimiter">]</span> </span> </span> </p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/src/LanguageThree.hs" data-first-line="342" data-last-line="363"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/src/LanguageThree.hs#L342-L363">LanguageThree.hs</a>, lines 342 through 363</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">342 </span><span class="lnt">343 </span><span class="lnt">344 </span><span class="lnt">345 </span><span class="lnt">346 </span><span class="lnt">347 </span><span class="lnt">348 </span><span class="lnt">349 </span><span class="lnt">350 </span><span class="lnt">351 </span><span class="lnt">352 </span><span class="lnt">353 </span><span class="lnt">354 </span><span class="lnt">355 </span><span class="lnt">356 </span><span class="lnt">357 </span><span class="lnt">358 </span><span class="lnt">359 </span><span class="lnt">360 </span><span class="lnt">361 </span><span class="lnt">362 </span><span class="lnt">363 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">translateExpr</span> <span class="p">(</span><span class="kt">TraverserCall</span> <span class="s">&#34;bisect&#34;</span> <span class="p">[</span><span class="kt">Var</span> <span class="n">s</span><span class="p">,</span> <span class="kt">Lambda</span> <span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="n">e</span><span class="p">])</span> <span class="ow">=</span> <span class="kr">do</span> </span></span><span class="line"><span class="cl"> <span class="n">vtd</span> <span class="ow">&lt;-</span> <span class="n">requireTraverser</span> <span class="n">s</span> </span></span><span class="line"><span class="cl"> <span class="n">newTemp</span> <span class="ow">&lt;-</span> <span class="n">freshTemp</span> </span></span><span class="line"><span class="cl"> <span class="n">lambdaExpr</span> <span class="ow">&lt;-</span> <span class="n">translateExpr</span> <span class="n">e</span> </span></span><span class="line"><span class="cl"> <span class="kr">let</span> <span class="n">access</span> <span class="ow">=</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Access</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="o">$</span> <span class="n">validList</span> <span class="n">vtd</span><span class="p">)</span> <span class="p">[</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="n">s</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="kr">let</span> <span class="n">translated</span> <span class="ow">=</span> <span class="n">substituteVariable</span> <span class="n">x</span> <span class="n">access</span> <span class="n">lambdaExpr</span> </span></span><span class="line"><span class="cl"> <span class="kr">let</span> <span class="n">append</span> <span class="n">s</span> <span class="ow">=</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">FunctionCall</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Member</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="n">s</span><span class="p">)</span> <span class="s">&#34;append&#34;</span><span class="p">)</span> <span class="p">[</span> <span class="n">access</span> <span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="kr">let</span> <span class="n">bisectStmt</span> <span class="ow">=</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">FunctionDef</span> <span class="n">newTemp</span> <span class="kt">[]</span> </span></span><span class="line"><span class="cl"> <span class="p">[</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Nonlocal</span> <span class="p">[</span><span class="n">s</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Assign</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">VarPat</span> <span class="s">&#34;l&#34;</span><span class="p">)</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">ListLiteral</span> <span class="kt">[]</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Assign</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">VarPat</span> <span class="s">&#34;r&#34;</span><span class="p">)</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">ListLiteral</span> <span class="kt">[]</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">While</span> <span class="p">(</span><span class="n">traverserValid</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="n">s</span><span class="p">)</span> <span class="n">vtd</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">[</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">IfElse</span> <span class="n">translated</span> </span></span><span class="line"><span class="cl"> <span class="p">[</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Standalone</span> <span class="o">$</span> <span class="n">append</span> <span class="s">&#34;l&#34;</span> <span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="kt">[]</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="kt">Just</span> <span class="p">[</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Standalone</span> <span class="o">$</span> <span class="n">append</span> <span class="s">&#34;r&#34;</span> <span class="p">])</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="n">traverserStep</span> <span class="n">s</span> <span class="n">vtd</span> </span></span><span class="line"><span class="cl"> <span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Return</span> <span class="o">$</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">TupleLiteral</span> <span class="p">[</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;l&#34;</span><span class="p">,</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;r&#34;</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="n">emitStatement</span> <span class="n">bisectStmt</span> </span></span><span class="line"><span class="cl"> <span class="n">return</span> <span class="o">$</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">FunctionCall</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="n">newTemp</span><span class="p">)</span> <span class="kt">[]</span></span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#the-output"> <h3 id="the-output">The Output</h3> </a> <p>Let&rsquo;s see what the compiler spits out:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Python" data-lang="Python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">bisect</span> <span class="kn">import</span> <span class="n">bisect</span> </span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">random</span> </span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">qselect</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span><span class="n">k</span><span class="p">,</span><span class="n">c</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">xs</span><span class="o">==</span><span class="p">[]:</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="n">bisector</span> <span class="o">=</span> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="n">pivot</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">randrange</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">xs</span><span class="p">))</span> </span></span><span class="line"><span class="cl"> <span class="n">pivotE</span> <span class="o">=</span> <span class="n">xs</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="n">pivot</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">temp1</span><span class="p">():</span> </span></span><span class="line"><span class="cl"> <span class="k">nonlocal</span> <span class="n">bisector</span> </span></span><span class="line"><span class="cl"> <span class="n">l</span> <span class="o">=</span> <span class="p">[]</span> </span></span><span class="line"><span class="cl"> <span class="n">r</span> <span class="o">=</span> <span class="p">[]</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="n">bisector</span><span class="o">&lt;</span><span class="nb">len</span><span class="p">(</span><span class="n">xs</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">c</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="n">bisector</span><span class="p">])</span><span class="o">&lt;</span><span class="n">c</span><span class="p">(</span><span class="n">pivotE</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">l</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="n">bisector</span><span class="p">])</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">r</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="n">bisector</span><span class="p">])</span> </span></span><span class="line"><span class="cl"> <span class="n">bisector</span> <span class="o">=</span> <span class="n">bisector</span><span class="o">+</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">(</span><span class="n">l</span><span class="p">,</span> <span class="n">r</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">leftList</span><span class="p">,</span><span class="n">rightList</span><span class="p">)</span> <span class="o">=</span> <span class="n">temp1</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">k</span><span class="o">&gt;</span><span class="nb">len</span><span class="p">(</span><span class="n">leftList</span><span class="p">)</span><span class="o">+</span><span class="mi">1</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">qselect</span><span class="p">(</span><span class="n">rightList</span><span class="p">,</span> <span class="n">k</span><span class="o">-</span><span class="nb">len</span><span class="p">(</span><span class="n">leftList</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">c</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">elif</span> <span class="n">k</span><span class="o">==</span><span class="nb">len</span><span class="p">(</span><span class="n">leftList</span><span class="p">)</span><span class="o">+</span><span class="mi">1</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">pivotE</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">qselect</span><span class="p">(</span><span class="n">leftList</span><span class="p">,</span> <span class="n">k</span><span class="p">,</span> <span class="n">c</span><span class="p">)</span> </span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">closestUnsorted</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span><span class="n">k</span><span class="p">,</span><span class="n">n</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="nb">min</span> <span class="o">=</span> <span class="n">qselect</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="n">xs</span><span class="p">),</span> <span class="n">k</span><span class="p">,</span> <span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="nb">abs</span><span class="p">(</span><span class="n">x</span><span class="o">-</span><span class="n">n</span><span class="p">)))</span> </span></span><span class="line"><span class="cl"> <span class="n">out</span> <span class="o">=</span> <span class="p">[]</span> </span></span><span class="line"><span class="cl"> <span class="n">countEqual</span> <span class="o">=</span> <span class="n">k</span> </span></span><span class="line"><span class="cl"> <span class="nb">iter</span> <span class="o">=</span> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="nb">iter</span><span class="o">&lt;</span><span class="nb">len</span><span class="p">(</span><span class="n">xs</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">abs</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="nb">iter</span><span class="p">]</span><span class="o">-</span><span class="n">n</span><span class="p">)</span><span class="o">&lt;</span><span class="nb">abs</span><span class="p">(</span><span class="nb">min</span><span class="o">-</span><span class="n">n</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">countEqual</span> <span class="o">=</span> <span class="n">countEqual</span><span class="o">-</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="nb">iter</span> <span class="o">=</span> <span class="nb">iter</span><span class="o">+</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="nb">iter</span> <span class="o">=</span> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="nb">iter</span><span class="o">&lt;</span><span class="nb">len</span><span class="p">(</span><span class="n">xs</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">abs</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="nb">iter</span><span class="p">]</span><span class="o">-</span><span class="n">n</span><span class="p">)</span><span class="o">==</span><span class="nb">abs</span><span class="p">(</span><span class="nb">min</span><span class="o">-</span><span class="n">n</span><span class="p">)</span> <span class="ow">and</span> <span class="n">countEqual</span><span class="o">&gt;</span><span class="mi">0</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">countEqual</span> <span class="o">=</span> <span class="n">countEqual</span><span class="o">-</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="n">out</span> <span class="o">=</span> <span class="n">out</span><span class="o">+</span><span class="p">[</span><span class="n">xs</span><span class="p">[</span><span class="nb">iter</span><span class="p">]]</span> </span></span><span class="line"><span class="cl"> <span class="k">elif</span> <span class="nb">abs</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="nb">iter</span><span class="p">]</span><span class="o">-</span><span class="n">n</span><span class="p">)</span><span class="o">&lt;</span><span class="nb">abs</span><span class="p">(</span><span class="nb">min</span><span class="o">-</span><span class="n">n</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">out</span> <span class="o">=</span> <span class="n">out</span><span class="o">+</span><span class="p">[</span><span class="n">xs</span><span class="p">[</span><span class="nb">iter</span><span class="p">]]</span> </span></span><span class="line"><span class="cl"> <span class="nb">iter</span> <span class="o">=</span> <span class="nb">iter</span><span class="o">+</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">out</span> </span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">closestSorted</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span><span class="n">k</span><span class="p">,</span><span class="n">n</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">start</span> <span class="o">=</span> <span class="n">bisect</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">counter</span> <span class="o">=</span> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="n">left</span> <span class="o">=</span> <span class="n">start</span> </span></span><span class="line"><span class="cl"> <span class="n">right</span> <span class="o">=</span> <span class="n">start</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="n">counter</span><span class="o">!=</span><span class="n">k</span> <span class="ow">and</span> <span class="n">left</span><span class="o">-</span><span class="mi">1</span><span class="o">*</span><span class="mi">1</span><span class="o">&gt;=</span><span class="mi">0</span> <span class="ow">and</span> <span class="n">right</span><span class="o">&lt;</span><span class="nb">len</span><span class="p">(</span><span class="n">xs</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">abs</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="n">left</span><span class="o">-</span><span class="mi">1</span><span class="o">*</span><span class="mi">1</span><span class="p">]</span><span class="o">-</span><span class="n">n</span><span class="p">)</span><span class="o">&lt;</span><span class="nb">abs</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="n">right</span><span class="p">]</span><span class="o">-</span><span class="n">n</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">left</span> <span class="o">=</span> <span class="n">left</span><span class="o">-</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">right</span> <span class="o">=</span> <span class="n">right</span><span class="o">+</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="n">counter</span> <span class="o">=</span> <span class="n">counter</span><span class="o">+</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="n">counter</span><span class="o">!=</span><span class="n">k</span> <span class="ow">and</span> <span class="p">(</span><span class="n">left</span><span class="o">-</span><span class="mi">1</span><span class="o">*</span><span class="mi">1</span><span class="o">&gt;=</span><span class="mi">0</span> <span class="ow">or</span> <span class="n">right</span><span class="o">&lt;</span><span class="nb">len</span><span class="p">(</span><span class="n">xs</span><span class="p">)):</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">left</span><span class="o">-</span><span class="mi">1</span><span class="o">*</span><span class="mi">1</span><span class="o">&gt;=</span><span class="mi">0</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">left</span> <span class="o">=</span> <span class="n">left</span><span class="o">-</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">right</span> <span class="o">=</span> <span class="n">right</span><span class="o">+</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="n">counter</span> <span class="o">=</span> <span class="n">counter</span><span class="o">+</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">xs</span><span class="p">[(</span><span class="n">left</span><span class="p">):(</span><span class="n">right</span><span class="p">)]</span> </span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">xyz</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span><span class="n">k</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">xs</span><span class="o">.</span><span class="n">sort</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="n">x</span> <span class="o">=</span> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="n">dest</span> <span class="o">=</span> <span class="p">[]</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="n">x</span><span class="o">&lt;</span><span class="nb">len</span><span class="p">(</span><span class="n">xs</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">z</span> <span class="o">=</span> <span class="n">x</span><span class="o">+</span><span class="mi">2</span> </span></span><span class="line"><span class="cl"> <span class="n">y</span> <span class="o">=</span> <span class="n">x</span><span class="o">+</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="n">y</span><span class="o">&lt;</span><span class="n">z</span> <span class="ow">and</span> <span class="n">z</span><span class="o">&lt;</span><span class="nb">len</span><span class="p">(</span><span class="n">xs</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">xs</span><span class="p">[</span><span class="n">x</span><span class="p">]</span><span class="o">+</span><span class="n">xs</span><span class="p">[</span><span class="n">y</span><span class="p">]</span><span class="o">==</span><span class="n">xs</span><span class="p">[</span><span class="n">z</span><span class="p">]:</span> </span></span><span class="line"><span class="cl"> <span class="n">dest</span> <span class="o">=</span> <span class="n">dest</span><span class="o">+</span><span class="p">[(</span><span class="n">xs</span><span class="p">[</span><span class="n">x</span><span class="p">],</span> <span class="n">xs</span><span class="p">[</span><span class="n">y</span><span class="p">],</span> <span class="n">xs</span><span class="p">[</span><span class="n">z</span><span class="p">])]</span> </span></span><span class="line"><span class="cl"> <span class="n">z</span> <span class="o">=</span> <span class="n">z</span><span class="o">+</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="k">elif</span> <span class="n">xs</span><span class="p">[</span><span class="n">x</span><span class="p">]</span><span class="o">+</span><span class="n">xs</span><span class="p">[</span><span class="n">y</span><span class="p">]</span><span class="o">&gt;</span><span class="n">xs</span><span class="p">[</span><span class="n">z</span><span class="p">]:</span> </span></span><span class="line"><span class="cl"> <span class="n">z</span> <span class="o">=</span> <span class="n">z</span><span class="o">+</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">y</span> <span class="o">=</span> <span class="n">y</span><span class="o">+</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="n">x</span> <span class="o">=</span> <span class="n">x</span><span class="o">+</span><span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">dest</span> </span></span></code></pre></div><p>Observe that the generated code just uses indices, <code>+</code>, <code>-</code>, and various comparison operators. Our traverser is an example of a <strong>zero cost abstraction</strong>, a feature that, conceptually, operates at a higher level, making us no longer worry about adding, subtracting, and comparing numbers, while, in the final output, not damaging the performance of safety of the code. Also observe the various <code>0</code> standalone statements. This is an issue with the translator: traverser macros may not always yield an expression, but the type of <code>translateExpr</code> and <code>translateStmt</code> effectively requires one. Thus, when a macro doesn&rsquo;t generate anything useful, we give it the placeholder expression <code>0</code>.</p> <p>That concludes this third post in the series. I hope to see you in the next one!</p> A Language for an Assignment - Homework 2 https://danilafe.com/blog/01_cs325_languages_hw2/ Mon, 30 Dec 2019 20:05:10 -0800 https://danilafe.com/blog/01_cs325_languages_hw2/ <p>After the madness of the <a href="https://danilafe.com/blog/00_cs325_languages_hw1/">language for homework 1</a>, the solution to the second homework offers a moment of respite. Let&rsquo;s get right into the problems, shall we?</p> <a href="#homework-2"> <h3 id="homework-2">Homework 2</h3> </a> <p>Besides some free-response questions, the homework contains two problems. The first:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/hws/hw2.txt" data-first-line="29" data-last-line="34"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/hws/hw2.txt#L29-L34">hw2.txt</a>, lines 29 through 34</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">1. Implement mergesort. </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> &gt;&gt;&gt; mergesort([4, 2, 5, 1, 6, 3]) </span></span><span class="line"><span class="cl"> [1, 2, 3, 4, 5, 6] </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> Filename: msort.py</span></span></code></pre></td></tr></table> </div> </div> </div> <p>And the second:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/hws/hw2.txt" data-first-line="36" data-last-line="44"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/hws/hw2.txt#L36-L44">hw2.txt</a>, lines 36 through 44</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">2. Calculate the number of inversions in a list. </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> &gt;&gt;&gt; num_inversions([4, 1, 3, 2]) </span></span><span class="line"><span class="cl"> 4 </span></span><span class="line"><span class="cl"> &gt;&gt;&gt; num_inversions([2, 4, 1, 3]) </span></span><span class="line"><span class="cl"> 3 </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> Filename: inversions.py </span></span><span class="line"><span class="cl"> Must run in O(nlogn) time.</span></span></code></pre></td></tr></table> </div> </div> </div> <p>At first glance, it&rsquo;s not obvious why these problems are good for us. However, there&rsquo;s one key observation: <strong><code>num_inversions</code> can be implemented using a slightly-modified <code>mergesort</code></strong>. The trick is to maintain a counter of inversions in every recursive call to <code>mergesort</code>, updating it every time we take an element from the <span class="sidenote"> <label class="sidenote-label" for="right-note">right list</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="right-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> If this nomenclature is not clear to you, recall that mergesort divides a list into two smaller lists. The "right list" refers to the second of the two, because if you visualize the original list as a rectangle, and cut it in half (vertically, down the middle), then the second list (from the left) is on the right. <span class="sidenote-delimiter">]</span> </span> </span> while there are still elements in the <span class="sidenote"> <label class="sidenote-label" for="left-note">left list</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="left-note"></input><span class="sidenote-content sidenote-left"><span class="sidenote-delimiter">[note:</span> Why this is the case is left as an exercise to the reader. <span class="sidenote-delimiter">]</span> </span> </span> . When we return from the call, we add up the number of inversions from running <code>num_inversions</code> on the smaller lists, and the number of inversions that we counted as I described. We then return both the total number of inversions and the sorted list.</p> <p>So, we either perform the standard mergesort, or we perform mergesort with additional steps added on. The additional steps can be divided into three general categories:</p> <ol> <li><strong>Initialization</strong>: We create / set some initial state. This state doesn&rsquo;t depend on the lists or anything else.</li> <li><strong>Effect</strong>: Each time that an element is moved from one of the two smaller lists into the output list, we may change the state in some way (create an effect).</li> <li><strong>Combination</strong>: The final state, and the results of the two sub-problem states, are combined into the output of the function.</li> </ol> <p>This is all very abstract. In the concrete case of inversions, these steps are as follows:</p> <ol> <li><strong>Initializtion</strong>: The initial state, which is just the counter, is set to 0.</li> <li><strong>Effect</strong>: Each time an element is moved, if it comes from the right list, the number of inversions is updated.</li> <li><strong>Combination</strong>: We update the state, simply adding the left and right inversion counts.</li> </ol> <p>We can make a language out of this!</p> <a href="#a-language"> <h3 id="a-language">A Language</h3> </a> <p>Again, let&rsquo;s start by visualizing what the solution will look like. How about this:</p> <div class="highlight-group" > <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/sols/hw2.lang">hw2.lang</a>, entire file</div><pre><code>state 0; effect { if(SOURCE == R) { STATE = STATE + |LEFT|; } } combine { STATE = STATE + LSTATE + RSTATE; } </code></pre> </div> <p>We divide the code into the same three steps that we described above. The first section is the initial state. Since it doesn&rsquo;t depend on anything, we expect it to be some kind of literal, like an integer. Next, we have the effect section, which has access to the variables below:</p> <ul> <li><code>STATE</code>, to manipulate or check the current state.</li> <li><code>LEFT</code> and <code>RIGHT</code>, to access the two lists being merged.</li> <li><code>L</code> and <code>R</code>, constants that are used to compare against the <code>SOURCE</code> variable.</li> <li><code>SOURCE</code>, to denote which list a number came from.</li> <li><code>LSTATE</code> and <code>RSTATE</code>, to denote the final states from the two subproblems.</li> </ul> <p>We use an <code>if</code>-statement to check if the element that was popped came from the right list (by checking <code>SOURCE == R</code>). If it did, we increment the counter (state) by the proper amount. In the combine step, which has access to the same variables, we simply increment the state by the counters from the left and right solutions, stored in <code>LSTATE</code> and <code>RSTATE</code>. That&rsquo;s it!</p> <a href="#implementation"> <h4 id="implementation">Implementation</h4> </a> <p>The implementation is not tricky at all. We don&rsquo;t need to use monads like we did last time, and nor do we have to perform any fancy Python nested function declarations.</p> <p>To keep with the Python convention of lowercase variables, we&rsquo;ll translate the uppercase &ldquo;global&rdquo; variables to lowercase. We&rsquo;ll do it like so:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/src/LanguageTwo.hs" data-first-line="167" data-last-line="176"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/src/LanguageTwo.hs#L167-L176">LanguageTwo.hs</a>, lines 167 through 176</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">167 </span><span class="lnt">168 </span><span class="lnt">169 </span><span class="lnt">170 </span><span class="lnt">171 </span><span class="lnt">172 </span><span class="lnt">173 </span><span class="lnt">174 </span><span class="lnt">175 </span><span class="lnt">176 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">translateExpr</span> <span class="p">(</span><span class="kt">Var</span> <span class="n">s</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">s</span> <span class="o">==</span> <span class="s">&#34;SOURCE&#34;</span> <span class="ow">=</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;source&#34;</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">s</span> <span class="o">==</span> <span class="s">&#34;LEFT&#34;</span> <span class="ow">=</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;left&#34;</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">s</span> <span class="o">==</span> <span class="s">&#34;RIGHT&#34;</span> <span class="ow">=</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;right&#34;</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">s</span> <span class="o">==</span> <span class="s">&#34;STATE&#34;</span> <span class="ow">=</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;state&#34;</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">s</span> <span class="o">==</span> <span class="s">&#34;LSTATE&#34;</span> <span class="ow">=</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;ls&#34;</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">s</span> <span class="o">==</span> <span class="s">&#34;RSTATE&#34;</span> <span class="ow">=</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;rs&#34;</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">s</span> <span class="o">==</span> <span class="s">&#34;L&#34;</span> <span class="ow">=</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">IntLiteral</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">s</span> <span class="o">==</span> <span class="s">&#34;R&#34;</span> <span class="ow">=</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">IntLiteral</span> <span class="mi">2</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">otherwise</span> <span class="ow">=</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="n">s</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Note that we translated <code>L</code> and <code>R</code> to integer literals. We&rsquo;ll indicate the source of each element with an integer, since there&rsquo;s no real point to representing it with a string or a variable. We&rsquo;ll need to be aware of this when we implement the actual, generic mergesort code. Let&rsquo;s do that now:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/src/LanguageTwo.hs" data-first-line="101" data-last-line="161"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/src/LanguageTwo.hs#L101-L161">LanguageTwo.hs</a>, lines 101 through 161</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">101 </span><span class="lnt">102 </span><span class="lnt">103 </span><span class="lnt">104 </span><span class="lnt">105 </span><span class="lnt">106 </span><span class="lnt">107 </span><span class="lnt">108 </span><span class="lnt">109 </span><span class="lnt">110 </span><span class="lnt">111 </span><span class="lnt">112 </span><span class="lnt">113 </span><span class="lnt">114 </span><span class="lnt">115 </span><span class="lnt">116 </span><span class="lnt">117 </span><span class="lnt">118 </span><span class="lnt">119 </span><span class="lnt">120 </span><span class="lnt">121 </span><span class="lnt">122 </span><span class="lnt">123 </span><span class="lnt">124 </span><span class="lnt">125 </span><span class="lnt">126 </span><span class="lnt">127 </span><span class="lnt">128 </span><span class="lnt">129 </span><span class="lnt">130 </span><span class="lnt">131 </span><span class="lnt">132 </span><span class="lnt">133 </span><span class="lnt">134 </span><span class="lnt">135 </span><span class="lnt">136 </span><span class="lnt">137 </span><span class="lnt">138 </span><span class="lnt">139 </span><span class="lnt">140 </span><span class="lnt">141 </span><span class="lnt">142 </span><span class="lnt">143 </span><span class="lnt">144 </span><span class="lnt">145 </span><span class="lnt">146 </span><span class="lnt">147 </span><span class="lnt">148 </span><span class="lnt">149 </span><span class="lnt">150 </span><span class="lnt">151 </span><span class="lnt">152 </span><span class="lnt">153 </span><span class="lnt">154 </span><span class="lnt">155 </span><span class="lnt">156 </span><span class="lnt">157 </span><span class="lnt">158 </span><span class="lnt">159 </span><span class="lnt">160 </span><span class="lnt">161 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">baseFunction</span> <span class="ow">::</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">PyExpr</span> <span class="ow">-&gt;</span> <span class="p">[</span><span class="kt">Py</span><span class="o">.</span><span class="kt">PyStmt</span><span class="p">]</span> <span class="ow">-&gt;</span> <span class="p">[</span><span class="kt">Py</span><span class="o">.</span><span class="kt">PyStmt</span><span class="p">]</span> <span class="ow">-&gt;</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">PyStmt</span> </span></span><span class="line"><span class="cl"><span class="nf">baseFunction</span> <span class="n">s</span> <span class="n">e</span> <span class="n">c</span> <span class="ow">=</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">FunctionDef</span> <span class="s">&#34;prog&#34;</span> <span class="p">[</span><span class="s">&#34;xs&#34;</span><span class="p">]</span> <span class="o">$</span> </span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="kt">Py</span><span class="o">.</span><span class="kt">IfElse</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">BinOp</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">LessThan</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">FunctionCall</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;len&#34;</span><span class="p">)</span> <span class="p">[</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;xs&#34;</span><span class="p">])</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">IntLiteral</span> <span class="mi">2</span><span class="p">))</span> </span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Return</span> <span class="o">$</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Tuple</span> <span class="p">[</span><span class="n">s</span><span class="p">,</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;xs&#34;</span><span class="p">]]</span> </span></span><span class="line"><span class="cl"> <span class="kt">[]</span> </span></span><span class="line"><span class="cl"> <span class="kt">Nothing</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Assign</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">VarPat</span> <span class="s">&#34;leng&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">BinOp</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">FloorDiv</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">FunctionCall</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;len&#34;</span><span class="p">)</span> <span class="p">[</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;xs&#34;</span><span class="p">])</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">IntLiteral</span> <span class="mi">2</span><span class="p">))</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Assign</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">VarPat</span> <span class="s">&#34;left&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Access</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;xs&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Slice</span> <span class="kt">Nothing</span> <span class="o">$</span> <span class="kt">Just</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;leng&#34;</span><span class="p">)])</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Assign</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">VarPat</span> <span class="s">&#34;right&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Access</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;xs&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Slice</span> <span class="p">(</span><span class="kt">Just</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;leng&#34;</span><span class="p">))</span> <span class="kt">Nothing</span><span class="p">])</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Assign</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">TuplePat</span> <span class="p">[</span><span class="kt">Py</span><span class="o">.</span><span class="kt">VarPat</span> <span class="s">&#34;ls&#34;</span><span class="p">,</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">VarPat</span> <span class="s">&#34;left&#34;</span><span class="p">])</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">FunctionCall</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;prog&#34;</span><span class="p">)</span> <span class="p">[</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;left&#34;</span><span class="p">])</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Assign</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">TuplePat</span> <span class="p">[</span><span class="kt">Py</span><span class="o">.</span><span class="kt">VarPat</span> <span class="s">&#34;rs&#34;</span><span class="p">,</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">VarPat</span> <span class="s">&#34;right&#34;</span><span class="p">])</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">FunctionCall</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;prog&#34;</span><span class="p">)</span> <span class="p">[</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;right&#34;</span><span class="p">])</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Standalone</span> <span class="o">$</span> </span></span><span class="line"><span class="cl"> <span class="kt">Py</span><span class="o">.</span><span class="kt">FunctionCall</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Member</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;left&#34;</span><span class="p">)</span> <span class="s">&#34;reverse&#34;</span><span class="p">)</span> <span class="kt">[]</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Standalone</span> <span class="o">$</span> </span></span><span class="line"><span class="cl"> <span class="kt">Py</span><span class="o">.</span><span class="kt">FunctionCall</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Member</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;right&#34;</span><span class="p">)</span> <span class="s">&#34;reverse&#34;</span><span class="p">)</span> <span class="kt">[]</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Assign</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">VarPat</span> <span class="s">&#34;state&#34;</span><span class="p">)</span> <span class="n">s</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Assign</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">VarPat</span> <span class="s">&#34;source&#34;</span><span class="p">)</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">IntLiteral</span> <span class="mi">0</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Assign</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">VarPat</span> <span class="s">&#34;total&#34;</span><span class="p">)</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">ListLiteral</span> <span class="kt">[]</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">While</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">BinOp</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">And</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">BinOp</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">NotEqual</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;left&#34;</span><span class="p">)</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">ListLiteral</span> <span class="kt">[]</span><span class="p">))</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">BinOp</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">NotEqual</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;right&#34;</span><span class="p">)</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">ListLiteral</span> <span class="kt">[]</span><span class="p">)))</span> <span class="o">$</span> </span></span><span class="line"><span class="cl"> <span class="p">[</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">IfElse</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">BinOp</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">LessThanEq</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Access</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;left&#34;</span><span class="p">)</span> <span class="p">[</span><span class="kt">Py</span><span class="o">.</span><span class="kt">IntLiteral</span> <span class="o">$</span> <span class="o">-</span><span class="mi">1</span><span class="p">])</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Access</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;right&#34;</span><span class="p">)</span> <span class="p">[</span><span class="kt">Py</span><span class="o">.</span><span class="kt">IntLiteral</span> <span class="o">$</span> <span class="o">-</span><span class="mi">1</span><span class="p">]))</span> </span></span><span class="line"><span class="cl"> <span class="p">[</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Standalone</span> <span class="o">$</span> </span></span><span class="line"><span class="cl"> <span class="kt">Py</span><span class="o">.</span><span class="kt">FunctionCall</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Member</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;total&#34;</span><span class="p">)</span> <span class="s">&#34;append&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="kt">Py</span><span class="o">.</span><span class="kt">FunctionCall</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Member</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;left&#34;</span><span class="p">)</span> <span class="s">&#34;pop&#34;</span><span class="p">)</span> <span class="kt">[]</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Assign</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">VarPat</span> <span class="s">&#34;source&#34;</span><span class="p">)</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">IntLiteral</span> <span class="mi">1</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="kt">[]</span> <span class="o">$</span> </span></span><span class="line"><span class="cl"> <span class="kt">Just</span> </span></span><span class="line"><span class="cl"> <span class="p">[</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Standalone</span> <span class="o">$</span> </span></span><span class="line"><span class="cl"> <span class="kt">Py</span><span class="o">.</span><span class="kt">FunctionCall</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Member</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;total&#34;</span><span class="p">)</span> <span class="s">&#34;append&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">[</span><span class="kt">Py</span><span class="o">.</span><span class="kt">FunctionCall</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Member</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;right&#34;</span><span class="p">)</span> <span class="s">&#34;pop&#34;</span><span class="p">)</span> <span class="kt">[]</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Assign</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">VarPat</span> <span class="s">&#34;source&#34;</span><span class="p">)</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">IntLiteral</span> <span class="mi">2</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="p">]</span> <span class="o">++</span> <span class="n">e</span> </span></span><span class="line"><span class="cl"> <span class="p">]</span> <span class="o">++</span> <span class="n">c</span> <span class="o">++</span> </span></span><span class="line"><span class="cl"> <span class="p">[</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Standalone</span> <span class="o">$</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">FunctionCall</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Member</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;left&#34;</span><span class="p">)</span> <span class="s">&#34;reverse&#34;</span><span class="p">)</span> <span class="kt">[]</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Standalone</span> <span class="o">$</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">FunctionCall</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Member</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;right&#34;</span><span class="p">)</span> <span class="s">&#34;reverse&#34;</span><span class="p">)</span> <span class="kt">[]</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Return</span> <span class="o">$</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Tuple</span> </span></span><span class="line"><span class="cl"> <span class="p">[</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;state&#34;</span> </span></span><span class="line"><span class="cl"> <span class="p">,</span> <span class="n">foldl</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">BinOp</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Add</span><span class="p">)</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;total&#34;</span><span class="p">)</span> <span class="p">[</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;left&#34;</span><span class="p">,</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;right&#34;</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="p">]</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This is probably the ugliest part of this assignment: we handwrote a Python AST in Haskell that implements mergesort with our augmentations. Note that this is a function, which takes a <code>Py.PyExpr</code> (the initial state expression), and two lists of <code>Py.PyStmt</code>, which are the &ldquo;effect&rdquo; and &ldquo;combination&rdquo; code, respectively. We simply splice them into our regular mergesort function. The translation is otherwise pretty trivial, so there&rsquo;s no real reason to show it here.</p> <a href="#the-output"> <h3 id="the-output">The Output</h3> </a> <p>What&rsquo;s the output of our solution to <code>num_inversions</code>? Take a look for yourself:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Python" data-lang="Python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">prog</span><span class="p">(</span><span class="n">xs</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">xs</span><span class="p">)</span><span class="o">&lt;</span><span class="mi">2</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">xs</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">leng</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">xs</span><span class="p">)</span><span class="o">//</span><span class="mi">2</span> </span></span><span class="line"><span class="cl"> <span class="n">left</span> <span class="o">=</span> <span class="n">xs</span><span class="p">[:(</span><span class="n">leng</span><span class="p">)]</span> </span></span><span class="line"><span class="cl"> <span class="n">right</span> <span class="o">=</span> <span class="n">xs</span><span class="p">[(</span><span class="n">leng</span><span class="p">):]</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">ls</span><span class="p">,</span><span class="n">left</span><span class="p">)</span> <span class="o">=</span> <span class="n">prog</span><span class="p">(</span><span class="n">left</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">rs</span><span class="p">,</span><span class="n">right</span><span class="p">)</span> <span class="o">=</span> <span class="n">prog</span><span class="p">(</span><span class="n">right</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">left</span><span class="o">.</span><span class="n">reverse</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="n">right</span><span class="o">.</span><span class="n">reverse</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="n">state</span> <span class="o">=</span> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="n">source</span> <span class="o">=</span> <span class="mi">0</span> </span></span><span class="line"><span class="cl"> <span class="n">total</span> <span class="o">=</span> <span class="p">[]</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span> <span class="p">(</span><span class="n">left</span><span class="o">!=</span><span class="p">[])</span><span class="ow">and</span><span class="p">(</span><span class="n">right</span><span class="o">!=</span><span class="p">[]):</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">left</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="o">&lt;=</span><span class="n">right</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]:</span> </span></span><span class="line"><span class="cl"> <span class="n">total</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">left</span><span class="o">.</span><span class="n">pop</span><span class="p">())</span> </span></span><span class="line"><span class="cl"> <span class="n">source</span> <span class="o">=</span> <span class="mi">1</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">total</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">right</span><span class="o">.</span><span class="n">pop</span><span class="p">())</span> </span></span><span class="line"><span class="cl"> <span class="n">source</span> <span class="o">=</span> <span class="mi">2</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">source</span><span class="o">==</span><span class="mi">2</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">state</span> <span class="o">=</span> <span class="n">state</span><span class="o">+</span><span class="nb">len</span><span class="p">(</span><span class="n">left</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">state</span> <span class="o">=</span> <span class="n">state</span><span class="o">+</span><span class="n">ls</span><span class="o">+</span><span class="n">rs</span> </span></span><span class="line"><span class="cl"> <span class="n">left</span><span class="o">.</span><span class="n">reverse</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="n">right</span><span class="o">.</span><span class="n">reverse</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">(</span><span class="n">state</span><span class="p">,</span> <span class="n">total</span><span class="o">+</span><span class="n">left</span><span class="o">+</span><span class="n">right</span><span class="p">)</span> </span></span></code></pre></div><p>Honestly, that&rsquo;s pretty clean. As clean as <code>left.reverse()</code> to allow for \(O(1)\) pop is. What&rsquo;s really clean, however, is the implementation of mergesort in our language. It goes as follows:</p> <pre tabindex="0"><code>state 0; effect {} combine {} </code></pre><p>To implement mergesort in our language, which describes mergesort variants, all we have to do is not specify any additional behavior. Cool, huh?</p> <p>That&rsquo;s the end of this post. If you liked this one (and the previous one!), keep an eye out for more!</p> <a href="#appendix-missing-homework-question"> <h3 id="appendix-missing-homework-question">Appendix (Missing Homework Question)</h3> </a> <p>I should not view homework assignments on a small-screen device. There <strong>was</strong> a third problem on homework 2:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/hws/hw2.txt" data-first-line="46" data-last-line="65"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/hws/hw2.txt#L46-L65">hw2.txt</a>, lines 46 through 65</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span><span class="lnt">65 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">3. [WILL BE GRADED] </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> Length of the longest path in a binary tree (number of edges). </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> We will use the &#34;buggy qsort&#34; representation of binary trees from HW1: </span></span><span class="line"><span class="cl"> [left_subtree, root, right_subtree] </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> &gt;&gt;&gt; longest([[], 1, []]) </span></span><span class="line"><span class="cl"> 0 </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> &gt;&gt;&gt; longest([[[], 1, []], 2, [[], 3, []]]) </span></span><span class="line"><span class="cl"> 2 </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> &gt;&gt;&gt; longest([[[[], 1, []], 2, [[], 3, []]], 4, [[[], 5, []], 6, [[], 7, [[], 9, []]]]]) </span></span><span class="line"><span class="cl"> 5 </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> Note the answer is 5 because the longest path is 1-2-4-6-7-9. </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> Filename: longest.py </span></span><span class="line"><span class="cl"> Must run in O(n) time.</span></span></code></pre></td></tr></table> </div> </div> </div> <p>This is not a mergesort variant, and adding support for it into our second language will prevent us from making it the neat specialized <span class="sidenote"> <label class="sidenote-label" for="dsl-note">DSL</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="dsl-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> DSL is a shortened form of "domain specific language", which was briefly described in another sidenote while solving homework 1. <span class="sidenote-delimiter">]</span> </span> </span> that was just saw. We&rsquo;ll do something else, instead: we&rsquo;ll use the language we defined in homework 1 to solve this problem:</p> <pre tabindex="0"><code>empty() = [0, 0]; longest(xs) = if |xs| != 0 then _longest(longest(xs[0]), longest(xs[2])) else empty(); _longest(l, r) = [max(l[0], r[0]) + 1, max(l[0]+r[0], max(l[1], r[1]))]; </code></pre><p><span class="sidenote"> <label class="sidenote-label" for="terrible-note">This is quite terrible.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="terrible-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> This is probably true with any program written in our first language. <span class="sidenote-delimiter">]</span> </span> </span> In these 6 lines of code, there are two hacks to work around the peculiarities of the language.</p> <p>At each recursive call, we want to keep track of both the depth of the tree and the existing longest path. This is because the longest path could be found either somewhere down a subtree, or from combining the largest depths of two subtrees. To return two values from a function in Python, we&rsquo;d use a tuple. Here, we use a list.</p> <p>Alarm bells should be going off here. There&rsquo;s no reason why we should ever return an empty list from the recursive call: at the very least, we want to return <code>[0,0]</code>. But placing such a list literal in a function will trigger the special case insertion. So, we have to hide this literal from the compiler. Fortunately, that&rsquo;s not too hard to do - the compiler is pretty halfhearted in its inference of types. Simply putting the literal behind a constant function (<code>empty</code>) does the trick.</p> <p>The program uses the subproblem depths multiple times in the final computation. We thus probably want to assign these values to names so we don&rsquo;t have to perform any repeated work. Since the only two mechanisms for <span class="sidenote"> <label class="sidenote-label" for="binding-note">binding variables</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="binding-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> To bind a variable means to assign a value to it. <span class="sidenote-delimiter">]</span> </span> </span> in this language are function calls and list selectors, we use a helper function <code>_longest</code>, which takes two subproblem solutions an combines them into a new solution. It&rsquo;s pretty obvious that <code>_longest</code> returns a list, so the compiler will try insert a base case. Fortunately, subproblem solutions are always lists of two numbers, so this doesn&rsquo;t affect us too much.</p> A Language for an Assignment - Homework 1 https://danilafe.com/blog/00_cs325_languages_hw1/ Fri, 27 Dec 2019 23:27:09 -0800 https://danilafe.com/blog/00_cs325_languages_hw1/ <p>On a rainy Oregon day, I was walking between classes with a group of friends. We were discussing the various ways to obfuscate solutions to the weekly homework assignments in our Algorithms course: replace every <code>if</code> with a ternary expression, use single variable names, put everything on one line. I said:</p> <blockquote> <p>The <span class="sidenote"> <label class="sidenote-label" for="chad-note">chad</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="chad-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> This is in reference to a meme, <a href="https://knowyourmeme.com/memes/virgin-vs-chad">Virgin vs Chad</a>. A "chad" characteristic is masculine or "alpha" to the point of absurdity. <span class="sidenote-delimiter">]</span> </span> </span> move would be to make your own, different language for every homework assignment.</p> </blockquote> <p>It was required of us to use <span class="sidenote"> <label class="sidenote-label" for="python-note">Python</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="python-note"></input><span class="sidenote-content sidenote-left"><span class="sidenote-delimiter">[note:</span> A friend suggested making a Haskell program that generates Python-based interpreters for languages. While that would be truly absurd, I'll leave <em>this</em> challenge for another day. <span class="sidenote-delimiter">]</span> </span> </span> for our solutions, so that was the first limitation on this challenge. Someone suggested to write the languages in Haskell, since that&rsquo;s what we used in our Programming Languages class. So the final goal ended up:</p> <ul> <li>For each of the 10 homework assignments in CS325 - Analysis of Algorithms,</li> <li>Create a Haskell program that translates a language into,</li> <li>A valid Python program that works (nearly) out of the box and passes all the test cases.</li> </ul> <p>It may not be worth it to create a whole <span class="sidenote"> <label class="sidenote-label" for="general-purpose-note">general-purpose</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="general-purpose-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> A general purpose language is one that's designed to be used in various domains. For instance, C++ is a general-purpose language because it can be used for embedded systems, GUI programs, and pretty much anything else. This is in contrast to a domain-specific language, such as Game Maker Language, which is aimed at a much narrower set of uses. <span class="sidenote-delimiter">]</span> </span> </span> language for each problem, but nowhere in the challenge did we say that it had to be general-purpose. In fact, some interesting design thinking can go into designing a domain-specific language for a particular assignment. So let&rsquo;s jump right into it, and make a language for the first homework assignment.</p> <a href="#homework-1"> <h3 id="homework-1">Homework 1</h3> </a> <p>There are two problems in Homework 1. Here they are, verbatim:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/hws/hw1.txt" data-first-line="32" data-last-line="38"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/hws/hw1.txt#L32-L38">hw1.txt</a>, lines 32 through 38</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl"> Quickselect with Randomized Pivot (CLRS Ch. 9.2). </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> &gt;&gt;&gt; from qselect import * </span></span><span class="line"><span class="cl"> &gt;&gt;&gt; qselect(2, [3, 10, 4, 7, 19]) </span></span><span class="line"><span class="cl"> 4 </span></span><span class="line"><span class="cl"> &gt;&gt;&gt; qselect(4, [11, 2, 8, 3]) </span></span><span class="line"><span class="cl"> 11</span></span></code></pre></td></tr></table> </div> </div> </div> <p>And the second:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/hws/hw1.txt" data-first-line="47" data-last-line="68"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/hws/hw1.txt#L47-L68">hw1.txt</a>, lines 47 through 68</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span><span class="lnt">65 </span><span class="lnt">66 </span><span class="lnt">67 </span><span class="lnt">68 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl"> In the slides we showed a buggy version of qsort which is weird in an interesting way: </span></span><span class="line"><span class="cl"> it actually returns a binary search tree for the given array, rooted at the pivot: </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> &gt;&gt;&gt; from qsort import * </span></span><span class="line"><span class="cl"> &gt;&gt;&gt; tree = sort([4,2,6,3,5,7,1,9]) </span></span><span class="line"><span class="cl"> &gt;&gt;&gt; tree </span></span><span class="line"><span class="cl"> [[[[], 1, []], 2, [[], 3, []]], 4, [[[], 5, []], 6, [[], 7, [[], 9, []]]]] </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> which encodes a binary search tree: </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> 4 </span></span><span class="line"><span class="cl"> / \ </span></span><span class="line"><span class="cl"> 2 6 </span></span><span class="line"><span class="cl"> / \ / \ </span></span><span class="line"><span class="cl"> 1 3 5 7 </span></span><span class="line"><span class="cl"> \ </span></span><span class="line"><span class="cl"> 9 </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> Now on top of that piece of code, add three functions: </span></span><span class="line"><span class="cl"> * sorted(t): returns the sorted order (infix traversal) </span></span><span class="line"><span class="cl"> * search(t, x): returns whether x is in t </span></span><span class="line"><span class="cl"> * insert(t, x): inserts x into t (in-place) if it is missing, otherwise does nothing.</span></span></code></pre></td></tr></table> </div> </div> </div> <p>We want to make a language <strong>specifically</strong> for these two tasks (one of which is split into many tasks). What common things can we isolate? I see two:</p> <p>First, <strong>all the problems deal with lists</strong>. This may seem like a trivial observation, but these two problems are the <strong>only</strong> thing we use our language for. We have list access, <span class="sidenote"> <label class="sidenote-label" for="filterting-note">list filtering</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="filterting-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Quickselect is a variation on quicksort, which itself finds all the "lesser" and "greater" elements in the input array. <span class="sidenote-delimiter">]</span> </span> </span> and list creation. That should serve as a good base!</p> <p>If you squint a little bit, <strong>all the problems are recursive with the same base case</strong>. Consider the first few lines of <code>search</code>, implemented naively:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Python" data-lang="Python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">search</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">k</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">xs</span> <span class="o">==</span> <span class="p">[]:</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">false</span> </span></span></code></pre></div><p>How about <code>sorted</code>? Take a look:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Python" data-lang="Python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">sorted</span><span class="p">(</span><span class="n">xs</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">xs</span> <span class="o">==</span> <span class="p">[]:</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">[]</span> </span></span></code></pre></div><p>I&rsquo;m sure you see the picture. But it will take some real mental gymnastics to twist the rest of the problems into this shape. What about <code>qselect</code>, for instance? There&rsquo;s two cases for what it may return:</p> <ul> <li><code>None</code> or equivalent if the index is out of bounds (we give it <code>4</code> an a list <code>[1, 2]</code>).</li> <li>A number if <code>qselect</code> worked.</li> </ul> <p>The test cases never provide a concrete example of what should be returned from <code>qselect</code> in the first case, so we&rsquo;ll interpret it like <span class="sidenote"> <label class="sidenote-label" for="undefined-note">undefined behavior</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="undefined-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> For a quick sidenote about undefined behavior, check out how C++ optimizes the <a href="https://godbolt.org/z/3skK9j">Collatz Conjecture function</a>. Clang doesn't know whether or not the function will terminate (whether the Collatz Conjecture function terminates is an <a href="https://en.wikipedia.org/wiki/Collatz_conjecture">unsolved problem</a>), but functions that don't terminate are undefined behavior. There's only one other way the function returns, and that's with "1". Thus, clang optimizes the entire function to a single "return 1" call. <span class="sidenote-delimiter">]</span> </span> </span> in C++: we can do whatever we want. So, let&rsquo;s allow it to return <code>[]</code> in the <code>None</code> case. This makes this base case valid:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Python" data-lang="Python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">qselect</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">k</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">xs</span> <span class="o">==</span> <span class="p">[]:</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">[]</span> </span></span></code></pre></div><p>&ldquo;Oh yeah, now it&rsquo;s all coming together.&rdquo; With one more observation (which will come from a piece I haven&rsquo;t yet shown you!), we&rsquo;ll be able to generalize this base case.</p> <p>The observation is this section in the assignment:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/hws/hw1.txt" data-first-line="83" data-last-line="98"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/hws/hw1.txt#L83-L98">hw1.txt</a>, lines 83 through 98</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">83 </span><span class="lnt">84 </span><span class="lnt">85 </span><span class="lnt">86 </span><span class="lnt">87 </span><span class="lnt">88 </span><span class="lnt">89 </span><span class="lnt">90 </span><span class="lnt">91 </span><span class="lnt">92 </span><span class="lnt">93 </span><span class="lnt">94 </span><span class="lnt">95 </span><span class="lnt">96 </span><span class="lnt">97 </span><span class="lnt">98 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl"> Hint: both search and insert should depend on a helper function _search(tree, x) which </span></span><span class="line"><span class="cl"> returns the subtree (a list) rooted at x when x is found, or the [] where x should </span></span><span class="line"><span class="cl"> be inserted. </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> e.g., </span></span><span class="line"><span class="cl"> &gt;&gt;&gt; tree = sort([4,2,6,3,5,7,1,9]) # starting from the initial tree </span></span><span class="line"><span class="cl"> &gt;&gt;&gt; _search(tree, 3) </span></span><span class="line"><span class="cl"> [[], 3, []] </span></span><span class="line"><span class="cl"> &gt;&gt;&gt; _search(tree, 0) </span></span><span class="line"><span class="cl"> [] </span></span><span class="line"><span class="cl"> &gt;&gt;&gt; _search(tree, 6.5) </span></span><span class="line"><span class="cl"> [] </span></span><span class="line"><span class="cl"> &gt;&gt;&gt; _search(tree, 0) is _search(tree, 6.5) </span></span><span class="line"><span class="cl"> False </span></span><span class="line"><span class="cl"> &gt;&gt;&gt; _search(tree, 0) == _search(tree, 6.5) </span></span><span class="line"><span class="cl"> True</span></span></code></pre></td></tr></table> </div> </div> </div> <p>The real key is the part about &ldquo;returning the <code>[]</code> where x should be inserted&rdquo;. It so happens that when the list given to the function is empty, the number should be inserted precisely into that list. Thus:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Python" data-lang="Python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">_search</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">k</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">xs</span> <span class="o">==</span> <span class="p">[]:</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">xs</span> </span></span></code></pre></div><p>The same works for <code>qselect</code>:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Python" data-lang="Python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">qselect</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">k</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">xs</span> <span class="o">==</span> <span class="p">[]:</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">xs</span> </span></span></code></pre></div><p>And for sorted, too:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Python" data-lang="Python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">sorted</span><span class="p">(</span><span class="n">xs</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">xs</span> <span class="o">==</span> <span class="p">[]:</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">xs</span> </span></span></code></pre></div><p>There are some functions that are exceptions, though:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Python" data-lang="Python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">insert</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">k</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="c1"># We can&#39;t return early here!</span> </span></span><span class="line"><span class="cl"> <span class="c1"># If we do, we&#39;ll never insert anything.</span> </span></span></code></pre></div><p>Also:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Python" data-lang="Python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">search</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">k</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="c1"># We have to return true or false, never</span> </span></span><span class="line"><span class="cl"> <span class="c1"># an empty list.</span> </span></span></code></pre></div><p>So, whenever we <strong>don&rsquo;t</strong> return a list, we don&rsquo;t want to add a special case. We arrive at the following common base case: <strong>whenever a function returns a list, if its first argument is the empty list, the first argument is immediately returned</strong>.</p> <p>We&rsquo;ve largely exhasuted the conclusiosn we can draw from these problems. Let&rsquo;s get to designing a language.</p> <a href="#a-silly-language"> <h3 id="a-silly-language">A Silly Language</h3> </a> <p>Let&rsquo;s start by visualizing our goals. Without base cases, the solution to <code>_search</code> would be something like this:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/sols/hw1.lang" data-first-line="11" data-last-line="14"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/sols/hw1.lang#L11-L14">hw1.lang</a>, lines 11 through 14</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">_search(xs, k) = </span></span><span class="line"><span class="cl"> if xs[1] == k then xs </span></span><span class="line"><span class="cl"> else if xs[1] &gt; k then _search(xs[0], k) </span></span><span class="line"><span class="cl"> else _search(xs[2], k);</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Here we have an <strong><code>if</code>-expression</strong>. It has to have an <code>else</code>, and evaluates to the value of the chosen branch. That is, <code>if true then 0 else 1</code> evaluates to <code>0</code>, while <code>if false then 0 else 1</code> evaluates to <code>1</code>. Otherwise, we follow the binary tree search algorithm faithfully.</p> <p>Using this definition of <code>_search</code>, we can define <code>search</code> pretty easily:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/sols/hw1.lang" data-first-line="17" data-last-line="17"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/sols/hw1.lang#L17-L17">hw1.lang</a>, line 17</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">17 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">search(xs, k) = |_search(xs, k)| != 0;</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Let&rsquo;s use Haskell&rsquo;s <code>(++)</code> operator for concatentation. This will help us understand when the user is operating on lists, and when they&rsquo;re not. With this, <code>sorted</code> becomes:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/sols/hw1.lang" data-first-line="16" data-last-line="16"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/sols/hw1.lang#L16-L16">hw1.lang</a>, line 16</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">16 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">sorted(xs) = sorted(xs[0]) ++ [xs[1]] ++ sorted(xs[2]);</span></span></code></pre></td></tr></table> </div> </div> </div> <p>Let&rsquo;s go for <code>qselect</code> now. We&rsquo;ll introduce a very silly language feature for this problem: <span class="sidenote"> <label class="sidenote-label" for="selector-note">list selectors</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="selector-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> You've probably never heard of list selectors, and for a good reason: this is a <em>terrible</em> language feature. I'll go in more detail later, but I wanted to make this clear right away. <span class="sidenote-delimiter">]</span> </span> </span> . We observe that <code>qselect</code> aims to partition the list into other lists. We thus add the following pieces of syntax:</p> <pre tabindex="0"><code>~xs -&gt; { pivot &lt;- xs[rand]! left &lt;- xs[#0 &lt;= pivot] ... } -&gt; ... </code></pre><p>There are three new things here.</p> <ol> <li>The actual &ldquo;list selector&rdquo;: <code>~xs -&gt; { .. } -&gt; ...</code>. Between the curly braces are branches which select parts of the list and assign them to new variables. Thus, <code>pivot &lt;- xs[rand]!</code> assigns the element at a random index to the variable <code>pivot</code>. the <code>!</code> at the end means &ldquo;after taking this out of <code>xs</code>, delete it from <code>xs</code>&rdquo;. The syntax <span class="sidenote"> <label class="sidenote-label" for="curly-note">starts with &ldquo;~&rdquo;</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="curly-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> An observant reader will note that there's no need for the "xs" after the "~". The idea was to add a special case syntax to reference the "selected list", but I ended up not bothering. So in fact, this part of the syntax is useless. <span class="sidenote-delimiter">]</span> </span> </span> to make it easier to parse.</li> <li>The <code>rand</code> list access syntax. <code>xs[rand]</code> is a special case that picks a random element from <code>xs</code>.</li> <li>The <code>xs[#0 &lt;= pivot]</code> syntax. This is another special case that selects all elements from <code>xs</code> that match the given predicate (where <code>#0</code> is replaced with each element in <code>xs</code>).</li> </ol> <p>The big part of qselect is to not evaluate <code>right</code> unless you have to. So, we shouldn&rsquo;t eagerly evaluate the list selector. We also don&rsquo;t want something like <code>right[|right|-1]</code> to evaluate <code>right</code> twice. So we settle on <span class="sidenote"> <label class="sidenote-label" for="lazy-note">lazy evaluation</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="lazy-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Lazy evaluation means only evaluating an expression when we need to. Thus, although we might encounter the expression for <code>right</code>, we only evaluate it when the time comes. Lazy evaluation, at least the way that Haskell has it, is more specific: an expression is evaluated only once, or not at all. <span class="sidenote-delimiter">]</span> </span> </span> . Ah, but the <code>!</code> marker introduces <span class="sidenote"> <label class="sidenote-label" for="side-effect-note">side effects</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="side-effect-note"></input><span class="sidenote-content sidenote-left"><span class="sidenote-delimiter">[note:</span> A side effect is a term frequently used when talking about functional programming. Evaluating the expression <code>xs[rand]!</code> doesn't just get a random element, it also changes <em>something else</em>. In this case, that something else is the <code>xs</code> list. <span class="sidenote-delimiter">]</span> </span> </span> . So we can&rsquo;t just evaluate these things all willy-nilly. So, let&rsquo;s make it so that each expression in the selector list requires the ones above it. Thus, <code>left</code> will require <code>pivot</code>, and <code>right</code> will require <code>left</code> and <code>pivot</code>. So, lazily evaluated, ordered expressions. The whole <code>qselect</code> becomes:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/sols/hw1.lang" data-first-line="1" data-last-line="9"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/sols/hw1.lang#L1-L9">hw1.lang</a>, lines 1 through 9</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span><span class="lnt">6 </span><span class="lnt">7 </span><span class="lnt">8 </span><span class="lnt">9 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">qselect(xs,k) = </span></span><span class="line"><span class="cl"> ~xs -&gt; { </span></span><span class="line"><span class="cl"> pivot &lt;- xs[0]! </span></span><span class="line"><span class="cl"> left &lt;- xs[#0 &lt;= pivot] </span></span><span class="line"><span class="cl"> right &lt;- xs[#0 &gt; pivot] </span></span><span class="line"><span class="cl"> } -&gt; </span></span><span class="line"><span class="cl"> if k &gt; |left| + 1 then qselect(right, k - |left| - 1) </span></span><span class="line"><span class="cl"> else if k == |left| + 1 then [pivot] </span></span><span class="line"><span class="cl"> else qselect(left, k);</span></span></code></pre></td></tr></table> </div> </div> </div> <p>We&rsquo;ve now figured out all the language constructs. Let&rsquo;s start working on some implementation!</p> <a href="#implementation"> <h4 id="implementation">Implementation</h4> </a> <p>It would be silly of me to explain every detail of creating a language in Haskell in this post; this is neither the purpose of the post, nor is it plausible to do this without covering monads, parser combinators, grammars, abstract syntax trees, and more. So, instead, I&rsquo;ll discuss the <em>interesting</em> parts of the implementation.</p> <a href="#temporary-variables"> <h5 id="temporary-variables">Temporary Variables</h5> </a> <p>Our language is expression-based, yes. A function is a single, arbitrarily complex expression (involving <code>if/else</code>, list selectors, and more). So it would make sense to translate a function to a single, arbitrarily complex Python expression. However, the way we&rsquo;ve designed our language makes it not-so-suitable for converting to a single expression! For instance, consider <code>xs[rand]</code>. We need to compute the list, get its length, generate a random number, and then access the corresponding element in the list. We use the list here twice, and simply repeating the expression would not be very smart: we&rsquo;d be evaluating twice. So instead, we&rsquo;ll use a variable, assign the list to that variable, and then access that variable multiple times.</p> <p>To be extra safe, let&rsquo;s use a fresh temporary variable every time we need to store something. The simplest way is to simply maintain a counter of how many temporary variables we&rsquo;ve already used, and generate a new variable by prepending the word &ldquo;temp&rdquo; to that number. We start with <code>temp0</code>, then <code>temp1</code>, and so on. To keep a counter, we can use a state monad:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/src/LanguageOne.hs" data-first-line="230" data-last-line="230"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/src/LanguageOne.hs#L230-L230">LanguageOne.hs</a>, line 230</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">230 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="kr">type</span> <span class="kt">Translator</span> <span class="ow">=</span> <span class="kt">Control</span><span class="o">.</span><span class="kt">Monad</span><span class="o">.</span><span class="kt">State</span><span class="o">.</span><span class="kt">State</span> <span class="p">(</span><span class="kt">Map</span><span class="o">.</span><span class="kt">Map</span> <span class="kt">String</span> <span class="p">[</span><span class="kt">String</span><span class="p">],</span> <span class="kt">Int</span><span class="p">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Don&rsquo;t worry about the <code>Map.Map String [String]</code>, we&rsquo;ll get to that in a bit. For now, all we have to worry about is the second element of the tuple, the integer counting how many temporary variables we&rsquo;ve used. We can get the current temporary variable as follows:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/src/LanguageOne.hs" data-first-line="232" data-last-line="235"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/src/LanguageOne.hs#L232-L235">LanguageOne.hs</a>, lines 232 through 235</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">232 </span><span class="lnt">233 </span><span class="lnt">234 </span><span class="lnt">235 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">currentTemp</span> <span class="ow">::</span> <span class="kt">Translator</span> <span class="kt">String</span> </span></span><span class="line"><span class="cl"><span class="nf">currentTemp</span> <span class="ow">=</span> <span class="kr">do</span> </span></span><span class="line"><span class="cl"> <span class="n">t</span> <span class="ow">&lt;-</span> <span class="n">gets</span> <span class="n">snd</span> </span></span><span class="line"><span class="cl"> <span class="n">return</span> <span class="o">$</span> <span class="s">&#34;temp&#34;</span> <span class="o">++</span> <span class="n">show</span> <span class="n">t</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We can also get a fresh temporary variable like this:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/src/LanguageOne.hs" data-first-line="237" data-last-line="240"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/src/LanguageOne.hs#L237-L240">LanguageOne.hs</a>, lines 237 through 240</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">237 </span><span class="lnt">238 </span><span class="lnt">239 </span><span class="lnt">240 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">incrementTemp</span> <span class="ow">::</span> <span class="kt">Translator</span> <span class="kt">String</span> </span></span><span class="line"><span class="cl"><span class="nf">incrementTemp</span> <span class="ow">=</span> <span class="kr">do</span> </span></span><span class="line"><span class="cl"> <span class="n">modify</span> <span class="p">(</span><span class="n">second</span> <span class="p">(</span><span class="o">+</span><span class="mi">1</span><span class="p">))</span> </span></span><span class="line"><span class="cl"> <span class="n">currentTemp</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Now, the <span class="sidenote"> <label class="sidenote-label" for="code-note">code</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="code-note"></input><span class="sidenote-content sidenote-left"><span class="sidenote-delimiter">[note:</span> Since we are translating an expression, we must have the result of the translation yield an Python expression we can use in generating larger Python expressions. However, as we've seen, we occasionally have to use statements. Thus, the <code>translateExpr</code> function returns a <code>Translator ([Py.PyStmt], Py.PyExpr)</code>. <span class="sidenote-delimiter">]</span> </span> </span> for generating a random list access looks like <span class="sidenote"> <label class="sidenote-label" for="ast-note">this:</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="ast-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> The <code>Py.*</code> constructors are a part of a Python AST module I quickly threw together. I won't showcase it here, but you can always look at the source code for the blog (which includes this project) <a href="https://dev.danilafe.com/Web-Projects/blog-static">here</a>. <span class="sidenote-delimiter">]</span> </span> </span> </p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/src/LanguageOne.hs" data-first-line="325" data-last-line="330"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/src/LanguageOne.hs#L325-L330">LanguageOne.hs</a>, lines 325 through 330</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">325 </span><span class="lnt">326 </span><span class="lnt">327 </span><span class="lnt">328 </span><span class="lnt">329 </span><span class="lnt">330 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">translateExpr</span> <span class="p">(</span><span class="kt">Access</span> <span class="n">e</span> <span class="kt">Random</span> <span class="n">m</span><span class="p">)</span> <span class="ow">=</span> <span class="kr">do</span> </span></span><span class="line"><span class="cl"> <span class="n">temp</span> <span class="ow">&lt;-</span> <span class="n">incrementTemp</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">sts</span><span class="p">,</span> <span class="n">ce</span><span class="p">)</span> <span class="ow">&lt;-</span> <span class="n">translateExpr</span> <span class="n">e</span> </span></span><span class="line"><span class="cl"> <span class="kr">let</span> <span class="n">lenExpr</span> <span class="ow">=</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">FunctionCall</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;len&#34;</span><span class="p">)</span> <span class="p">[</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="n">temp</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="kr">let</span> <span class="n">randExpr</span> <span class="ow">=</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">FunctionCall</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;randint&#34;</span><span class="p">)</span> <span class="p">[</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">IntLiteral</span> <span class="mi">0</span><span class="p">,</span> <span class="n">lenExpr</span> <span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="n">return</span> <span class="p">(</span><span class="n">sts</span><span class="p">,</span> <span class="n">singleAccess</span> <span class="n">ce</span> <span class="n">randExpr</span> <span class="n">m</span><span class="p">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#implementing-lazy-evaluation"> <h5 id="implementing-lazy-evaluation">Implementing &ldquo;lazy evaluation&rdquo;</h5> </a> <p>Lazy evaluation in functional programs usually arises from <span class="sidenote"> <label class="sidenote-label" for="graph-note">graph reduction</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="graph-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Graph reduction, more specifically the <em>Spineless, Tagless G-machine</em> is at the core of the Glasgow Haskell Compiler (GHC). Simon Peyton Jones' earlier book, <em>Implementing Functional Languages: a tutorial</em> details an earlier version of the G-machine. <span class="sidenote-delimiter">]</span> </span> </span> . However, Python is neither functional nor graph-based, and we only lazily evaluate list selectors. Thus, we&rsquo;ll have to do some work to get our lazy evaluation to work as we desire. Here&rsquo;s what I came up with:</p> <ol> <li>It&rsquo;s difficult to insert Python statements where they are needed: we&rsquo;d have to figure out in which scope each variable has already been declared, and in which scope it&rsquo;s yet to be assigned.</li> <li>Instead, we can use a Python dictionary, called <code>cache</code>, and store computed versions of each variable in the cache.</li> <li>It&rsquo;s pretty difficult to check if a variable is in the cache, compute it if not, and then return the result of the computation, in one expression. This is true, unless that single expression is a function call, and we have a dedicated function that takes no arguments, computes the expression if needed, and uses the cache otherwise. We choose this route.</li> <li>We have already promised that we&rsquo;d evaluate all the selected variables above a given variable before evaluating the variable itself. So, each function will first call (and therefore <span class="sidenote"> <label class="sidenote-label" for="force-note">force</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="force-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> Forcing, in this case, comes from the context of lazy evaluation. To force a variable or an expression is to tell the program to compute its value, even though it may have been putting it off. <span class="sidenote-delimiter">]</span> </span> </span> ) the functions generated for variables declared above the function&rsquo;s own variable.</li> <li>To keep track of all of this, we use the already-existing state monad as a reader monad (that is, we clear the changes we make to the monad after we&rsquo;re done translating the list selector). This is where the <code>Map.Map String [String]</code> comes from.</li> </ol> <p>The <code>Map.Map String [String]</code> keeps track of variables that will be lazily computed, and also of the dependencies of each variable (the variables that need to be access before the variable itself). We compute such a map for each selector as follows:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/src/LanguageOne.hs" data-first-line="298" data-last-line="298"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/src/LanguageOne.hs#L298-L298">LanguageOne.hs</a>, line 298</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">298 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"> <span class="kr">let</span> <span class="n">prereqs</span> <span class="ow">=</span> <span class="n">snd</span> <span class="o">$</span> <span class="n">foldl</span> <span class="p">(</span><span class="nf">\</span><span class="p">(</span><span class="n">ds</span><span class="p">,</span> <span class="n">m</span><span class="p">)</span> <span class="p">(</span><span class="kt">Selector</span> <span class="n">n</span> <span class="n">es</span><span class="p">)</span> <span class="ow">-&gt;</span> <span class="p">(</span><span class="n">n</span><span class="kt">:</span><span class="n">ds</span><span class="p">,</span> <span class="kt">Map</span><span class="o">.</span><span class="n">insert</span> <span class="n">n</span> <span class="n">ds</span> <span class="n">m</span><span class="p">))</span> <span class="p">(</span><span class="kt">[]</span><span class="p">,</span> <span class="kt">Map</span><span class="o">.</span><span class="n">empty</span><span class="p">)</span> <span class="n">ss</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We update the existing map using <code>Map.union</code>:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/src/LanguageOne.hs" data-first-line="299" data-last-line="299"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/src/LanguageOne.hs#L299-L299">LanguageOne.hs</a>, line 299</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">299 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"> <span class="n">modify</span> <span class="o">$</span> <span class="n">first</span> <span class="o">$</span> <span class="kt">Map</span><span class="o">.</span><span class="n">union</span> <span class="n">prereqs</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>And, after we&rsquo;re done generating expressions in the body of this selector, we clear it to its previous value <code>vs</code>:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/src/LanguageOne.hs" data-first-line="302" data-last-line="302"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/src/LanguageOne.hs#L302-L302">LanguageOne.hs</a>, line 302</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">302 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"> <span class="n">modify</span> <span class="o">$</span> <span class="n">first</span> <span class="o">$</span> <span class="n">const</span> <span class="n">vs</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We generate a single selector as follows:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/src/LanguageOne.hs" data-first-line="268" data-last-line="281"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/src/LanguageOne.hs#L268-L281">LanguageOne.hs</a>, lines 268 through 281</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">268 </span><span class="lnt">269 </span><span class="lnt">270 </span><span class="lnt">271 </span><span class="lnt">272 </span><span class="lnt">273 </span><span class="lnt">274 </span><span class="lnt">275 </span><span class="lnt">276 </span><span class="lnt">277 </span><span class="lnt">278 </span><span class="lnt">279 </span><span class="lnt">280 </span><span class="lnt">281 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">translateSelector</span> <span class="ow">::</span> <span class="kt">Selector</span> <span class="ow">-&gt;</span> <span class="kt">Translator</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">PyStmt</span> </span></span><span class="line"><span class="cl"><span class="nf">translateSelector</span> <span class="p">(</span><span class="kt">Selector</span> <span class="n">n</span> <span class="n">e</span><span class="p">)</span> <span class="ow">=</span> </span></span><span class="line"><span class="cl"> <span class="kr">let</span> </span></span><span class="line"><span class="cl"> <span class="n">cacheCheck</span> <span class="ow">=</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">NotIn</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">StrLiteral</span> <span class="n">n</span><span class="p">)</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;cache&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">cacheAccess</span> <span class="ow">=</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Access</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;cache&#34;</span><span class="p">)</span> <span class="p">[</span><span class="kt">Py</span><span class="o">.</span><span class="kt">StrLiteral</span> <span class="n">n</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="n">cacheSet</span> <span class="ow">=</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Assign</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">AccessPat</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="s">&#34;cache&#34;</span><span class="p">)</span> <span class="p">[</span><span class="kt">Py</span><span class="o">.</span><span class="kt">StrLiteral</span> <span class="n">n</span><span class="p">])</span> </span></span><span class="line"><span class="cl"> <span class="n">body</span> <span class="n">e&#39;</span> <span class="ow">=</span> <span class="p">[</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">IfElse</span> <span class="n">cacheCheck</span> <span class="p">[</span><span class="n">cacheSet</span> <span class="n">e&#39;</span><span class="p">]</span> <span class="kt">[]</span> <span class="kt">Nothing</span><span class="p">,</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Return</span> <span class="n">cacheAccess</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="kr">in</span> </span></span><span class="line"><span class="cl"> <span class="kr">do</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">ss</span><span class="p">,</span> <span class="n">e&#39;</span><span class="p">)</span> <span class="ow">&lt;-</span> <span class="n">translateExpr</span> <span class="n">e</span> </span></span><span class="line"><span class="cl"> <span class="n">vs</span> <span class="ow">&lt;-</span> <span class="n">gets</span> <span class="n">fst</span> </span></span><span class="line"><span class="cl"> <span class="kr">let</span> <span class="n">callPrereq</span> <span class="n">p</span> <span class="ow">=</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Standalone</span> <span class="o">$</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">FunctionCall</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="n">p</span><span class="p">)</span> <span class="kt">[]</span> </span></span><span class="line"><span class="cl"> <span class="kr">let</span> <span class="n">prereqs</span> <span class="ow">=</span> <span class="n">maybe</span> <span class="kt">[]</span> <span class="p">(</span><span class="n">map</span> <span class="n">callPrereq</span><span class="p">)</span> <span class="o">$</span> <span class="kt">Map</span><span class="o">.</span><span class="n">lookup</span> <span class="n">n</span> <span class="n">vs</span> </span></span><span class="line"><span class="cl"> <span class="n">return</span> <span class="o">$</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">FunctionDef</span> <span class="n">n</span> <span class="kt">[]</span> <span class="o">$</span> <span class="n">ss</span> <span class="o">++</span> <span class="n">prereqs</span> <span class="o">++</span> <span class="n">body</span> <span class="n">e&#39;</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This generates a function definition statement, which we will examine in generated Python code later on.</p> <p>Solving the problem this way also introduces another gotcha: sometimes, a variable is produced by a function call, and other times the variable is just a Python variable. We write this as follows:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/src/LanguageOne.hs" data-first-line="283" data-last-line="288"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/src/LanguageOne.hs#L283-L288">LanguageOne.hs</a>, lines 283 through 288</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">283 </span><span class="lnt">284 </span><span class="lnt">285 </span><span class="lnt">286 </span><span class="lnt">287 </span><span class="lnt">288 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">translateExpr</span> <span class="ow">::</span> <span class="kt">Expr</span> <span class="ow">-&gt;</span> <span class="kt">Translator</span> <span class="p">([</span><span class="kt">Py</span><span class="o">.</span><span class="kt">PyStmt</span><span class="p">],</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">PyExpr</span><span class="p">)</span> </span></span><span class="line"><span class="cl"><span class="nf">translateExpr</span> <span class="p">(</span><span class="kt">Var</span> <span class="n">s</span><span class="p">)</span> <span class="ow">=</span> <span class="kr">do</span> </span></span><span class="line"><span class="cl"> <span class="n">vs</span> <span class="ow">&lt;-</span> <span class="n">gets</span> <span class="n">fst</span> </span></span><span class="line"><span class="cl"> <span class="kr">let</span> <span class="n">sVar</span> <span class="ow">=</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="n">s</span> </span></span><span class="line"><span class="cl"> <span class="kr">let</span> <span class="n">expr</span> <span class="ow">=</span> <span class="kr">if</span> <span class="kt">Map</span><span class="o">.</span><span class="n">member</span> <span class="n">s</span> <span class="n">vs</span> <span class="kr">then</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">FunctionCall</span> <span class="n">sVar</span> <span class="kt">[]</span> <span class="kr">else</span> <span class="n">sVar</span> </span></span><span class="line"><span class="cl"> <span class="n">return</span> <span class="p">(</span><span class="kt">[]</span><span class="p">,</span> <span class="n">expr</span><span class="p">)</span> </span></span></code></pre></td></tr></table> </div> </div> </div> <a href="#special-case-insertion"> <h5 id="special-case-insertion">Special Case Insertion</h5> </a> <p>This is a silly language for a single homework assignment. I&rsquo;m not planning to implement Hindley-Milner type inference, or anything of that sort. For the purpose of this language, things will be either a list, or not a list. And as long as a function <strong>can</strong> return a list, it can also return the list from its base case. Thus, that&rsquo;s all we will try to figure out. The checking code is so short that we can include the whole snippet at once:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/src/LanguageOne.hs" data-first-line="219" data-last-line="227"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/src/LanguageOne.hs#L219-L227">LanguageOne.hs</a>, lines 219 through 227</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">219 </span><span class="lnt">220 </span><span class="lnt">221 </span><span class="lnt">222 </span><span class="lnt">223 </span><span class="lnt">224 </span><span class="lnt">225 </span><span class="lnt">226 </span><span class="lnt">227 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">getPossibleType</span> <span class="ow">::</span> <span class="kt">String</span> <span class="ow">-&gt;</span> <span class="kt">Expr</span> <span class="ow">-&gt;</span> <span class="kt">PossibleType</span> </span></span><span class="line"><span class="cl"><span class="nf">getPossibleType</span> <span class="n">s</span> <span class="p">(</span><span class="kt">Var</span> <span class="n">s&#39;</span><span class="p">)</span> <span class="ow">=</span> <span class="kr">if</span> <span class="n">s</span> <span class="o">==</span> <span class="n">s&#39;</span> <span class="kr">then</span> <span class="kt">List</span> <span class="kr">else</span> <span class="kt">Any</span> </span></span><span class="line"><span class="cl"><span class="nf">getPossibleType</span> <span class="kr">_</span> <span class="p">(</span><span class="kt">ListLiteral</span> <span class="kr">_</span><span class="p">)</span> <span class="ow">=</span> <span class="kt">List</span> </span></span><span class="line"><span class="cl"><span class="nf">getPossibleType</span> <span class="n">s</span> <span class="p">(</span><span class="kt">Split</span> <span class="kr">_</span> <span class="kr">_</span> <span class="n">e</span><span class="p">)</span> <span class="ow">=</span> <span class="n">getPossibleType</span> <span class="n">s</span> <span class="n">e</span> </span></span><span class="line"><span class="cl"><span class="nf">getPossibleType</span> <span class="n">s</span> <span class="p">(</span><span class="kt">IfElse</span> <span class="n">i</span> <span class="n">t</span> <span class="n">e</span><span class="p">)</span> <span class="ow">=</span> </span></span><span class="line"><span class="cl"> <span class="n">foldl1</span> <span class="n">mergePossibleType</span> <span class="o">$</span> <span class="n">map</span> <span class="p">(</span><span class="n">getPossibleType</span> <span class="n">s</span><span class="p">)</span> <span class="p">[</span><span class="n">i</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="n">e</span><span class="p">]</span> </span></span><span class="line"><span class="cl"><span class="nf">getPossibleType</span> <span class="kr">_</span> <span class="p">(</span><span class="kt">BinOp</span> <span class="kt">Insert</span> <span class="kr">_</span> <span class="kr">_</span><span class="p">)</span> <span class="ow">=</span> <span class="kt">List</span> </span></span><span class="line"><span class="cl"><span class="nf">getPossibleType</span> <span class="kr">_</span> <span class="p">(</span><span class="kt">BinOp</span> <span class="kt">Concat</span> <span class="kr">_</span> <span class="kr">_</span><span class="p">)</span> <span class="ow">=</span> <span class="kt">List</span> </span></span><span class="line"><span class="cl"><span class="nf">getPossibleType</span> <span class="kr">_</span> <span class="kr">_</span> <span class="ow">=</span> <span class="kt">Any</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p><code>mergePossibleType</code> <span class="sidenote"> <label class="sidenote-label" for="bool-identity-note">figures out</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="bool-identity-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> An observant reader will note that this is just a logical OR function. It's not, however, good practice to use booleans for types that have two constructors with no arguments. Check out this <a href="https://programming-elm.com/blog/2019-05-20-solving-the-boolean-identity-crisis-part-1/"> Elm-based article</a> about this, which the author calls the Boolean Identity Crisis. <span class="sidenote-delimiter">]</span> </span> </span> , given two possible types for an expression, the final type for the expression.</p> <p>There&rsquo;s only one real trick to this. Sometimes, like in <code>_search</code>, the only time we return something <em>known</em> to be a list, that something is <code>xs</code>. Since we&rsquo;re making a list manipulation language, let&rsquo;s <strong>assume the first argument to the function is a list</strong>, and <strong>use this information to determine expression types</strong>. We guess types in a very basic manner otherwise: If you use the concatenation operator, or a list literal, then obviously we&rsquo;re working on a list. If you&rsquo;re returning the first argument of the function, that&rsquo;s also a list. Otherwise, it could be anything.</p> <p>My Haskell linter actually suggested a pretty clever way of writing the whole &ldquo;add a base case if this function returns a list&rdquo; code. Check it out:</p> <div class="highlight-group" data-base-path="" data-file-path="cs325-langs/src/LanguageOne.hs" data-first-line="260" data-last-line="266"> <div class="highlight-label">From <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/cs325-langs/src/LanguageOne.hs#L260-L266">LanguageOne.hs</a>, lines 260 through 266</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">260 </span><span class="lnt">261 </span><span class="lnt">262 </span><span class="lnt">263 </span><span class="lnt">264 </span><span class="lnt">265 </span><span class="lnt">266 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">translateFunction</span> <span class="ow">::</span> <span class="kt">Function</span> <span class="ow">-&gt;</span> <span class="kt">Translator</span> <span class="p">[</span><span class="kt">Py</span><span class="o">.</span><span class="kt">PyStmt</span><span class="p">]</span> </span></span><span class="line"><span class="cl"><span class="nf">translateFunction</span> <span class="p">(</span><span class="kt">Function</span> <span class="n">n</span> <span class="n">ps</span> <span class="n">ex</span><span class="p">)</span> <span class="ow">=</span> <span class="kr">do</span> </span></span><span class="line"><span class="cl"> <span class="kr">let</span> <span class="n">createIf</span> <span class="n">p</span> <span class="ow">=</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">BinOp</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">Equal</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="n">p</span><span class="p">)</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">ListLiteral</span> <span class="kt">[]</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="kr">let</span> <span class="n">createReturn</span> <span class="n">p</span> <span class="ow">=</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">IfElse</span> <span class="p">(</span><span class="n">createIf</span> <span class="n">p</span><span class="p">)</span> <span class="p">[</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Return</span> <span class="p">(</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Var</span> <span class="n">p</span><span class="p">)]</span> <span class="kt">[]</span> <span class="kt">Nothing</span> </span></span><span class="line"><span class="cl"> <span class="kr">let</span> <span class="n">fastReturn</span> <span class="ow">=</span> <span class="p">[</span><span class="n">createReturn</span> <span class="n">p</span> <span class="o">|</span> <span class="n">p</span> <span class="ow">&lt;-</span> <span class="n">take</span> <span class="mi">1</span> <span class="n">ps</span><span class="p">,</span> <span class="n">getPossibleType</span> <span class="n">p</span> <span class="n">ex</span> <span class="o">==</span> <span class="kt">List</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">ss</span><span class="p">,</span> <span class="n">e</span><span class="p">)</span> <span class="ow">&lt;-</span> <span class="n">translateExpr</span> <span class="n">ex</span> </span></span><span class="line"><span class="cl"> <span class="n">return</span> <span class="o">$</span> <span class="n">return</span> <span class="o">$</span> <span class="kt">Py</span><span class="o">.</span><span class="kt">FunctionDef</span> <span class="n">n</span> <span class="n">ps</span> <span class="o">$</span> <span class="n">fastReturn</span> <span class="o">++</span> <span class="n">ss</span> <span class="o">++</span> <span class="p">[</span><span class="kt">Py</span><span class="o">.</span><span class="kt">Return</span> <span class="n">e</span><span class="p">]</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Specifically, look at the line with <code>let fastReturn = ...</code>. It uses a list comprehension: we take a parameter <code>p</code> from the list of parameter <code>ps</code>, but only produce the statements for the base case if the possible type computed using <code>p</code> is <code>List</code>.</p> <a href="#the-output"> <h3 id="the-output">The Output</h3> </a> <p>What kind of beast have we created? Take a look for yourself:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Python" data-lang="Python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">qselect</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span><span class="n">k</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">xs</span><span class="o">==</span><span class="p">[]:</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">xs</span> </span></span><span class="line"><span class="cl"> <span class="n">cache</span> <span class="o">=</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">pivot</span><span class="p">():</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="s2">&#34;pivot&#34;</span><span class="p">)</span> <span class="ow">not</span> <span class="ow">in</span> <span class="p">(</span><span class="n">cache</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">cache</span><span class="p">[</span><span class="s2">&#34;pivot&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="n">xs</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">cache</span><span class="p">[</span><span class="s2">&#34;pivot&#34;</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">left</span><span class="p">():</span> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">temp2</span><span class="p">(</span><span class="n">arg</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">out</span> <span class="o">=</span> <span class="p">[]</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">arg0</span> <span class="ow">in</span> <span class="n">arg</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">arg0</span><span class="o">&lt;=</span><span class="n">pivot</span><span class="p">():</span> </span></span><span class="line"><span class="cl"> <span class="n">out</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">arg0</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">out</span> </span></span><span class="line"><span class="cl"> <span class="n">pivot</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="s2">&#34;left&#34;</span><span class="p">)</span> <span class="ow">not</span> <span class="ow">in</span> <span class="p">(</span><span class="n">cache</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">cache</span><span class="p">[</span><span class="s2">&#34;left&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="n">temp2</span><span class="p">(</span><span class="n">xs</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">cache</span><span class="p">[</span><span class="s2">&#34;left&#34;</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">right</span><span class="p">():</span> </span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">temp3</span><span class="p">(</span><span class="n">arg</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">out</span> <span class="o">=</span> <span class="p">[]</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="n">arg0</span> <span class="ow">in</span> <span class="n">arg</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">arg0</span><span class="o">&gt;</span><span class="n">pivot</span><span class="p">():</span> </span></span><span class="line"><span class="cl"> <span class="n">out</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">arg0</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">out</span> </span></span><span class="line"><span class="cl"> <span class="n">left</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="n">pivot</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="s2">&#34;right&#34;</span><span class="p">)</span> <span class="ow">not</span> <span class="ow">in</span> <span class="p">(</span><span class="n">cache</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">cache</span><span class="p">[</span><span class="s2">&#34;right&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="n">temp3</span><span class="p">(</span><span class="n">xs</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">cache</span><span class="p">[</span><span class="s2">&#34;right&#34;</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">k</span><span class="o">&gt;</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">left</span><span class="p">())</span><span class="o">+</span><span class="mi">1</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">temp4</span> <span class="o">=</span> <span class="n">qselect</span><span class="p">(</span><span class="n">right</span><span class="p">(),</span> <span class="n">k</span><span class="o">-</span><span class="nb">len</span><span class="p">(</span><span class="n">left</span><span class="p">())</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">k</span><span class="o">==</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">left</span><span class="p">())</span><span class="o">+</span><span class="mi">1</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="n">temp5</span> <span class="o">=</span> <span class="p">[</span><span class="n">pivot</span><span class="p">()]</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">temp5</span> <span class="o">=</span> <span class="n">qselect</span><span class="p">(</span><span class="n">left</span><span class="p">(),</span> <span class="n">k</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">temp4</span> <span class="o">=</span> <span class="n">temp5</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">temp4</span> </span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">_search</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span><span class="n">k</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">xs</span><span class="o">==</span><span class="p">[]:</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">xs</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">xs</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">==</span><span class="n">k</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">temp6</span> <span class="o">=</span> <span class="n">xs</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">xs</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">&gt;</span><span class="n">k</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">temp8</span> <span class="o">=</span> <span class="n">_search</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">k</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">temp8</span> <span class="o">=</span> <span class="n">_search</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span> <span class="n">k</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">temp6</span> <span class="o">=</span> <span class="n">temp8</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">temp6</span> </span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">sorted</span><span class="p">(</span><span class="n">xs</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">xs</span><span class="o">==</span><span class="p">[]:</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">xs</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span><span class="o">+</span><span class="p">[</span><span class="n">xs</span><span class="p">[</span><span class="mi">1</span><span class="p">]]</span><span class="o">+</span><span class="nb">sorted</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span> </span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">search</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span><span class="n">k</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="n">_search</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">k</span><span class="p">))</span><span class="o">!=</span><span class="mi">0</span> </span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">insert</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span><span class="n">k</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">_insert</span><span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">_search</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">k</span><span class="p">))</span> </span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">_insert</span><span class="p">(</span><span class="n">k</span><span class="p">,</span><span class="n">xs</span><span class="p">):</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="n">k</span><span class="o">==</span><span class="p">[]:</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">k</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">xs</span><span class="p">)</span><span class="o">==</span><span class="mi">0</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">temp16</span> <span class="o">=</span> <span class="n">xs</span> </span></span><span class="line"><span class="cl"> <span class="n">temp16</span><span class="o">.</span><span class="n">append</span><span class="p">([])</span> </span></span><span class="line"><span class="cl"> <span class="n">temp17</span> <span class="o">=</span> <span class="n">temp16</span> </span></span><span class="line"><span class="cl"> <span class="n">temp17</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">k</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="n">temp18</span> <span class="o">=</span> <span class="n">temp17</span> </span></span><span class="line"><span class="cl"> <span class="n">temp18</span><span class="o">.</span><span class="n">append</span><span class="p">([])</span> </span></span><span class="line"><span class="cl"> <span class="n">temp15</span> <span class="o">=</span> <span class="n">temp18</span> </span></span><span class="line"><span class="cl"> <span class="k">else</span><span class="p">:</span> </span></span><span class="line"><span class="cl"> <span class="n">temp15</span> <span class="o">=</span> <span class="n">xs</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">temp15</span> </span></span></code></pre></div><p>It&rsquo;s&hellip;horrible! All the <code>tempX</code> variables, <strong>three layers of nested function declarations</strong>, hardcoded cache access. This is not something you&rsquo;d ever want to write. Even to get this code, I had to come up with hacks <strong>in a language I created</strong>. The first is the hack is to make the <code>qselect</code> function use the <code>xs == []</code> base case. This doesn&rsquo;t happen by default, because <code>qselect</code> doesn&rsquo;t return a list! To &ldquo;fix&rdquo; this, I made <code>qselect</code> return the number it found, wrapped in a list literal. This is not up to spec, and would require another function to unwrap this list.</p> <p>While <code>qselect</code> was struggling with not having the base case, <code>insert</code> had a base case it didn&rsquo;t need: <code>insert</code> shouldn&rsquo;t return the list itself when it&rsquo;s empty, it should insert into it! However, when we use the <code>&lt;&lt;</code> list insertion operator, the language infers <code>insert</code> to be a list-returning function itself, inserting into an empty list will always fail. So, we make a function <code>_insert</code>, which <strong>takes the arguments in reverse</strong>. The base case will still be generated, but the first argument (against which the base case is checked) will be a number, so the <code>k == []</code> check will always fail.</p> <p>That concludes this post. I&rsquo;ll be working on more solutions to homework assignments in self-made languages, so keep an eye out!</p> JavaScript-Free Sidenotes in Hugo https://danilafe.com/blog/sidenotes/ Sat, 07 Dec 2019 00:23:34 -0800 https://danilafe.com/blog/sidenotes/ <p>A friend recently showed me a website, the design of which I really liked: Gwern Branwen&rsquo;s <a href="https://www.gwern.net/index"class="external-link">personal website<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. In particular, I found that <strong>sidenotes</strong> were a feature that I didn&rsquo;t even know I needed. A lot of my writing seems to use small parenthesized remarks (like this), which, although it doesn&rsquo;t break the flow in a grammatical sense, lengthens the sentence, and makes it harder to follow. Since I do my best to write content to help explain stuff (like the <a href="https://danilafe.com/blog/00_compiler_intro/">compiler series</a>), making sentences <strong>more</strong> difficult to understand is a no-go.</p> <p>So, what do they look like? <span class="sidenote"> <label class="sidenote-label" for="example-note">Here&rsquo;s an example sidenote.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="example-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> This is this example note's content. <span class="sidenote-delimiter">]</span> </span> </span> If you&rsquo;re on a mobile device, the content is hidden by default: there&rsquo;s no &ldquo;side&rdquo; on which the note fits. In this case, you can click or tap the underlined portion of the text, which is the part to which the sidenote is related or refers to. Otherwise, the example sidenote should be visible <span class="sidenote"> <label class="sidenote-label" for="left-note">on the right side of the screen.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="left-note"></input><span class="sidenote-content sidenote-left"><span class="sidenote-delimiter">[note:</span> Sidenotes can also appear on the left of the screen, to help prevent situations in which there are too many sidenotes and not enough space. <span class="sidenote-delimiter">]</span> </span> </span> </p> <p>A major goal of mine in implementing these sidenotes was to avoid the use of JavaScript. This is driven by my recent installation of uMatrix. uMatrix is an extension that blocks JavaScript loaded from domains other than the one you&rsquo;re visiting. To my frustration, a lot of the websites I commonly visit ended up broken: <a href="https://github.com/instructure/canvas-lms"class="external-link">Canvas<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, Discord, YouTube, and Disquss all fail catastrophically when they aren&rsquo;t allowed to load dozens of scripts from various sources. Out of spite, I want my site to work without any JavaScript, and these notes are no exception.</p> <a href="#implementation"> <h3 id="implementation">Implementation</h3> </a> <p>Some of this work has been inspired by <a href="https://www.kooslooijesteijn.net/blog/semantic-sidenotes"class="external-link">this article<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. The first concern was not having to write raw HTML to add the side notes, but this is fairly simple with Hugo&rsquo;s shortcodes: I write the HTML once in a new <code>sidenote</code> shortcode, then call the shortcode from my posts. The next issue is a matter of HTML standards. Markdown rendering generates <code>&lt;p&gt;</code> tags. According the to spec, <code>&lt;p&gt;</code> tags cannot have a block element inside them. When you <em>try</em> to put a block element, such as <code>&lt;div&gt;</code> inside <code>&lt;p&gt;</code>, the browser will automatically close the <code>&lt;p&gt;</code> tag, breaking the rest of the page. So, even though conceptually (and visually) the content of the sidenote is a block, <span class="sidenote"> <label class="sidenote-label" for="markdown-note">it has to be inside an inline element.</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="markdown-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> There's another consequence to this. Hugo implements Markdown inside shortcodes by rendering the "inner" part of the shortcode, substituting the result into the shortcode's definition, and then finally placing that into the final output. Since the Markdown renderer wraps text in paragraphs, which are block elements, the inside of the shortcode ends up with block elements. The same tag-closing issue manifests, and the view ends up broken. So, Markdown cannot be used inside sidenotes. <span class="sidenote-delimiter">]</span> </span> </span> </p> <p>That&rsquo;s not too bad, overall. We end up with a shortcode definition as follows:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-HTML" data-lang="HTML"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;sidenote&#34;</span><span class="p">&gt;</span> </span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">label</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;sidenote-label&#34;</span> <span class="na">for</span><span class="o">=</span><span class="s">&#34;{{ .Get 1 }}&#34;</span><span class="p">&gt;</span>{{ .Get 2 }}<span class="p">&lt;/</span><span class="nt">label</span><span class="p">&gt;</span> </span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">input</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;sidenote-checkbox&#34;</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;checkbox&#34;</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;{{ .Get 1 }}&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">input</span><span class="p">&gt;</span> </span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;sidenote-content sidenote-{{ .Get 0 }}&#34;</span><span class="p">&gt;</span> </span></span><span class="line"><span class="cl">{{ .Inner }} </span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span> </span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span> </span></span></code></pre></div><p>As Koos points out, &ldquo;label&rdquo; works as a semantic tag for the text that references the sidenote. It also helps us with the checkbox <code>&lt;input&gt;</code>, which we will examine later. Since it will receive its own style, the inner content of the sidenote is wrapped in another <code>&lt;span&gt;</code>. Let&rsquo;s get started on styling the parts of a sidenote, beginning with the content:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-SCSS" data-lang="SCSS"><span class="line"><span class="cl"><span class="nc">.sidenote-content</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="na">display</span><span class="o">:</span> <span class="ni">block</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="na">position</span><span class="o">:</span> <span class="ni">absolute</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="na">width</span><span class="o">:</span> <span class="nv">$sidenote-width</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="na">box-sizing</span><span class="o">:</span> <span class="ni">border-box</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="na">margin-top</span><span class="o">:</span> <span class="o">-</span><span class="mi">1</span><span class="mf">.5</span><span class="kt">em</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">&amp;</span><span class="nc">.sidenote-right</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="na">right</span><span class="o">:</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="na">margin-right</span><span class="o">:</span> <span class="nf">-</span><span class="p">(</span><span class="nv">$sidenote-width</span> <span class="o">+</span> <span class="nv">$sidenote-offset</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1">// ... </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> </span></span></code></pre></div><p>As you can see from the sidenotes on this page, they are displayed as a block. We start with that, then switch the sidenotes to be positioned absolutely, so that we can place them exactly to the right of the content, and then some. We also make sure that the box is sized <strong>exactly</strong> the amount in <code>$sidenote-width</code>, by ensuring that the border and padding are included in the size calculation using <code>border-box</code>. We also hide the checkbox:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-SCSS" data-lang="SCSS"><span class="line"><span class="cl"><span class="nc">.sidenote-checkbox</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="na">display</span><span class="o">:</span> <span class="ni">none</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></div><p>Finally, let&rsquo;s make one more adjustment to the sidenote and its label: when you hover over one of them, the other will change its appearence slightly, so that you can tell which note refers to which label. We can do so by detecting hover of the parent element:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-SCSS" data-lang="SCSS"><span class="line"><span class="cl"><span class="nc">.sidenote</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">&amp;</span><span class="nd">:hover</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nc">.sidenote-label</span> <span class="p">{</span> <span class="cm">/* style for the label */</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="nc">.sidenote-content</span> <span class="p">{</span> <span class="cm">/* style for the sidenote */</span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></div><a href="#hiding-and-showing"> <h3 id="hiding-and-showing">Hiding and Showing</h3> </a> <p>So far, it&rsquo;s hard to imagine where JavaScript would come in. If you were always looking at the page from a wide-screen machine, it wouldn&rsquo;t at all. Unfortunately phones don&rsquo;t leave a lot of room for margins and sidenotes, so to make sure that these notes are visible to mobile users, we want to show them inline. Since the entire idea of sidenotes is to present more information <strong>without</strong> interrupting the main text, we don&rsquo;t want to plop something down in the middle of the screen by default. So we hide sidenotes, and show them only when their label is clicked.</p> <p>Gwern&rsquo;s site doesn&rsquo;t show the notes on mobile at all (when simulated using Firefox&rsquo;s responsive design mode), and Koos uses JavaScript to toggle the sidenote text. We will go another route.</p> <p>This is where the checkbox <code>&lt;input&gt;</code> comes in. When the <code>&lt;input&gt;</code> checkbox is checked, we show the sidenote text, as a block, in the middle of the page. When it is not checked, we keep it hidden. Of course, keeping a checkbox in the middle of the page is not pretty, so we keep it hidden. Rather than clicking the checkbox directly, <span class="sidenote"> <label class="sidenote-label" for="accessibility-note">the users can click the text that refers to the sidenote,</label> <input class="sidenote-checkbox" style="display: none;" type="checkbox" id="accessibility-note"></input><span class="sidenote-content sidenote-right"><span class="sidenote-delimiter">[note:</span> I'm not sure about the accessibility of such an arrangement. The label is semantic, sure, but the checkbox is more sketchy. Take this design with a grain of salt. <span class="sidenote-delimiter">]</span> </span> </span> which happens to also be a label for the checkbox input. Clicking the label toggles the checkbox, and with it the display of the sidenote. We can use the following CSS to get that to work:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-SCSS" data-lang="SCSS"><span class="line"><span class="cl"><span class="nc">.sidenote-content</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// ... </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">@media</span> <span class="ni">screen</span> <span class="ow">and</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="na">max-width</span><span class="o">:</span> <span class="nv">$container-width</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">*</span> <span class="p">(</span><span class="nv">$sidenote-width</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">*</span> <span class="nv">$sidenote-offset</span><span class="p">))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="na">position</span><span class="o">:</span> <span class="ni">static</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="na">margin-top</span><span class="o">:</span> <span class="mi">10</span><span class="kt">px</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="na">margin-bottom</span><span class="o">:</span> <span class="mi">10</span><span class="kt">px</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="na">width</span><span class="o">:</span> <span class="mi">100</span><span class="kt">%</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="na">display</span><span class="o">:</span> <span class="ni">none</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="nc">.sidenote-checkbox</span><span class="nd">:checked</span> <span class="o">~</span> <span class="k">&amp;</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="na">display</span><span class="o">:</span> <span class="ni">block</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">&amp;</span><span class="nc">.sidenote-right</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="na">margin-right</span><span class="o">:</span> <span class="mi">0</span><span class="kt">px</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1">// ... </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="c1">// ... </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> </span></span></code></pre></div><p>We put the position back to <code>static</code>, and add margins on the top and bottom of the node. We keep the <code>display</code> to <code>none</code>, unless the checkbox contained in the sidenote span is checked. Finally, we reset the margin we created earlier, since we&rsquo;re not moving this note anywhere.</p> <a href="#conclusion"> <h3 id="conclusion">Conclusion</h3> </a> <p>Here, we&rsquo;ve implemented sidenotes in Hugo with zero JavaScript. They work well on both mobile and desktop devices, though their accessibility is, at present, somewhat uncertain.</p> Compiling a Functional Language Using C++, Part 8 - LLVM https://danilafe.com/blog/08_compiler_llvm/ Wed, 30 Oct 2019 22:16:22 -0700 https://danilafe.com/blog/08_compiler_llvm/ <p>We don&rsquo;t want a compiler that can only generate code for a single platform. Our language should work on macOS, Windows, and Linux, on x86_64, ARM, and maybe some other architectures. We also don&rsquo;t want to manually implement the compiler for each platform, dealing with the specifics of each architecture and operating system.</p> <p>This is where LLVM comes in. LLVM (which stands for <strong>Low Level Virtual Machine</strong>), is a project which presents us with a kind of generic assembly language, an <strong>Intermediate Representation</strong> (IR). It also provides tooling to compile the IR into platform-specific instructions, as well as to apply a host of various optimizations. We can thus translate our G-machine instructions to LLVM, and then use LLVM to generate machine code, which gets us to our ultimate goal of compiling our language.</p> <p>We start with adding LLVM to our CMake project. <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/08/CMakeLists.txt" data-first-line="7" data-last-line="7"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/08/CMakeLists.txt#L7-L7">CMakeLists.txt</a>, line 7</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">7 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-CMake" data-lang="CMake"><span class="line"><span class="cl"><span class="nb">find_package</span><span class="p">(</span><span class="s">LLVM</span> <span class="s">REQUIRED</span> <span class="s">CONFIG</span><span class="p">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>LLVM is a huge project, and has many components. We don&rsquo;t need most of them. We do need the core libraries, the x86 assembly generator, and x86 assembly parser. I&rsquo;m not sure why we need the last one, but I ran into linking errors without them. We find the required link targets for these components using this CMake command:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/08/CMakeLists.txt" data-first-line="19" data-last-line="20"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/08/CMakeLists.txt#L19-L20">CMakeLists.txt</a>, lines 19 through 20</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">19 </span><span class="lnt">20 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-CMake" data-lang="CMake"><span class="line"><span class="cl"><span class="c"># Find all the relevant LLVM components </span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="nb">llvm_map_components_to_libnames</span><span class="p">(</span><span class="s">LLVM_LIBS</span> <span class="s">core</span> <span class="s">x86asmparser</span> <span class="s">x86codegen</span><span class="p">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Finally, we add the new include directories, link targets, and definitions to our compiler executable:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/08/CMakeLists.txt" data-first-line="39" data-last-line="41"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/08/CMakeLists.txt#L39-L41">CMakeLists.txt</a>, lines 39 through 41</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-CMake" data-lang="CMake"><span class="line"><span class="cl"><span class="nb">target_include_directories</span><span class="p">(</span><span class="s">compiler</span> <span class="s">PUBLIC</span> <span class="o">${</span><span class="nv">CMAKE_CURRENT_BINARY_DIR</span><span class="o">}</span><span class="p">)</span><span class="err"> </span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="nb">target_include_directories</span><span class="p">(</span><span class="s">compiler</span> <span class="s">PUBLIC</span> <span class="o">${</span><span class="nv">LLVM_INCLUDE_DIRS</span><span class="o">}</span><span class="p">)</span><span class="err"> </span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="nb">target_compile_definitions</span><span class="p">(</span><span class="s">compiler</span> <span class="s">PUBLIC</span> <span class="o">${</span><span class="nv">LLVM_DEFINITIONS</span><span class="o">}</span><span class="p">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Great, we have the infrastructure updated to work with LLVM. It&rsquo;s now time to start using the LLVM API to compile our G-machine instructions into assembly. We start with <code>LLVMContext</code>. The LLVM documentation states:</p> <blockquote> <p>This is an important class for using LLVM in a threaded context. It (opaquely) owns and manages the core &ldquo;global&rdquo; data of LLVM&rsquo;s core infrastructure, including the type and constant uniquing tables.</p> </blockquote> <p>We will have exactly one instance of such a class in our program.</p> <p>Additionally, we want an <code>IRBuilder</code>, which will help us generate IR instructions, placing them into basic blocks (more on that in a bit). Also, we want a <code>Module</code> object, which represents some collection of code and declarations (perhaps like a C++ source file). Let&rsquo;s keep these things in our own <code>llvm_context</code> class. Here&rsquo;s what that looks like:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/08/llvm_context.hpp"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/08/llvm_context.hpp">llvm_context.hpp</a>, entire file</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span><span class="lnt">65 </span><span class="lnt">66 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="cp">#pragma once </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;llvm/IR/DerivedTypes.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;llvm/IR/Function.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;llvm/IR/LLVMContext.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;llvm/IR/IRBuilder.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;llvm/IR/Module.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;map&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">llvm_context</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="nc">custom_function</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">Function</span><span class="o">*</span> <span class="n">function</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int32_t</span> <span class="n">arity</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">using</span> <span class="n">custom_function_ptr</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">unique_ptr</span><span class="o">&lt;</span><span class="n">custom_function</span><span class="o">&gt;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">LLVMContext</span> <span class="n">ctx</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">IRBuilder</span><span class="o">&lt;&gt;</span> <span class="n">builder</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">Module</span> <span class="n">module</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">,</span> <span class="n">custom_function_ptr</span><span class="o">&gt;</span> <span class="n">custom_functions</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">,</span> <span class="n">llvm</span><span class="o">::</span><span class="n">Function</span><span class="o">*&gt;</span> <span class="n">functions</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">,</span> <span class="n">llvm</span><span class="o">::</span><span class="n">StructType</span><span class="o">*&gt;</span> <span class="n">struct_types</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">StructType</span><span class="o">*</span> <span class="n">stack_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">PointerType</span><span class="o">*</span> <span class="n">stack_ptr_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">PointerType</span><span class="o">*</span> <span class="n">node_ptr_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">IntegerType</span><span class="o">*</span> <span class="n">tag_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">FunctionType</span><span class="o">*</span> <span class="n">function_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">llvm_context</span><span class="p">()</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">builder</span><span class="p">(</span><span class="n">ctx</span><span class="p">),</span> <span class="n">module</span><span class="p">(</span><span class="s">&#34;bloglang&#34;</span><span class="p">,</span> <span class="n">ctx</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">create_types</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">create_functions</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">create_types</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">create_functions</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">ConstantInt</span><span class="o">*</span> <span class="n">create_i8</span><span class="p">(</span><span class="kt">int8_t</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">ConstantInt</span><span class="o">*</span> <span class="n">create_i32</span><span class="p">(</span><span class="kt">int32_t</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">ConstantInt</span><span class="o">*</span> <span class="n">create_size</span><span class="p">(</span><span class="n">size_t</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">Value</span><span class="o">*</span> <span class="n">create_pop</span><span class="p">(</span><span class="n">llvm</span><span class="o">::</span><span class="n">Function</span><span class="o">*</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">Value</span><span class="o">*</span> <span class="n">create_peek</span><span class="p">(</span><span class="n">llvm</span><span class="o">::</span><span class="n">Function</span><span class="o">*</span><span class="p">,</span> <span class="n">llvm</span><span class="o">::</span><span class="n">Value</span><span class="o">*</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">create_push</span><span class="p">(</span><span class="n">llvm</span><span class="o">::</span><span class="n">Function</span><span class="o">*</span><span class="p">,</span> <span class="n">llvm</span><span class="o">::</span><span class="n">Value</span><span class="o">*</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">create_popn</span><span class="p">(</span><span class="n">llvm</span><span class="o">::</span><span class="n">Function</span><span class="o">*</span><span class="p">,</span> <span class="n">llvm</span><span class="o">::</span><span class="n">Value</span><span class="o">*</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">create_update</span><span class="p">(</span><span class="n">llvm</span><span class="o">::</span><span class="n">Function</span><span class="o">*</span><span class="p">,</span> <span class="n">llvm</span><span class="o">::</span><span class="n">Value</span><span class="o">*</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">create_pack</span><span class="p">(</span><span class="n">llvm</span><span class="o">::</span><span class="n">Function</span><span class="o">*</span><span class="p">,</span> <span class="n">llvm</span><span class="o">::</span><span class="n">Value</span><span class="o">*</span><span class="p">,</span> <span class="n">llvm</span><span class="o">::</span><span class="n">Value</span><span class="o">*</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">create_split</span><span class="p">(</span><span class="n">llvm</span><span class="o">::</span><span class="n">Function</span><span class="o">*</span><span class="p">,</span> <span class="n">llvm</span><span class="o">::</span><span class="n">Value</span><span class="o">*</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">create_slide</span><span class="p">(</span><span class="n">llvm</span><span class="o">::</span><span class="n">Function</span><span class="o">*</span><span class="p">,</span> <span class="n">llvm</span><span class="o">::</span><span class="n">Value</span><span class="o">*</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">create_alloc</span><span class="p">(</span><span class="n">llvm</span><span class="o">::</span><span class="n">Function</span><span class="o">*</span><span class="p">,</span> <span class="n">llvm</span><span class="o">::</span><span class="n">Value</span><span class="o">*</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">Value</span><span class="o">*</span> <span class="n">create_eval</span><span class="p">(</span><span class="n">llvm</span><span class="o">::</span><span class="n">Value</span><span class="o">*</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">Value</span><span class="o">*</span> <span class="n">unwrap_num</span><span class="p">(</span><span class="n">llvm</span><span class="o">::</span><span class="n">Value</span><span class="o">*</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">Value</span><span class="o">*</span> <span class="n">create_num</span><span class="p">(</span><span class="n">llvm</span><span class="o">::</span><span class="n">Value</span><span class="o">*</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">Value</span><span class="o">*</span> <span class="n">unwrap_data_tag</span><span class="p">(</span><span class="n">llvm</span><span class="o">::</span><span class="n">Value</span><span class="o">*</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">Value</span><span class="o">*</span> <span class="n">create_global</span><span class="p">(</span><span class="n">llvm</span><span class="o">::</span><span class="n">Value</span><span class="o">*</span><span class="p">,</span> <span class="n">llvm</span><span class="o">::</span><span class="n">Value</span><span class="o">*</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">Value</span><span class="o">*</span> <span class="n">create_app</span><span class="p">(</span><span class="n">llvm</span><span class="o">::</span><span class="n">Value</span><span class="o">*</span><span class="p">,</span> <span class="n">llvm</span><span class="o">::</span><span class="n">Value</span><span class="o">*</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">Function</span><span class="o">*</span> <span class="n">create_custom_function</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">,</span> <span class="kt">int32_t</span> <span class="n">arity</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span></code></pre></td></tr></table> </div> </div> </div> <p>We include the LLVM context, builder, and module as members of the context struct. Since the builder and the module need the context, we initialize them in the constructor, where they can safely reference it.</p> <p>Besides these fields, we added a few others, namely the <code>functions</code> and <code>struct_types</code> maps, and the various <code>llvm::Type</code> subclasses such as <code>stack_type</code>. We did this because we want to be able to call our runtime functions (and use our runtime structs) from LLVM. To generate a function call from LLVM, we need to have access to an <code>llvm::Function</code> object. We thus want to have an <code>llvm::Function</code> object for each runtime function we want to call. We could declare a member variable in our <code>llvm_context</code> for each runtime function, but it&rsquo;s easier to leave this to be an implementation detail, and only have a dynamically created map between runtime function names and their corresponding <code>llvm::Function</code> objects.</p> <p>We populate the maps and other type-related variables in the two methods, <code>create_functions()</code> and <code>create_types()</code>. To create an <code>llvm::Function</code>, we must provide an <code>llvm::FunctionType</code>, an <code>llvm::LinkageType</code>, the name of the function, and the module in which the function is declared. Since we only have one module (the one we initialized in the constructor) that&rsquo;s the module we pass in. The name of the function is the same as its name in the runtime. The linkage type is a little more complicated - it tells LLVM the &ldquo;visibility&rdquo; of a function. &ldquo;Private&rdquo; or &ldquo;Internal&rdquo; would hide this function from the linker (like <code>static</code> functions in C). However, we want to do the opposite: our generated functions should be accessible from other code. Thus, our linkage type is &ldquo;External&rdquo;.</p> <p>The only remaining parameter is the <code>llvm::FunctionType</code>, which is created using code like:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">llvm</span><span class="o">::</span><span class="n">FunctionType</span><span class="o">::</span><span class="n">get</span><span class="p">(</span><span class="n">return_type</span><span class="p">,</span> <span class="p">{</span><span class="n">param_type_1</span><span class="p">,</span> <span class="n">param_type_2</span><span class="p">,</span> <span class="p">...},</span> <span class="n">is_variadic</span><span class="p">)</span> </span></span></code></pre></div><p>Declaring all the functions and types in our runtime is mostly just tedious. Here are a few lines from <code>create_functions()</code>, which give a very good idea of the rest of that method:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/08/llvm_context.cpp" data-first-line="47" data-last-line="60"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/08/llvm_context.cpp#L47-L60">llvm_context.cpp</a>, lines 47 through 60</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">void_type</span> <span class="o">=</span> <span class="n">Type</span><span class="o">::</span><span class="n">getVoidTy</span><span class="p">(</span><span class="n">ctx</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">sizet_type</span> <span class="o">=</span> <span class="n">IntegerType</span><span class="o">::</span><span class="n">get</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">size_t</span><span class="p">)</span> <span class="o">*</span> <span class="mi">8</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">functions</span><span class="p">[</span><span class="s">&#34;stack_init&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="n">Function</span><span class="o">::</span><span class="n">Create</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="n">FunctionType</span><span class="o">::</span><span class="n">get</span><span class="p">(</span><span class="n">void_type</span><span class="p">,</span> <span class="p">{</span> <span class="n">stack_ptr_type</span> <span class="p">},</span> <span class="nb">false</span><span class="p">),</span> </span></span><span class="line"><span class="cl"> <span class="n">Function</span><span class="o">::</span><span class="n">LinkageTypes</span><span class="o">::</span><span class="n">ExternalLinkage</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;stack_init&#34;</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="o">&amp;</span><span class="n">module</span> </span></span><span class="line"><span class="cl"> <span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">functions</span><span class="p">[</span><span class="s">&#34;stack_free&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="n">Function</span><span class="o">::</span><span class="n">Create</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="n">FunctionType</span><span class="o">::</span><span class="n">get</span><span class="p">(</span><span class="n">void_type</span><span class="p">,</span> <span class="p">{</span> <span class="n">stack_ptr_type</span> <span class="p">},</span> <span class="nb">false</span><span class="p">),</span> </span></span><span class="line"><span class="cl"> <span class="n">Function</span><span class="o">::</span><span class="n">LinkageTypes</span><span class="o">::</span><span class="n">ExternalLinkage</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;stack_free&#34;</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="o">&amp;</span><span class="n">module</span> </span></span><span class="line"><span class="cl"> <span class="p">);</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Similarly, here are a few lines from <code>create_types()</code>, from which you can extrapolate the rest:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/08/llvm_context.cpp" data-first-line="7" data-last-line="11"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/08/llvm_context.cpp#L7-L11">llvm_context.cpp</a>, lines 7 through 11</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="n">stack_type</span> <span class="o">=</span> <span class="n">StructType</span><span class="o">::</span><span class="n">create</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="s">&#34;stack&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">stack_ptr_type</span> <span class="o">=</span> <span class="n">PointerType</span><span class="o">::</span><span class="n">getUnqual</span><span class="p">(</span><span class="n">stack_type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">tag_type</span> <span class="o">=</span> <span class="n">IntegerType</span><span class="o">::</span><span class="n">getInt8Ty</span><span class="p">(</span><span class="n">ctx</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">struct_types</span><span class="p">[</span><span class="s">&#34;node_base&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="n">StructType</span><span class="o">::</span><span class="n">create</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="s">&#34;node_base&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">struct_types</span><span class="p">[</span><span class="s">&#34;node_app&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="n">StructType</span><span class="o">::</span><span class="n">create</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="s">&#34;node_app&#34;</span><span class="p">);</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We also tell LLVM the contents of our structs, so that we may later reference specific fields. This is just like forward declaration - we can forward declare a struct in C/C++, but unless we also declare its contents, we can&rsquo;t access what&rsquo;s inside. Below is the code for specifying the body of <code>node_base</code> and <code>node_app</code>.</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/08/llvm_context.cpp" data-first-line="19" data-last-line="26"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/08/llvm_context.cpp#L19-L26">llvm_context.cpp</a>, lines 19 through 26</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="n">struct_types</span><span class="p">.</span><span class="n">at</span><span class="p">(</span><span class="s">&#34;node_base&#34;</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">setBody</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="n">IntegerType</span><span class="o">::</span><span class="n">getInt32Ty</span><span class="p">(</span><span class="n">ctx</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">struct_types</span><span class="p">.</span><span class="n">at</span><span class="p">(</span><span class="s">&#34;node_app&#34;</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">setBody</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="n">struct_types</span><span class="p">.</span><span class="n">at</span><span class="p">(</span><span class="s">&#34;node_base&#34;</span><span class="p">),</span> </span></span><span class="line"><span class="cl"> <span class="n">node_ptr_type</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">node_ptr_type</span> </span></span><span class="line"><span class="cl"> <span class="p">);</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>There&rsquo;s still more functionality packed into <code>llvm_context</code>. Let&rsquo;s next take a look into <code>custom_function</code>, and the <code>create_custom_function</code> method. Why do we need these? To highlight the need for the custom class, let&rsquo;s take a look at <code>instruction_pushglobal</code> which occurs at the G-machine level, and then at <code>alloc_global</code>, which will be a function call generated as part of the PushGlobal instruction. <code>instruction_pushglobal</code>&rsquo;s only member variable is <code>name</code>, which stands for the name of the global function it&rsquo;s referencing. However, <code>alloc_global</code> requires an arity argument! We can try to get this information from the <code>llvm::Function</code> corresponding to the global we&rsquo;re trying to reference, but this doesn&rsquo;t get us anywhere: as far as LLVM is concerned, any global function only takes one parameter, the stack. The rest of the parameters are given through that stack, and their number cannot be easily deduced from the function alone.</p> <p>Instead, we decide to store global functions together with their arity. We thus create a class to combine these two things (<code>custom_function</code>), define a map from global function names to instances of <code>custom_function</code>, and add a convenience method (<code>create_custom_function</code>) that takes care of constructing an <code>llvm::Function</code> object, creating a <code>custom_function</code>, and storing it in the map.</p> <p>The implementation for <code>custom_function</code> is straightforward:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/08/llvm_context.cpp" data-first-line="234" data-last-line="252"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/08/llvm_context.cpp#L234-L252">llvm_context.cpp</a>, lines 234 through 252</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">234 </span><span class="lnt">235 </span><span class="lnt">236 </span><span class="lnt">237 </span><span class="lnt">238 </span><span class="lnt">239 </span><span class="lnt">240 </span><span class="lnt">241 </span><span class="lnt">242 </span><span class="lnt">243 </span><span class="lnt">244 </span><span class="lnt">245 </span><span class="lnt">246 </span><span class="lnt">247 </span><span class="lnt">248 </span><span class="lnt">249 </span><span class="lnt">250 </span><span class="lnt">251 </span><span class="lnt">252 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">llvm</span><span class="o">::</span><span class="n">Function</span><span class="o">*</span> <span class="n">llvm_context</span><span class="o">::</span><span class="n">create_custom_function</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">,</span> <span class="kt">int32_t</span> <span class="n">arity</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">void_type</span> <span class="o">=</span> <span class="n">llvm</span><span class="o">::</span><span class="n">Type</span><span class="o">::</span><span class="n">getVoidTy</span><span class="p">(</span><span class="n">ctx</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">function_type</span> <span class="o">=</span> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">FunctionType</span><span class="o">::</span><span class="n">get</span><span class="p">(</span><span class="n">void_type</span><span class="p">,</span> <span class="p">{</span> <span class="n">stack_ptr_type</span> <span class="p">},</span> <span class="nb">false</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">new_function</span> <span class="o">=</span> <span class="n">llvm</span><span class="o">::</span><span class="n">Function</span><span class="o">::</span><span class="n">Create</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="n">function_type</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">Function</span><span class="o">::</span><span class="n">LinkageTypes</span><span class="o">::</span><span class="n">ExternalLinkage</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="s">&#34;f_&#34;</span> <span class="o">+</span> <span class="n">name</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="o">&amp;</span><span class="n">module</span> </span></span><span class="line"><span class="cl"> <span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">start_block</span> <span class="o">=</span> <span class="n">llvm</span><span class="o">::</span><span class="n">BasicBlock</span><span class="o">::</span><span class="n">Create</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="s">&#34;entry&#34;</span><span class="p">,</span> <span class="n">new_function</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">new_custom_f</span> <span class="o">=</span> <span class="n">custom_function_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">custom_function</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="n">new_custom_f</span><span class="o">-&gt;</span><span class="n">arity</span> <span class="o">=</span> <span class="n">arity</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">new_custom_f</span><span class="o">-&gt;</span><span class="n">function</span> <span class="o">=</span> <span class="n">new_function</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">custom_functions</span><span class="p">[</span><span class="s">&#34;f_&#34;</span> <span class="o">+</span> <span class="n">name</span><span class="p">]</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">new_custom_f</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">new_function</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We create a function type, then a function, and finally initialize a <code>custom_function</code>. There&rsquo;s one thing we haven&rsquo;t seen yet in this function, which is the <code>BasicBlock</code> class. We&rsquo;ll get to what basic blocks are shortly, but for now it&rsquo;s sufficient to know that the basic block gives us a place to insert code.</p> <p>This isn&rsquo;t the end of our <code>llvm_context</code> class: it also has a variety of other <code>create_*</code> methods! Let&rsquo;s take a look at their signatures. Most return either <code>void</code>, <code>llvm::ConstantInt*</code>, or <code>llvm::Value*</code>. Since <code>llvm::ConstantInt*</code> is a subclass of <code>llvm::Value*</code>, let&rsquo;s just treat it as simply an <code>llvm::Value*</code> while trying to understand these methods.</p> <p>So, what is <code>llvm::Value</code>? To answer this question, let&rsquo;s first understand how the LLVM IR works.</p> <a href="#llvm-ir"> <h3 id="llvm-ir">LLVM IR</h3> </a> <p>An important property of LLVM IR is that it is in <strong>Single Static Assignment</strong> (SSA) form. This means that each variable can only be assigned to once. For instance, if we use <code>&lt;-</code> to represent assignment, the following program is valid:</p> <pre tabindex="0"><code>x &lt;- 1 y &lt;- 2 z &lt;- x + y </code></pre><p>However, the following program is <strong>not</strong> valid:</p> <pre tabindex="0"><code>x &lt;- 1 x &lt;- x + 1 </code></pre><p>But what if we <strong>do</strong> want to modify a variable <code>x</code>? We can declare another &ldquo;version&rdquo; of <code>x</code> every time we modify it. For instance, if we wanted to increment <code>x</code> twice, we&rsquo;d do this:</p> <pre tabindex="0"><code>x &lt;- 1 x1 &lt;- x + 1 x2 &lt;- x1 + 1 </code></pre><p>In practice, LLVM&rsquo;s C++ API can take care of versioning variables on its own, by auto-incrementing numbers associated with each variable we use.</p> <p>Assigned to each variable is <code>llvm::Value</code>. The LLVM documentation states:</p> <blockquote> <p>It is the base class of all values computed by a program that may be used as operands to other values.</p> </blockquote> <p>It&rsquo;s important to understand that <code>llvm::Value</code> <strong>does not store the result of the computation</strong>. It rather represents how something may be computed. 1 is a value because it computed by just returning 1. <code>x + 1</code> is a value because it is computed by adding the value inside of <code>x</code> to 1. Since we cannot modify a variable once we&rsquo;ve declared it, we will keep assigning intermediate results to new variables, constructing new values out of values that we&rsquo;ve already specified.</p> <p>This somewhat elucidates what the <code>create_*</code> functions do: <code>create_i8</code> creates an 8-bit integer value, and <code>create_pop</code> creates a value that is computed by calling our runtime <code>stack_pop</code> function.</p> <p>Before we move on to look at the implementations of these functions, we need to understand another concept from the world of compiler design: <strong>basic blocks</strong>. A basic block is a sequence of instructions that are guaranteed to be executed one after another. This means that a basic block cannot have an if/else, jump, or any other type of control flow anywhere except at the end. If control flow could appear inside the basic block, there would be opporunity for execution of some, but not all, instructions in the block, violating the definition. Every time we add an IR instruction in LLVM, we add it to a basic block. Writing control flow involves creating several blocks, with each block serving as the destination of a potential jump. We will see this used to compile the Jump instruction.</p> <a href="#generating-llvm-ir"> <h3 id="generating-llvm-ir">Generating LLVM IR</h3> </a> <p>Now that we understand what <code>llvm::Value</code> is, and have a vague understanding of how LLVM is structured, let&rsquo;s take a look at the implementations of the <code>create_*</code> functions. The simplest is <code>create_i8</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/08/llvm_context.cpp" data-first-line="150" data-last-line="152"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/08/llvm_context.cpp#L150-L152">llvm_context.cpp</a>, lines 150 through 152</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">150 </span><span class="lnt">151 </span><span class="lnt">152 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">ConstantInt</span><span class="o">*</span> <span class="n">llvm_context</span><span class="o">::</span><span class="n">create_i8</span><span class="p">(</span><span class="kt">int8_t</span> <span class="n">i</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">ConstantInt</span><span class="o">::</span><span class="n">get</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">APInt</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="n">i</span><span class="p">));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Not much to see here. We create an instance of the <code>llvm::ConstantInt</code> class, from the actual integer given to the method. As we said before, <code>llvm::ConstantInt</code> is a subclass of <code>llvm::Value</code>. Next up, let&rsquo;s look at <code>create_pop</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/08/llvm_context.cpp" data-first-line="160" data-last-line="163"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/08/llvm_context.cpp#L160-L163">llvm_context.cpp</a>, lines 160 through 163</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">160 </span><span class="lnt">161 </span><span class="lnt">162 </span><span class="lnt">163 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">Value</span><span class="o">*</span> <span class="n">llvm_context</span><span class="o">::</span><span class="n">create_pop</span><span class="p">(</span><span class="n">Function</span><span class="o">*</span> <span class="n">f</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">pop_f</span> <span class="o">=</span> <span class="n">functions</span><span class="p">.</span><span class="n">at</span><span class="p">(</span><span class="s">&#34;stack_pop&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">builder</span><span class="p">.</span><span class="n">CreateCall</span><span class="p">(</span><span class="n">pop_f</span><span class="p">,</span> <span class="p">{</span> <span class="n">f</span><span class="o">-&gt;</span><span class="n">arg_begin</span><span class="p">()</span> <span class="p">});</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We first retrieve an <code>llvm::Function</code> associated with <code>stack_pop</code> from our map, and then use <code>llvm::IRBuilder::CreateCall</code> to insert a value that represents a function call into the currently selected basic block (the builder&rsquo;s state is what dictates what the &ldquo;selected basic block&rdquo; is). <code>CreateCall</code> takes as parameters the function we want to call (<code>stack_pop</code>, which we store into the <code>pop_f</code> variable), as well as the arguments to the function (for which we pass <code>f-&gt;arg_begin()</code>).</p> <p>Hold on. What the heck is <code>arg_begin()</code>? Why do we take a function as a paramter to this method? The answer is fairly simple: this method is used when we are generating a function with signature <code>void f_(struct stack* s)</code> (we discussed the signature in the previous post). The parameter that we give to <code>create_pop</code> is this function we&rsquo;re generating, and <code>arg_begin()</code> gets the value that represents the first parameter to our function - <code>s</code>! Since <code>stack_pop</code> takes a stack, we need to give it the stack we&rsquo;re working on, and so we use <code>f-&gt;arg_begin()</code> to access it.</p> <p>Most of the other functions follow this exact pattern, with small deviations. However, another function uses a more complicated LLVM instruction:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/08/llvm_context.cpp" data-first-line="202" data-last-line="209"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/08/llvm_context.cpp#L202-L209">llvm_context.cpp</a>, lines 202 through 209</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">202 </span><span class="lnt">203 </span><span class="lnt">204 </span><span class="lnt">205 </span><span class="lnt">206 </span><span class="lnt">207 </span><span class="lnt">208 </span><span class="lnt">209 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">Value</span><span class="o">*</span> <span class="n">llvm_context</span><span class="o">::</span><span class="n">unwrap_num</span><span class="p">(</span><span class="n">Value</span><span class="o">*</span> <span class="n">v</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">num_ptr_type</span> <span class="o">=</span> <span class="n">PointerType</span><span class="o">::</span><span class="n">getUnqual</span><span class="p">(</span><span class="n">struct_types</span><span class="p">.</span><span class="n">at</span><span class="p">(</span><span class="s">&#34;node_num&#34;</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">cast</span> <span class="o">=</span> <span class="n">builder</span><span class="p">.</span><span class="n">CreatePointerCast</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="n">num_ptr_type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">offset_0</span> <span class="o">=</span> <span class="n">create_i32</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">offset_1</span> <span class="o">=</span> <span class="n">create_i32</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">int_ptr</span> <span class="o">=</span> <span class="n">builder</span><span class="p">.</span><span class="n">CreateGEP</span><span class="p">(</span><span class="n">cast</span><span class="p">,</span> <span class="p">{</span> <span class="n">offset_0</span><span class="p">,</span> <span class="n">offset_1</span> <span class="p">});</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">builder</span><span class="p">.</span><span class="n">CreateLoad</span><span class="p">(</span><span class="n">int_ptr</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p><code>unwrap_num</code> is used to cast a given node pointer to a pointer to a number node, and then return the integer value from that number node. It starts fairly innocently: we ask LLVM for the type of a pointer to a <code>node_num</code> struct, and then use <code>CreatePointerCast</code> to create a value that is the same node pointer we&rsquo;re given, but now interpreted as a number node pointer. We now have to access the <code>value</code> field of our node. <code>CreateGEP</code> helps us with this: given a pointer to a node, and two offsets <code>n</code> and <code>k</code>, it effectively performs the following:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="o">&amp;</span><span class="p">(</span><span class="n">num_pointer</span><span class="p">[</span><span class="n">n</span><span class="p">]</span><span class="o">-&gt;</span><span class="n">kth_field</span><span class="p">)</span> </span></span></code></pre></div><p>The first offset, then, gives an index into the &ldquo;array&rdquo; represented by the pointer, while the second offset gives the index of the field we want to access. We want to dereference the pointer (<code>num_pointer[0]</code>), and we want the second field (<code>1</code>, when counting from 0). Thus, we call <code>CreateGEP</code> with these offsets and our pointers.</p> <p>This still leaves us with a pointer to a number, rather than the number itself. To dereference the pointer, we use <code>CreateLoad</code>. This gives us the value of the number node, which we promptly return.</p> <p>This concludes our implementation of the <code>llvm_context</code> - it&rsquo;s time to move on to the G-machine instructions.</p> <a href="#g-machine-instructions-to-llvm-ir"> <h3 id="g-machine-instructions-to-llvm-ir">G-machine Instructions to LLVM IR</h3> </a> <p>Let&rsquo;s now envision a <code>gen_llvm</code> method on the <code>instruction</code> struct, which will turn the still-abstract G-machine instruction into tangible, close-to-metal LLVM IR. As we&rsquo;ve seen in our implementation of <code>llvm_context</code>, to access the stack, we need access to the first argument of the function we&rsquo;re generating. Thus, we need this method to accept the function whose instructions are being converted to LLVM. We also pass in the <code>llvm_context</code>, since it contains the LLVM builder, context, module, and a map of globally declared functions.</p> <p>With these things in mind, here&rsquo;s the signature for <code>gen_llvm</code>:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">virtual</span> <span class="kt">void</span> <span class="nf">gen_llvm</span><span class="p">(</span><span class="n">llvm_context</span><span class="o">&amp;</span><span class="p">,</span> <span class="n">llvm</span><span class="o">::</span><span class="n">Function</span><span class="o">*</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span></code></pre></div><p>Let&rsquo;s get right to it! <code>instruction_pushint</code> gives us an easy start:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/08/instruction.cpp" data-first-line="17" data-last-line="19"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/08/instruction.cpp#L17-L19">instruction.cpp</a>, lines 17 through 19</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">instruction_pushint</span><span class="o">::</span><span class="n">gen_llvm</span><span class="p">(</span><span class="n">llvm_context</span><span class="o">&amp;</span> <span class="n">ctx</span><span class="p">,</span> <span class="n">Function</span><span class="o">*</span> <span class="n">f</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">create_push</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">ctx</span><span class="p">.</span><span class="n">create_num</span><span class="p">(</span><span class="n">ctx</span><span class="p">.</span><span class="n">create_i32</span><span class="p">(</span><span class="n">value</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We create an LLVM integer constant with the value of our integer, and push it onto the stack.</p> <p><code>instruction_push</code> is equally terse:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/08/instruction.cpp" data-first-line="37" data-last-line="39"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/08/instruction.cpp#L37-L39">instruction.cpp</a>, lines 37 through 39</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">instruction_push</span><span class="o">::</span><span class="n">gen_llvm</span><span class="p">(</span><span class="n">llvm_context</span><span class="o">&amp;</span> <span class="n">ctx</span><span class="p">,</span> <span class="n">Function</span><span class="o">*</span> <span class="n">f</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">create_push</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">ctx</span><span class="p">.</span><span class="n">create_peek</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">ctx</span><span class="p">.</span><span class="n">create_size</span><span class="p">(</span><span class="n">offset</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We simply peek at the value of the stack at the given offset (an integer of the same size as <code>size_t</code>, which we create using <code>create_size</code>). Once we have the result of the peek, we push it onto the stack.</p> <p><code>instruction_pushglobal</code> is more involved. Let&rsquo;s take a look:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/08/instruction.cpp" data-first-line="26" data-last-line="30"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/08/instruction.cpp#L26-L30">instruction.cpp</a>, lines 26 through 30</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">instruction_pushglobal</span><span class="o">::</span><span class="n">gen_llvm</span><span class="p">(</span><span class="n">llvm_context</span><span class="o">&amp;</span> <span class="n">ctx</span><span class="p">,</span> <span class="n">Function</span><span class="o">*</span> <span class="n">f</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span><span class="o">&amp;</span> <span class="n">global_f</span> <span class="o">=</span> <span class="n">ctx</span><span class="p">.</span><span class="n">custom_functions</span><span class="p">.</span><span class="n">at</span><span class="p">(</span><span class="s">&#34;f_&#34;</span> <span class="o">+</span> <span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">arity</span> <span class="o">=</span> <span class="n">ctx</span><span class="p">.</span><span class="n">create_i32</span><span class="p">(</span><span class="n">global_f</span><span class="o">-&gt;</span><span class="n">arity</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">create_push</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">ctx</span><span class="p">.</span><span class="n">create_global</span><span class="p">(</span><span class="n">global_f</span><span class="o">-&gt;</span><span class="n">function</span><span class="p">,</span> <span class="n">arity</span><span class="p">));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>First, we retrive the <code>custom_function</code> associated with the given global name. We then create an LLVM integer constant representing the arity of the function, and then push onto the stack the result of <code>alloc_global</code>, giving it the function and arity just like it expects.</p> <p><code>instruction_pop</code> is also short, and doesn&rsquo;t require much further explanation:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/08/instruction.cpp" data-first-line="46" data-last-line="48"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/08/instruction.cpp#L46-L48">instruction.cpp</a>, lines 46 through 48</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">instruction_pop</span><span class="o">::</span><span class="n">gen_llvm</span><span class="p">(</span><span class="n">llvm_context</span><span class="o">&amp;</span> <span class="n">ctx</span><span class="p">,</span> <span class="n">Function</span><span class="o">*</span> <span class="n">f</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">create_popn</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">ctx</span><span class="p">.</span><span class="n">create_size</span><span class="p">(</span><span class="n">count</span><span class="p">));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Some other instructions, such as <code>instruction_update</code>, <code>instruction_pack</code>, <code>instruction_split</code>, <code>instruction_slide</code>, <code>instruction_alloc</code> and <code>instruction_eval</code> are equally as simple, and we omit them for the purpose of brevity.</p> <p>What remains are two &ldquo;meaty&rdquo; functions, <code>instruction_jump</code> and <code>instruction_binop</code>. Let&rsquo;s start with the former:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/08/instruction.cpp" data-first-line="101" data-last-line="123"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/08/instruction.cpp#L101-L123">instruction.cpp</a>, lines 101 through 123</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">101 </span><span class="lnt">102 </span><span class="lnt">103 </span><span class="lnt">104 </span><span class="lnt">105 </span><span class="lnt">106 </span><span class="lnt">107 </span><span class="lnt">108 </span><span class="lnt">109 </span><span class="lnt">110 </span><span class="lnt">111 </span><span class="lnt">112 </span><span class="lnt">113 </span><span class="lnt">114 </span><span class="lnt">115 </span><span class="lnt">116 </span><span class="lnt">117 </span><span class="lnt">118 </span><span class="lnt">119 </span><span class="lnt">120 </span><span class="lnt">121 </span><span class="lnt">122 </span><span class="lnt">123 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">instruction_jump</span><span class="o">::</span><span class="n">gen_llvm</span><span class="p">(</span><span class="n">llvm_context</span><span class="o">&amp;</span> <span class="n">ctx</span><span class="p">,</span> <span class="n">Function</span><span class="o">*</span> <span class="n">f</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">top_node</span> <span class="o">=</span> <span class="n">ctx</span><span class="p">.</span><span class="n">create_peek</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">ctx</span><span class="p">.</span><span class="n">create_size</span><span class="p">(</span><span class="mi">0</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">tag</span> <span class="o">=</span> <span class="n">ctx</span><span class="p">.</span><span class="n">unwrap_data_tag</span><span class="p">(</span><span class="n">top_node</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">safety_block</span> <span class="o">=</span> <span class="n">BasicBlock</span><span class="o">::</span><span class="n">Create</span><span class="p">(</span><span class="n">ctx</span><span class="p">.</span><span class="n">ctx</span><span class="p">,</span> <span class="s">&#34;safety&#34;</span><span class="p">,</span> <span class="n">f</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">switch_op</span> <span class="o">=</span> <span class="n">ctx</span><span class="p">.</span><span class="n">builder</span><span class="p">.</span><span class="n">CreateSwitch</span><span class="p">(</span><span class="n">tag</span><span class="p">,</span> <span class="n">safety_block</span><span class="p">,</span> <span class="n">tag_mappings</span><span class="p">.</span><span class="n">size</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">BasicBlock</span><span class="o">*&gt;</span> <span class="n">blocks</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">branch</span> <span class="p">:</span> <span class="n">branches</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">branch_block</span> <span class="o">=</span> <span class="n">BasicBlock</span><span class="o">::</span><span class="n">Create</span><span class="p">(</span><span class="n">ctx</span><span class="p">.</span><span class="n">ctx</span><span class="p">,</span> <span class="s">&#34;branch&#34;</span><span class="p">,</span> <span class="n">f</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">builder</span><span class="p">.</span><span class="n">SetInsertPoint</span><span class="p">(</span><span class="n">branch_block</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">instruction</span> <span class="p">:</span> <span class="n">branch</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">instruction</span><span class="o">-&gt;</span><span class="n">gen_llvm</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">f</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">builder</span><span class="p">.</span><span class="n">CreateBr</span><span class="p">(</span><span class="n">safety_block</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">blocks</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">branch_block</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">mapping</span> <span class="p">:</span> <span class="n">tag_mappings</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">switch_op</span><span class="o">-&gt;</span><span class="n">addCase</span><span class="p">(</span><span class="n">ctx</span><span class="p">.</span><span class="n">create_i8</span><span class="p">(</span><span class="n">mapping</span><span class="p">.</span><span class="n">first</span><span class="p">),</span> <span class="n">blocks</span><span class="p">[</span><span class="n">mapping</span><span class="p">.</span><span class="n">second</span><span class="p">]);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">builder</span><span class="p">.</span><span class="n">SetInsertPoint</span><span class="p">(</span><span class="n">safety_block</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>This is the one and only function in which we have to take care of control flow. Conceptually, depending on the tag of the <code>node_data</code> at the top of the stack, we want to pick one of many branches and jump to it. As we discussed, a basic block has to be executed in its entirety; since the branches of a case expression are mutually exclusive (only one of them is executed in any given case), we have to create a separate basic block for each branch. Given these blocks, we then want to branch to the correct one using the tag of the node on top of the stack.</p> <p>This is exactly what we do in this function. We first peek at the node on top of the stack, and use <code>CreateGEP</code> through <code>unwrap_data_tag</code> to get access to its tag. What we then need is LLVM&rsquo;s switch instruction, created using <code>CreateSwitch</code>. We must provide the switch with a &ldquo;default&rdquo; case in case the tag value is something we don&rsquo;t recognize. To do this, we create a &ldquo;safety&rdquo; <code>BasicBlock</code>. With this new safety block in hand, we&rsquo;re able to call <code>CreateSwitch</code>, giving it the tag value to switch on, the safety block to default to, and the expected number of branches (to optimize memory allocation).</p> <p>Next, we create a vector of blocks, and for each branch, we append to it a corresponding block <code>branch_block</code>, into which we insert the LLVM IR corresponding to the instructions of the branch. No matter the branch we take, we eventually want to come back to the same basic block, which will perform the usual function cleanup via Update and Slide. We re-use the safety block for this, and use <code>CreateBr</code> at the end of each <code>branch_block</code> to perform an unconditional jump.</p> <p>After we create each of the blocks, we use the <code>tag_mappings</code> to add cases to the switch instruction, using <code>addCase</code>. Finally, we set the builder&rsquo;s insertion point to the safety block, meaning that the next instructions will insert their LLVM IR into that block. Since we have all branches jump to the safety block at the end, this means that no matter which branch we take in the case expression, we will still execute the subsequent instructions as expected.</p> <p>Let&rsquo;s now look at <code>instruction_binop</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/08/instruction.cpp" data-first-line="139" data-last-line="150"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/08/instruction.cpp#L139-L150">instruction.cpp</a>, lines 139 through 150</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">139 </span><span class="lnt">140 </span><span class="lnt">141 </span><span class="lnt">142 </span><span class="lnt">143 </span><span class="lnt">144 </span><span class="lnt">145 </span><span class="lnt">146 </span><span class="lnt">147 </span><span class="lnt">148 </span><span class="lnt">149 </span><span class="lnt">150 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">instruction_binop</span><span class="o">::</span><span class="n">gen_llvm</span><span class="p">(</span><span class="n">llvm_context</span><span class="o">&amp;</span> <span class="n">ctx</span><span class="p">,</span> <span class="n">Function</span><span class="o">*</span> <span class="n">f</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">left_int</span> <span class="o">=</span> <span class="n">ctx</span><span class="p">.</span><span class="n">unwrap_num</span><span class="p">(</span><span class="n">ctx</span><span class="p">.</span><span class="n">create_pop</span><span class="p">(</span><span class="n">f</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">right_int</span> <span class="o">=</span> <span class="n">ctx</span><span class="p">.</span><span class="n">unwrap_num</span><span class="p">(</span><span class="n">ctx</span><span class="p">.</span><span class="n">create_pop</span><span class="p">(</span><span class="n">f</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">Value</span><span class="o">*</span> <span class="n">result</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">switch</span><span class="p">(</span><span class="n">op</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nl">PLUS</span><span class="p">:</span> <span class="n">result</span> <span class="o">=</span> <span class="n">ctx</span><span class="p">.</span><span class="n">builder</span><span class="p">.</span><span class="n">CreateAdd</span><span class="p">(</span><span class="n">left_int</span><span class="p">,</span> <span class="n">right_int</span><span class="p">);</span> <span class="k">break</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nl">MINUS</span><span class="p">:</span> <span class="n">result</span> <span class="o">=</span> <span class="n">ctx</span><span class="p">.</span><span class="n">builder</span><span class="p">.</span><span class="n">CreateSub</span><span class="p">(</span><span class="n">left_int</span><span class="p">,</span> <span class="n">right_int</span><span class="p">);</span> <span class="k">break</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nl">TIMES</span><span class="p">:</span> <span class="n">result</span> <span class="o">=</span> <span class="n">ctx</span><span class="p">.</span><span class="n">builder</span><span class="p">.</span><span class="n">CreateMul</span><span class="p">(</span><span class="n">left_int</span><span class="p">,</span> <span class="n">right_int</span><span class="p">);</span> <span class="k">break</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nl">DIVIDE</span><span class="p">:</span> <span class="n">result</span> <span class="o">=</span> <span class="n">ctx</span><span class="p">.</span><span class="n">builder</span><span class="p">.</span><span class="n">CreateSDiv</span><span class="p">(</span><span class="n">left_int</span><span class="p">,</span> <span class="n">right_int</span><span class="p">);</span> <span class="k">break</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">create_push</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">ctx</span><span class="p">.</span><span class="n">create_num</span><span class="p">(</span><span class="n">result</span><span class="p">));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>In this instruction, we pop and unwrap two integers from the stack (assuming they are integers). Depending on the type of operation the instruction is set to, we then push the result of the corresponding LLVM instruction. <code>PLUS</code> calls LLVM&rsquo;s <code>CreateAdd</code> to insert addition, <code>MINUS</code> calls <code>CreateSub</code>, and so on. No matter what the operation was, we push the result onto the stack.</p> <p>That&rsquo;s all for our instructions! We&rsquo;re so very close now. Let&rsquo;s move on to compiling definitions.</p> <a href="#definitions-to-llvm-ir"> <h3 id="definitions-to-llvm-ir">Definitions to LLVM IR</h3> </a> <p>As with typechecking, to allow for mutually recursive functions, we need to be able each global function from any other function. We then take the same approah as before, going in two passes. This leads to two new methods for <code>definition</code>:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">virtual</span> <span class="kt">void</span> <span class="nf">gen_llvm_first</span><span class="p">(</span><span class="n">llvm_context</span><span class="o">&amp;</span> <span class="n">ctx</span><span class="p">)</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="k">virtual</span> <span class="kt">void</span> <span class="nf">gen_llvm_second</span><span class="p">(</span><span class="n">llvm_context</span><span class="o">&amp;</span> <span class="n">ctx</span><span class="p">)</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span></code></pre></div><p>The first pass is intended to register all functions into the <code>llvm_context</code>, making them visible to other functions. The second pass is used to actually generate the code for each function, now having access to all the other global functions. Let&rsquo;s see the implementation for <code>gen_llvm_first</code> for <code>definition_defn</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/08/definition.cpp" data-first-line="58" data-last-line="60"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/08/definition.cpp#L58-L60">definition.cpp</a>, lines 58 through 60</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">definition_defn</span><span class="o">::</span><span class="n">gen_llvm_first</span><span class="p">(</span><span class="n">llvm_context</span><span class="o">&amp;</span> <span class="n">ctx</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">generated_function</span> <span class="o">=</span> <span class="n">ctx</span><span class="p">.</span><span class="n">create_custom_function</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">params</span><span class="p">.</span><span class="n">size</span><span class="p">());</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Since <code>create_custom_function</code> already creates a function <strong>and</strong> registers it with <code>llvm_context</code>, this is all we need. Note that we created a new member variable for <code>definition_defn</code> which stores this newly created function. In the second pass, we will populate this function with LLVM IR from the definition&rsquo;s instructions.</p> <p>We actually create functions for each of the constructors of data types, but they&rsquo;re quite special: all they do is pack their arguments! Since they don&rsquo;t need access to the other global functions, we might as well create their bodies then and there:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/08/definition.cpp" data-first-line="101" data-last-line="112"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/08/definition.cpp#L101-L112">definition.cpp</a>, lines 101 through 112</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">101 </span><span class="lnt">102 </span><span class="lnt">103 </span><span class="lnt">104 </span><span class="lnt">105 </span><span class="lnt">106 </span><span class="lnt">107 </span><span class="lnt">108 </span><span class="lnt">109 </span><span class="lnt">110 </span><span class="lnt">111 </span><span class="lnt">112 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">definition_data</span><span class="o">::</span><span class="n">gen_llvm_first</span><span class="p">(</span><span class="n">llvm_context</span><span class="o">&amp;</span> <span class="n">ctx</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">constructor</span> <span class="p">:</span> <span class="n">constructors</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">new_function</span> <span class="o">=</span> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">create_custom_function</span><span class="p">(</span><span class="n">constructor</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">,</span> <span class="n">constructor</span><span class="o">-&gt;</span><span class="n">types</span><span class="p">.</span><span class="n">size</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">builder</span><span class="p">.</span><span class="n">SetInsertPoint</span><span class="p">(</span><span class="o">&amp;</span><span class="n">new_function</span><span class="o">-&gt;</span><span class="n">getEntryBlock</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">create_pack</span><span class="p">(</span><span class="n">new_function</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">create_size</span><span class="p">(</span><span class="n">constructor</span><span class="o">-&gt;</span><span class="n">types</span><span class="p">.</span><span class="n">size</span><span class="p">()),</span> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">create_i8</span><span class="p">(</span><span class="n">constructor</span><span class="o">-&gt;</span><span class="n">tag</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">builder</span><span class="p">.</span><span class="n">CreateRetVoid</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Like in <code>definition_defn</code>, we use <code>create_custom_function</code>. However, we then use <code>SetInsertPoint</code> to configure our builder to insert code into the newly created function (which already has a <code>BasicBlock</code>, thanks to that one previously unexplained line in <code>create_custom_function</code>!). Since we decided to only include the Pack instruction, we generate a call to it directly using <code>create_pack</code>. We follow this up with <code>CreateRetVoid</code>, which tells LLVM that this is the end of the function, and that it is now safe to return from it.</p> <p>Great! We now implement the second pass of <code>gen_llvm</code>. In the case of <code>definition_defn</code>, we do almost exactly what we did in the first pass of <code>definition_data</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/08/definition.cpp" data-first-line="62" data-last-line="68"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/08/definition.cpp#L62-L68">definition.cpp</a>, lines 62 through 68</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span><span class="lnt">65 </span><span class="lnt">66 </span><span class="lnt">67 </span><span class="lnt">68 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">definition_defn</span><span class="o">::</span><span class="n">gen_llvm_second</span><span class="p">(</span><span class="n">llvm_context</span><span class="o">&amp;</span> <span class="n">ctx</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">builder</span><span class="p">.</span><span class="n">SetInsertPoint</span><span class="p">(</span><span class="o">&amp;</span><span class="n">generated_function</span><span class="o">-&gt;</span><span class="n">getEntryBlock</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">instruction</span> <span class="p">:</span> <span class="n">instructions</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">instruction</span><span class="o">-&gt;</span><span class="n">gen_llvm</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">generated_function</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">builder</span><span class="p">.</span><span class="n">CreateRetVoid</span><span class="p">();</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>As for <code>definition_data</code>, we have nothing to do in the second pass. We&rsquo;re done!</p> <a href="#getting-results"> <h3 id="getting-results">Getting Results</h3> </a> <p>We&rsquo;re almost there. Two things remain. The first: our implementation of <code>ast_binop</code>, implement each binary operation as simply a function call: <code>+</code> calls <code>f_plus</code>, and so on. But so far, we have not implemented <code>f_plus</code>, or any other binary operator function. We do this in <code>main.cpp</code>, creating a function <code>gen_llvm_internal_op</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/08/main.cpp" data-first-line="70" data-last-line="83"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/08/main.cpp#L70-L83">main.cpp</a>, lines 70 through 83</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">70 </span><span class="lnt">71 </span><span class="lnt">72 </span><span class="lnt">73 </span><span class="lnt">74 </span><span class="lnt">75 </span><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span><span class="lnt">79 </span><span class="lnt">80 </span><span class="lnt">81 </span><span class="lnt">82 </span><span class="lnt">83 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">gen_llvm_internal_op</span><span class="p">(</span><span class="n">llvm_context</span><span class="o">&amp;</span> <span class="n">ctx</span><span class="p">,</span> <span class="n">binop</span> <span class="n">op</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">new_function</span> <span class="o">=</span> <span class="n">ctx</span><span class="p">.</span><span class="n">create_custom_function</span><span class="p">(</span><span class="n">op_action</span><span class="p">(</span><span class="n">op</span><span class="p">),</span> <span class="mi">2</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">instruction_ptr</span><span class="o">&gt;</span> <span class="n">instructions</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">instructions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_push</span><span class="p">(</span><span class="mi">1</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"> <span class="n">instructions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_eval</span><span class="p">()));</span> </span></span><span class="line"><span class="cl"> <span class="n">instructions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_push</span><span class="p">(</span><span class="mi">1</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"> <span class="n">instructions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_eval</span><span class="p">()));</span> </span></span><span class="line"><span class="cl"> <span class="n">instructions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_binop</span><span class="p">(</span><span class="n">op</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">builder</span><span class="p">.</span><span class="n">SetInsertPoint</span><span class="p">(</span><span class="o">&amp;</span><span class="n">new_function</span><span class="o">-&gt;</span><span class="n">getEntryBlock</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">instruction</span> <span class="p">:</span> <span class="n">instructions</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">instruction</span><span class="o">-&gt;</span><span class="n">gen_llvm</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">new_function</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">builder</span><span class="p">.</span><span class="n">CreateRetVoid</span><span class="p">();</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We create a simple function body. We then append G-machine instructions that take each argument, evaluate it, and then perform the corresponding binary operation. With these instructions in the body, we insert them into a new function, just like we did in our code for <code>definition_defn</code> and <code>definition_data</code>.</p> <p>Finally, we write our <code>gen_llvm</code> function that we will call from <code>main</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/08/main.cpp" data-first-line="125" data-last-line="141"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/08/main.cpp#L125-L141">main.cpp</a>, lines 125 through 141</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">125 </span><span class="lnt">126 </span><span class="lnt">127 </span><span class="lnt">128 </span><span class="lnt">129 </span><span class="lnt">130 </span><span class="lnt">131 </span><span class="lnt">132 </span><span class="lnt">133 </span><span class="lnt">134 </span><span class="lnt">135 </span><span class="lnt">136 </span><span class="lnt">137 </span><span class="lnt">138 </span><span class="lnt">139 </span><span class="lnt">140 </span><span class="lnt">141 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">gen_llvm</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">definition_ptr</span><span class="o">&gt;&amp;</span> <span class="n">prog</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">llvm_context</span> <span class="n">ctx</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">gen_llvm_internal_op</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">PLUS</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">gen_llvm_internal_op</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">MINUS</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">gen_llvm_internal_op</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">TIMES</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">gen_llvm_internal_op</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">DIVIDE</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">definition</span> <span class="p">:</span> <span class="n">prog</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">definition</span><span class="o">-&gt;</span><span class="n">gen_llvm_first</span><span class="p">(</span><span class="n">ctx</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">definition</span> <span class="p">:</span> <span class="n">prog</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">definition</span><span class="o">-&gt;</span><span class="n">gen_llvm_second</span><span class="p">(</span><span class="n">ctx</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">module</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="n">llvm</span><span class="o">::</span><span class="n">outs</span><span class="p">(),</span> <span class="k">nullptr</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">output_llvm</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="s">&#34;program.o&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>It first creates the functions for <code>+</code>, <code>-</code>, <code>*</code>, and <code>/</code>. Then, it calls the first pass of <code>gen_llvm</code> on all definitions, followed by the second pass. Lastly, it uses LLVM&rsquo;s built-in functionality to print out the generated IR in our module, and then uses a function <code>output_llvm</code> to create an object file ready for linking.</p> <p>To be very honest, I took the <code>output_llvm</code> function almost entirely from instructional material for my university&rsquo;s compilers course. The gist of it, though, is: we determine the target architecture and platform, specify a &ldquo;generic&rdquo; CPU, create a default set of options, and then generate an object file. Here it is:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/08/main.cpp" data-first-line="85" data-last-line="123"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/08/main.cpp#L85-L123">main.cpp</a>, lines 85 through 123</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 85 </span><span class="lnt"> 86 </span><span class="lnt"> 87 </span><span class="lnt"> 88 </span><span class="lnt"> 89 </span><span class="lnt"> 90 </span><span class="lnt"> 91 </span><span class="lnt"> 92 </span><span class="lnt"> 93 </span><span class="lnt"> 94 </span><span class="lnt"> 95 </span><span class="lnt"> 96 </span><span class="lnt"> 97 </span><span class="lnt"> 98 </span><span class="lnt"> 99 </span><span class="lnt">100 </span><span class="lnt">101 </span><span class="lnt">102 </span><span class="lnt">103 </span><span class="lnt">104 </span><span class="lnt">105 </span><span class="lnt">106 </span><span class="lnt">107 </span><span class="lnt">108 </span><span class="lnt">109 </span><span class="lnt">110 </span><span class="lnt">111 </span><span class="lnt">112 </span><span class="lnt">113 </span><span class="lnt">114 </span><span class="lnt">115 </span><span class="lnt">116 </span><span class="lnt">117 </span><span class="lnt">118 </span><span class="lnt">119 </span><span class="lnt">120 </span><span class="lnt">121 </span><span class="lnt">122 </span><span class="lnt">123 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">output_llvm</span><span class="p">(</span><span class="n">llvm_context</span><span class="o">&amp;</span> <span class="n">ctx</span><span class="p">,</span> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">filename</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">targetTriple</span> <span class="o">=</span> <span class="n">llvm</span><span class="o">::</span><span class="n">sys</span><span class="o">::</span><span class="n">getDefaultTargetTriple</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">InitializeNativeTarget</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">InitializeNativeTargetAsmParser</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">InitializeNativeTargetAsmPrinter</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">error</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="n">llvm</span><span class="o">::</span><span class="n">Target</span><span class="o">*</span> <span class="n">target</span> <span class="o">=</span> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">TargetRegistry</span><span class="o">::</span><span class="n">lookupTarget</span><span class="p">(</span><span class="n">targetTriple</span><span class="p">,</span> <span class="n">error</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">target</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">cerr</span> <span class="o">&lt;&lt;</span> <span class="n">error</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">cpu</span> <span class="o">=</span> <span class="s">&#34;generic&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">features</span> <span class="o">=</span> <span class="s">&#34;&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">TargetOptions</span> <span class="n">options</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">TargetMachine</span><span class="o">*</span> <span class="n">targetMachine</span> <span class="o">=</span> </span></span><span class="line"><span class="cl"> <span class="n">target</span><span class="o">-&gt;</span><span class="n">createTargetMachine</span><span class="p">(</span><span class="n">targetTriple</span><span class="p">,</span> <span class="n">cpu</span><span class="p">,</span> <span class="n">features</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">options</span><span class="p">,</span> <span class="n">llvm</span><span class="o">::</span><span class="n">Optional</span><span class="o">&lt;</span><span class="n">llvm</span><span class="o">::</span><span class="n">Reloc</span><span class="o">::</span><span class="n">Model</span><span class="o">&gt;</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">module</span><span class="p">.</span><span class="n">setDataLayout</span><span class="p">(</span><span class="n">targetMachine</span><span class="o">-&gt;</span><span class="n">createDataLayout</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="n">ctx</span><span class="p">.</span><span class="n">module</span><span class="p">.</span><span class="n">setTargetTriple</span><span class="p">(</span><span class="n">targetTriple</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">error_code</span> <span class="n">ec</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">raw_fd_ostream</span> <span class="n">file</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="n">ec</span><span class="p">,</span> <span class="n">llvm</span><span class="o">::</span><span class="n">sys</span><span class="o">::</span><span class="n">fs</span><span class="o">::</span><span class="n">F_None</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">ec</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">TargetMachine</span><span class="o">::</span><span class="n">CodeGenFileType</span> <span class="n">type</span> <span class="o">=</span> <span class="n">llvm</span><span class="o">::</span><span class="n">TargetMachine</span><span class="o">::</span><span class="n">CGFT_ObjectFile</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">llvm</span><span class="o">::</span><span class="n">legacy</span><span class="o">::</span><span class="n">PassManager</span> <span class="n">pm</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">targetMachine</span><span class="o">-&gt;</span><span class="n">addPassesToEmitFile</span><span class="p">(</span><span class="n">pm</span><span class="p">,</span> <span class="n">file</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">type</span><span class="p">))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">pm</span><span class="p">.</span><span class="n">run</span><span class="p">(</span><span class="n">ctx</span><span class="p">.</span><span class="n">module</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">file</span><span class="p">.</span><span class="n">close</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We now add a <code>generate_llvm</code> call to <code>main</code>.</p> <p>Are we there?</p> <p>Let&rsquo;s try to compile our first example, <code>works1.txt</code>. The file:</p> <div class="highlight-group" > <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/08/examples/works1.txt">works1.txt</a>, entire file</div><pre><code>defn main = { sum 320 6 } defn sum x y = { x + y } </code></pre> </div> <p>We run the following commands in our build directory:</p> <pre tabindex="0"><code>./compiler &lt; ../examples/work1.txt gcc -no-pie ../runtime.c program.o ./a.out </code></pre><p>Nothing happens. How anticlimactic! Our runtime has no way of printing out the result of the evaluation. Let&rsquo;s change that:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/08/runtime.c" data-first-line="157" data-last-line="183"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/08/runtime.c#L157-L183">runtime.c</a>, lines 157 through 183</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">157 </span><span class="lnt">158 </span><span class="lnt">159 </span><span class="lnt">160 </span><span class="lnt">161 </span><span class="lnt">162 </span><span class="lnt">163 </span><span class="lnt">164 </span><span class="lnt">165 </span><span class="lnt">166 </span><span class="lnt">167 </span><span class="lnt">168 </span><span class="lnt">169 </span><span class="lnt">170 </span><span class="lnt">171 </span><span class="lnt">172 </span><span class="lnt">173 </span><span class="lnt">174 </span><span class="lnt">175 </span><span class="lnt">176 </span><span class="lnt">177 </span><span class="lnt">178 </span><span class="lnt">179 </span><span class="lnt">180 </span><span class="lnt">181 </span><span class="lnt">182 </span><span class="lnt">183 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">print_node</span><span class="p">(</span><span class="k">struct</span> <span class="nc">node_base</span><span class="o">*</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">n</span><span class="o">-&gt;</span><span class="n">tag</span> <span class="o">==</span> <span class="n">NODE_APP</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="nc">node_app</span><span class="o">*</span> <span class="n">app</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="nc">node_app</span><span class="o">*</span><span class="p">)</span> <span class="n">n</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">print_node</span><span class="p">(</span><span class="n">app</span><span class="o">-&gt;</span><span class="n">left</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">putchar</span><span class="p">(</span><span class="sc">&#39; &#39;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">print_node</span><span class="p">(</span><span class="n">app</span><span class="o">-&gt;</span><span class="n">right</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="n">n</span><span class="o">-&gt;</span><span class="n">tag</span> <span class="o">==</span> <span class="n">NODE_DATA</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">printf</span><span class="p">(</span><span class="s">&#34;(Packed)&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="n">n</span><span class="o">-&gt;</span><span class="n">tag</span> <span class="o">==</span> <span class="n">NODE_GLOBAL</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="nc">node_global</span><span class="o">*</span> <span class="n">global</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="nc">node_global</span><span class="o">*</span><span class="p">)</span> <span class="n">n</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">printf</span><span class="p">(</span><span class="s">&#34;(Global: %p)&#34;</span><span class="p">,</span> <span class="n">global</span><span class="o">-&gt;</span><span class="n">function</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="n">n</span><span class="o">-&gt;</span><span class="n">tag</span> <span class="o">==</span> <span class="n">NODE_IND</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">print_node</span><span class="p">(((</span><span class="k">struct</span> <span class="nc">node_ind</span><span class="o">*</span><span class="p">)</span> <span class="n">n</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">next</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="n">n</span><span class="o">-&gt;</span><span class="n">tag</span> <span class="o">==</span> <span class="n">NODE_NUM</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="nc">node_num</span><span class="o">*</span> <span class="n">num</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="nc">node_num</span><span class="o">*</span><span class="p">)</span> <span class="n">n</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">printf</span><span class="p">(</span><span class="s">&#34;%d&#34;</span><span class="p">,</span> <span class="n">num</span><span class="o">-&gt;</span><span class="n">value</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span><span class="o">**</span> <span class="n">argv</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="nc">node_global</span><span class="o">*</span> <span class="n">first_node</span> <span class="o">=</span> <span class="n">alloc_global</span><span class="p">(</span><span class="n">f_main</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="nc">node_base</span><span class="o">*</span> <span class="n">result</span> <span class="o">=</span> <span class="n">eval</span><span class="p">((</span><span class="k">struct</span> <span class="nc">node_base</span><span class="o">*</span><span class="p">)</span> <span class="n">first_node</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">printf</span><span class="p">(</span><span class="s">&#34;Result: &#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">print_node</span><span class="p">(</span><span class="n">result</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">putchar</span><span class="p">(</span><span class="sc">&#39;\n&#39;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Rerunning our commands, we get:</p> <pre tabindex="0"><code>Result: 326 </code></pre><p>The correct result! Let&rsquo;s try it with <code>works2.txt</code>:</p> <div class="highlight-group" > <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/08/examples/works2.txt">works2.txt</a>, entire file</div><pre><code>defn add x y = { x + y } defn double x = { add x x } defn main = { double 163 } </code></pre> </div> <p>And again, we get the right answer:</p> <pre tabindex="0"><code>Result: 326 </code></pre><p>This is child&rsquo;s play, though. Let&rsquo;s try with something more complicated, like <code>works3.txt</code>:</p> <div class="highlight-group" > <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/08/examples/works3.txt">works3.txt</a>, entire file</div><pre><code>data List = { Nil, Cons Int List } defn length l = { case l of { Nil -&gt; { 0 } Cons x xs -&gt; { 1 + length xs } } } defn main = { length (Cons 1 (Cons 2 (Cons 3 Nil))) } </code></pre> </div> <p>Once again, our program does exactly what we intended:</p> <pre tabindex="0"><code>Result: 3 </code></pre><p>Alright, this is neat, but we haven&rsquo;t yet confirmed that lazy evaluation works. How about we try it with <code>works5.txt</code>:</p> <div class="highlight-group" > <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/08/examples/works5.txt">works5.txt</a>, entire file</div><pre><code>data List = { Nil, Cons Int List } defn sumZip l m = { case l of { Nil -&gt; { 0 } Cons x xs -&gt; { case m of { Nil -&gt; { 0 } Cons y ys -&gt; { x + y + sumZip xs ys } } } } } defn ones = { Cons 1 ones } defn main = { sumZip ones (Cons 1 (Cons 2 (Cons 3 Nil))) } </code></pre> </div> <p>Yet again, the program works:</p> <pre tabindex="0"><code>Result: 9 </code></pre><p>At last, we have a working compiler!</p> <p>While this is a major victory, we are not yet finished with the compiler altogether. While we allocate nodes whenever we need them, we have not once uttered the phrase <code>free</code> in our runtime. Our language works, but we have no way of comparing numbers, no lambdas, no <code>let/in</code>. In the next several posts, we will improve our compiler to properly free unused memory usign a <strong>garbage collector</strong>, implement lambda functions using <strong>lambda lifting</strong>, and use our Alloc instruction to implement <code>let/in</code> expressions. We get started on the first of these tasks in <a href="https://danilafe.com/blog/09_compiler_garbage_collection/">Part 9 - Garbage Collection</a>.</p> Thoughts on Better Explanations https://danilafe.com/blog/better_explanations/ Sat, 12 Oct 2019 00:33:02 -0700 https://danilafe.com/blog/better_explanations/ <p>How do you explain how to write a program?</p> <p>Instructional material is becoming more and more popular on the web, with thousands of programming tutorials for languages, frameworks, and technologies created on YouTube, Medium, and peole&rsquo;s personal sites. And yet, there seem to be little standardization or progress towards an &ldquo;effective&rdquo; way. Everyone is pasting code examples, showing gists, or even sharing whole projects on GitHub. When I was writing the earliest posts on this site, I did the same. Write some code, copy paste it, be done. Write some code, link it, be done. If I&rsquo;m feeling fancy, write some code, gist it, be done. It&rsquo;s not unlikely for code presented in this way to become outdated and dysfunctional.</p> <p>I discovered a whole new perspective when going through <a href="https://softwarefoundations.cis.upenn.edu/"class="external-link">Software Foundations<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. What&rsquo;s different about that book is that the line between source code and instructional text is blurred - the HTML is generated from the comments in the Coq file, and code from the Coq file is included as snippets in the book. Rather than having readers piece together the snippets from the HTML, it simply directed them to the Coq file from which the page was generated. It maintained both the benefits of a live code example, and of a textbook written to teach, not to simply explain what the code does.</p> <p>This is reminiscent of <a href="https://en.wikipedia.org/wiki/Literate_programming"class="external-link">Literate Programming<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, a style of programming in which the explanation of the program, in human-oriented order, is presented, with code as supporting material. Tools such as CWEB implement Literate Programming, allowing users to write files that are then converted into C source, and can be compiled as usual. I was intrigued by the idea, but in all honesty, found it lacking.</p> <p>For one, there is the problem of an extra processing step. Compilers are written to compile C, and not CWEB files. Thus, a program must take CWEB source, convert it to C, and then a compiler must convert the C code to machine language. This doesn&rsquo;t feel elegant - you&rsquo;re effectively stripping the CWEB source files of the text you added to them. In technical terms, it&rsquo;s not really that big of an issue - software build systems already have support for multiple processing steps, and it would be hard to CWEB a piece of software large enough that the intermediate step will cause problems.</p> <p>Another issue is the lack of universality. CWEB is specialized for C. WEB, the original literate programming tool, is specialized for Pascal. There&rsquo;s tools that are language agnostic, of course, such as noweb. But the <a href="https://en.wikipedia.org/wiki/Noweb"class="external-link">Wikipedia page for noweb<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> drops this bomb:</p> <blockquote> <p>noweb defines a specific file format and a file is likely to interleave three different formats (noweb, latex and the language used for the software). This is not recognised by other software development tools and consequently using noweb excludes the use of UML or code documentation tools.</p> </blockquote> <p>This may be the worst trade deal in the history of trade deals, maybe ever! By trying to explain how our code works, <strong>we sacrifce all other tooling</strong>. Worse, because Literal Programming encourages presenting code in fragments and out of order, it is particularly difficult to reason about programs in an automated setting.</p> <p>When I present code to a reader, I want to write it with the use of existing tooling. I want my syntax highlighting. I want my linting. I want my build system. And in the same way, a user who is reading my code wants to be able to view it, change it, experiment with it. Furthermore, though, I want to be able to guide the reader&rsquo;s attention. Text-in-comments works great for Coq, but other languages like C++, in which the order of declarations matters, may not be as suited for such an approach.</p> <p>In essense, I want:</p> <ul> <li>The power of language-specific tooling, without having to extend the tooling itself</li> <li>A universal way of describing a program in any language</li> <li>A way of maintaining synchrony between the explanation and the source</li> </ul> <p>I have an idea of a piece of software that can do such a thing.</p> <a href="#a-language-server-based-tool"> <h3 id="a-language-server-based-tool">A Language Server Based Tool</h3> </a> <p>It is a well known problem that various editors support different languages with mixed success. The idea of the Language Server Protocol is to allow for a program (the server) to be in charge of making sense of the code, and then communicate the results to an editor. The editor, in that case, doesn&rsquo;t have to do as much heavy lifting, and instead just queries the language server when it needs information.</p> <p>While this technology is used for text editors, I think it can be adapted to educational texts that reference a particular codebase. I envision the following workflow:</p> <ol> <li>An author writes their tutorial/book/blog post in their markup language of choice (Markdown).</li> <li>They reference a fragment of code (a function, a variable) through a specialized syntax.</li> <li>When the HTML/LaTeX output is created, a language server is started. The language server uses information from the references in step 2 to insert code fragments into the generated output.</li> </ol> <p>After each &ldquo;conversion&rdquo; of source text to HTML/LaTeX, the code in the generated snippets will be in sync with the codebase. At the same time, changing the source text will not require changing the source files. Finally, since language servers exist for most established languages, this sytem can work nearly out of the box, and even be added to established projects with no changes to the projects themselves.</p> <p>Of course, this is just a rough idea. I&rsquo;m not sure how plausible it is to include snippets with the use of Language Server Protocol. But I certainly would like to try!</p> Compiling a Functional Language Using C++, Part 3 - Type Checking https://danilafe.com/blog/03_compiler_typechecking/ Tue, 06 Aug 2019 14:26:38 -0700 https://danilafe.com/blog/03_compiler_typechecking/ <p>I think tokenizing and parsing are boring. The thing is, looking at syntax is a pretty shallow measure of how interesting a language is. It&rsquo;s like the cover of a book. Every language has one, and it so happens that to make our &ldquo;book&rdquo;, we need to start with making the cover. But the content of the book is what matters, and that&rsquo;s where we&rsquo;ve arrived now. We must make decisions about our language, and give meaning to programs written in it. But before we can give our programs meaning, we need to make sense of the current domain of programs that we receive from our parser. Let&rsquo;s consider a few wonderful examples.</p> <pre tabindex="0"><code>defn main = { plus 320 6 } defn plus x y = { x + y } </code></pre><p>This is a valid program, as far as we&rsquo;re concerned. But are <strong>all</strong> programs we get from the parser valid? See for yourself:</p> <pre tabindex="0"><code>data Bool = { True, False } defn main = { 3 + True } </code></pre><p>Obviously, that&rsquo;s not right. The parser accepts it - this matches our grammar. But giving meaning to this program is not easy, since we have no clear way of adding 3 and some data type. Similarly:</p> <pre tabindex="0"><code>defn main = { 1 2 3 4 5 } </code></pre><p>What is this? It&rsquo;s a sequence of applications, starting with <code>1 2</code>. Numbers are not functions. Their type is <code>Int</code>, not <code>Int -&gt; a</code>. I can&rsquo;t even think of a type numbers would need to have for this program to be valid (though perhaps one could come up with one).</p> <p>Before we give meaning to programs in our language, we&rsquo;ll need to toss away the ones that don&rsquo;t make sense. To do so, we will use type checking. During the process of type checking, we will collect information about various parts of our abstract syntax trees, classifying them by the types of values they create. Using this information, we&rsquo;ll be able to throw away blatantly incorrect programs.</p> <a href="#basic-type-checking"> <h3 id="basic-type-checking">Basic Type Checking</h3> </a> <p>You may have noticed in the very first post that I have chosen to avoid polymorphism. This will simplify our type checking algorithm. If a more robust algorithm is desired, take a look at the <a href="https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system"class="external-link">Hindley-Milner type system<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. Personally, I enjoyed <a href="http://dev.stephendiehl.com/fun/006_hindley_milner.html"class="external-link">this<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> section of <em>Write You a Haskell</em>, which I used to sanity check this very post.</p> <p>Let&rsquo;s start with the types of constants - those are pretty obvious. The constant <code>3</code> is an integer, and we shall mark it as such: <code>3 :: Int</code>. Variables seem like the natural next step, but they&rsquo;re fairly different. Without outside knowledge, we can&rsquo;t tell what type a variable <code>x</code> is. If we know more information, like the fact that <code>x</code> was declared to be an integer, we can instead say that. This tells us that throughout type checking we&rsquo;ll have to keep some kind of record of names and their associated types.</p> <p>Next, let&rsquo;s take a look at functions, which are admittedly more interesting than the previous two examples. I&rsquo;m not talking about the case of seeing something like a function name <code>f</code>. This is the same as the variable case - we don&rsquo;t even know it&rsquo;s a function unless there is context, and if there <strong>is</strong> context, then that context is probably the most useful information we have. I&rsquo;m talking about something like the application of a function to another value, in the form <code>f x</code>. In this case, we know that for the program to be valid, <code>f</code> must have the type <code>a -&gt; b</code>, a function from something to something. Furthermore, since <code>f</code> is being applied to <code>x</code>, the type of <code>x</code> (let&rsquo;s call it <code>c</code>) must be the same as the type <code>a</code>.</p> <p>Our rules are getting more complicated. In order to check that they hold, we will use <a href="https://en.wikipedia.org/wiki/Unification_%28computer_science%29"class="external-link">unification<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. This means that we&rsquo;ll be creating various equations (such as &ldquo;the type of <code>f</code> is equal to <code>a -&gt; b</code>&rdquo;), and finding substitutions to solve these equations. If we&rsquo;re unable to find a solution to our equations, the program is invalid and we throw it away.</p> <a href="#basic-examples"> <h4 id="basic-examples">Basic Examples</h4> </a> <p>Let&rsquo;s try an example. We&rsquo;ll try to determine the type of the following expression, and extract any other information from this expression that we might use later.</p> <pre tabindex="0"><code>foo 320 6 </code></pre><p>In out parse tree, this will be represented as <code>(foo 320) 6</code>, meaning that the outermost application will be at the top. Let&rsquo;s assume we know only that <code>foo</code> is defined.</p> <p>To figure out the type of the application, we will need to know the types of the thing being applied, and the thing that serves as the argument. The latter is easy: the type of <code>6</code> is <code>Int</code>. Before we proceed into the left child of the application, there&rsquo;s one more piece of information we can deduce: since <code>foo 320</code> is applied to an argument, it has to be of type <code>a -&gt; b</code>.</p> <p>Let&rsquo;s proceed to the left child. It&rsquo;s another application, this time of <code>foo</code> to <code>320</code>. Again, the right child is simple: the type of <code>320</code> is <code>Int</code>. Again, we know that <code>foo</code> has to have a type <code>c -&gt; d</code> (we&rsquo;re using different variable names since the types of <code>foo</code> and <code>foo 320</code> are not the same).</p> <p>Now, we need to combine the pieces of information that we have. Since <code>foo :: c -&gt; d</code>, we know that its first parameter <strong>must</strong> be of type <code>c</code>. We also know that its first parameter is of type <code>Int</code>. The only way for both of these to be true is for <code>c = Int</code>. This also tells us that <code>foo :: Int -&gt; d</code>. Finally, since <code>foo</code> has now been applied to its first argument, we know that the <code>foo 320 :: d</code>.</p> <p>We&rsquo;ve done what we can from this innermost application; it&rsquo;s time to return to the outermost one. We now know that the left child is of type <code>d</code>, and that it also has to be of type <code>a -&gt; b</code>. The only way for this to be true is for <code>d = a -&gt; b</code>. So, <code>foo 320</code> is a function from <code>a</code> to <code>b</code>. Again, we can conclude the first parameter has to be of type <code>a</code>. We also know that the first parameter is of type <code>Int</code>. Clearly, this means that <code>a = Int</code>. After the application, we know that the whole expression has some type <code>b</code>.</p> <p>Let&rsquo;s revisit what we know about <code>foo</code>. Last time we checked in on it, <code>foo</code> was of type <code>Int -&gt; d</code>. But since we know that <code>d = a -&gt; b</code>, and that <code>a = Int</code>, we can now say that <code>foo :: Int -&gt; Int -&gt; b</code>.</p> <p>We haven&rsquo;t found any issues with the expression, and we learned some new information about the type of <code>foo</code>. Awesome!</p> <p>Let&rsquo;s apply this to a simplified example from the beginning of this post. Let&rsquo;s check the type of:</p> <pre tabindex="0"><code>1 2 </code></pre><p>Once again, the application is what we see first. The right child of the application, just like in the previous example, is <code>Int</code>. We also kno that since <code>1</code> is being applied as a function, its type must be <code>a -&gt; b</code>. However, we also know that the left child, being a number, is also of type <code>Int</code>! There&rsquo;s no way to combine <code>a -&gt; b</code> with <code>Int</code>, and thus, there is no solution we can find for the type of <code>1 2</code>. This means our program is invalid. We can toss it away, give an error, and exit.</p> <a href="#some-notation"> <h4 id="some-notation">Some Notation</h4> </a> <p>If you go to the Wikipedia page on the Hindley-Milner type system, you will see quite a lot of symbols and greek letters. This is a <strong>good thing</strong>, because the way that I presented to you the rules for figuring out types of expressions is very verbose. You have to read several paragraphs of text, and that&rsquo;s only for the first three logical rules! If you&rsquo;re anything like me, you want to be able to read just the important parts, and with some notation, I&rsquo;ll be able to show you these important parts concisely, while continuing to explain the rules in detail in paragraphs of text.</p> <p>Let&rsquo;s start with inference rules. An inference rule is an expression in the form:</p> $$ \frac{A_1 \ldots A_n} {B_1 \ldots B_m} $$ <p>This reads, &ldquo;given that the premises \(A_1\) through \(A_n\) are true, it holds that the conclusions \(B_1\) through \(B_m\) are true&rdquo;.</p> <p>For example, we can have the following inference rule:</p> $$ \frac {\text{if it&amp;#39;s cold, I wear a jacket} \quad \text{it&amp;#39;s cold}} {\text{I wear a jacket}} $$ <p>Since you wear a jacket when it&rsquo;s cold, and it&rsquo;s cold, we can conclude that you will wear a jacket.</p> <p>When talking about type systems, it&rsquo;s common to represent a type with \(\tau\). The letter, which is the greek character &ldquo;tau&rdquo;, is used as a placeholder for some <strong>concrete type</strong>. It&rsquo;s kind of like a template, to be filled in with an actual value. When we plug in an actual value into a rule containing \(\tau\), we say we are <strong>instantiating</strong> it. Similarly, we will use \(e\) to serve as a placeholder for an expression (matched by our \(A_{add}\) grammar rule from part 2). Next, we have the typing relation, written as \(e:\tau\). This says that &ldquo;expression \(e\) has the type \(\tau\)&rdquo;.</p> <p>Alright, this is enough to get us started with some typing rules. Let&rsquo;s start with one for numbers. If we define \(n\) to mean &ldquo;any expression that is a just a number, like 3, 2, 6, etc.&rdquo;, we can write the typing rule as follows:</p> $$ \frac{}{n : \text{Int}} $$ <p>There&rsquo;s nothing above the line - there are no premises that are needed for a number to have the type <code>Int</code>.</p> <p>Now, let&rsquo;s move on to the rule for function application:</p> $$ \frac {e_1 : \tau_1 \rightarrow \tau_2 \quad e_2 : \tau_1} {e_1 \; e_2 : \tau_2} $$ <p>This rule includes everything we&rsquo;ve said before: the thing being applied has to have a function type (\(\tau_1 \rightarrow \tau_2\)), and the expression the function is applied to has to have the same type \(\tau_1\) as the left type of the function.</p> <p>It&rsquo;s the variable rule that forces us to adjust our notation. Our rules don&rsquo;t take into account the context that we&rsquo;ve already discussed, and thus, we can&rsquo;t bring in any outside information. Let&rsquo;s fix that! It&rsquo;s convention to use the symbol \(\Gamma\) for the context. We then add notation to say, &ldquo;using the context \(\Gamma\), we can deduce that \(e\) has type \(\tau\)&rdquo;. We will write this as \(\Gamma \vdash e : \tau\).</p> <p>But what <strong>is</strong> our context? We can think of it as a mapping from variable names to their known types. We can represent such a mapping using a set of pairs in the form \(x : \tau\), where \(x\) represents a variable name.</p> <p>Since \(\Gamma\) is just a regular set, we can write \(x : \tau \in \Gamma\), meaning that in the current context, it is known that \(x\) has the type \(\tau\).</p> <p>Let&rsquo;s update our rules with this new addition.</p> <p>The integer rule just needs a slight adjustment:</p> $$ \frac{}{\Gamma \vdash n : \text{Int}} $$ <p>The same is true for the application rule:</p> $$ \frac {\Gamma \vdash e_1 : \tau_1 \rightarrow \tau_2 \quad \Gamma \vdash e_2 : \tau_1} {\Gamma \vdash e_1 \; e_2 : \tau_2} $$ <p>And finally, we can represent the variable rule:</p> $$ \frac{x : \tau \in \Gamma}{\Gamma \vdash x : \tau} $$ <p>In these three expressions, we&rsquo;ve captured all of the rules that we&rsquo;ve seen so far. It&rsquo;s important to know that these rules leave out the process of unification altogether: we use unification to find types that satisfy the rules.</p> <a href="#checking-case-expressions"> <h4 id="checking-case-expressions">Checking Case Expressions</h4> </a> <p>So far, we&rsquo;ve only checked types of numbers, applications, and variables. Our language has more than that, though!</p> <p>Binary operators are by far the simplest to extend our language with; We can simply say that <code>(+)</code> is a function, <code>Int -&gt; Int -&gt; Int</code>, and <code>x+y</code> is the same as <code>(+) x y</code>. This way, we simply translate operators into function application, and the same typing rules apply.</p> <p>Next up, we have case expressions. This is one of the two places where we will introduce new variables into the context, and also a place where we will need several rules.</p> <p>Let&rsquo;s first take a look at the whole case expression rule:</p> $$ \frac {\Gamma \vdash e : \tau \quad \text{matcht}(\tau, p_i) = b_i \quad \Gamma,b_i \vdash e_i : \tau_c} {\Gamma \vdash \text{case} \; e \; \text{of} \; \{ (p_1,e_1) \ldots (p_n, e_n) \} : \tau_c } $$ <p>This is a lot more complicated than the other rules we&rsquo;ve seen, and we&rsquo;ve used some notation that we haven&rsquo;t seen before. Let&rsquo;s take this step by step:</p> <ol> <li>\(e : \tau\), in this case, means that the expression between <code>case</code> and <code>of</code>, is of type \(\tau\).</li> <li>\(\text{matcht}(\tau, p_i) = b_i\) means that the pattern \(p_i\) can match a value of type \(\tau\), producing additional type pairs \(b_i\). We need \(b_i\) because a pattern such as <code>Cons x xs</code> will introduce new type information, namely \(\text{x} : \text{Int}\) and \(\text{xs} : \text{List}\).</li> <li>\(\Gamma,b_i \vdash e_i : \tau_c\) means that each individual branch can be deduced to have the type \(\tau_c\), using the previously existing context \(\Gamma\), with the addition of \(b_i\), the new type information.</li> <li>Finally, the conclusion is that the case expression, if all the premises are met, is of type \(\tau_c\).</li> </ol> <p>For completeness, let&rsquo;s add rules for \(\text{matcht}(\tau, p_i) = b_i\). We&rsquo;ll need two: one for the &ldquo;basic&rdquo; pattern, which always matches the value and binds a variable to it, and one for a constructor pattern, that matches a constructor and its parameters.</p> <p>Let&rsquo;s define \(v\) to be a variable name in the context of a pattern. For the basic pattern:</p> $$ \frac {} {\text{matcht}(\tau, v) = \{v : \tau \}} $$ <p>For the next rule, let&rsquo;s define \(c\) to be a constructor name. The rule for the constructor pattern, then, is:</p> $$ \frac {\Gamma \vdash c : \tau_1 \rightarrow ... \rightarrow \tau_n \rightarrow \tau} {\text{matcht}(\tau, c \; v_1 ... v_n) = \{ v_1 : \tau_1, ..., v_n : \tau_n \}} $$ <p>This rule means that whenever we have a pattern in the form of a constructor applied to \(n\) variable names, if the constructor takes \(n\) arguments of types \(\tau_1\) through \(\tau_n\), then the each variable will have a corresponding type.</p> <p>We didn&rsquo;t include lambda expressions in our syntax, and thus we won&rsquo;t need typing rules for them, so it actually seems like we&rsquo;re done with the first draft of our type rules.</p> <a href="#implementation"> <h4 id="implementation">Implementation</h4> </a> <p>Let&rsquo;s work towards some code. Before we write anything down though, let&rsquo;s get a definition of what a &ldquo;type&rdquo; is, in the context of our type checker. Let&rsquo;s say a type is one of 3 things:</p> <ol> <li>A &ldquo;base type&rdquo;, like <code>Int</code>, <code>Bool</code>, or <code>List</code>.</li> <li>A type that&rsquo;s a function from one type to another.</li> <li>A placeholder / type variable (like the kind we used for type inference).</li> </ol> <p>We represent a plceholder type with a unique string, such as &ldquo;a&rdquo;, or &ldquo;b&rdquo;, and this makes our placeholder type class very similar to the base type class.</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/03/type.hpp"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/03/type.hpp">type.hpp</a>, entire file</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="cp">#pragma once </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;memory&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;map&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">type</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">virtual</span> <span class="o">~</span><span class="n">type</span><span class="p">()</span> <span class="o">=</span> <span class="k">default</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="n">type_ptr</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">shared_ptr</span><span class="o">&lt;</span><span class="n">type</span><span class="o">&gt;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">type_var</span> <span class="o">:</span> <span class="k">public</span> <span class="n">type</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">type_var</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">n</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">name</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">n</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">type_base</span> <span class="o">:</span> <span class="k">public</span> <span class="n">type</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">type_base</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">n</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">name</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">n</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">type_arr</span> <span class="o">:</span> <span class="k">public</span> <span class="n">type</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">left</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">right</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">type_arr</span><span class="p">(</span><span class="n">type_ptr</span> <span class="n">l</span><span class="p">,</span> <span class="n">type_ptr</span> <span class="n">r</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">left</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">l</span><span class="p">)),</span> <span class="n">right</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">r</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">type_mgr</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">last_id</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">,</span> <span class="n">type_ptr</span><span class="o">&gt;</span> <span class="n">types</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">new_type_name</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="nf">new_type</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="nf">new_arrow_type</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">unify</span><span class="p">(</span><span class="n">type_ptr</span> <span class="n">l</span><span class="p">,</span> <span class="n">type_ptr</span> <span class="n">r</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="nf">resolve</span><span class="p">(</span><span class="n">type_ptr</span> <span class="n">t</span><span class="p">,</span> <span class="n">type_var</span><span class="o">*&amp;</span> <span class="n">var</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">bind</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">s</span><span class="p">,</span> <span class="n">type_ptr</span> <span class="n">t</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span></code></pre></td></tr></table> </div> </div> </div> <p>As you can see, we also declared a <code>type_mgr</code>, or type manager class. This class will keep the state used for generating more placeholder type names, as well as the information about which placeholder type is mapped to what. We gave it 3 methods:</p> <ul> <li><code>unify</code>, to perform unification. It will take two types and find values for placeholder variables such that they can equal.</li> <li><code>resolve</code>, to get to the &ldquo;bottom&rdquo; of a chain of equations. For instance, we have placeholder <code>a</code> be mapped to a placeholder <code>b</code>, an finally, the placeholder <code>b</code> to be mapped to <code>Int</code>. <code>resolve</code> will return for us <code>Int</code>, and, if the &ldquo;bottom&rdquo; of the chain is a placeholder, it will set <code>var</code> to be a pointer to that placeholder.</li> <li><code>bind</code>, inspired by <a href="http://dev.stephendiehl.com/fun/006_hindley_milner.html"class="external-link">this post<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, will map a type variable of some name to a type. This function will also check if we&rsquo;re binding a type variable to itself, and do nothing in that case, since <code>a = a</code> is not a very useful equation to have.</li> </ul> <p>To fit its original purpose, we also give the manager class the methods <code>new_type_name</code>, and two convenience methods to create placeholder types, <code>new_type</code> (in the form <code>a</code>) and <code>new_arrow_type</code> (in the form <code>a-&gt;b</code>).</p> <p>Let&rsquo;s take a look at the implementation now:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/03/type.cpp"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/03/type.cpp">type.cpp</a>, entire file</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span><span class="lnt">65 </span><span class="lnt">66 </span><span class="lnt">67 </span><span class="lnt">68 </span><span class="lnt">69 </span><span class="lnt">70 </span><span class="lnt">71 </span><span class="lnt">72 </span><span class="lnt">73 </span><span class="lnt">74 </span><span class="lnt">75 </span><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;type.hpp&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;sstream&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;algorithm&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">type_mgr</span><span class="o">::</span><span class="n">new_type_name</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">temp</span> <span class="o">=</span> <span class="n">last_id</span><span class="o">++</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">str</span> <span class="o">=</span> <span class="s">&#34;&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">while</span><span class="p">(</span><span class="n">temp</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">str</span> <span class="o">+=</span> <span class="p">(</span><span class="kt">char</span><span class="p">)</span> <span class="p">(</span><span class="sc">&#39;a&#39;</span> <span class="o">+</span> <span class="p">(</span><span class="n">temp</span> <span class="o">%</span> <span class="mi">26</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="n">temp</span> <span class="o">=</span> <span class="n">temp</span> <span class="o">/</span> <span class="mi">26</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">reverse</span><span class="p">(</span><span class="n">str</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">str</span><span class="p">.</span><span class="n">end</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">str</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">type_ptr</span> <span class="n">type_mgr</span><span class="o">::</span><span class="n">new_type</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nf">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_var</span><span class="p">(</span><span class="n">new_type_name</span><span class="p">()));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">type_ptr</span> <span class="n">type_mgr</span><span class="o">::</span><span class="n">new_arrow_type</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nf">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_arr</span><span class="p">(</span><span class="n">new_type</span><span class="p">(),</span> <span class="n">new_type</span><span class="p">()));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">type_ptr</span> <span class="n">type_mgr</span><span class="o">::</span><span class="n">resolve</span><span class="p">(</span><span class="n">type_ptr</span> <span class="n">t</span><span class="p">,</span> <span class="n">type_var</span><span class="o">*&amp;</span> <span class="n">var</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_var</span><span class="o">*</span> <span class="n">cast</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">var</span> <span class="o">=</span> <span class="k">nullptr</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span><span class="p">((</span><span class="n">cast</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">type_var</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">t</span><span class="p">.</span><span class="n">get</span><span class="p">())))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">types</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">cast</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">it</span> <span class="o">==</span> <span class="n">types</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">var</span> <span class="o">=</span> <span class="n">cast</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">break</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">t</span> <span class="o">=</span> <span class="n">it</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">t</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">type_mgr</span><span class="o">::</span><span class="n">unify</span><span class="p">(</span><span class="n">type_ptr</span> <span class="n">l</span><span class="p">,</span> <span class="n">type_ptr</span> <span class="n">r</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_var</span><span class="o">*</span> <span class="n">lvar</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_var</span><span class="o">*</span> <span class="n">rvar</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_arr</span><span class="o">*</span> <span class="n">larr</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_arr</span><span class="o">*</span> <span class="n">rarr</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_base</span><span class="o">*</span> <span class="n">lid</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_base</span><span class="o">*</span> <span class="n">rid</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">l</span> <span class="o">=</span> <span class="n">resolve</span><span class="p">(</span><span class="n">l</span><span class="p">,</span> <span class="n">lvar</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">r</span> <span class="o">=</span> <span class="n">resolve</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">rvar</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">lvar</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">bind</span><span class="p">(</span><span class="n">lvar</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">,</span> <span class="n">r</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="nf">if</span><span class="p">(</span><span class="n">rvar</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">bind</span><span class="p">(</span><span class="n">rvar</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">,</span> <span class="n">l</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="nf">if</span><span class="p">((</span><span class="n">larr</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">type_arr</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">l</span><span class="p">.</span><span class="n">get</span><span class="p">()))</span> <span class="o">&amp;&amp;</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">rarr</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">type_arr</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">get</span><span class="p">())))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">unify</span><span class="p">(</span><span class="n">larr</span><span class="o">-&gt;</span><span class="n">left</span><span class="p">,</span> <span class="n">rarr</span><span class="o">-&gt;</span><span class="n">left</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">unify</span><span class="p">(</span><span class="n">larr</span><span class="o">-&gt;</span><span class="n">right</span><span class="p">,</span> <span class="n">rarr</span><span class="o">-&gt;</span><span class="n">right</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="nf">if</span><span class="p">((</span><span class="n">lid</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">type_base</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">l</span><span class="p">.</span><span class="n">get</span><span class="p">()))</span> <span class="o">&amp;&amp;</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">rid</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">type_base</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">get</span><span class="p">())))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">lid</span><span class="o">-&gt;</span><span class="n">name</span> <span class="o">==</span> <span class="n">rid</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">type_mgr</span><span class="o">::</span><span class="n">bind</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">s</span><span class="p">,</span> <span class="n">type_ptr</span> <span class="n">t</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_var</span><span class="o">*</span> <span class="n">other</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">type_var</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">t</span><span class="p">.</span><span class="n">get</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">other</span> <span class="o">&amp;&amp;</span> <span class="n">other</span><span class="o">-&gt;</span><span class="n">name</span> <span class="o">==</span> <span class="n">s</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">types</span><span class="p">[</span><span class="n">s</span><span class="p">]</span> <span class="o">=</span> <span class="n">t</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div> </div> <p>Here, <code>new_type_name</code> is actually pretty boring. My goal was to generate type names like <code>a</code>, then <code>b</code>, eventually getting to <code>z</code>, and then move on to <code>aa</code>. This provides is with an endless stream of placeholder types.</p> <p>Time for the interesting functions. <code>resolve</code> keeps trying <code>dynamic_cast</code> to a type variable, and if that succeeds, then either:</p> <ol> <li>It&rsquo;s a type variable that&rsquo;s already been set to something, in which case we try resolve the thing it was set to (<code>t = it-&gt;second</code>)</li> <li>It&rsquo;s a type variable that hasn&rsquo;t been set to something. We set <code>var</code> to it (the caller will use this info), and stop our resolution loop (<code>break</code>).</li> </ol> <p>In <code>unify</code>, we start by calling <code>resolve</code> - we don&rsquo;t want to accidentally compare <code>a</code> and <code>b</code> (and try to bind <code>a</code> to <code>b</code>) when <code>a</code> is already bound to something else (like <code>Int</code>).</p> <p>From resolve, we will have <code>lvar</code> and <code>rvar</code> set to something not NULL if <code>l</code> or <code>r</code> were type variables that haven&rsquo;t yet been bound (we defined <code>resolve</code> to behave this way). So, if one of the variables is not NULL, we try to bind it.</p> <p>Next, <code>unify</code> checks if both types are either base types or arrow types. If they&rsquo;re base types, it compares their names, and if they&rsquo;re arrow types, it recursively unifies their children. We return in all cases when unification succeeds, and then throw an exception (currently 0) if all the cases fell thorugh, and thus, unification failed.</p> <p>Finally, <code>bind</code> places the type we&rsquo;re binding to into the <code>types</code> map, but not before it checks that the type we&rsquo;re binding is the same as the string we&rsquo;re binding it to (since, again, <code>a=a</code> is not a useful equation).</p> <p>We now have a unification algorithm, but we still need to implement our rules. Our rules usually include three things: an environment \(\Gamma\), an expression \(e\), and a type \(\tau\). We will represent this as a method on <code>ast</code>, our struct for an expression tree. This method will take an environment and return a type.</p> <a href="#environment"> <h5 id="environment">Environment</h5> </a> <p>How should we implement our environment? Conceptually, an environment maps a name string to a type. So naively, we can implement this simply using an <code>std::map</code>. But observe that we only extend the environment in one case so far: a case expression. In a case expression, we have the base envrionment \(\Gamma\), and for each branch, we extend it with the bindings produced by the pattern match. Each branch receives a modified copy of the original environment, one that doesn&rsquo;t see the effects of the other branches.</p> <p>Using our naive approach, we&rsquo;d create a new <code>std::map</code> for each branch that&rsquo;s a copy of the original environment, and place into it the new pairs. But this means we&rsquo;ll need to copy a map for each branch of the pattern!</p> <p>There&rsquo;s a better way. We structure our environment like a linked list. Each node in the linked list contains an <code>std::map</code>. When we encounter a new scope (such as in a case branch), we create a new such node, and add all variables introduced in this scope to that node&rsquo;s map. We make it point to our current environment. Then, we pass around the new node as the environment.</p> <p>When we look up a variable name, we first look in this node we created. If we don&rsquo;t find the variable we&rsquo;re looking for, we move on to the next node. The benefit of this is that we won&rsquo;t be re-creating a map for each branch, and just creating a node with the changes. Let&rsquo;s implement exactly that. the header:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/03/env.hpp"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/03/env.hpp">env.hpp</a>, entire file</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="cp">#pragma once </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;map&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;type.hpp&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">type_env</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">,</span> <span class="n">type_ptr</span><span class="o">&gt;</span> <span class="n">names</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_env</span> <span class="k">const</span><span class="o">*</span> <span class="n">parent</span> <span class="o">=</span> <span class="k">nullptr</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">type_env</span><span class="p">(</span><span class="n">type_env</span> <span class="k">const</span><span class="o">*</span> <span class="n">p</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">parent</span><span class="p">(</span><span class="n">p</span><span class="p">)</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> <span class="n">type_env</span><span class="p">()</span> <span class="o">:</span> <span class="n">type_env</span><span class="p">(</span><span class="k">nullptr</span><span class="p">)</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="nf">lookup</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">bind</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">,</span> <span class="n">type_ptr</span> <span class="n">t</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">type_env</span> <span class="nf">scope</span><span class="p">()</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span></code></pre></td></tr></table> </div> </div> </div> <p>And the source file:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/03/env.cpp"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/03/env.cpp">env.cpp</a>, entire file</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;env.hpp&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="n">type_ptr</span> <span class="n">type_env</span><span class="o">::</span><span class="n">lookup</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">names</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">it</span> <span class="o">!=</span> <span class="n">names</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> <span class="k">return</span> <span class="n">it</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">parent</span><span class="p">)</span> <span class="k">return</span> <span class="n">parent</span><span class="o">-&gt;</span><span class="n">lookup</span><span class="p">(</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="k">nullptr</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">type_env</span><span class="o">::</span><span class="n">bind</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">,</span> <span class="n">type_ptr</span> <span class="n">t</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">names</span><span class="p">[</span><span class="n">name</span><span class="p">]</span> <span class="o">=</span> <span class="n">t</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">type_env</span> <span class="n">type_env</span><span class="o">::</span><span class="n">scope</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nf">type_env</span><span class="p">(</span><span class="k">this</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div> </div> <p>Nothing should seem too surprising. Of note is the fact that we&rsquo;re not using smart pointers for <code>scope</code>, and that the child we create during the call would be invalid if the parent goes out of scope / is released. We&rsquo;re gearing this towards creating new environments on the stack, and we&rsquo;ll take care not to let a parent go out of scope before the child.</p> <a href="#typechecking-expressions"> <h5 id="typechecking-expressions">Typechecking Expressions</h5> </a> <p>At last, it&rsquo;s time to declare a new type checking method. We start with with a signature inside <code>ast</code>:</p> <pre tabindex="0"><code>virtual type_ptr typecheck(type_mgr&amp; mgr, const type_env&amp; env) const; </code></pre><p>We also implement the \(\text{matchp}\) function as a method <code>match</code> on <code>pattern</code> with the following signature:</p> <pre tabindex="0"><code>virtual void match(type_ptr t, type_mgr&amp; mgr, type_env&amp; env) const; </code></pre><p>We declare this in every subclass of <code>ast</code>. Let&rsquo;s take a look at the implementation now:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/03/ast.cpp"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/03/ast.cpp">ast.cpp</a>, entire file</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span><span class="lnt">65 </span><span class="lnt">66 </span><span class="lnt">67 </span><span class="lnt">68 </span><span class="lnt">69 </span><span class="lnt">70 </span><span class="lnt">71 </span><span class="lnt">72 </span><span class="lnt">73 </span><span class="lnt">74 </span><span class="lnt">75 </span><span class="lnt">76 </span><span class="lnt">77 </span><span class="lnt">78 </span><span class="lnt">79 </span><span class="lnt">80 </span><span class="lnt">81 </span><span class="lnt">82 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;ast.hpp&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">op_name</span><span class="p">(</span><span class="n">binop</span> <span class="n">op</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">switch</span><span class="p">(</span><span class="n">op</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nl">PLUS</span><span class="p">:</span> <span class="k">return</span> <span class="s">&#34;+&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nl">MINUS</span><span class="p">:</span> <span class="k">return</span> <span class="s">&#34;-&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nl">TIMES</span><span class="p">:</span> <span class="k">return</span> <span class="s">&#34;*&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nl">DIVIDE</span><span class="p">:</span> <span class="k">return</span> <span class="s">&#34;/&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">type_ptr</span> <span class="n">ast_int</span><span class="o">::</span><span class="n">typecheck</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="k">const</span> <span class="n">type_env</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nf">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_base</span><span class="p">(</span><span class="s">&#34;Int&#34;</span><span class="p">));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">type_ptr</span> <span class="n">ast_lid</span><span class="o">::</span><span class="n">typecheck</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="k">const</span> <span class="n">type_env</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">env</span><span class="p">.</span><span class="n">lookup</span><span class="p">(</span><span class="n">id</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">type_ptr</span> <span class="n">ast_uid</span><span class="o">::</span><span class="n">typecheck</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="k">const</span> <span class="n">type_env</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">env</span><span class="p">.</span><span class="n">lookup</span><span class="p">(</span><span class="n">id</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">type_ptr</span> <span class="n">ast_binop</span><span class="o">::</span><span class="n">typecheck</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="k">const</span> <span class="n">type_env</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">ltype</span> <span class="o">=</span> <span class="n">left</span><span class="o">-&gt;</span><span class="n">typecheck</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">rtype</span> <span class="o">=</span> <span class="n">right</span><span class="o">-&gt;</span><span class="n">typecheck</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">ftype</span> <span class="o">=</span> <span class="n">env</span><span class="p">.</span><span class="n">lookup</span><span class="p">(</span><span class="n">op_name</span><span class="p">(</span><span class="n">op</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="n">ftype</span><span class="p">)</span> <span class="k">throw</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">return_type</span> <span class="o">=</span> <span class="n">mgr</span><span class="p">.</span><span class="n">new_type</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">arrow_one</span> <span class="o">=</span> <span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_arr</span><span class="p">(</span><span class="n">rtype</span><span class="p">,</span> <span class="n">return_type</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">arrow_two</span> <span class="o">=</span> <span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_arr</span><span class="p">(</span><span class="n">ltype</span><span class="p">,</span> <span class="n">arrow_one</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">mgr</span><span class="p">.</span><span class="n">unify</span><span class="p">(</span><span class="n">arrow_two</span><span class="p">,</span> <span class="n">ftype</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">return_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">type_ptr</span> <span class="n">ast_app</span><span class="o">::</span><span class="n">typecheck</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="k">const</span> <span class="n">type_env</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">ltype</span> <span class="o">=</span> <span class="n">left</span><span class="o">-&gt;</span><span class="n">typecheck</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">rtype</span> <span class="o">=</span> <span class="n">right</span><span class="o">-&gt;</span><span class="n">typecheck</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">return_type</span> <span class="o">=</span> <span class="n">mgr</span><span class="p">.</span><span class="n">new_type</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">arrow</span> <span class="o">=</span> <span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_arr</span><span class="p">(</span><span class="n">rtype</span><span class="p">,</span> <span class="n">return_type</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="n">mgr</span><span class="p">.</span><span class="n">unify</span><span class="p">(</span><span class="n">arrow</span><span class="p">,</span> <span class="n">ltype</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">return_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">type_ptr</span> <span class="n">ast_case</span><span class="o">::</span><span class="n">typecheck</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="k">const</span> <span class="n">type_env</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">case_type</span> <span class="o">=</span> <span class="n">of</span><span class="o">-&gt;</span><span class="n">typecheck</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">branch_type</span> <span class="o">=</span> <span class="n">mgr</span><span class="p">.</span><span class="n">new_type</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">branch</span> <span class="p">:</span> <span class="n">branches</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_env</span> <span class="n">new_env</span> <span class="o">=</span> <span class="n">env</span><span class="p">.</span><span class="n">scope</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">branch</span><span class="o">-&gt;</span><span class="n">pat</span><span class="o">-&gt;</span><span class="n">match</span><span class="p">(</span><span class="n">case_type</span><span class="p">,</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">new_env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">curr_branch_type</span> <span class="o">=</span> <span class="n">branch</span><span class="o">-&gt;</span><span class="n">expr</span><span class="o">-&gt;</span><span class="n">typecheck</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">new_env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">mgr</span><span class="p">.</span><span class="n">unify</span><span class="p">(</span><span class="n">branch_type</span><span class="p">,</span> <span class="n">curr_branch_type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">branch_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">pattern_var</span><span class="o">::</span><span class="n">match</span><span class="p">(</span><span class="n">type_ptr</span> <span class="n">t</span><span class="p">,</span> <span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">env</span><span class="p">.</span><span class="n">bind</span><span class="p">(</span><span class="n">var</span><span class="p">,</span> <span class="n">t</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">pattern_constr</span><span class="o">::</span><span class="n">match</span><span class="p">(</span><span class="n">type_ptr</span> <span class="n">t</span><span class="p">,</span> <span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">constructor_type</span> <span class="o">=</span> <span class="n">env</span><span class="p">.</span><span class="n">lookup</span><span class="p">(</span><span class="n">constr</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="n">constructor_type</span><span class="p">)</span> <span class="k">throw</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">params</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_arr</span><span class="o">*</span> <span class="n">arr</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">type_arr</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">constructor_type</span><span class="p">.</span><span class="n">get</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="n">arr</span><span class="p">)</span> <span class="k">throw</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">env</span><span class="p">.</span><span class="n">bind</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">arr</span><span class="o">-&gt;</span><span class="n">left</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">constructor_type</span> <span class="o">=</span> <span class="n">arr</span><span class="o">-&gt;</span><span class="n">right</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">mgr</span><span class="p">.</span><span class="n">unify</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">constructor_type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">type_base</span><span class="o">*</span> <span class="n">result_type</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">type_base</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">constructor_type</span><span class="p">.</span><span class="n">get</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="n">result_type</span><span class="p">)</span> <span class="k">throw</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div> </div> <p>The <code>typecheck</code> implementation for <code>ast_int</code>, <code>ast_lid</code>, and <code>ast_uid</code> is quite intuitive. The type of a number is always <code>Int</code>, and varible names we simply look up in the environment.</p> <p>For <code>ast_binop</code> and <code>ast_app</code>, we check the types of the children first, and store their resulting types. In the case of <code>binop</code>, we assume that we have the type of the binary operator (such as <code>+</code>) in the environment, and throw if it isn&rsquo;t. Then, we know that the operator has to be a function of at least two arguments, with the types of the left and right children of the application. We also know its actual type, as it&rsquo;s in the environment. We unify these two types, and then return.</p> <p>In the case of <code>app</code>, we know that the left side (the thing being applied), has to be a function from the type of the right child. We also know its computed type. Once again, we unify the two, and then return.</p> <p>The type checking for <code>case</code> offloads most of the work onto the <code>match</code> method on patterns. We start by computing the type of the expression we&rsquo;re matching. Then, for each branch, we create a new scope, and populate it with the new bindings created by the pattern match (<code>match</code> does this). Once we have a new environment, we typecheck the body of the branch. Finally, we unify the type of the branch&rsquo;s body with the previous body types (since branches of the case expression have to have the same type).</p> <p>Let&rsquo;s take a look at <code>match</code> now. The <code>match</code> method for a variable pattern is very simple: it always introduces a new variable bindings, and stops there. The <code>match</code> method for a constructor pattern is more interesting. First, it looks up the constructor that the pattern is trying to match. This needs to exist, so if we don&rsquo;t find a type for it, we throw. Next, for each variable name in the pattern, we know that there has to be a corresponding parameter in the constructor. Because of this, we cast the constructor type to an function type (throwing if it isn&rsquo;t one), and create a new mapping from the variable name to the left side (parameter) of the function type. We then move on to examine the right side of the function type, and the remaining variables of the pattern.</p> <p>Once we have gone through the parameters, what remains <strong>should</strong> be the type of the thing the constructor creates. Not only that, but the remaining type should match the type of the value the pattern is being matched against. To ensure this, we unify the two types.</p> <p>There&rsquo;s only one thing that can still go wrong. The value <strong>and</strong> the pattern can <strong>both</strong> be a partial application. For ease of implementation, we will not allow this case. Thus, we make sure the final type is a base type, and throw otherwise.</p> <a href="#typechecking-definitions"> <h5 id="typechecking-definitions">Typechecking Definitions</h5> </a> <p>This looks good, but we&rsquo;re not done yet. We can type check expressions, but our program ins&rsquo;t made up of expressions. Rather, it&rsquo;s made up of definitions. Further, we can&rsquo;t look at the definitions in isolation. Consider these two functions:</p> <pre tabindex="0"><code>defn double x = { x + x } defn quadruple x = { double (double x) } </code></pre><p>Assuming we have an environment containing <code>x</code> when we typecheck the body of <code>double</code>, our algorithm will work out fine. But what about <code>quadruple</code>? It needs to know what <code>double</code> is, or at least that it exists.</p> <p>We could also envision two mutually recursive functions. Let&rsquo;s assume we have the functions <code>eq</code> and <code>if</code> in global scope. We can write two functions, <code>even</code> and <code>odd</code>:</p> <pre tabindex="0"><code>defn even x = { if (eq x 0) True (odd (x-1)) } defn odd x = { if (eq x 0) False (even (n-1)) } </code></pre><p><code>odd</code> needs to know about <code>even</code>, and <code>even</code> needs to know about <code>odd</code>. Thus, before we do any checking, we need to populate a global environment with <strong>some</strong> type for each function we declare. We will use what we know about the function for our initial declaration: if the function takes two parameters, its type will be <code>a -&gt; b -&gt; c</code>. If it takes one parameter, its type will be <code>a -&gt; b</code>. What&rsquo;s more, though, is that we need to make sure that the function&rsquo;s parameters are passed in the environment when checking its body, and that these parameters&rsquo; types are the same as the placeholder types in the function&rsquo;s &ldquo;declaration&rdquo;.</p> <p>We&rsquo;ll typecheck the program in two passes. First, we&rsquo;ll go through each definition, and add any functions it declares to the global scope. Then, we will go through each definition again, and, if it&rsquo;s a function, typecheck its body using the previously fleshed out global scope.</p> <p>We&rsquo;ll add two functions, <code>typecheck_first</code> and <code>typecheck_second</code> corresponding to these two stages. Their signatures:</p> <pre tabindex="0"><code>virtual void typecheck_first(type_mgr&amp; mgr, type_env&amp; env); virtual void typecheck_second(type_mgr&amp; mgr, const type_env&amp; env) const; </code></pre><p>Furthermore, in the <code>definition_defn</code>, we will keep an <code>std::vector</code> of <code>type_ptr</code>, in which the first element is the type of the <strong>last</strong> parameter, and so on. We switch around the order of arguments because we build up the <code>a -&gt; b -&gt; ...</code> type signature from the right (<code>-&gt;</code> is right associative), and thus we&rsquo;ll be creating the types right-to-left, too. We also add a <code>type_ptr</code> field which holds the function&rsquo;s return type. We keep these two things in the <code>definition_defn</code> so that they persist between the two typechecking stages: we want to use the types from the first stage to aid in checking the body in the second stage.</p> <p>Here&rsquo;s the code for the implementation: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/03/definition.cpp"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/03/definition.cpp">definition.cpp</a>, entire file</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;ast.hpp&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">definition_defn</span><span class="o">::</span><span class="n">typecheck_first</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">return_type</span> <span class="o">=</span> <span class="n">mgr</span><span class="p">.</span><span class="n">new_type</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">full_type</span> <span class="o">=</span> <span class="n">return_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">params</span><span class="p">.</span><span class="n">rbegin</span><span class="p">();</span> <span class="n">it</span> <span class="o">!=</span> <span class="n">params</span><span class="p">.</span><span class="n">rend</span><span class="p">();</span> <span class="n">it</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">param_type</span> <span class="o">=</span> <span class="n">mgr</span><span class="p">.</span><span class="n">new_type</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">full_type</span> <span class="o">=</span> <span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_arr</span><span class="p">(</span><span class="n">param_type</span><span class="p">,</span> <span class="n">full_type</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="n">param_types</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">param_type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">env</span><span class="p">.</span><span class="n">bind</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">full_type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">definition_defn</span><span class="o">::</span><span class="n">typecheck_second</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="k">const</span> <span class="n">type_env</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_env</span> <span class="n">new_env</span> <span class="o">=</span> <span class="n">env</span><span class="p">.</span><span class="n">scope</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">param_it</span> <span class="o">=</span> <span class="n">params</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">type_it</span> <span class="o">=</span> <span class="n">param_types</span><span class="p">.</span><span class="n">rbegin</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">while</span><span class="p">(</span><span class="n">param_it</span> <span class="o">!=</span> <span class="n">params</span><span class="p">.</span><span class="n">end</span><span class="p">()</span> <span class="o">&amp;&amp;</span> <span class="n">type_it</span> <span class="o">!=</span> <span class="n">param_types</span><span class="p">.</span><span class="n">rend</span><span class="p">())</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">new_env</span><span class="p">.</span><span class="n">bind</span><span class="p">(</span><span class="o">*</span><span class="n">param_it</span><span class="p">,</span> <span class="o">*</span><span class="n">type_it</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">param_it</span><span class="o">++</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_it</span><span class="o">++</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">body_type</span> <span class="o">=</span> <span class="n">body</span><span class="o">-&gt;</span><span class="n">typecheck</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">new_env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">mgr</span><span class="p">.</span><span class="n">unify</span><span class="p">(</span><span class="n">return_type</span><span class="p">,</span> <span class="n">body_type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">definition_data</span><span class="o">::</span><span class="n">typecheck_first</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">return_type</span> <span class="o">=</span> <span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_base</span><span class="p">(</span><span class="n">name</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">constructor</span> <span class="p">:</span> <span class="n">constructors</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">full_type</span> <span class="o">=</span> <span class="n">return_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">type_name</span> <span class="p">:</span> <span class="n">constructor</span><span class="o">-&gt;</span><span class="n">types</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">type</span> <span class="o">=</span> <span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_base</span><span class="p">(</span><span class="n">type_name</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="n">full_type</span> <span class="o">=</span> <span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_arr</span><span class="p">(</span><span class="n">type</span><span class="p">,</span> <span class="n">full_type</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">env</span><span class="p">.</span><span class="n">bind</span><span class="p">(</span><span class="n">constructor</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">,</span> <span class="n">full_type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">definition_data</span><span class="o">::</span><span class="n">typecheck_second</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="k">const</span> <span class="n">type_env</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// Nothing </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>And finally, our updated main: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/03/main.cpp"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/03/main.cpp">main.cpp</a>, entire file</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;ast.hpp&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;parser.hpp&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;type.hpp&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">yy</span><span class="o">::</span><span class="n">parser</span><span class="o">::</span><span class="n">error</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">msg</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;An error occured: &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">msg</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">extern</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">definition_ptr</span><span class="o">&gt;</span> <span class="n">program</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">typecheck_program</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">definition_ptr</span><span class="o">&gt;&amp;</span> <span class="n">prog</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_mgr</span> <span class="n">mgr</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_env</span> <span class="n">env</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">int_type</span> <span class="o">=</span> <span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_base</span><span class="p">(</span><span class="s">&#34;Int&#34;</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">binop_type</span> <span class="o">=</span> <span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_arr</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="n">int_type</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_arr</span><span class="p">(</span><span class="n">int_type</span><span class="p">,</span> <span class="n">int_type</span><span class="p">))));</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">env</span><span class="p">.</span><span class="n">bind</span><span class="p">(</span><span class="s">&#34;+&#34;</span><span class="p">,</span> <span class="n">binop_type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">env</span><span class="p">.</span><span class="n">bind</span><span class="p">(</span><span class="s">&#34;-&#34;</span><span class="p">,</span> <span class="n">binop_type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">env</span><span class="p">.</span><span class="n">bind</span><span class="p">(</span><span class="s">&#34;*&#34;</span><span class="p">,</span> <span class="n">binop_type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">env</span><span class="p">.</span><span class="n">bind</span><span class="p">(</span><span class="s">&#34;/&#34;</span><span class="p">,</span> <span class="n">binop_type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">def</span> <span class="p">:</span> <span class="n">prog</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">def</span><span class="o">-&gt;</span><span class="n">typecheck_first</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">def</span> <span class="p">:</span> <span class="n">prog</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">def</span><span class="o">-&gt;</span><span class="n">typecheck_second</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">yy</span><span class="o">::</span><span class="n">parser</span> <span class="n">parser</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">parser</span><span class="p">.</span><span class="n">parse</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">typecheck_program</span><span class="p">(</span><span class="n">program</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">program</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>Notice that we manually add the types for our binary operators to the environment.</p> <p>Let&rsquo;s run our project on a few examples. On our two &ldquo;bad&rdquo; examples, we get the very eloquent error:</p> <pre tabindex="0"><code>terminate called after throwing an instance of &#39;int&#39; [2] 9776 abort (core dumped) ./a.out &lt; bad2.txt </code></pre><p>That&rsquo;s what we get for throwing 0.</p> <p>So far, our program has thrown in 100% of cases. Let&rsquo;s verify it actually accepts valid programs! We&rsquo;ll try our very first example from today, as well as these two: <div class="highlight-group" > <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/03/works2.txt">works2.txt</a>, entire file</div><pre><code>defn add x y = { x + y } defn double x = { add x x } defn main = { double 163 } </code></pre> </div> <div class="highlight-group" > <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/03/works3.txt">works3.txt</a>, entire file</div><pre><code>data List = { Nil, Cons Int List } defn length l = { case l of { Nil -&gt; { 0 } Cons x xs -&gt; { 1 + length xs } } } </code></pre> </div> </p> <p>All of our examples print the number of declarations in the program, which means they don&rsquo;t throw 0. And so, we have typechecking! Before we look at how we will execute our source code, we will slow down and make quality of life improvements in our codebase in <a href="https://danilafe.com/blog/04_compiler_improvements/">Part 4 - Small Improvements</a>.</p> Compiling a Functional Language Using C++, Part 4 - Small Improvements https://danilafe.com/blog/04_compiler_improvements/ Tue, 06 Aug 2019 14:26:38 -0700 https://danilafe.com/blog/04_compiler_improvements/ <p>We&rsquo;ve done quite a big push in the previous post. We defined type rules for our language, implemented unification, and then implemented unification to enforce these rules for our program. The post was pretty long, and even then we weren&rsquo;t able to fit quite everything into it.</p> <p>For instance, we threw 0 whenever an error occured. This gives us no indication of what actually went wrong. We should probably define an exception class, one that can contain information about the error, and report it to the user.</p> <p>Also, when there&rsquo;s no error, our compiler doesn&rsquo;t really tell us anything at all about the code besides the number of definitions. We probably want to see the types of these definitions, or at least some intermediate information. At the very least, we want to have the <strong>ability</strong> to see this information.</p> <p>Finally, we have no build system. We are creating more and more source files, and so far (unless you&rsquo;ve taken initiative), we&rsquo;ve been compiling them by hand. We want to only compile source files that have changed, and we want to have a standard definition of how to build our program.</p> <a href="#printing-syntax-trees"> <h3 id="printing-syntax-trees">Printing Syntax Trees</h3> </a> <p>Let&rsquo;s start by printing the trees we get from our parser. This is long overdue - we had no way to verify the structure of what our parser returned to us since Part 2. We&rsquo;ll print the trees top-down, with the children of a node indent one block further than the node itself. For this, we&rsquo;ll make a new virtual function with the signature:</p> <pre tabindex="0"><code>virtual void print(int indent, std::ostream&amp; to) const; </code></pre><p>We&rsquo;ll include a similar printing function into our pattern struct, too:</p> <pre tabindex="0"><code>virtual void print(std::ostream&amp; to) const; </code></pre><p>Let&rsquo;s take a look at the implementation. For <code>ast_int</code>, <code>ast_lid</code>, and <code>ast_uid</code>: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/04/ast.cpp" data-first-line="19" data-last-line="22"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/04/ast.cpp#L19-L22">ast.cpp</a>, lines 19 through 22</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast_int</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="kt">int</span> <span class="n">indent</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">print_indent</span><span class="p">(</span><span class="n">indent</span><span class="p">,</span> <span class="n">to</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">to</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;INT: &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">value</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/04/ast.cpp" data-first-line="28" data-last-line="31"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/04/ast.cpp#L28-L31">ast.cpp</a>, lines 28 through 31</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast_lid</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="kt">int</span> <span class="n">indent</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">print_indent</span><span class="p">(</span><span class="n">indent</span><span class="p">,</span> <span class="n">to</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">to</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;LID: &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">id</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/04/ast.cpp" data-first-line="37" data-last-line="40"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/04/ast.cpp#L37-L40">ast.cpp</a>, lines 37 through 40</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast_uid</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="kt">int</span> <span class="n">indent</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">print_indent</span><span class="p">(</span><span class="n">indent</span><span class="p">,</span> <span class="n">to</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">to</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;UID: &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">id</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>With <code>ast_binop</code> things get a bit more interesting. We call <code>print</code> recursively on the children of the <code>binop</code> node: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/04/ast.cpp" data-first-line="46" data-last-line="51"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/04/ast.cpp#L46-L51">ast.cpp</a>, lines 46 through 51</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast_binop</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="kt">int</span> <span class="n">indent</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">print_indent</span><span class="p">(</span><span class="n">indent</span><span class="p">,</span> <span class="n">to</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">to</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;BINOP: &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">op_name</span><span class="p">(</span><span class="n">op</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">left</span><span class="o">-&gt;</span><span class="n">print</span><span class="p">(</span><span class="n">indent</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">to</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">right</span><span class="o">-&gt;</span><span class="n">print</span><span class="p">(</span><span class="n">indent</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">to</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>The same idea for <code>ast_app</code>: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/04/ast.cpp" data-first-line="67" data-last-line="72"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/04/ast.cpp#L67-L72">ast.cpp</a>, lines 67 through 72</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">67 </span><span class="lnt">68 </span><span class="lnt">69 </span><span class="lnt">70 </span><span class="lnt">71 </span><span class="lnt">72 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast_app</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="kt">int</span> <span class="n">indent</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">print_indent</span><span class="p">(</span><span class="n">indent</span><span class="p">,</span> <span class="n">to</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">to</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;APP:&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">left</span><span class="o">-&gt;</span><span class="n">print</span><span class="p">(</span><span class="n">indent</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">to</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">right</span><span class="o">-&gt;</span><span class="n">print</span><span class="p">(</span><span class="n">indent</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">to</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>Finally, just like <code>ast_case::typecheck</code> called <code>pattern::match</code>, <code>ast_case::print</code> calls <code>pattern::print</code>: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/04/ast.cpp" data-first-line="84" data-last-line="93"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/04/ast.cpp#L84-L93">ast.cpp</a>, lines 84 through 93</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">84 </span><span class="lnt">85 </span><span class="lnt">86 </span><span class="lnt">87 </span><span class="lnt">88 </span><span class="lnt">89 </span><span class="lnt">90 </span><span class="lnt">91 </span><span class="lnt">92 </span><span class="lnt">93 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast_case</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="kt">int</span> <span class="n">indent</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">print_indent</span><span class="p">(</span><span class="n">indent</span><span class="p">,</span> <span class="n">to</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">to</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;CASE: &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">branch</span> <span class="p">:</span> <span class="n">branches</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">print_indent</span><span class="p">(</span><span class="n">indent</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">to</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">branch</span><span class="o">-&gt;</span><span class="n">pat</span><span class="o">-&gt;</span><span class="n">print</span><span class="p">(</span><span class="n">to</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">to</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">branch</span><span class="o">-&gt;</span><span class="n">expr</span><span class="o">-&gt;</span><span class="n">print</span><span class="p">(</span><span class="n">indent</span> <span class="o">+</span> <span class="mi">2</span><span class="p">,</span> <span class="n">to</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>We follow the same implementation strategy for patterns, but we don&rsquo;t need indentation, or recursion: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/04/ast.cpp" data-first-line="115" data-last-line="117"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/04/ast.cpp#L115-L117">ast.cpp</a>, lines 115 through 117</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">115 </span><span class="lnt">116 </span><span class="lnt">117 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">pattern_var</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">to</span> <span class="o">&lt;&lt;</span> <span class="n">var</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/04/ast.cpp" data-first-line="123" data-last-line="128"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/04/ast.cpp#L123-L128">ast.cpp</a>, lines 123 through 128</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">123 </span><span class="lnt">124 </span><span class="lnt">125 </span><span class="lnt">126 </span><span class="lnt">127 </span><span class="lnt">128 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">pattern_constr</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">to</span> <span class="o">&lt;&lt;</span> <span class="n">constr</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">param</span> <span class="p">:</span> <span class="n">params</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">to</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">param</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>In <code>main</code>, let&rsquo;s print the bodies of each function we receive from the parser: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/04/main.cpp" data-first-line="47" data-last-line="56"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/04/main.cpp#L47-L56">main.cpp</a>, lines 47 through 56</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">definition</span> <span class="p">:</span> <span class="n">program</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">definition_defn</span><span class="o">*</span> <span class="n">def</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">definition_defn</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">definition</span><span class="p">.</span><span class="n">get</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="n">def</span><span class="p">)</span> <span class="k">continue</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">def</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">param</span> <span class="p">:</span> <span class="n">def</span><span class="o">-&gt;</span><span class="n">params</span><span class="p">)</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">param</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;:&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">def</span><span class="o">-&gt;</span><span class="n">body</span><span class="o">-&gt;</span><span class="n">print</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> </p> <a href="#printing-types"> <h3 id="printing-types">Printing Types</h3> </a> <p>Types are another thing that we want to be able to inspect, so let&rsquo;s add a similar print method to them:</p> <pre tabindex="0"><code>virtual void print(const type_mgr&amp; mgr, std::ostream&amp; to) const; </code></pre><p>We need the type manager so we can follow substitutions. The implementation is simple enough: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/04/type.cpp" data-first-line="6" data-last-line="24"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/04/type.cpp#L6-L24">type.cpp</a>, lines 6 through 24</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">type_var</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="k">const</span> <span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">mgr</span><span class="p">.</span><span class="n">types</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">it</span> <span class="o">!=</span> <span class="n">mgr</span><span class="p">.</span><span class="n">types</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">it</span><span class="o">-&gt;</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">print</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">to</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">to</span> <span class="o">&lt;&lt;</span> <span class="n">name</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">type_base</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="k">const</span> <span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">to</span> <span class="o">&lt;&lt;</span> <span class="n">name</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">type_arr</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="k">const</span> <span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">left</span><span class="o">-&gt;</span><span class="n">print</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">to</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">to</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; -&gt; (&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">right</span><span class="o">-&gt;</span><span class="n">print</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">to</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">to</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;)&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>Let&rsquo;s also print out the types we infer. We&rsquo;ll make it a separate loop at the bottom of the <code>typecheck_program</code> function, because it&rsquo;s mostly just for debugging purposes: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/04/main.cpp" data-first-line="34" data-last-line="38"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/04/main.cpp#L34-L38">main.cpp</a>, lines 34 through 38</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">pair</span> <span class="p">:</span> <span class="n">env</span><span class="p">.</span><span class="n">names</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">pair</span><span class="p">.</span><span class="n">first</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;: &#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">pair</span><span class="p">.</span><span class="n">second</span><span class="o">-&gt;</span><span class="n">print</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> </p> <a href="#fixing-bugs"> <h3 id="fixing-bugs">Fixing Bugs</h3> </a> <p>We actually discover not one, but two bugs in our implementation thanks to the output we get from printing trees and types. Observe the output for <code>works3.txt</code>:</p> <pre tabindex="0"><code>length l: CASE: Nil INT: 0 *: Int -&gt; (Int -&gt; (Int)) +: Int -&gt; (Int -&gt; (Int)) -: Int -&gt; (Int -&gt; (Int)) /: Int -&gt; (Int -&gt; (Int)) Cons: List -&gt; (Int -&gt; (List)) Nil: List length: List -&gt; (Int) 2 </code></pre><p>First, we&rsquo;re missing the <code>Cons</code> branch. The culprit is <code>parser.y</code>, specifically this line:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">branches</span> <span class="n">branch</span> <span class="p">{</span> <span class="err">$$</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="err">$</span><span class="mi">1</span><span class="p">);</span> <span class="err">$</span><span class="mf">1.</span><span class="n">push_back</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="err">$</span><span class="mi">2</span><span class="p">));</span> <span class="p">}</span> </span></span></code></pre></div><p>Notice that we move our list of branches out of <code>$1</code>. However, when we <code>push_back</code>, we use <code>$1</code> again. That&rsquo;s wrong! We need to <code>push_back</code> to <code>$$</code> instead: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/04/parser.y" data-first-line="110" data-last-line="110"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/04/parser.y#L110-L110">parser.y</a>, line 110</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">110 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">branches</span> <span class="n">branch</span> <span class="p">{</span> <span class="err">$$</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="err">$</span><span class="mi">1</span><span class="p">);</span> <span class="err">$$</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="err">$</span><span class="mi">2</span><span class="p">));</span> <span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>Next, observe that <code>Cons</code> has type <code>List -&gt; Int -&gt; List</code>. That&rsquo;s not right, since <code>Int</code> comes first in our definition. The culprit is this fragment of code:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">type_name</span> <span class="p">:</span> <span class="n">constructor</span><span class="o">-&gt;</span><span class="n">types</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">type</span> <span class="o">=</span> <span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_base</span><span class="p">(</span><span class="n">type_name</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="n">full_type</span> <span class="o">=</span> <span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_arr</span><span class="p">(</span><span class="n">type</span><span class="p">,</span> <span class="n">full_type</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span></code></pre></div><p>Remember how we build the function type backwards in Part 3? We have to do the same here. We replace the fragment with the proper reverse iteration: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/04/definition.cpp" data-first-line="37" data-last-line="40"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/04/definition.cpp#L37-L40">definition.cpp</a>, lines 37 through 40</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">constructor</span><span class="o">-&gt;</span><span class="n">types</span><span class="p">.</span><span class="n">rbegin</span><span class="p">();</span> <span class="n">it</span> <span class="o">!=</span> <span class="n">constructor</span><span class="o">-&gt;</span><span class="n">types</span><span class="p">.</span><span class="n">rend</span><span class="p">();</span> <span class="n">it</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">type</span> <span class="o">=</span> <span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_base</span><span class="p">(</span><span class="o">*</span><span class="n">it</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="n">full_type</span> <span class="o">=</span> <span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_arr</span><span class="p">(</span><span class="n">type</span><span class="p">,</span> <span class="n">full_type</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> </p> <a href="#throwing-exceptions"> <h3 id="throwing-exceptions">Throwing Exceptions</h3> </a> <p>Throwing 0 is never a good idea. Such an exception doesn&rsquo;t contain any information that we may find useful in debugging, nor any information that would benefit the users of the compiler. Instead, let&rsquo;s define our own exception classes, and throw them instead. We&rsquo;ll make two: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/04/error.hpp"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/04/error.hpp">error.hpp</a>, entire file</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="cp">#pragma once </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;exception&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;type.hpp&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">type_error</span> <span class="o">:</span> <span class="n">std</span><span class="o">::</span><span class="n">exception</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">description</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">type_error</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">d</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">description</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">d</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="nf">what</span><span class="p">()</span> <span class="k">const</span> <span class="k">noexcept</span> <span class="k">override</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">unification_error</span> <span class="o">:</span> <span class="k">public</span> <span class="n">type_error</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">left</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">right</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">unification_error</span><span class="p">(</span><span class="n">type_ptr</span> <span class="n">l</span><span class="p">,</span> <span class="n">type_ptr</span> <span class="n">r</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">left</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">l</span><span class="p">)),</span> <span class="n">right</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">r</span><span class="p">)),</span> </span></span><span class="line"><span class="cl"> <span class="n">type_error</span><span class="p">(</span><span class="s">&#34;failed to unify types&#34;</span><span class="p">)</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>Only one function needs to be implemented, and it&rsquo;s pretty boring: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/04/error.cpp"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/04/error.cpp">error.cpp</a>, entire file</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;error.hpp&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">type_error</span><span class="o">::</span><span class="n">what</span><span class="p">()</span> <span class="k">const</span> <span class="k">noexcept</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="s">&#34;an error occured while checking the types of the program&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>It&rsquo;s time to throw these instead of 0. Let&rsquo;s take a look at the places we do so.</p> <p>First, we throw 0 in <code>type.cpp</code>, in the <code>type_mgr::unify</code> method. This is where our <code>unification_error</code> comes in. The error will contain the two types that we failed to unify, which we will later report to the user: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/04/type.cpp" data-first-line="91" data-last-line="91"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/04/type.cpp#L91-L91">type.cpp</a>, line 91</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">91 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="k">throw</span> <span class="nf">unification_error</span><span class="p">(</span><span class="n">l</span><span class="p">,</span> <span class="n">r</span><span class="p">);</span></span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>Next up, we have a few throws in <code>ast.cpp</code>. The first is in <code>op_string</code>, but we will simply replace it with <code>return &quot;??&quot;</code>, which will be caught later on (either way, the case expression falling through would be a compiler bug, since the user has no way of providing an invalid binary operator). The first throw we need to address is in <code>ast_binop::typecheck</code>, in the case that we don&rsquo;t find a type for a binary operator. We report this directly: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/04/ast.cpp" data-first-line="57" data-last-line="57"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/04/ast.cpp#L57-L57">ast.cpp</a>, line 57</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">57 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="n">ftype</span><span class="p">)</span> <span class="k">throw</span> <span class="n">type_error</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">(</span><span class="s">&#34;unknown binary operator &#34;</span><span class="p">)</span> <span class="o">+</span> <span class="n">op_name</span><span class="p">(</span><span class="n">op</span><span class="p">));</span></span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>We will introduce a new exception into <code>ast_case::typecheck</code>. Previously, we simply pass the type of the expression to be case analyzed into the pattern matching method. However, since we don&rsquo;t want case analysis on functions, we ensure that the type of the expression is <code>type_base</code>. If not, we report this: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/04/ast.cpp" data-first-line="107" data-last-line="110"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/04/ast.cpp#L107-L110">ast.cpp</a>, lines 107 through 110</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">107 </span><span class="lnt">108 </span><span class="lnt">109 </span><span class="lnt">110 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="n">case_type</span> <span class="o">=</span> <span class="n">mgr</span><span class="p">.</span><span class="n">resolve</span><span class="p">(</span><span class="n">case_type</span><span class="p">,</span> <span class="n">var</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">type_base</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">case_type</span><span class="p">.</span><span class="n">get</span><span class="p">()))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="nf">type_error</span><span class="p">(</span><span class="s">&#34;attempting case analysis of non-data type&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>The next exception is in <code>pattern_constr::match</code>. It occurs when the pattern has a constructor we don&rsquo;t recognize, and that&rsquo;s exactly what we report: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/04/ast.cpp" data-first-line="132" data-last-line="134"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/04/ast.cpp#L132-L134">ast.cpp</a>, lines 132 through 134</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">132 </span><span class="lnt">133 </span><span class="lnt">134 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="n">constructor_type</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="nf">type_error</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">(</span><span class="s">&#34;pattern using unknown constructor &#34;</span><span class="p">)</span> <span class="o">+</span> <span class="n">constr</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>The next exception occurs in a loop, when we bind types for each of the constructor pattern&rsquo;s variables. We throw when we are unable to cast the remaining constructor type to a <code>type_arr</code>. Conceptually, this means that the pattern wants to apply the constructor to more parameters than it actually takes: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/04/ast.cpp" data-first-line="138" data-last-line="138"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/04/ast.cpp#L138-L138">ast.cpp</a>, line 138</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">138 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="n">arr</span><span class="p">)</span> <span class="k">throw</span> <span class="n">type_error</span><span class="p">(</span><span class="s">&#34;too many parameters in constructor pattern&#34;</span><span class="p">);</span></span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>We remove the last throw at the bottom of <code>pattern_constr::match</code>. This is because once unification succeeds, we know that the return type of the pattern is a base type since we know the type of the case expression is a base type (we know this because we added that check to <code>ast_case::typecheck</code>).</p> <p>Finally, let&rsquo;s catch and report these exceptions. We could do it in <code>typecheck_program</code>, but I think doing so in <code>main</code> is neater. Since printing types requires a <code>type_mgr</code>, we&rsquo;ll move the declarations of both <code>type_mgr</code> and <code>type_env</code> to the top of main, and pass them to <code>typecheck_program</code> as parameters. Then, we can surround the call to <code>typecheck_program</code> with try/catch: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/04/main.cpp" data-first-line="57" data-last-line="69"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/04/main.cpp#L57-L69">main.cpp</a>, lines 57 through 69</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span><span class="lnt">65 </span><span class="lnt">66 </span><span class="lnt">67 </span><span class="lnt">68 </span><span class="lnt">69 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="k">try</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">typecheck_program</span><span class="p">(</span><span class="n">program</span><span class="p">,</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="n">unification_error</span><span class="o">&amp;</span> <span class="n">err</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;failed to unify types: &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; (1) </span><span class="se">\033</span><span class="s">[34m&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">err</span><span class="p">.</span><span class="n">left</span><span class="o">-&gt;</span><span class="n">print</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;</span><span class="se">\033</span><span class="s">[0m&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34; (2) </span><span class="se">\033</span><span class="s">[32m&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">err</span><span class="p">.</span><span class="n">right</span><span class="o">-&gt;</span><span class="n">print</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;</span><span class="se">\033</span><span class="s">[0m&#34;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="n">type_error</span><span class="o">&amp;</span> <span class="n">err</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;failed to type check program: &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">err</span><span class="p">.</span><span class="n">description</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>We use some <a href="https://en.wikipedia.org/wiki/ANSI_escape_code"class="external-link">ANSI escape codes<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> to color the types in the case of a unification error.</p> <a href="#setting-up-cmake"> <h3 id="setting-up-cmake">Setting up CMake</h3> </a> <p>We will set up CMake as our build system. This would be extremely easy if not for Flex and Bison, but it&rsquo;s not hard either way. We start with the usual: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/04/CMakeLists.txt" data-first-line="1" data-last-line="2"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/04/CMakeLists.txt#L1-L2">CMakeLists.txt</a>, lines 1 through 2</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-CMake" data-lang="CMake"><span class="line"><span class="cl"><span class="nb">cmake_minimum_required</span><span class="p">(</span><span class="s">VERSION</span> <span class="s">3.1</span><span class="p">)</span><span class="err"> </span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="nb">project</span><span class="p">(</span><span class="s">compiler</span><span class="p">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>Next, we want to set up Flex and Bison. CMake provides two commands for this: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/04/CMakeLists.txt" data-first-line="4" data-last-line="5"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/04/CMakeLists.txt#L4-L5">CMakeLists.txt</a>, lines 4 through 5</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">4 </span><span class="lnt">5 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-CMake" data-lang="CMake"><span class="line"><span class="cl"><span class="nb">find_package</span><span class="p">(</span><span class="s">BISON</span><span class="p">)</span><span class="err"> </span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="nb">find_package</span><span class="p">(</span><span class="s">FLEX</span><span class="p">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>We now have access to commands that allow us to tell CMake about our parser and tokenizer (or scanner). We use them as follows: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/04/CMakeLists.txt" data-first-line="6" data-last-line="12"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/04/CMakeLists.txt#L6-L12">CMakeLists.txt</a>, lines 6 through 12</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-CMake" data-lang="CMake"><span class="line"><span class="cl"><span class="nb">bison_target</span><span class="p">(</span><span class="s">parser</span> </span></span><span class="line"><span class="cl"> <span class="o">${</span><span class="nv">CMAKE_CURRENT_SOURCE_DIR</span><span class="o">}</span><span class="s">/parser.y</span> </span></span><span class="line"><span class="cl"> <span class="o">${</span><span class="nv">CMAKE_CURRENT_BINARY_DIR</span><span class="o">}</span><span class="s">/parser.cpp</span> </span></span><span class="line"><span class="cl"> <span class="s">COMPILE_FLAGS</span> <span class="s2">&#34;-d&#34;</span><span class="p">)</span><span class="err"> </span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="nb">flex_target</span><span class="p">(</span><span class="s">scanner</span> </span></span><span class="line"><span class="cl"> <span class="o">${</span><span class="nv">CMAKE_CURRENT_SOURCE_DIR</span><span class="o">}</span><span class="s">/scanner.l</span> </span></span><span class="line"><span class="cl"> <span class="o">${</span><span class="nv">CMAKE_CURRENT_BINARY_DIR</span><span class="o">}</span><span class="s">/scanner.cpp</span><span class="p">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>We also want CMake to know that the scanner needs to parser&rsquo;s header file in order to compile. We add this dependency: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/04/CMakeLists.txt" data-first-line="13" data-last-line="13"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/04/CMakeLists.txt#L13-L13">CMakeLists.txt</a>, line 13</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">13 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-CMake" data-lang="CMake"><span class="line"><span class="cl"><span class="nb">add_flex_bison_dependency</span><span class="p">(</span><span class="s">scanner</span> <span class="s">parser</span><span class="p">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>Finally, we add our source code to a CMake target. We use the <code>BISON_parser_OUTPUTS</code> and <code>FLEX_scanner_OUTPUTS</code> to pass in the source files generated by Flex and Bison. <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/04/CMakeLists.txt" data-first-line="15" data-last-line="23"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/04/CMakeLists.txt#L15-L23">CMakeLists.txt</a>, lines 15 through 23</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-CMake" data-lang="CMake"><span class="line"><span class="cl"><span class="nb">add_executable</span><span class="p">(</span><span class="s">compiler</span> </span></span><span class="line"><span class="cl"> <span class="s">ast.cpp</span> <span class="s">ast.hpp</span> <span class="s">definition.cpp</span> </span></span><span class="line"><span class="cl"> <span class="s">env.cpp</span> <span class="s">env.hpp</span> </span></span><span class="line"><span class="cl"> <span class="s">type.cpp</span> <span class="s">type.hpp</span> </span></span><span class="line"><span class="cl"> <span class="s">error.cpp</span> <span class="s">error.hpp</span> </span></span><span class="line"><span class="cl"> <span class="o">${</span><span class="nv">BISON_parser_OUTPUTS</span><span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="o">${</span><span class="nv">FLEX_scanner_OUTPUTS</span><span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="s">main.cpp</span> </span></span><span class="line"><span class="cl"><span class="p">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>Almost there! <code>parser.cpp</code> will be generated in the <code>build</code> directory during an out-of-source build, and so will <code>parser.hpp</code>. When building, <code>parser.cpp</code> will try to look for <code>ast.hpp</code>, and <code>main.cpp</code> will look for <code>parser.hpp</code>. We want them to be able to find each other, so we add both the source directory and the build (binary) directory to the list of include directories:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/04/CMakeLists.txt" data-first-line="24" data-last-line="25"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/04/CMakeLists.txt#L24-L25">CMakeLists.txt</a>, lines 24 through 25</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">24 </span><span class="lnt">25 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-CMake" data-lang="CMake"><span class="line"><span class="cl"><span class="nb">target_include_directories</span><span class="p">(</span><span class="s">compiler</span> <span class="s">PUBLIC</span> <span class="o">${</span><span class="nv">CMAKE_CURRENT_SOURCE_DIR</span><span class="o">}</span><span class="p">)</span><span class="err"> </span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="nb">target_include_directories</span><span class="p">(</span><span class="s">compiler</span> <span class="s">PUBLIC</span> <span class="o">${</span><span class="nv">CMAKE_CURRENT_BINARY_DIR</span><span class="o">}</span><span class="p">)</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>That&rsquo;s it for CMake! Let&rsquo;s try our build:</p> <pre tabindex="0"><code>cmake -S . -B build cd build &amp;&amp; make -j8 </code></pre><a href="#updated-code"> <h3 id="updated-code">Updated Code</h3> </a> <p>We&rsquo;ve made a lot of changes to the codebase, and I&rsquo;ve only shown snippets of the code so far. If you&rsquo;de like to see the whole codebase, you can go to my site&rsquo;s git repository and check out <a href="https://dev.danilafe.com/Web-Projects/blog-static/src/branch/master/code/compiler/04"class="external-link">the code so far<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>.</p> <p>Having taken this little break, it&rsquo;s time for our next push. We will define how our programs will be evaluated in <a href="https://danilafe.com/blog/05_compiler_execution/">Part 5 - Execution</a>.</p> Compiling a Functional Language Using C++, Part 5 - Execution https://danilafe.com/blog/05_compiler_execution/ Tue, 06 Aug 2019 14:26:38 -0700 https://danilafe.com/blog/05_compiler_execution/ <p> <link rel="stylesheet" href="https://danilafe.com/scss/gmachine.min.css"> We now have trees representing valid programs in our language, and it&rsquo;s time to think about how to compile them into machine code, to be executed on hardware. But <strong>how should we execute programs</strong>?</p> <p>The programs we define are actually lists of definitions. But you can&rsquo;t evaluate definitions - they just tell you, well, how things are defined. Expressions, on the other hand, can be simplified. So, let&rsquo;s start by evaluating the body of the function called <code>main</code>, similarly to how C/C++ programs start.</p> <p>Alright, we&rsquo;ve made it past that hurdle. Next, to figure out how to evaluate expressions. It&rsquo;s easy enough with binary operators: <code>3+2*6</code> becomes <code>3+12</code>, and <code>3+12</code> becomes <code>15</code>. Functions are when things get interesting. Consider:</p> <pre tabindex="0"><code>double (160+3) </code></pre><p>There&rsquo;s many perfectly valid ways to evaluate the program. When we get to a function application, we can first evaluate the arguments, and then expand the function definition:</p> <pre tabindex="0"><code>double (160+3) double 163 163+163 326 </code></pre><p>Let&rsquo;s come up with a more interesting program to illustrate execution. How about:</p> <pre tabindex="0"><code>data Pair = { P Int Int } defn fst p = { case p of { P x y -&gt; { x } } } defn snd p = { case p of { P x y -&gt; { y } } } defn slow x = { returns x after waiting for 1 second } defn main = { fst (P (slow 320) (slow 6)) } </code></pre><p>If we follow our rules for evaluating functions, the execution will follow the following steps:</p> <pre tabindex="0"><code>fst (P (slow 320) (slow 6)) fst (P 320 (slow 6)) &lt;- after 1 second fst (P 320 6) &lt;- after 1 second 320 </code></pre><p>We waited for two seconds, even though we really only needed to wait one. To avoid this, we could instead define our function application to substitute in the parameters of a function before evaluating them:</p> <pre tabindex="0"><code>fst (P (slow 320) (slow 6)) (slow 320) 320 &lt;- after 1 second </code></pre><p>This seems good, until we try doubling an expression again:</p> <pre tabindex="0"><code>double (slow 163) (slow 163) + (slow 163) 163 + (slow 163) &lt;- after 1 second 163 + 163 &lt;- after 1 second 326 </code></pre><p>With ony one argument, we&rsquo;ve actually spent two seconds on the evaluation! If we instead tried to triple using addition, we&rsquo;d spend three seconds.</p> <p>Observe that with these new rules (called &ldquo;call by name&rdquo; in programming language theory), we only waste time because we evaluate an expression that was passed in more than 1 time. What if we didn&rsquo;t have to do that? Since we have a functional language, there&rsquo;s no way that two expressions that are the same evaluate to a different value. Thus, once we know the result of an expression, we can replace all occurences of that expression with the result:</p> <pre tabindex="0"><code>double (slow 163) (slow 163) + (slow 163) 163 + 163 &lt;- after 1 second 326 </code></pre><p>We&rsquo;re back down to one second, and since we&rsquo;re still substituting parameters before we evaluate them, we still only take one second.</p> <p>Alright, this all sounds good. How do we go about implementing this? Since we&rsquo;re substituting variables for whole expressions, we can&rsquo;t just use values. Instead, because expressions are represented with trees, we might as well consider operating on trees. When we evaluate a tree, we can substitute it in-place with what it evaluates to. We&rsquo;ll do this depth-first, replacing the children of a node with their reduced trees, and then moving on to the parent.</p> <p>There&rsquo;s only one problem with this: if we substitute a variable that occurs many times with the same expression tree, we no longer have a tree! Trees, by definition, have only one path from the root to any other node. Since we now have many ways to reach that expression we substituted, we instead have a <strong>graph</strong>. Indeed, the way we will be executing our functional code is called <strong>graph reduction</strong>.</p> <a href="#building-graphs"> <h3 id="building-graphs">Building Graphs</h3> </a> <p>Naively, we might consider creating a tree for each function at the beginning of our program, and then, when that function is called, substituting the variables in it with the parameters of the application. But that approach quickly goes out the window when we realize that we could be applying a function multiple times - in fact, an arbitrary number of times. This means we can&rsquo;t have a single tree, and we must build a new tree every time we call a function.</p> <p>The question, then, is: how do we construct a new graph? We could reach into Plato&rsquo;s <a href="https://en.wikipedia.org/wiki/Theory_of_forms"class="external-link">Theory of Forms<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> and have a &ldquo;reference&rdquo; tree which we then copy every time we apply the function. But how do you copy a tree? Copying a tree is usually a recursive function, and <strong>every</strong> time that we copy a tree, we&rsquo;ll have to look at each node and decide whether or not to visit its children (or if it has any at all). If we copy a tree 100 times, we will have to look at each &ldquo;reference&rdquo; node 100 times. Since the reference tree doesn&rsquo;t change, <strong>we&rsquo;d be following the exact same sequence of decisions 100 times</strong>. That&rsquo;s no good!</p> <p>An alternative approach, one that we&rsquo;ll use from now on, is to instead convert each function&rsquo;s expression tree into a sequence of instructions that you can follow to build an identical tree. Every time we have to apply a function, we&rsquo;ll follow the corresponding recipe for that function, and end up with a new tree that we continue evaluating.</p> <a href="#g-machine"> <h3 id="g-machine">G-machine</h3> </a> <p>&ldquo;Instructions&rdquo; is a very generic term. Specifically, we will be creating instructions for a <a href="https://link.springer.com/chapter/10.1007/3-540-15975-4_50"class="external-link">G-machine<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, an abstract architecture which we will use to reduce our graphs. The G-machine is stack-based - all operations push and pop items from a stack. The machine will also have a &ldquo;dump&rdquo;, which is a stack of stacks; this will help with separating evaluation of various graphs.</p> <p>We will follow the same notation as Simon Peyton Jones in <a href="https://www.microsoft.com/en-us/research/wp-content/uploads/1992/01/student.pdf"class="external-link">his book<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> , which was my source of truth when implementing my compiler. The machine will be executing instructions that we give it, and as such, it must have an instruction queue, which we will reference as \(i\). We will write \(x:i\) to mean &ldquo;an instruction queue that starts with an instruction x and ends with instructions \(i\)&rdquo;. A stack machine obviously needs to have a stack - we will call it \(s\), and will adopt a similar notation to the instruction queue: \(a_1, a_2, a_3 : s\) will mean &ldquo;a stack with the top values \(a_1\), \(a_2\), and \(a_3\), and remaining instructions \(s\)&rdquo;. Finally, as we said, our stack machine has a dump, which we will write as \(d\). On this dump, we will push not only the current stack, but also the current instructions that we are executing, so we may resume execution later. We will write \(\langle i, s \rangle : d\) to mean &ldquo;a dump with instructions \(i\) and stack \(s\) on top, followed by instructions and stacks in \(d\)&rdquo;.</p> <p>There&rsquo;s one more thing the G-machine will have that we&rsquo;ve not yet discussed at all, and it&rsquo;s needed because of the following quip earlier in the post:</p> <blockquote> <p>When we evaluate a tree, we can substitute it in-place with what it evaluates to.</p> </blockquote> <p>How can we substitute a value in place? Surely we won&rsquo;t iterate over the entire tree and look for an occurence of the tree we evaluted. Rather, wouldn&rsquo;t it be nice if we could update all references to a tree to be something else? Indeed, we can achieve this effect by using <strong>pointers</strong>. I don&rsquo;t mean specifically C/C++ pointers - I mean the more general concept of &ldquo;an address in memory&rdquo;. The G-machine has a <strong>heap</strong>, much like the heap of a C/C++ process. We can create a tree node on the heap, and then get an <strong>address</strong> of the node. We then have trees use these addresses to link their child nodes. If we want to replace a tree node with its reduced form, we keep its address the same, but change the value on the heap. This way, all trees that reference the node we change become updated, without us having to change them - their child address remains the same, but the child has now been updated. We represent the heap using \(h\). We write \(h[a : v]\) to say &ldquo;the address \(a\) points to value \(v\) in the heap \(h\)&rdquo;. Now you also know why we used the letter \(a\) when describing values on the stack - the stack contains addresses of (or pointers to) tree nodes.</p> <p><em>Compiling Functional Languages: a tutorial</em> also keeps another component of the G-machine, the <strong>global map</strong>, which maps function names to addresses of nodes that represent them. We&rsquo;ll stick with this, and call this global map \(m\).</p> <p>Finally, let&rsquo;s talk about what kind of nodes our trees will be made of. We don&rsquo;t have to include every node that we&rsquo;ve defined as a subclass of <code>ast</code> - some nodes we can compile to instructions, without having to build them. We will also include nodes that we didn&rsquo;t need for to represent expressions. Here&rsquo;s the list of nodes types we&rsquo;ll have:</p> <ul> <li><code>NInt</code> - represents an integer.</li> <li><code>NApp</code> - represents an application (has two children).</li> <li><code>NGlobal</code> - represents a global function (like the <code>f</code> in <code>f x</code>).</li> <li><code>NInd</code> - an &ldquo;indrection&rdquo; node that points to another node. This will help with &ldquo;replacing&rdquo; a node.</li> <li><code>NData</code> - a &ldquo;packed&rdquo; node that will represent a constructor with all the arguments.</li> </ul> <p>With these nodes in mind, let&rsquo;s try defining some instructions for the G-machine. We start with instructions we&rsquo;ll use to assemble new version of function body trees as we discussed above. First up is <strong>PushInt</strong>:</p> <div class="gmachine-instruction"> <div class="gmachine-instruction-name"> PushInt </div> <div class="gmachine-instruction-sem"> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Before </div> <div class="gmachine-inner-text"> \( \text{PushInt} \; n : i \quad s \quad d \quad h \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> After </div> <div class="gmachine-inner-text"> \( i \quad a : s \quad d \quad h[a : \text{NInt} \; n] \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Description </div> <div class="gmachine-inner-text"> Push an integer \(n\) onto the stack. </div> </div> </div> </div> <p>Let&rsquo;s go through this. We start with an instruction queue with <code>PushInt n</code> on top. We allocate a new <code>NInt</code> with the number <code>n</code> on the heap at address \(a\). We then push the address of the <code>NInt</code> node on top of the stack. Next, <strong>PushGlobal</strong>:</p> <div class="gmachine-instruction"> <div class="gmachine-instruction-name"> PushGlobal </div> <div class="gmachine-instruction-sem"> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Before </div> <div class="gmachine-inner-text"> \( \text{PushGlobal} \; f : i \quad s \quad d \quad h \quad m[f : a] \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> After </div> <div class="gmachine-inner-text"> \( i \quad a : s \quad d \quad h \quad m[f : a] \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Description </div> <div class="gmachine-inner-text"> Push a global function \(f\) onto the stack. </div> </div> </div> </div> <p>We don&rsquo;t allocate anything new on the heap for this one - we already have a node for the global function. Next up, <strong>Push</strong>:</p> <div class="gmachine-instruction"> <div class="gmachine-instruction-name"> Push </div> <div class="gmachine-instruction-sem"> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Before </div> <div class="gmachine-inner-text"> \( \text{Push} \; n : i \quad a_0, a_1, ..., a_n : s \quad d \quad h \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> After </div> <div class="gmachine-inner-text"> \( i \quad a_n, a_0, a_1, ..., a_n : s \quad d \quad h \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Description </div> <div class="gmachine-inner-text"> Push a value at offset \(n\) from the top of the stack onto the stack. </div> </div> </div> </div> <p>We define this instruction to work if and only if there exists an address on the stack at offset \(n\). We take the value at that offset, and push it onto the stack again. This can be helpful for something like <code>f x x</code>, where we use the same tree twice. Speaking of that - let&rsquo;s define an instruction to combine two nodes into an application:</p> <div class="gmachine-instruction"> <div class="gmachine-instruction-name"> MkApp </div> <div class="gmachine-instruction-sem"> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Before </div> <div class="gmachine-inner-text"> \( \text{MkApp} : i \quad a_0, a_1 : s \quad d \quad h \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> After </div> <div class="gmachine-inner-text"> \( i \quad a : s \quad d \quad h[ a : \text{NApp} \; a_0 \; a_1] \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Description </div> <div class="gmachine-inner-text"> Apply a function at the top of the stack to a value after it. </div> </div> </div> </div> <p>We pop two things off the stack: first, the thing we&rsquo;re applying, then the thing we apply it to. We then create a new node on the heap that is an <code>NApp</code> node, with its two children being the nodes we popped off. Finally, we push it onto the stack.</p> <p>Let&rsquo;s try use these instructions to get a feel for it. In order to conserve space, let&rsquo;s use \(\text{G}\) for PushGlobal, \(\text{I}\) for PushInt, and \(\text{A}\) for PushApp. Let&rsquo;s say we want to construct a graph for <code>double 326</code>. We&rsquo;ll use the instructions \(\text{I} \; 326\), \(\text{G} \; \text{double}\), and \(\text{A}\). Let&rsquo;s watch these instructions play out: $$ \begin{aligned} [\text{I} \; 326, \text{G} \; \text{double}, \text{A}] &amp;amp; \quad s \quad &amp;amp; d \quad &amp;amp; h \quad &amp;amp; m[\text{double} : a_d] \\ [\text{G} \; \text{double}, \text{A}] &amp;amp; \quad a_1 : s \quad &amp;amp; d \quad &amp;amp; h[a_1 : \text{NInt} \; 326] \quad &amp;amp; m[\text{double} : a_d] \\ [\text{A}] &amp;amp; \quad a_d, a_1 : s \quad &amp;amp; d \quad &amp;amp; h[a_1 : \text{NInt} \; 326] \quad &amp;amp; m[\text{double} : a_d] \\ [] &amp;amp; \quad a_2 : s \quad &amp;amp; d \quad &amp;amp; h[\substack{\begin{aligned}a_1 &amp;amp; : \text{NInt} \; 326 \\ a_2 &amp;amp; : \text{NApp} \; a_d \; a_1 \end{aligned}}] \quad &amp;amp; m[\text{double} : a_d] \\ \end{aligned} $$ How did we come up with these instructions? We&rsquo;ll answer this question with more generality later, but let&rsquo;s take a look at this particular expression right now. We know it&rsquo;s an application, so we&rsquo;ll be using MkApp eventually. We also know that MkApp expects two values on top of the stack from which to make an application. The node on top has to be the function, and the next node is the value to be passed into that function. Since a stack is first-in-last-out, for the function (<code>double</code>, in our case) to be on top, we need to push it last. Thus, we push <code>double</code> first, then 326. Finally, we call MkApp now that the stack is in the right state.</p> <p>Having defined instructions to <strong>build</strong> graphs, it&rsquo;s now time to move on to instructions to <strong>reduce</strong> graphs - after all, we&rsquo;re performing graph reduction. A crucial instruction for the G-machine is <strong>Unwind</strong>. What Unwind does depends on what nodes are on the stack. Its name comes from how it behaves when the top of the stack is an <code>NApp</code> node that is at the top of a potentially long chain of applications: given an application node, it pushes its left hand side onto the stack. It then <strong>continues to run Unwind</strong>. This is effectively a while loop: applications nodes continue to be expanded this way until the left hand side of an application is finally something that <strong>isn&rsquo;t</strong> an application. Let&rsquo;s write this rule as follows:</p> <div class="gmachine-instruction"> <div class="gmachine-instruction-name"> Unwind-App </div> <div class="gmachine-instruction-sem"> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Before </div> <div class="gmachine-inner-text"> \( \text{Unwind} : i \quad a : s \quad d \quad h[a : \text{NApp} \; a_0 \; a_1] \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> After </div> <div class="gmachine-inner-text"> \( \text{Unwind} : i \quad a_0, a : s \quad d \quad h[ a : \text{NApp} \; a_0 \; a_1] \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Description </div> <div class="gmachine-inner-text"> Unwind an application by pushing its left node. </div> </div> </div> </div> <p>Let&rsquo;s talk about what happens when Unwind hits a node that isn&rsquo;t an application. Of all nodes we have described, <code>NGlobal</code> seems to be the most likely to be on top of the stack after an application chain has finished unwinding. In this case we want to run the instructions for building the referenced global function. Naturally, these instructions may reference the arguments of the application. We can find the first argument by looking at offset 1 on the stack, which will be an <code>NApp</code> node, and then going to its right child. The same can be done for the second and third arguments, if they exist. But this doesn&rsquo;t feel right - we don&rsquo;t want to constantly be looking at the right child of a node on the stack. Instead, we replace each application node on the stack with its right child. Once that&rsquo;s done, we run the actual code for the global function:</p> <div class="gmachine-instruction"> <div class="gmachine-instruction-name"> Unwind-Global </div> <div class="gmachine-instruction-sem"> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Before </div> <div class="gmachine-inner-text"> \( \text{Unwind} : i \quad a, a_0, a_1, ..., a_{n-1} : s \quad d \quad h[\substack{a : \text{NGlobal} \; n \; c \\ a_k : \text{NApp} \; a_{k-1} \; a_k'}] \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> After </div> <div class="gmachine-inner-text"> \( c \quad a_0', a_1', ..., a_{n-1}', a_{n-1} : s \quad d \quad h[\substack{a : \text{NGlobal} \; n \; c \\ a_k : \text{NApp} \; a_{k-1} \; a_k'}] \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Description </div> <div class="gmachine-inner-text"> Call a global function. </div> </div> </div> </div> <p>In this rule, we used a general rule for \(a_k\), in which \(k\) is any number between 1 and \(n-1\). We also expect the <code>NGlobal</code> node to contain two parameters, \(n\) and \(c\). \(n\) is the arity of the function (the number of arguments it expects), and \(c\) are the instructions to construct the function&rsquo;s tree.</p> <p>The attentive reader will have noticed a catch: we kept \(a_{n-1}\) on the stack! This once again goes back to replacing a node in-place. \(a_{n-1}\) is the address of the &ldquo;root&rdquo; of the whole expression we&rsquo;re simplifying. Thus, to replace the value at this address, we need to keep the address until we have something to replace it with.</p> <p>There&rsquo;s one more thing that can be found at the leftmost end of a tree of applications: <code>NInd</code>. We simply replace <code>NInd</code> with the node it points to, and resume Unwind:</p> <div class="gmachine-instruction"> <div class="gmachine-instruction-name"> Unwind-Ind </div> <div class="gmachine-instruction-sem"> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Before </div> <div class="gmachine-inner-text"> \( \text{Unwind} : i \quad a : s \quad d \quad h[a : \text{NInd} \; a' ] \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> After </div> <div class="gmachine-inner-text"> \( \text{Unwind} : i \quad a' : s \quad d \quad h[a : \text{NInd} \; a' ] \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Description </div> <div class="gmachine-inner-text"> Replace indirection node with its target. </div> </div> </div> </div> <p>We&rsquo;ve talked about replacing a node, and we&rsquo;ve talked about indirection, but we haven&rsquo;t yet an instruction to perform these actions. Let&rsquo;s do so now:</p> <div class="gmachine-instruction"> <div class="gmachine-instruction-name"> Update </div> <div class="gmachine-instruction-sem"> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Before </div> <div class="gmachine-inner-text"> \( \text{Update} \; n : i \quad a,a_0,a_1,...a_n : s \quad d \quad h \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> After </div> <div class="gmachine-inner-text"> \( i \quad a_0,a_1,...,a_n : s \quad d \quad h[a_n : \text{NInd} \; a ] \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Description </div> <div class="gmachine-inner-text"> Transform node at offset into an indirection. </div> </div> </div> </div> <p>This instruction pops an address from the top of the stack, and replaces a node at the given offset with an indirection to the popped node. After we evaluate a function call, we will use <code>update</code> to make sure it&rsquo;s not evaluated again.</p> <p>Now, let&rsquo;s talk about data structures. We have mentioned an <code>NData</code> node, but we&rsquo;ve given no explanation of how it will work. Obviously, we need to distinguish values of a type created by different constructors: If we have a value of type <code>List</code>, it could have been created either using <code>Nil</code> or <code>Cons</code>. Depending on which constructor was used to create a value of a type, we might treat it differently. Furthermore, it&rsquo;s not always possible to know what constructor was used to create what value at compile time. So, we need a way to know, at runtime, how the value was constructed. We do this using a <strong>tag</strong>. A tag is an integer value that will be contained in the <code>NData</code> node. We assign a tag number to each constructor, and when we create a node with that constructor, we set the node&rsquo;s tag accordingly. This way, we can easily tell if a <code>List</code> value is a <code>Nil</code> or a <code>Cons</code>, or if a <code>Tree</code> value is a <code>Node</code> or a <code>Leaf</code>.</p> <p>To operate on <code>NData</code> nodes, we will need two primitive operations: <strong>Pack</strong> and <strong>Split</strong>. Pack will create an <code>NData</code> node with a tag from some number of nodes on the stack. These nodes will be placed into a dynamically allocated array:</p> <div class="gmachine-instruction"> <div class="gmachine-instruction-name"> Pack </div> <div class="gmachine-instruction-sem"> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Before </div> <div class="gmachine-inner-text"> \( \text{Pack} \; t \; n : i \quad a_1,a_2,...a_n : s \quad d \quad h \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> After </div> <div class="gmachine-inner-text"> \( i \quad a : s \quad d \quad h[a : \text{NData} \; t \; [a_1, a_2, ..., a_n] ] \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Description </div> <div class="gmachine-inner-text"> Pack \(n\) nodes from the stack into a node with tag \(t\). </div> </div> </div> </div> <p>Split will do the opposite, by popping of an <code>NData</code> node and moving the contents of its array onto the stack:</p> <div class="gmachine-instruction"> <div class="gmachine-instruction-name"> Split </div> <div class="gmachine-instruction-sem"> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Before </div> <div class="gmachine-inner-text"> \( \text{Split} : i \quad a : s \quad d \quad h[a : \text{NData} \; t \; [a_1, a_2, ..., a_n] ] \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> After </div> <div class="gmachine-inner-text"> \( i \quad a_1, a_2, ...,a_n : s \quad d \quad h[a : \text{NData} \; t \; [a_1, a_2, ..., a_n] ] \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Description </div> <div class="gmachine-inner-text"> Unpack a data node on top of the stack. </div> </div> </div> </div> <p>These two instructions are a good start, but we&rsquo;re missing something fairly big: case analysis. After we&rsquo;ve constructed a data type, to perform operations on it, we want to figure out what constructor and values which were used to create it. In order to implement patterns and case expressions, we&rsquo;ll need another instruction that&rsquo;s capable of making a decision based on the tag of an <code>NData</code> node. We&rsquo;ll call this instruction <strong>Jump</strong>, and define it to contain a mapping from tags to instructions to be executed for a value of that tag. For instance, if the constructor <code>Nil</code> has tag 0, and <code>Cons</code> has tag 1, the mapping for the case expression of a length function could be written as \([0 \rightarrow [\text{PushInt} \; 0], 1 \rightarrow [\text{PushGlobal} \; \text{length}, ...] ]\). Let&rsquo;s define the rule for it:</p> <div class="gmachine-instruction"> <div class="gmachine-instruction-name"> Jump </div> <div class="gmachine-instruction-sem"> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Before </div> <div class="gmachine-inner-text"> \( \text{Jump} [..., t \rightarrow i_t, ...] : i \quad a : s \quad d \quad h[a : \text{NData} \; t \; as ] \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> After </div> <div class="gmachine-inner-text"> \( i_t, i \quad a : s \quad d \quad h[a : \text{NData} \; t \; as ] \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Description </div> <div class="gmachine-inner-text"> Execute instructions corresponding to a tag. </div> </div> </div> </div> <p>Alright, we&rsquo;ve made it through the interesting instructions, but there&rsquo;s still a few that are needed, but less shiny and cool. For instance: imagine we&rsquo;ve made a function call. As per the rules for Unwind, we&rsquo;ve placed the right hand sides of all applications on the stack, and ran the instructions provided by the function, creating a final graph. We then continue to reduce this final graph. But we&rsquo;ve left the function parameters on the stack! This is untidy. We define a <strong>Slide</strong> instruction, which keeps the address at the top of the stack, but gets rid of the next \(n\) addresses:</p> <div class="gmachine-instruction"> <div class="gmachine-instruction-name"> Slide </div> <div class="gmachine-instruction-sem"> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Before </div> <div class="gmachine-inner-text"> \( \text{Slide} \; n : i \quad a_0, a_1, ..., a_n : s \quad d \quad h \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> After </div> <div class="gmachine-inner-text"> \( i \quad a_0 : s \quad d \quad h \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Description </div> <div class="gmachine-inner-text"> Remove \(n\) addresses after the top from the stack. </div> </div> </div> </div> <p>Just a few more. Next up, we observe that we have not defined any way for our G-machine to perform arithmetic, or indeed, any primitive operations. Since we&rsquo;ve not defined any built-in type for booleans, let&rsquo;s avoid talking about operations like <code>&lt;</code>, <code>==</code>, and so on (in fact, we&rsquo;ve omitted them from the grammar so far). So instead, let&rsquo;s talk about the <a href="https://en.wikipedia.org/wiki/Closure_%28mathematics%29"class="external-link">closed<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> operations, namely <code>+</code>, <code>-</code>, <code>*</code>, and <code>/</code>. We&rsquo;ll define a special instruction for them, called <strong>BinOp</strong>:</p> <div class="gmachine-instruction"> <div class="gmachine-instruction-name"> BinOp </div> <div class="gmachine-instruction-sem"> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Before </div> <div class="gmachine-inner-text"> \( \text{BinOp} \; \text{op} : i \quad a_0, a_1 : s \quad d \quad h[\substack{a_0 : \text{NInt} \; n \\ a_1 : \text{NInt} \; m}] \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> After </div> <div class="gmachine-inner-text"> \( i \quad a : s \quad d \quad h[\substack{a_0 : \text{NInt} \; n \\ a_1 : \text{NInt} \; m \\ a : \text{NInt} \; (\text{op} \; n \; m)}] \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Description </div> <div class="gmachine-inner-text"> Apply a binary operator on integers. </div> </div> </div> </div> <p>Nothing should be particularly surprising here: the instruction pops two integers off the stack, applies the given binary operation to them, and places the result on the stack.</p> <p>We&rsquo;re not yet done with primitive operations, though. We have a lazy graph reduction machine, which means something like the expression <code>3*(2+6)</code> might not be a binary operator applied to two <code>NInt</code> nodes. We keep around graphs until they <strong>really</strong> need to be reduced. So now we need an instruction to trigger reducing a graph, to say, &ldquo;we need this value now&rdquo;. We call this instruction <strong>Eval</strong>. This is where the dump finally comes in!</p> <p>When we execute Eval, another graph becomes our &ldquo;focus&rdquo;, and we switch to a new stack. We obviously want to return from this once we&rsquo;ve finished evaluating what we &ldquo;focused&rdquo; on, so we must store the program state somewhere - on the dump. Here&rsquo;s the rule:</p> <div class="gmachine-instruction"> <div class="gmachine-instruction-name"> Eval </div> <div class="gmachine-instruction-sem"> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Before </div> <div class="gmachine-inner-text"> \( \text{Eval} : i \quad a : s \quad d \quad h \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> After </div> <div class="gmachine-inner-text"> \( [\text{Unwind}] \quad [a] \quad \langle i, s\rangle : d \quad h \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Description </div> <div class="gmachine-inner-text"> Evaluate graph to its normal form. </div> </div> </div> </div> <p>We store the current set of instructions and the current stack on the dump, and start with only Unwind and the value we want to evaluate. That does the job, but we&rsquo;re missing one thing - a way to return to the state we placed onto the dump. To do this, we add <strong>another</strong> rule to Unwind:</p> <div class="gmachine-instruction"> <div class="gmachine-instruction-name"> Unwind-Return </div> <div class="gmachine-instruction-sem"> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Before </div> <div class="gmachine-inner-text"> \( \text{Unwind} : i \quad a : s \quad \langle i', s'\rangle : d \quad h[a : \text{NInt} \; n] \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> After </div> <div class="gmachine-inner-text"> \( i' \quad a : s' \quad d \quad h[a : \text{NInt} \; n] \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Description </div> <div class="gmachine-inner-text"> Return from Eval instruction. </div> </div> </div> </div> <p>Just a couple more special-purpose instructions, and we&rsquo;re done!</p> <p>Sometimes, it&rsquo;s possible for a tree node to reference itself. For instance, Haskell defines the <a href="https://en.wikipedia.org/wiki/Fixed-point_combinator"class="external-link">fixpoint combinator<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> as follows:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Haskell" data-lang="Haskell"><span class="line"><span class="cl"><span class="nf">fix</span> <span class="n">f</span> <span class="ow">=</span> <span class="kr">let</span> <span class="n">x</span> <span class="ow">=</span> <span class="n">f</span> <span class="n">x</span> <span class="kr">in</span> <span class="n">x</span> </span></span></code></pre></div><p>In order to do this, an address that references a node must be present while the node is being constructed. We define an instruction, <strong>Alloc</strong>, which helps with that:</p> <div class="gmachine-instruction"> <div class="gmachine-instruction-name"> Alloc </div> <div class="gmachine-instruction-sem"> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Before </div> <div class="gmachine-inner-text"> \( \text{Alloc} \; n : i \quad s \quad d \quad h \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> After </div> <div class="gmachine-inner-text"> \( i \quad s \quad d \quad h[a_k : \text{NInd} \; \text{null}] \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Description </div> <div class="gmachine-inner-text"> Allocate indirection nodes. </div> </div> </div> </div> <p>We can allocate an indirection on the stack, and call Update on it when we&rsquo;ve constructed a node. While we&rsquo;re constructing the tree, we can refer to the indirection when a self-reference is required.</p> <p>Lastly, we also define a Pop instruction, which just removes some number of nodes from the stack. We want this because calling Update at the end of a function modifies a node further up the stack, leaving anything on top of the stack after that node as scratch work. We get rid of that scratch work simply by popping it.</p> <div class="gmachine-instruction"> <div class="gmachine-instruction-name"> Pop </div> <div class="gmachine-instruction-sem"> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Before </div> <div class="gmachine-inner-text"> \( \text{Pop} \; n : i \quad a_1, a_2, ..., a_n : s \quad d \quad h \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> After </div> <div class="gmachine-inner-text"> \( i \quad s \quad d \quad h \quad m \) </div> </div> <div class="gmachine-inner"> <div class="gmachine-inner-label"> Description </div> <div class="gmachine-inner-text"> Pop \(n\) nodes from the stack. </div> </div> </div> </div> <p>That&rsquo;s it for the instructions. Knowing them, however, doesn&rsquo;t tell us what to do with our <code>ast</code> structs. We&rsquo;ll need to define rules to translate trees into these instructions, and I&rsquo;ve already alluded to this when we went over <code>double 326</code>. However, this has already gotten pretty long, so we&rsquo;ll do it in the next post: <a href="https://danilafe.com/blog/06_compiler_compilation/">Part 6 - Compilation</a>.</p> Compiling a Functional Language Using C++, Part 6 - Compilation https://danilafe.com/blog/06_compiler_compilation/ Tue, 06 Aug 2019 14:26:38 -0700 https://danilafe.com/blog/06_compiler_compilation/ <p>In the previous post, we defined a machine for graph reduction, called a G-machine. However, this machine is still not particularly connected to <strong>our</strong> language. In this post, we will give meanings to programs in our language in the context of this G-machine. We will define a <strong>compilation scheme</strong>, which will be a set of rules that tell us how to translate programs in our language into G-machine instructions. To mirror <em>Implementing Functional Languages: a tutorial</em>, we&rsquo;ll call this compilation scheme \(\mathcal{C}\), and write it as \(\mathcal{C} ⟦e⟧ = i\), meaning &ldquo;the expression \(e\) compiles to the instructions \(i\)&rdquo;.</p> <p>To follow our route from the typechecking, let&rsquo;s start with compiling expressions that are numbers. It&rsquo;s pretty easy:</p> $$ \mathcal{C} ⟦n⟧ = [\text{PushInt} \; n] $$ <p>Here, we compiled a number expression to a list of instructions with only one element - PushInt.</p> <p>Just like when we did typechecking, let&rsquo;s move on to compiling function applications. As we informally stated in the previous chapter, since the thing we&rsquo;re applying has to be on top, we want to compile it last:</p> $$ \mathcal{C} ⟦e_1 \; e_2⟧ = \mathcal{C} ⟦e_2⟧ ⧺ \mathcal{C} ⟦e_1⟧ ⧺ [\text{MkApp}] $$ <p>Here, we used the \(⧺\) operator to represent the concatenation of two lists. Otherwise, this should be pretty intutive - we first run the instructions to create the parameter, then we run the instructions to create the function, and finally, we combine them using MkApp.</p> <p>It&rsquo;s variables that once again force us to adjust our strategy. If our program is well-typed, we know our variable will be on the stack: our definition of Unwind makes it so for functions, and we will define our case expression compilation scheme to match. However, we still need to know <strong>where</strong> on the stack each variable is, and this changes as the stack is modified.</p> <p>To accommodate for this, we define an environment, \(\rho\), to be a partial function mapping variable names to thier offsets on the stack. We write \(\rho = [x \rightarrow n, y \rightarrow m]\) to say &ldquo;the environment \(\rho\) maps variable \(x\) to stack offset \(n\), and variable \(y\) to stack offset \(m\)&rdquo;. We also write \(\rho \; x\) to say &ldquo;look up \(x\) in \(\rho\)&rdquo;, since \(\rho\) is a function. Finally, to help with the ever-changing stack, we define an augmented environment \(\rho^{+n}\), such that \(\rho^{+n} \; x = \rho \; x + n\). In words, this basically means &ldquo;\(\rho^{+n}\) has all the variables from \(\rho\), but their addresses are incremented by \(n\)&rdquo;. We now pass \(\rho\) in to \(\mathcal{C}\) together with the expression \(e\). Let&rsquo;s rewrite our first two rules. For numbers:</p> $$ \mathcal{C} ⟦n⟧ \; \rho = [\text{PushInt} \; n] $$ <p>For function application:</p> $$ \mathcal{C} ⟦e_1 \; e_2⟧ \; \rho = \mathcal{C} ⟦e_2⟧ \; \rho \; ⧺ \;\mathcal{C} ⟦e_1⟧ \; \rho^{&#43;1} \; ⧺ \; [\text{MkApp}] $$ <p>Notice how in that last rule, we passed in \(\rho^{+1}\) when compiling the function&rsquo;s expression. This is because the result of running the instructions for \(e_2\) will have left on the stack the function&rsquo;s parameter. Whatever was at the top of the stack (and thus, had index 0), is now the second element from the top (address 1). The same is true for all other things that were on the stack. So, we increment the environment accordingly.</p> <p>With the environment, the variable rule is simple:</p> $$ \mathcal{C} ⟦x⟧ \; \rho = [\text{Push} \; (\rho \; x)] $$ <p>One more thing. If we run across a function name, we want to use PushGlobal rather than Push. Defining \(f\) to be a name of a global function, we capture this using the following rule:</p> $$ \mathcal{C} ⟦f⟧ \; \rho = [\text{PushGlobal} \; f] $$ <p>Now it&rsquo;s time for us to compile case expressions, but there&rsquo;s a bit of an issue - our case expressions branches don&rsquo;t map one-to-one with the \(t \rightarrow i_t\) format of the Jump instruction. This is because we allow for name patterns in the form \(x\), which can possibly match more than one tag. Consider this rather useless example:</p> <pre tabindex="0"><code>data Bool = { True, False } defn weird b = { case b of { b -&gt; { False } } } </code></pre><p>We only have one branch, but we have two tags that should lead to it! Not only that, but variable patterns are location-dependent: if a variable pattern comes before a constructor pattern, then the constructor pattern will never be reached. On the other hand, if a constructor pattern comes before a variable pattern, it will be tried before the varible pattern, and thus is reachable.</p> <p>We will ignore this problem for now - we will define our semantics as though each case expression branch can match exactly one tag. In our C++ code, we will write a conversion function that will figure out which tag goes to which sequence of instructions. Effectively, we&rsquo;ll be performing <a href="https://en.wikipedia.org/wiki/Syntactic_sugar"class="external-link">desugaring<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>.</p> <p>Now, on to defining the compilation rules for case expressions. It&rsquo;s helpful to define compiling a single branch of a case expression separately. For a branch in the form \(t \; x_1 \; x_2 \; ... \; x_n \rightarrow \text{body}\), we define a compilation scheme \(\mathcal{A}\) as follows:</p> $$ \begin{aligned} \mathcal{A} ⟦t \; x_1 \; ... \; x_n \rightarrow \text{body}⟧ \; \rho &amp;amp; = t \rightarrow [\text{Split} \; n] \; ⧺ \; \mathcal{C}⟦\text{body}⟧ \; \rho&amp;#39; \; ⧺ \; [\text{Slide} \; n] \\ \text{where} \; \rho&amp;#39; &amp;amp;= \rho^{&#43;n}[x_1 \rightarrow 0, ..., x_n \rightarrow n - 1] \end{aligned} $$ <p>First, we run Split - the node on the top of the stack is a packed constructor, and we want access to its member variables, since they can be referenced by the branch&rsquo;s body via \(x_i\). For the same reason, we must make sure to include \(x_1\) through \(x_n\) in our environment. Furthermore, since the split values now occupy the stack, we have to offset our environment by \(n\) before adding bindings to our new variables. Doing all these things gives us \(\rho'\), which we use to compile the body, placing the resulting instructions after Split. This leaves us with the desired graph on top of the stack - the only thing left to do is to clean up the stack of the unpacked values, which we do using Slide.</p> <p>Notice that we didn&rsquo;t just create instructions - we created a mapping from the tag \(t\) to the instructions that correspond to it.</p> <p>Now, it&rsquo;s time for compiling the whole case expression. We first want to construct the graph for the expression we want to perform case analysis on. Next, we want to evaluate it (since we need a packed value, not a graph, to read the tag). Finally, we perform a jump depending on the tag. This is captured by the following rule:</p> $$ \mathcal{C} ⟦\text{case} \; e \; \text{of} \; \text{alt}_1 ... \text{alt}_n⟧ \; \rho = \mathcal{C} ⟦e⟧ \; \rho \; ⧺ [\text{Eval}, \text{Jump} \; [\mathcal{A} ⟦\text{alt}_1⟧ \; \rho, ..., \mathcal{A} ⟦\text{alt}_n⟧ \; \rho]] $$ <p>This works because \(\mathcal{A}\) creates not only instructions, but also a tag mapping. We simply populate our Jump instruction such mappings resulting from compiling each branch.</p> <p>You may have noticed that we didn&rsquo;t add rules for binary operators. Just like with type checking, we treat them as function calls. However, rather that constructing graphs when we have to instantiate those functions, we simply evaluate the arguments and perform the relevant arithmetic operation using BinOp. We will do a similar thing for constructors.</p> <a href="#implementation"> <h3 id="implementation">Implementation</h3> </a> <p>With that out of the way, we can get around to writing some code. Let&rsquo;s first define C++ structs for the instructions of the G-machine:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/06/instruction.hpp"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/06/instruction.hpp">instruction.hpp</a>, entire file</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt"> 10 </span><span class="lnt"> 11 </span><span class="lnt"> 12 </span><span class="lnt"> 13 </span><span class="lnt"> 14 </span><span class="lnt"> 15 </span><span class="lnt"> 16 </span><span class="lnt"> 17 </span><span class="lnt"> 18 </span><span class="lnt"> 19 </span><span class="lnt"> 20 </span><span class="lnt"> 21 </span><span class="lnt"> 22 </span><span class="lnt"> 23 </span><span class="lnt"> 24 </span><span class="lnt"> 25 </span><span class="lnt"> 26 </span><span class="lnt"> 27 </span><span class="lnt"> 28 </span><span class="lnt"> 29 </span><span class="lnt"> 30 </span><span class="lnt"> 31 </span><span class="lnt"> 32 </span><span class="lnt"> 33 </span><span class="lnt"> 34 </span><span class="lnt"> 35 </span><span class="lnt"> 36 </span><span class="lnt"> 37 </span><span class="lnt"> 38 </span><span class="lnt"> 39 </span><span class="lnt"> 40 </span><span class="lnt"> 41 </span><span class="lnt"> 42 </span><span class="lnt"> 43 </span><span class="lnt"> 44 </span><span class="lnt"> 45 </span><span class="lnt"> 46 </span><span class="lnt"> 47 </span><span class="lnt"> 48 </span><span class="lnt"> 49 </span><span class="lnt"> 50 </span><span class="lnt"> 51 </span><span class="lnt"> 52 </span><span class="lnt"> 53 </span><span class="lnt"> 54 </span><span class="lnt"> 55 </span><span class="lnt"> 56 </span><span class="lnt"> 57 </span><span class="lnt"> 58 </span><span class="lnt"> 59 </span><span class="lnt"> 60 </span><span class="lnt"> 61 </span><span class="lnt"> 62 </span><span class="lnt"> 63 </span><span class="lnt"> 64 </span><span class="lnt"> 65 </span><span class="lnt"> 66 </span><span class="lnt"> 67 </span><span class="lnt"> 68 </span><span class="lnt"> 69 </span><span class="lnt"> 70 </span><span class="lnt"> 71 </span><span class="lnt"> 72 </span><span class="lnt"> 73 </span><span class="lnt"> 74 </span><span class="lnt"> 75 </span><span class="lnt"> 76 </span><span class="lnt"> 77 </span><span class="lnt"> 78 </span><span class="lnt"> 79 </span><span class="lnt"> 80 </span><span class="lnt"> 81 </span><span class="lnt"> 82 </span><span class="lnt"> 83 </span><span class="lnt"> 84 </span><span class="lnt"> 85 </span><span class="lnt"> 86 </span><span class="lnt"> 87 </span><span class="lnt"> 88 </span><span class="lnt"> 89 </span><span class="lnt"> 90 </span><span class="lnt"> 91 </span><span class="lnt"> 92 </span><span class="lnt"> 93 </span><span class="lnt"> 94 </span><span class="lnt"> 95 </span><span class="lnt"> 96 </span><span class="lnt"> 97 </span><span class="lnt"> 98 </span><span class="lnt"> 99 </span><span class="lnt">100 </span><span class="lnt">101 </span><span class="lnt">102 </span><span class="lnt">103 </span><span class="lnt">104 </span><span class="lnt">105 </span><span class="lnt">106 </span><span class="lnt">107 </span><span class="lnt">108 </span><span class="lnt">109 </span><span class="lnt">110 </span><span class="lnt">111 </span><span class="lnt">112 </span><span class="lnt">113 </span><span class="lnt">114 </span><span class="lnt">115 </span><span class="lnt">116 </span><span class="lnt">117 </span><span class="lnt">118 </span><span class="lnt">119 </span><span class="lnt">120 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="cp">#pragma once </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;string&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;memory&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;vector&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;map&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;ostream&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;binop.hpp&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">instruction</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">virtual</span> <span class="o">~</span><span class="n">instruction</span><span class="p">()</span> <span class="o">=</span> <span class="k">default</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">virtual</span> <span class="kt">void</span> <span class="nf">print</span><span class="p">(</span><span class="kt">int</span> <span class="n">indent</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="n">instruction_ptr</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">unique_ptr</span><span class="o">&lt;</span><span class="n">instruction</span><span class="o">&gt;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">instruction_pushint</span> <span class="o">:</span> <span class="k">public</span> <span class="n">instruction</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">value</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">instruction_pushint</span><span class="p">(</span><span class="kt">int</span> <span class="n">v</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">value</span><span class="p">(</span><span class="n">v</span><span class="p">)</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">print</span><span class="p">(</span><span class="kt">int</span> <span class="n">indent</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">instruction_pushglobal</span> <span class="o">:</span> <span class="k">public</span> <span class="n">instruction</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">instruction_pushglobal</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">n</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">name</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">n</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">print</span><span class="p">(</span><span class="kt">int</span> <span class="n">indent</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">instruction_push</span> <span class="o">:</span> <span class="k">public</span> <span class="n">instruction</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">offset</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">instruction_push</span><span class="p">(</span><span class="kt">int</span> <span class="n">o</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">offset</span><span class="p">(</span><span class="n">o</span><span class="p">)</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">print</span><span class="p">(</span><span class="kt">int</span> <span class="n">indent</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">instruction_pop</span> <span class="o">:</span> <span class="k">public</span> <span class="n">instruction</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">count</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">instruction_pop</span><span class="p">(</span><span class="kt">int</span> <span class="n">c</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">count</span><span class="p">(</span><span class="n">c</span><span class="p">)</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">print</span><span class="p">(</span><span class="kt">int</span> <span class="n">indent</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">instruction_mkapp</span> <span class="o">:</span> <span class="k">public</span> <span class="n">instruction</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">print</span><span class="p">(</span><span class="kt">int</span> <span class="n">indent</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">instruction_update</span> <span class="o">:</span> <span class="k">public</span> <span class="n">instruction</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">offset</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">instruction_update</span><span class="p">(</span><span class="kt">int</span> <span class="n">o</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">offset</span><span class="p">(</span><span class="n">o</span><span class="p">)</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">print</span><span class="p">(</span><span class="kt">int</span> <span class="n">indent</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">instruction_pack</span> <span class="o">:</span> <span class="k">public</span> <span class="n">instruction</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">tag</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">size</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">instruction_pack</span><span class="p">(</span><span class="kt">int</span> <span class="n">t</span><span class="p">,</span> <span class="kt">int</span> <span class="n">s</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">tag</span><span class="p">(</span><span class="n">t</span><span class="p">),</span> <span class="n">size</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">print</span><span class="p">(</span><span class="kt">int</span> <span class="n">indent</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">instruction_split</span> <span class="o">:</span> <span class="k">public</span> <span class="n">instruction</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">print</span><span class="p">(</span><span class="kt">int</span> <span class="n">indent</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">instruction_jump</span> <span class="o">:</span> <span class="k">public</span> <span class="n">instruction</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">instruction_ptr</span><span class="o">&gt;&gt;</span> <span class="n">branches</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span> <span class="kt">int</span><span class="o">&gt;</span> <span class="n">tag_mappings</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">print</span><span class="p">(</span><span class="kt">int</span> <span class="n">indent</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">instruction_slide</span> <span class="o">:</span> <span class="k">public</span> <span class="n">instruction</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">offset</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">instruction_slide</span><span class="p">(</span><span class="kt">int</span> <span class="n">o</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">offset</span><span class="p">(</span><span class="n">o</span><span class="p">)</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">print</span><span class="p">(</span><span class="kt">int</span> <span class="n">indent</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">instruction_binop</span> <span class="o">:</span> <span class="k">public</span> <span class="n">instruction</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">binop</span> <span class="n">op</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">instruction_binop</span><span class="p">(</span><span class="n">binop</span> <span class="n">o</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">op</span><span class="p">(</span><span class="n">o</span><span class="p">)</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">print</span><span class="p">(</span><span class="kt">int</span> <span class="n">indent</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">instruction_eval</span> <span class="o">:</span> <span class="k">public</span> <span class="n">instruction</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">print</span><span class="p">(</span><span class="kt">int</span> <span class="n">indent</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">instruction_alloc</span> <span class="o">:</span> <span class="k">public</span> <span class="n">instruction</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">amount</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">instruction_alloc</span><span class="p">(</span><span class="kt">int</span> <span class="n">a</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">amount</span><span class="p">(</span><span class="n">a</span><span class="p">)</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">print</span><span class="p">(</span><span class="kt">int</span> <span class="n">indent</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">instruction_unwind</span> <span class="o">:</span> <span class="k">public</span> <span class="n">instruction</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="nf">print</span><span class="p">(</span><span class="kt">int</span> <span class="n">indent</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ostream</span><span class="o">&amp;</span> <span class="n">to</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span></code></pre></td></tr></table> </div> </div> </div> <p>I omit the implementation of the various (trivial) <code>print</code> methods in this post; as always, you can look at the full project source code, which is freely available for each post in the series.</p> <p>We can now envision a method on the <code>ast</code> struct that takes an environment (just like our compilation scheme takes the environment \(\rho\)), and compiles the <code>ast</code>. Rather than returning a vector of instructions (which involves copying, unless we get some optimization kicking in), we&rsquo;ll pass a reference to a vector to our method. The method will then place the generated instructions into the vector.</p> <p>There&rsquo;s one more thing to be considered. How do we tell apart a &ldquo;global&rdquo; from a variable? A naive solution would be to take a list or map of global functions as a third parameter to our <code>compile</code> method. But there&rsquo;s an easier way! We know that the program passed type checking. This means that every referenced variable exists. From then, the situation is easy - if actual variable names are kept in the environment, \(\rho\), then whenever we see a variable that <strong>isn&rsquo;t</strong> in the current environment, it must be a function name.</p> <p>Having finished contemplating out method, it&rsquo;s time to define a signature:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">virtual</span> <span class="kt">void</span> <span class="nf">compile</span><span class="p">(</span><span class="k">const</span> <span class="n">env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">instruction_ptr</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span></code></pre></div><p>Ah, but now we have to define &ldquo;environment&rdquo;. Let&rsquo;s do that. Here&rsquo;s our header:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/06/env.hpp"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/06/env.hpp">env.hpp</a>, entire file</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="cp">#pragma once </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;memory&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;string&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">env</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">virtual</span> <span class="o">~</span><span class="n">env</span><span class="p">()</span> <span class="o">=</span> <span class="k">default</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">virtual</span> <span class="kt">int</span> <span class="nf">get_offset</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">)</span> <span class="k">const</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">virtual</span> <span class="kt">bool</span> <span class="nf">has_variable</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">)</span> <span class="k">const</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="n">env_ptr</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">shared_ptr</span><span class="o">&lt;</span><span class="n">env</span><span class="o">&gt;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">env_var</span> <span class="o">:</span> <span class="k">public</span> <span class="n">env</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">env_ptr</span> <span class="n">parent</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">env_var</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">n</span><span class="p">,</span> <span class="n">env_ptr</span> <span class="n">p</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">name</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">n</span><span class="p">)),</span> <span class="n">parent</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">p</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="nf">get_offset</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">bool</span> <span class="nf">has_variable</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">env_offset</span> <span class="o">:</span> <span class="k">public</span> <span class="n">env</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">offset</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">env_ptr</span> <span class="n">parent</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">env_offset</span><span class="p">(</span><span class="kt">int</span> <span class="n">o</span><span class="p">,</span> <span class="n">env_ptr</span> <span class="n">p</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">offset</span><span class="p">(</span><span class="n">o</span><span class="p">),</span> <span class="n">parent</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">p</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="nf">get_offset</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">bool</span> <span class="nf">has_variable</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span></code></pre></td></tr></table> </div> </div> </div> <p>And here&rsquo;s the source file:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/06/env.cpp"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/06/env.cpp">env.cpp</a>, entire file</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;env.hpp&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">env_var</span><span class="o">::</span><span class="n">get_offset</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">name</span> <span class="o">==</span> <span class="k">this</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">)</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">parent</span><span class="p">)</span> <span class="k">return</span> <span class="n">parent</span><span class="o">-&gt;</span><span class="n">get_offset</span><span class="p">(</span><span class="n">name</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">bool</span> <span class="n">env_var</span><span class="o">::</span><span class="n">has_variable</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">name</span> <span class="o">==</span> <span class="k">this</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">)</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">parent</span><span class="p">)</span> <span class="k">return</span> <span class="n">parent</span><span class="o">-&gt;</span><span class="n">has_variable</span><span class="p">(</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">false</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="n">env_offset</span><span class="o">::</span><span class="n">get_offset</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">parent</span><span class="p">)</span> <span class="k">return</span> <span class="n">parent</span><span class="o">-&gt;</span><span class="n">get_offset</span><span class="p">(</span><span class="n">name</span><span class="p">)</span> <span class="o">+</span> <span class="n">offset</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">bool</span> <span class="n">env_offset</span><span class="o">::</span><span class="n">has_variable</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">name</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">parent</span><span class="p">)</span> <span class="k">return</span> <span class="n">parent</span><span class="o">-&gt;</span><span class="n">has_variable</span><span class="p">(</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">false</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div> </div> <p>There&rsquo;s not that much to see here, but let&rsquo;s go through it anyway. We define an environment as a linked list, kind of like we did with the type environment. This time, though, we use shared pointers instead of raw pointers to reference the parent. I decided on this because we will need to be using virtual methods (since we have two subclasses of <code>env</code>), and thus will need to be passing the <code>env</code> by pointer. At that point, we might as well use the &ldquo;proper&rdquo; way!</p> <p>I implemented the environment as a linked list because it is, in essence, a stack. However, not every &ldquo;offset&rdquo; in a stack is introduced by binding variables - for instance, when we create an application node, we first build the argument value on the stack, and then, with that value still on the stack, build the left hand side of the application. Thus, all the variable positions are offset by the presence of the argument on the stack, and we must account for that. Similarly, in cases when we will allocate space on the stack (we will run into these cases later), we will need to account for that change. Thus, since we can increment the offset by two ways (binding a variable and building something on the stack), we allow for two types of nodes in our <code>env</code> stack.</p> <p>During recursion we will be tweaking the return value of <code>get_offset</code> to calculate the final location of a variable on the stack (if the parent of a node returned offset <code>1</code>, but the node itself is a variable node and thus introduces another offset, we need to return <code>2</code>). Because of this, we cannot reasonably return a constant like <code>-1</code> (it will quickly be made positive on a long list), and thus we throw an exception. To allow for a safe way to check for an offset, without try-catch, we also add a <code>has_variable</code> method which checks if the lookup will succeed. A better approach would be to use <code>std::optional</code>, but it&rsquo;s C++17, so we&rsquo;ll shy away from it.</p> <p>It will also help to move some of the functions on the <code>binop</code> enum into a separate file. The new neader is pretty small:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/06/binop.hpp"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/06/binop.hpp">binop.hpp</a>, entire file</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="cp">#pragma once </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;string&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="k">enum</span> <span class="nc">binop</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">PLUS</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">MINUS</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">TIMES</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">DIVIDE</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">op_name</span><span class="p">(</span><span class="n">binop</span> <span class="n">op</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">op_action</span><span class="p">(</span><span class="n">binop</span> <span class="n">op</span><span class="p">);</span> </span></span></code></pre></td></tr></table> </div> </div> </div> <p>The new source file is not much longer:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/06/binop.cpp"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/06/binop.cpp">binop.cpp</a>, entire file</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;binop.hpp&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">op_name</span><span class="p">(</span><span class="n">binop</span> <span class="n">op</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">switch</span><span class="p">(</span><span class="n">op</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nl">PLUS</span><span class="p">:</span> <span class="k">return</span> <span class="s">&#34;+&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nl">MINUS</span><span class="p">:</span> <span class="k">return</span> <span class="s">&#34;-&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nl">TIMES</span><span class="p">:</span> <span class="k">return</span> <span class="s">&#34;*&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nl">DIVIDE</span><span class="p">:</span> <span class="k">return</span> <span class="s">&#34;/&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="s">&#34;??&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">op_action</span><span class="p">(</span><span class="n">binop</span> <span class="n">op</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">switch</span><span class="p">(</span><span class="n">op</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nl">PLUS</span><span class="p">:</span> <span class="k">return</span> <span class="s">&#34;plus&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nl">MINUS</span><span class="p">:</span> <span class="k">return</span> <span class="s">&#34;minus&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nl">TIMES</span><span class="p">:</span> <span class="k">return</span> <span class="s">&#34;times&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nl">DIVIDE</span><span class="p">:</span> <span class="k">return</span> <span class="s">&#34;divide&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="s">&#34;??&#34;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div> </div> <p>And now, we begin our implementation. Let&rsquo;s start with the easy ones: <code>ast_int</code>, <code>ast_lid</code> and <code>ast_uid</code>. The code for <code>ast_int</code> involves just pushing the integer into the stack:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/06/ast.cpp" data-first-line="36" data-last-line="38"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/06/ast.cpp#L36-L38">ast.cpp</a>, lines 36 through 38</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast_int</span><span class="o">::</span><span class="n">compile</span><span class="p">(</span><span class="k">const</span> <span class="n">env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">instruction_ptr</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">into</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_pushint</span><span class="p">(</span><span class="n">value</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The code for <code>ast_lid</code> needs to check if the variable is global or local, just like we discussed:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/06/ast.cpp" data-first-line="53" data-last-line="58"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/06/ast.cpp#L53-L58">ast.cpp</a>, lines 53 through 58</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast_lid</span><span class="o">::</span><span class="n">compile</span><span class="p">(</span><span class="k">const</span> <span class="n">env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">instruction_ptr</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">into</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="n">env</span><span class="o">-&gt;</span><span class="n">has_variable</span><span class="p">(</span><span class="n">id</span><span class="p">)</span> <span class="o">?</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">instruction</span><span class="o">*</span><span class="p">)</span> <span class="k">new</span> <span class="n">instruction_push</span><span class="p">(</span><span class="n">env</span><span class="o">-&gt;</span><span class="n">get_offset</span><span class="p">(</span><span class="n">id</span><span class="p">))</span> <span class="o">:</span> </span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">instruction</span><span class="o">*</span><span class="p">)</span> <span class="k">new</span> <span class="n">instruction_pushglobal</span><span class="p">(</span><span class="n">id</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We do not have to do this for <code>ast_uid</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/06/ast.cpp" data-first-line="73" data-last-line="75"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/06/ast.cpp#L73-L75">ast.cpp</a>, lines 73 through 75</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">73 </span><span class="lnt">74 </span><span class="lnt">75 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast_uid</span><span class="o">::</span><span class="n">compile</span><span class="p">(</span><span class="k">const</span> <span class="n">env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">instruction_ptr</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">into</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_pushglobal</span><span class="p">(</span><span class="n">id</span><span class="p">)));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>On to <code>ast_binop</code>! This is the first time we have to change our environment. As we said earlier, once we build the right operand on the stack, every offset that we counted from the top of the stack will have been shifted by 1 (we see this in our compilation scheme for function application). So, we create a new environment with <code>env_offset</code>, and use that when we compile the left child:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/06/ast.cpp" data-first-line="103" data-last-line="110"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/06/ast.cpp#L103-L110">ast.cpp</a>, lines 103 through 110</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">103 </span><span class="lnt">104 </span><span class="lnt">105 </span><span class="lnt">106 </span><span class="lnt">107 </span><span class="lnt">108 </span><span class="lnt">109 </span><span class="lnt">110 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast_binop</span><span class="o">::</span><span class="n">compile</span><span class="p">(</span><span class="k">const</span> <span class="n">env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">instruction_ptr</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">right</span><span class="o">-&gt;</span><span class="n">compile</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">left</span><span class="o">-&gt;</span><span class="n">compile</span><span class="p">(</span><span class="n">env_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">env_offset</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">env</span><span class="p">)),</span> <span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">into</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_pushglobal</span><span class="p">(</span><span class="n">op_action</span><span class="p">(</span><span class="n">op</span><span class="p">))));</span> </span></span><span class="line"><span class="cl"> <span class="n">into</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_mkapp</span><span class="p">()));</span> </span></span><span class="line"><span class="cl"> <span class="n">into</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_mkapp</span><span class="p">()));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p><code>ast_binop</code> performs two applications: <code>(+) lhs rhs</code>. We push <code>rhs</code>, then <code>lhs</code>, then <code>(+)</code>, and then use MkApp twice. In <code>ast_app</code>, we only need to perform one application, <code>lhs rhs</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/06/ast.cpp" data-first-line="134" data-last-line="138"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/06/ast.cpp#L134-L138">ast.cpp</a>, lines 134 through 138</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">134 </span><span class="lnt">135 </span><span class="lnt">136 </span><span class="lnt">137 </span><span class="lnt">138 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast_app</span><span class="o">::</span><span class="n">compile</span><span class="p">(</span><span class="k">const</span> <span class="n">env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">instruction_ptr</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">right</span><span class="o">-&gt;</span><span class="n">compile</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">left</span><span class="o">-&gt;</span><span class="n">compile</span><span class="p">(</span><span class="n">env_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">env_offset</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">env</span><span class="p">)),</span> <span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">into</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_mkapp</span><span class="p">()));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Note that we also extend our environment in this one, for the exact same reason as before.</p> <p>Case expressions are the only thing left on the agenda. This is the time during which we have to perform desugaring. Here, though, we run into an issue: we don&rsquo;t have tags assigned to constructors! We need to adjust our code to keep track of the tags of the various constructors of a type. To do this, we add a subclass for the <code>type_base</code> struct, called <code>type_data</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/06/type.hpp" data-first-line="33" data-last-line="42"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/06/type.hpp#L33-L42">type.hpp</a>, lines 33 through 42</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">type_data</span> <span class="o">:</span> <span class="k">public</span> <span class="n">type_base</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="nc">constructor</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">tag</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">,</span> <span class="n">constructor</span><span class="o">&gt;</span> <span class="n">constructors</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">type_data</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">n</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">type_base</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">n</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"><span class="p">};</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>When we create types from <code>definition_data</code>, we tag the corresponding constructors:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/06/definition.cpp" data-first-line="54" data-last-line="71"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/06/definition.cpp#L54-L71">definition.cpp</a>, lines 54 through 71</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span><span class="lnt">65 </span><span class="lnt">66 </span><span class="lnt">67 </span><span class="lnt">68 </span><span class="lnt">69 </span><span class="lnt">70 </span><span class="lnt">71 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">definition_data</span><span class="o">::</span><span class="n">typecheck_first</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="n">type_env</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_data</span><span class="o">*</span> <span class="n">this_type</span> <span class="o">=</span> <span class="k">new</span> <span class="n">type_data</span><span class="p">(</span><span class="n">name</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">return_type</span> <span class="o">=</span> <span class="n">type_ptr</span><span class="p">(</span><span class="n">this_type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">next_tag</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">constructor</span> <span class="p">:</span> <span class="n">constructors</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">constructor</span><span class="o">-&gt;</span><span class="n">tag</span> <span class="o">=</span> <span class="n">next_tag</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">this_type</span><span class="o">-&gt;</span><span class="n">constructors</span><span class="p">[</span><span class="n">constructor</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="n">next_tag</span><span class="o">++</span> <span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">full_type</span> <span class="o">=</span> <span class="n">return_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">constructor</span><span class="o">-&gt;</span><span class="n">types</span><span class="p">.</span><span class="n">rbegin</span><span class="p">();</span> <span class="n">it</span> <span class="o">!=</span> <span class="n">constructor</span><span class="o">-&gt;</span><span class="n">types</span><span class="p">.</span><span class="n">rend</span><span class="p">();</span> <span class="n">it</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">type</span> <span class="o">=</span> <span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_base</span><span class="p">(</span><span class="o">*</span><span class="n">it</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="n">full_type</span> <span class="o">=</span> <span class="n">type_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">type_arr</span><span class="p">(</span><span class="n">type</span><span class="p">,</span> <span class="n">full_type</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">env</span><span class="p">.</span><span class="n">bind</span><span class="p">(</span><span class="n">constructor</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">,</span> <span class="n">full_type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Ah, but adding constructor info to the type doesn&rsquo;t solve the problem. Once we performed type checking, we don&rsquo;t keep the types that we computed for an AST node, in the node. And obviously, we don&rsquo;t want to go looking for them again. Furthermore, we can&rsquo;t just look up a constructor in the environment, since we can well have patterns that don&rsquo;t have <strong>any</strong> constructors:</p> <pre tabindex="0"><code>match l { l -&gt; { 0 } } </code></pre><p>So, we want each <code>ast</code> node to store its type (well, in practice we only need this for <code>ast_case</code>, but we might as well store it for all nodes). We can add it, no problem. To add to that, we can add another, non-virtual <code>typecheck</code> method (let&rsquo;s call it <code>typecheck_common</code>, since naming is hard). This method will call <code>typecheck</code>, and store the output into the <code>node_type</code> field.</p> <p>The signature is identical to <code>typecheck</code>, except it&rsquo;s neither virtual nor const:</p> <pre tabindex="0"><code>type_ptr typecheck_common(type_mgr&amp; mgr, const type_env&amp; env); </code></pre><p>And the implementation is as simple as you think:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/06/ast.cpp" data-first-line="9" data-last-line="12"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/06/ast.cpp#L9-L12">ast.cpp</a>, lines 9 through 12</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="n">type_ptr</span> <span class="n">ast</span><span class="o">::</span><span class="n">typecheck_common</span><span class="p">(</span><span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">,</span> <span class="k">const</span> <span class="n">type_env</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">node_type</span> <span class="o">=</span> <span class="n">typecheck</span><span class="p">(</span><span class="n">mgr</span><span class="p">,</span> <span class="n">env</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">node_type</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>In client code (<code>definition_defn::typecheck_first</code> for instance), we should now use <code>typecheck_common</code> instead of <code>typecheck</code>. With that done, we&rsquo;re almost there. However, we&rsquo;re still missing something: most likely, the initial type assigned to any node is a <code>type_var</code>, or a type variable. In this case, <code>type_var</code> <strong>needs</strong> the information from <code>type_mgr</code>, which we will not be keeping around. Besides, it&rsquo;s cleaner to keep the actual type as a member of the node, not a variable type that references it. In order to address this, we write two conversion functions that call <code>resolve</code> on all types in an AST, given a type manager. After this is done, the type manager can be thrown away. The signatures of the functions are as follows:</p> <pre tabindex="0"><code>void resolve_common(const type_mgr&amp; mgr); virtual void resolve(const type_mgr&amp; mgr) const = 0; </code></pre><p>We also add the <code>resolve</code> method to <code>definition</code>, so that we can call it without having to run <code>dynamic_cast</code>. The implementation for <code>ast::resolve_common</code> just resolves the type:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/06/ast.cpp" data-first-line="14" data-last-line="21"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/06/ast.cpp#L14-L21">ast.cpp</a>, lines 14 through 21</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast</span><span class="o">::</span><span class="n">resolve_common</span><span class="p">(</span><span class="k">const</span> <span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_var</span><span class="o">*</span> <span class="n">var</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">type_ptr</span> <span class="n">resolved_type</span> <span class="o">=</span> <span class="n">mgr</span><span class="p">.</span><span class="n">resolve</span><span class="p">(</span><span class="n">node_type</span><span class="p">,</span> <span class="n">var</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">var</span><span class="p">)</span> <span class="k">throw</span> <span class="n">type_error</span><span class="p">(</span><span class="s">&#34;ambiguously typed program&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">resolve</span><span class="p">(</span><span class="n">mgr</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">node_type</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">resolved_type</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>The virtual <code>ast::resolve</code> just calls <code>ast::resolve_common</code> on an all <code>ast</code> children of a node. Here&rsquo;s a sample implementation from <code>ast_binop</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/06/ast.cpp" data-first-line="98" data-last-line="101"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/06/ast.cpp#L98-L101">ast.cpp</a>, lines 98 through 101</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 98 </span><span class="lnt"> 99 </span><span class="lnt">100 </span><span class="lnt">101 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast_binop</span><span class="o">::</span><span class="n">resolve</span><span class="p">(</span><span class="k">const</span> <span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">left</span><span class="o">-&gt;</span><span class="n">resolve_common</span><span class="p">(</span><span class="n">mgr</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">right</span><span class="o">-&gt;</span><span class="n">resolve_common</span><span class="p">(</span><span class="n">mgr</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>And here&rsquo;s the implementation of <code>definition::resolve</code> on <code>definition_defn</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/06/definition.cpp" data-first-line="32" data-last-line="42"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/06/definition.cpp#L32-L42">definition.cpp</a>, lines 32 through 42</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">definition_defn</span><span class="o">::</span><span class="n">resolve</span><span class="p">(</span><span class="k">const</span> <span class="n">type_mgr</span><span class="o">&amp;</span> <span class="n">mgr</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_var</span><span class="o">*</span> <span class="n">var</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">body</span><span class="o">-&gt;</span><span class="n">resolve_common</span><span class="p">(</span><span class="n">mgr</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">return_type</span> <span class="o">=</span> <span class="n">mgr</span><span class="p">.</span><span class="n">resolve</span><span class="p">(</span><span class="n">return_type</span><span class="p">,</span> <span class="n">var</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">var</span><span class="p">)</span> <span class="k">throw</span> <span class="n">type_error</span><span class="p">(</span><span class="s">&#34;ambiguously typed program&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">param_type</span> <span class="p">:</span> <span class="n">param_types</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">param_type</span> <span class="o">=</span> <span class="n">mgr</span><span class="p">.</span><span class="n">resolve</span><span class="p">(</span><span class="n">param_type</span><span class="p">,</span> <span class="n">var</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">var</span><span class="p">)</span> <span class="k">throw</span> <span class="n">type_error</span><span class="p">(</span><span class="s">&#34;ambiguously typed program&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Finally, we call <code>resolve</code> at the end <code>typecheck_program</code> in <code>main.cpp</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/06/main.cpp" data-first-line="40" data-last-line="42"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/06/main.cpp#L40-L42">main.cpp</a>, lines 40 through 42</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">def</span> <span class="p">:</span> <span class="n">prog</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">def</span><span class="o">-&gt;</span><span class="n">resolve</span><span class="p">(</span><span class="n">mgr</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>At last, we&rsquo;re ready to implement the code for compiling <code>ast_case</code>. Here it is, in all its glory:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/06/ast.cpp" data-first-line="178" data-last-line="230"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/06/ast.cpp#L178-L230">ast.cpp</a>, lines 178 through 230</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">178 </span><span class="lnt">179 </span><span class="lnt">180 </span><span class="lnt">181 </span><span class="lnt">182 </span><span class="lnt">183 </span><span class="lnt">184 </span><span class="lnt">185 </span><span class="lnt">186 </span><span class="lnt">187 </span><span class="lnt">188 </span><span class="lnt">189 </span><span class="lnt">190 </span><span class="lnt">191 </span><span class="lnt">192 </span><span class="lnt">193 </span><span class="lnt">194 </span><span class="lnt">195 </span><span class="lnt">196 </span><span class="lnt">197 </span><span class="lnt">198 </span><span class="lnt">199 </span><span class="lnt">200 </span><span class="lnt">201 </span><span class="lnt">202 </span><span class="lnt">203 </span><span class="lnt">204 </span><span class="lnt">205 </span><span class="lnt">206 </span><span class="lnt">207 </span><span class="lnt">208 </span><span class="lnt">209 </span><span class="lnt">210 </span><span class="lnt">211 </span><span class="lnt">212 </span><span class="lnt">213 </span><span class="lnt">214 </span><span class="lnt">215 </span><span class="lnt">216 </span><span class="lnt">217 </span><span class="lnt">218 </span><span class="lnt">219 </span><span class="lnt">220 </span><span class="lnt">221 </span><span class="lnt">222 </span><span class="lnt">223 </span><span class="lnt">224 </span><span class="lnt">225 </span><span class="lnt">226 </span><span class="lnt">227 </span><span class="lnt">228 </span><span class="lnt">229 </span><span class="lnt">230 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">ast_case</span><span class="o">::</span><span class="n">compile</span><span class="p">(</span><span class="k">const</span> <span class="n">env_ptr</span><span class="o">&amp;</span> <span class="n">env</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">instruction_ptr</span><span class="o">&gt;&amp;</span> <span class="n">into</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">type_data</span><span class="o">*</span> <span class="n">type</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">type_data</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">of</span><span class="o">-&gt;</span><span class="n">node_type</span><span class="p">.</span><span class="n">get</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">of</span><span class="o">-&gt;</span><span class="n">compile</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">into</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">into</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_eval</span><span class="p">()));</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">instruction_jump</span><span class="o">*</span> <span class="n">jump_instruction</span> <span class="o">=</span> <span class="k">new</span> <span class="n">instruction_jump</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">into</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="n">jump_instruction</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">branch</span> <span class="p">:</span> <span class="n">branches</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">instruction_ptr</span><span class="o">&gt;</span> <span class="n">branch_instructions</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">pattern_var</span><span class="o">*</span> <span class="n">vpat</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">pattern_constr</span><span class="o">*</span> <span class="n">cpat</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">((</span><span class="n">vpat</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">pattern_var</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">branch</span><span class="o">-&gt;</span><span class="n">pat</span><span class="p">.</span><span class="n">get</span><span class="p">())))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">branch</span><span class="o">-&gt;</span><span class="n">expr</span><span class="o">-&gt;</span><span class="n">compile</span><span class="p">(</span><span class="n">env_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">env_offset</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">env</span><span class="p">)),</span> <span class="n">branch_instructions</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">constr_pair</span> <span class="p">:</span> <span class="n">type</span><span class="o">-&gt;</span><span class="n">constructors</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">jump_instruction</span><span class="o">-&gt;</span><span class="n">tag_mappings</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">constr_pair</span><span class="p">.</span><span class="n">second</span><span class="p">.</span><span class="n">tag</span><span class="p">)</span> <span class="o">!=</span> </span></span><span class="line"><span class="cl"> <span class="n">jump_instruction</span><span class="o">-&gt;</span><span class="n">tag_mappings</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> </span></span><span class="line"><span class="cl"> <span class="k">break</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">jump_instruction</span><span class="o">-&gt;</span><span class="n">tag_mappings</span><span class="p">[</span><span class="n">constr_pair</span><span class="p">.</span><span class="n">second</span><span class="p">.</span><span class="n">tag</span><span class="p">]</span> <span class="o">=</span> </span></span><span class="line"><span class="cl"> <span class="n">jump_instruction</span><span class="o">-&gt;</span><span class="n">branches</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">jump_instruction</span><span class="o">-&gt;</span><span class="n">branches</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">branch_instructions</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="nf">if</span><span class="p">((</span><span class="n">cpat</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">pattern_constr</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">branch</span><span class="o">-&gt;</span><span class="n">pat</span><span class="p">.</span><span class="n">get</span><span class="p">())))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">env_ptr</span> <span class="n">new_env</span> <span class="o">=</span> <span class="n">env</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">cpat</span><span class="o">-&gt;</span><span class="n">params</span><span class="p">.</span><span class="n">rbegin</span><span class="p">();</span> <span class="n">it</span> <span class="o">!=</span> <span class="n">cpat</span><span class="o">-&gt;</span><span class="n">params</span><span class="p">.</span><span class="n">rend</span><span class="p">();</span> <span class="n">it</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">new_env</span> <span class="o">=</span> <span class="n">env_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">env_var</span><span class="p">(</span><span class="o">*</span><span class="n">it</span><span class="p">,</span> <span class="n">new_env</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">branch_instructions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_split</span><span class="p">()));</span> </span></span><span class="line"><span class="cl"> <span class="n">branch</span><span class="o">-&gt;</span><span class="n">expr</span><span class="o">-&gt;</span><span class="n">compile</span><span class="p">(</span><span class="n">new_env</span><span class="p">,</span> <span class="n">branch_instructions</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">branch_instructions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_slide</span><span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="n">cpat</span><span class="o">-&gt;</span><span class="n">params</span><span class="p">.</span><span class="n">size</span><span class="p">())));</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">new_tag</span> <span class="o">=</span> <span class="n">type</span><span class="o">-&gt;</span><span class="n">constructors</span><span class="p">[</span><span class="n">cpat</span><span class="o">-&gt;</span><span class="n">constr</span><span class="p">].</span><span class="n">tag</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">jump_instruction</span><span class="o">-&gt;</span><span class="n">tag_mappings</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">new_tag</span><span class="p">)</span> <span class="o">!=</span> </span></span><span class="line"><span class="cl"> <span class="n">jump_instruction</span><span class="o">-&gt;</span><span class="n">tag_mappings</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> </span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="n">type_error</span><span class="p">(</span><span class="s">&#34;technically not a type error: duplicate pattern&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">jump_instruction</span><span class="o">-&gt;</span><span class="n">tag_mappings</span><span class="p">[</span><span class="n">new_tag</span><span class="p">]</span> <span class="o">=</span> </span></span><span class="line"><span class="cl"> <span class="n">jump_instruction</span><span class="o">-&gt;</span><span class="n">branches</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">jump_instruction</span><span class="o">-&gt;</span><span class="n">branches</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">branch_instructions</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">constr_pair</span> <span class="p">:</span> <span class="n">type</span><span class="o">-&gt;</span><span class="n">constructors</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">jump_instruction</span><span class="o">-&gt;</span><span class="n">tag_mappings</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">constr_pair</span><span class="p">.</span><span class="n">second</span><span class="p">.</span><span class="n">tag</span><span class="p">)</span> <span class="o">==</span> </span></span><span class="line"><span class="cl"> <span class="n">jump_instruction</span><span class="o">-&gt;</span><span class="n">tag_mappings</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> </span></span><span class="line"><span class="cl"> <span class="k">throw</span> <span class="n">type_error</span><span class="p">(</span><span class="s">&#34;non-total pattern&#34;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>There&rsquo;s a lot to unpack here. First of all, just like we said in the compilation scheme, we want to build and evaluate the expression that&rsquo;s being analyzed. Once that&rsquo;s done, however, things get more tricky. We know that each branch of a case expression will correspond to a vector of instructions - in fact, our jump instruction contains a mapping from tags to instructions. As we also discussed above, each list of instructions can be mapped to by multiple tags. We don&rsquo;t want to recompile the same sequence of instructions multiple times (or indeed, generate machine code for it). So, we keep a mapping of tags to their corresponding sequences of instructions. We implement this by having a vector of vectors of instructions (in which each inner vector represents the code for a branch), and a map of tag number to index in the vector containing all the branches. This way, multiple tags can point to the same instruction set without duplicating information.</p> <p>We also don&rsquo;t allow a tag to be mapped to more than one sequence of instructions. This is handled differently depending on whether a variable pattern or a constructor pattern are encountered. Variable patterns map all tags that haven&rsquo;t been mapped yet, so no error can occur. Constructor patterns, though, can explicitly try to map the same tag twice, and we don&rsquo;t want that.</p> <p>I implied in the previous paragraph the implementation of our case expression compilation algorithm, but let&rsquo;s go through it. Once we&rsquo;ve compiled the expression to be analyzed, and evaluated it (just like in our definitions above), we proceed to look at all the branches specified in the case expression.</p> <p>If a branch has a variable pattern, we must map to the result of the compilation all the remaining, unmapped tags. We also aren&rsquo;t going to be taking apart our value, so we don&rsquo;t need to use Split, but we do need to add 1 to the environment offset to account the the presence of that value. So, we compile the branch body with that offset, and iterate through all the constructors of our data type. We skip a constructor if it&rsquo;s been mapped, and if it hasn&rsquo;t been, we map it to the index that this branch body will have in our list. Finally, we push the newly compiled instruction sequence into the list of branch bodies.</p> <p>If a branch is a constructor pattern, on the other hand, we lead our compilation output with a Split. This takes off the value from the stack, but pushes on all the parameters of the constructor. We account for this by incrementing the environment with the offset given by the number of arguments (just like we did in our definitions of our compilation scheme). Before we map the tag, we ensure that it hasn&rsquo;t already been mapped (and throw an exception, currently in the form of a type error due to the growing length of this post), and finally map it and insert the new branch code into the list of branches.</p> <p>After we&rsquo;re done with all the branches, we also check for non-exhaustive patterns, since otherwise we could run into runtime errors. With this, the case expression, and the last of the AST nodes, can be compiled.</p> <p>We also add a <code>compile</code> method to definitions, since they contain our AST nodes. The method is empty for <code>defn_data</code>, and looks as follows for <code>definition_defn</code>:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/06/definition.cpp" data-first-line="44" data-last-line="52"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/06/definition.cpp#L44-L52">definition.cpp</a>, lines 44 through 52</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">definition_defn</span><span class="o">::</span><span class="n">compile</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">env_ptr</span> <span class="n">new_env</span> <span class="o">=</span> <span class="n">env_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">env_offset</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="k">nullptr</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">params</span><span class="p">.</span><span class="n">rbegin</span><span class="p">();</span> <span class="n">it</span> <span class="o">!=</span> <span class="n">params</span><span class="p">.</span><span class="n">rend</span><span class="p">();</span> <span class="n">it</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">new_env</span> <span class="o">=</span> <span class="n">env_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">env_var</span><span class="p">(</span><span class="o">*</span><span class="n">it</span><span class="p">,</span> <span class="n">new_env</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">body</span><span class="o">-&gt;</span><span class="n">compile</span><span class="p">(</span><span class="n">new_env</span><span class="p">,</span> <span class="n">instructions</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">instructions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_update</span><span class="p">(</span><span class="n">params</span><span class="p">.</span><span class="n">size</span><span class="p">())));</span> </span></span><span class="line"><span class="cl"> <span class="n">instructions</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">instruction_ptr</span><span class="p">(</span><span class="k">new</span> <span class="n">instruction_pop</span><span class="p">(</span><span class="n">params</span><span class="p">.</span><span class="n">size</span><span class="p">())));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>Notice that we terminate the function with Update and Pop. Update will turn the <code>ast_app</code> node that served as the &ldquo;root&rdquo; of the application into an indirection to the value that we have computed. After this, Pop will remove all &ldquo;scratch work&rdquo; from the stack. In essense, this is how we can lazily evaluate expressions.</p> <p>Finally, we make a function in our <code>main.cpp</code> file to compile all the definitions:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/06/main.cpp" data-first-line="45" data-last-line="56"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/06/main.cpp#L45-L56">main.cpp</a>, lines 45 through 56</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span><span class="lnt">51 </span><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">compile_program</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">definition_ptr</span><span class="o">&gt;&amp;</span> <span class="n">prog</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">def</span> <span class="p">:</span> <span class="n">prog</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">def</span><span class="o">-&gt;</span><span class="n">compile</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">definition_defn</span><span class="o">*</span> <span class="n">defn</span> <span class="o">=</span> <span class="k">dynamic_cast</span><span class="o">&lt;</span><span class="n">definition_defn</span><span class="o">*&gt;</span><span class="p">(</span><span class="n">def</span><span class="p">.</span><span class="n">get</span><span class="p">());</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="n">defn</span><span class="p">)</span> <span class="k">continue</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="nl">instruction</span> <span class="p">:</span> <span class="n">defn</span><span class="o">-&gt;</span><span class="n">instructions</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">instruction</span><span class="o">-&gt;</span><span class="n">print</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>In this method, we also include some extra output to help us see the result of our compilation. Since at the moment, only the <code>definition_defn</code> program has to be compiled, we try cast all definitions to it, and if we succeed, we print them out.</p> <p>Let&rsquo;s try it all out! For the below sample program:</p> <div class="highlight-group" > <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/06/examples/works1.txt">works1.txt</a>, entire file</div><pre><code>defn main = { plus 320 6 } defn plus x y = { x + y } </code></pre> </div> <p>Our compiler produces the following new output:</p> <pre tabindex="0"><code>PushInt(6) PushInt(320) PushGlobal(plus) MkApp() MkApp() Update(0) Pop(0) Push(1) Push(1) PushGlobal(plus) MkApp() MkApp() Update(2) Pop(2) </code></pre><p>The first sequence of instructions is clearly <code>main</code>. It creates an application of <code>plus</code> to <code>320</code>, and then applies that to <code>6</code>, which results in <code>plus 320 6</code>, which is correct. The second sequence of instruction pushes the parameter that sits on offset 1 from the top of the stack (<code>y</code>). It then pushes a parameter from the same offset again, but this time, since <code>y</code> was previously pushed on the stack, <code>x</code> is now in that position, so <code>x</code> is pushed onto the stack. Finally, <code>+</code> is pushed, and the application <code>(+) x y</code> is created, which is equivalent to <code>x+y</code>.</p> <p>Let&rsquo;s also take a look at a case expression program:</p> <div class="highlight-group" > <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/06/examples/works3.txt">works3.txt</a>, entire file</div><pre><code>data List = { Nil, Cons Int List } defn length l = { case l of { Nil -&gt; { 0 } Cons x xs -&gt; { 1 + length xs } } } </code></pre> </div> <p>The result of the compilation is as follows:</p> <pre tabindex="0"><code>Push(0) Eval() Jump( Split() PushInt(0) Slide(0) Split() Push(1) PushGlobal(length) MkApp() PushInt(1) PushGlobal(plus) MkApp() MkApp() Slide(2) ) Update(1) Pop(1) </code></pre><p>We push the first (and only) parameter onto the stack. We then make sure it&rsquo;s evaluated, and perform case analysis: if the list is <code>Nil</code>, we simply push the number 0 onto the stack. If it&rsquo;s a concatenation of some <code>x</code> and another lists <code>xs</code>, we push <code>xs</code> and <code>length</code> onto the stack, make the application (<code>length xs</code>), push the 1, and finally apply <code>+</code> to the result. This all makes sense!</p> <p>With this, we&rsquo;ve been able to compile our expressions and functions into G-machine code. We&rsquo;re not done, however - our computers aren&rsquo;t G-machines. We&rsquo;ll need to compile our G-machine code to <strong>machine code</strong> (we will use LLVM for this), implement the <strong>runtime</strong>, and develop a <strong>garbage collector</strong>. We&rsquo;ll tackle the first of these in the next post - <a href="https://danilafe.com/blog/07_compiler_runtime/">Part 7 - Runtime</a>.</p> Compiling a Functional Language Using C++, Part 7 - Runtime https://danilafe.com/blog/07_compiler_runtime/ Tue, 06 Aug 2019 14:26:38 -0700 https://danilafe.com/blog/07_compiler_runtime/ <p>Wikipedia has the following definition for a <strong>runtime</strong>:</p> <blockquote> <p>A [runtime] primarily implements portions of an execution model.</p> </blockquote> <p>We know what our execution model is! We talked about it in Part 5 - it&rsquo;s the lazy graph reduction we&rsquo;ve specified. Creating and manipulating graph nodes is slightly above hardware level, and all programs in our functional language will rely on such manipulation (it&rsquo;s how they run!). Furthermore, most G-machine instructions are also above hardware level (especially unwind!).</p> <p>Push and Slide and other instructions are pretty complex. Most computers aren&rsquo;t stack machines. We&rsquo;ll have to implement our own stack, and whenever a graph-building function will want to modify the stack, it will have to call library routines for our stack implementation:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">stack_push</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="k">struct</span> <span class="n">node_s</span><span class="o">*</span> <span class="n">n</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">node_s</span><span class="o">*</span> <span class="nf">stack_slide</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">c</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="cm">/* other stack operations */</span> </span></span></code></pre></div><p>Furthermore, we observe that Unwind does a lot of the heavy lifting in our G-machine definition. After we build the graph, Unwind is what picks it apart and performs function calls. Furthermore, Unwind pushes Unwind back on the stack: once you&rsquo;ve hit it, you&rsquo;re continuing to Unwind until you reach a function call. This effectively means we can implement Unwind as a loop:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="k">while</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// Check for Unwind&#39;s first rule </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// Check for Unwind&#39;s second rule </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// ... </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span> </span></span></code></pre></div><p>In this implementation, Unwind is in charge. We won&rsquo;t need to insert the Unwind operations at the end of our generated functions, and you may have noticed we&rsquo;ve already been following this strategy in our implementation of the G-machine compilation.</p> <p>We can start working on an implementation of the runtime right now, beginning with the nodes:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/07/runtime.h" data-first-line="4" data-last-line="50"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/07/runtime.h#L4-L50">runtime.h</a>, lines 4 through 50</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span><span class="lnt">41 </span><span class="lnt">42 </span><span class="lnt">43 </span><span class="lnt">44 </span><span class="lnt">45 </span><span class="lnt">46 </span><span class="lnt">47 </span><span class="lnt">48 </span><span class="lnt">49 </span><span class="lnt">50 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">stack</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">enum</span> <span class="nc">node_tag</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">NODE_APP</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">NODE_NUM</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">NODE_GLOBAL</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">NODE_IND</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">NODE_DATA</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">node_base</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">enum</span> <span class="nc">node_tag</span> <span class="n">tag</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">node_app</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="nc">node_base</span> <span class="n">base</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="nc">node_base</span><span class="o">*</span> <span class="n">left</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="nc">node_base</span><span class="o">*</span> <span class="n">right</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">node_num</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="nc">node_base</span> <span class="n">base</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int32_t</span> <span class="n">value</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">node_global</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="nc">node_base</span> <span class="n">base</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int32_t</span> <span class="n">arity</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">function</span><span class="p">)(</span><span class="k">struct</span> <span class="nc">stack</span><span class="o">*</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">node_ind</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="nc">node_base</span> <span class="n">base</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="nc">node_base</span><span class="o">*</span> <span class="n">next</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">node_data</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="nc">node_base</span> <span class="n">base</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">int8_t</span> <span class="n">tag</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="nc">node_base</span><span class="o">**</span> <span class="n">array</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">node_base</span><span class="o">*</span> <span class="nf">alloc_node</span><span class="p">();</span> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">node_app</span><span class="o">*</span> <span class="nf">alloc_app</span><span class="p">(</span><span class="k">struct</span> <span class="nc">node_base</span><span class="o">*</span> <span class="n">l</span><span class="p">,</span> <span class="k">struct</span> <span class="nc">node_base</span><span class="o">*</span> <span class="n">r</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">node_num</span><span class="o">*</span> <span class="nf">alloc_num</span><span class="p">(</span><span class="kt">int32_t</span> <span class="n">n</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">node_global</span><span class="o">*</span> <span class="nf">alloc_global</span><span class="p">(</span><span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">f</span><span class="p">)(</span><span class="k">struct</span> <span class="nc">stack</span><span class="o">*</span><span class="p">),</span> <span class="kt">int32_t</span> <span class="n">a</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">node_ind</span><span class="o">*</span> <span class="nf">alloc_ind</span><span class="p">(</span><span class="k">struct</span> <span class="nc">node_base</span><span class="o">*</span> <span class="n">n</span><span class="p">);</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We have a variety of different nodes that can be on the stack, but without the magic of C++&rsquo;s <code>vtable</code> and RTTI, we have to take care of the bookkeeping ourselves. We add an enum, <code>node_tag</code>, which we will use to indicate what type of node we&rsquo;re looking at. We also add a &ldquo;base class&rdquo; <code>node_base</code>, which contains the fields that all nodes must contain (only <code>tag</code> at the moment). We then add to the beginning of each node struct a member of type <code>node_base</code>. With this, a pointer to a node struct can be interpreted as a pointer to <code>node_base</code>, which is our lowest common denominator. To go back, we check the <code>tag</code> of <code>node_base</code>, and cast the pointer appropriately. This way, we mimic inheritance, in a very basic manner.</p> <p>We also add an <code>alloc_node</code>, which allocates a region of memory big enough to be any node. We do this because we sometimes mutate nodes (replacing expressions with the results of their evaluation), changing their type. We then want to be able to change a node without reallocating memory. Since the biggest node we have is <code>node_app</code>, that&rsquo;s the one we choose.</p> <p>Finally, to make it easier to create nodes from our generated code, we add helper functions like <code>alloc_num</code>, which allocate a given node type, and set its tag and member fields appropriately. We don&rsquo;t include such a function for <code>node_data</code>, since this node will be created only in one possible way.</p> <p>Here&rsquo;s the implementation: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/07/runtime.c" data-first-line="6" data-last-line="40"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/07/runtime.c#L6-L40">runtime.c</a>, lines 6 through 40</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span><span class="lnt">32 </span><span class="lnt">33 </span><span class="lnt">34 </span><span class="lnt">35 </span><span class="lnt">36 </span><span class="lnt">37 </span><span class="lnt">38 </span><span class="lnt">39 </span><span class="lnt">40 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span> <span class="nf">alloc_node</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span> <span class="n">new_node</span> <span class="o">=</span> <span class="nf">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="k">struct</span> <span class="n">node_app</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="nf">assert</span><span class="p">(</span><span class="n">new_node</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">new_node</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">node_app</span><span class="o">*</span> <span class="nf">alloc_app</span><span class="p">(</span><span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span> <span class="n">l</span><span class="p">,</span> <span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span> <span class="n">r</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_app</span><span class="o">*</span> <span class="n">node</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">node_app</span><span class="o">*</span><span class="p">)</span> <span class="nf">alloc_node</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">node</span><span class="o">-&gt;</span><span class="n">base</span><span class="p">.</span><span class="n">tag</span> <span class="o">=</span> <span class="n">NODE_APP</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">node</span><span class="o">-&gt;</span><span class="n">left</span> <span class="o">=</span> <span class="n">l</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">node</span><span class="o">-&gt;</span><span class="n">right</span> <span class="o">=</span> <span class="n">r</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">node</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">node_num</span><span class="o">*</span> <span class="nf">alloc_num</span><span class="p">(</span><span class="kt">int32_t</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_num</span><span class="o">*</span> <span class="n">node</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">node_num</span><span class="o">*</span><span class="p">)</span> <span class="nf">alloc_node</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">node</span><span class="o">-&gt;</span><span class="n">base</span><span class="p">.</span><span class="n">tag</span> <span class="o">=</span> <span class="n">NODE_NUM</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">node</span><span class="o">-&gt;</span><span class="n">value</span> <span class="o">=</span> <span class="n">n</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">node</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">node_global</span><span class="o">*</span> <span class="nf">alloc_global</span><span class="p">(</span><span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">f</span><span class="p">)(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span><span class="p">),</span> <span class="kt">int32_t</span> <span class="n">a</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_global</span><span class="o">*</span> <span class="n">node</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">node_global</span><span class="o">*</span><span class="p">)</span> <span class="nf">alloc_node</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">node</span><span class="o">-&gt;</span><span class="n">base</span><span class="p">.</span><span class="n">tag</span> <span class="o">=</span> <span class="n">NODE_GLOBAL</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">node</span><span class="o">-&gt;</span><span class="n">arity</span> <span class="o">=</span> <span class="n">a</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">node</span><span class="o">-&gt;</span><span class="n">function</span> <span class="o">=</span> <span class="n">f</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">node</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">node_ind</span><span class="o">*</span> <span class="nf">alloc_ind</span><span class="p">(</span><span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_ind</span><span class="o">*</span> <span class="n">node</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">node_ind</span><span class="o">*</span><span class="p">)</span> <span class="nf">alloc_node</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">node</span><span class="o">-&gt;</span><span class="n">base</span><span class="p">.</span><span class="n">tag</span> <span class="o">=</span> <span class="n">NODE_IND</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">node</span><span class="o">-&gt;</span><span class="n">next</span> <span class="o">=</span> <span class="n">n</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">node</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>We now move on to implement some stack operations. Let&rsquo;s list them:</p> <ul> <li><code>stack_init</code> and <code>stack_free</code> - one allocates memory for the stack, the other releases it.</li> <li><code>stack_push</code>, <code>stack_pop</code> and <code>stack_peek</code> - the classic stack operations. We have <code>_peek</code> to take an offset, so we can peek relative to the top of the stack.</li> <li><code>stack_popn</code> - pop off some number of nodes instead of one.</li> <li><code>stack_slide</code> - the slide we specified in the semantics. Keeps the top, deletes the next several nodes.</li> <li><code>stack_update</code> - turns the node at the offset into an indirection to the result, which we will use for lazy evaluation (modifying expressions with their reduced forms).</li> <li><code>stack_alloc</code> - allocate indirection nodes on the stack. We will use this later.</li> <li><code>stack_pack</code> and <code>stack_split</code> - Wrap and unwrap constructors on the stack.</li> </ul> <p>We declare these in a header: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/07/runtime.h" data-first-line="52" data-last-line="68"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/07/runtime.h#L52-L68">runtime.h</a>, lines 52 through 68</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">52 </span><span class="lnt">53 </span><span class="lnt">54 </span><span class="lnt">55 </span><span class="lnt">56 </span><span class="lnt">57 </span><span class="lnt">58 </span><span class="lnt">59 </span><span class="lnt">60 </span><span class="lnt">61 </span><span class="lnt">62 </span><span class="lnt">63 </span><span class="lnt">64 </span><span class="lnt">65 </span><span class="lnt">66 </span><span class="lnt">67 </span><span class="lnt">68 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">stack</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">size_t</span> <span class="n">size</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="kt">size_t</span> <span class="n">count</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_base</span><span class="o">**</span> <span class="n">data</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">stack_init</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">stack_free</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">stack_push</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span> <span class="n">n</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span> <span class="nf">stack_pop</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span> <span class="nf">stack_peek</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">o</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">stack_popn</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">n</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">stack_slide</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">n</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">stack_update</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">o</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">stack_alloc</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">o</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">stack_pack</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">n</span><span class="p">,</span> <span class="kt">int8_t</span> <span class="n">t</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">stack_split</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">n</span><span class="p">);</span></span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>And implement them as follows: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/07/runtime.c" data-first-line="42" data-last-line="116"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/07/runtime.c#L42-L116">runtime.c</a>, lines 42 through 116</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 42 </span><span class="lnt"> 43 </span><span class="lnt"> 44 </span><span class="lnt"> 45 </span><span class="lnt"> 46 </span><span class="lnt"> 47 </span><span class="lnt"> 48 </span><span class="lnt"> 49 </span><span class="lnt"> 50 </span><span class="lnt"> 51 </span><span class="lnt"> 52 </span><span class="lnt"> 53 </span><span class="lnt"> 54 </span><span class="lnt"> 55 </span><span class="lnt"> 56 </span><span class="lnt"> 57 </span><span class="lnt"> 58 </span><span class="lnt"> 59 </span><span class="lnt"> 60 </span><span class="lnt"> 61 </span><span class="lnt"> 62 </span><span class="lnt"> 63 </span><span class="lnt"> 64 </span><span class="lnt"> 65 </span><span class="lnt"> 66 </span><span class="lnt"> 67 </span><span class="lnt"> 68 </span><span class="lnt"> 69 </span><span class="lnt"> 70 </span><span class="lnt"> 71 </span><span class="lnt"> 72 </span><span class="lnt"> 73 </span><span class="lnt"> 74 </span><span class="lnt"> 75 </span><span class="lnt"> 76 </span><span class="lnt"> 77 </span><span class="lnt"> 78 </span><span class="lnt"> 79 </span><span class="lnt"> 80 </span><span class="lnt"> 81 </span><span class="lnt"> 82 </span><span class="lnt"> 83 </span><span class="lnt"> 84 </span><span class="lnt"> 85 </span><span class="lnt"> 86 </span><span class="lnt"> 87 </span><span class="lnt"> 88 </span><span class="lnt"> 89 </span><span class="lnt"> 90 </span><span class="lnt"> 91 </span><span class="lnt"> 92 </span><span class="lnt"> 93 </span><span class="lnt"> 94 </span><span class="lnt"> 95 </span><span class="lnt"> 96 </span><span class="lnt"> 97 </span><span class="lnt"> 98 </span><span class="lnt"> 99 </span><span class="lnt">100 </span><span class="lnt">101 </span><span class="lnt">102 </span><span class="lnt">103 </span><span class="lnt">104 </span><span class="lnt">105 </span><span class="lnt">106 </span><span class="lnt">107 </span><span class="lnt">108 </span><span class="lnt">109 </span><span class="lnt">110 </span><span class="lnt">111 </span><span class="lnt">112 </span><span class="lnt">113 </span><span class="lnt">114 </span><span class="lnt">115 </span><span class="lnt">116 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">stack_init</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">s</span><span class="o">-&gt;</span><span class="n">size</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">s</span><span class="o">-&gt;</span><span class="n">count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">s</span><span class="o">-&gt;</span><span class="n">data</span> <span class="o">=</span> <span class="nf">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="o">*</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">)</span> <span class="o">*</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">size</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">assert</span><span class="p">(</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">data</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">stack_free</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">free</span><span class="p">(</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">stack_push</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span><span class="p">(</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">count</span> <span class="o">&gt;=</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">size</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">s</span><span class="o">-&gt;</span><span class="n">data</span> <span class="o">=</span> <span class="nf">realloc</span><span class="p">(</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="o">*</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">size</span> <span class="o">*=</span> <span class="mi">2</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="nf">assert</span><span class="p">(</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">data</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="n">s</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">[</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">count</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">n</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span> <span class="nf">stack_pop</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">assert</span><span class="p">(</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">count</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">[</span><span class="o">--</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">count</span><span class="p">];</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span> <span class="nf">stack_peek</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">o</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">assert</span><span class="p">(</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">count</span> <span class="o">&gt;</span> <span class="n">o</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">[</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">count</span> <span class="o">-</span> <span class="n">o</span> <span class="o">-</span> <span class="mi">1</span><span class="p">];</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">stack_popn</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">assert</span><span class="p">(</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">count</span> <span class="o">&gt;=</span> <span class="n">n</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">s</span><span class="o">-&gt;</span><span class="n">count</span> <span class="o">-=</span> <span class="n">n</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">stack_slide</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">assert</span><span class="p">(</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">count</span> <span class="o">&gt;</span> <span class="n">n</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">s</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">[</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">count</span> <span class="o">-</span> <span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">[</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">count</span> <span class="o">-</span> <span class="mi">1</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="n">s</span><span class="o">-&gt;</span><span class="n">count</span> <span class="o">-=</span> <span class="n">n</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">stack_update</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">o</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">assert</span><span class="p">(</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">count</span> <span class="o">&gt;</span> <span class="n">o</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_ind</span><span class="o">*</span> <span class="n">ind</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">node_ind</span><span class="o">*</span><span class="p">)</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">[</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">count</span> <span class="o">-</span> <span class="n">o</span> <span class="o">-</span> <span class="mi">2</span><span class="p">];</span> </span></span><span class="line"><span class="cl"> <span class="n">ind</span><span class="o">-&gt;</span><span class="n">base</span><span class="p">.</span><span class="n">tag</span> <span class="o">=</span> <span class="n">NODE_IND</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">ind</span><span class="o">-&gt;</span><span class="n">next</span> <span class="o">=</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">[</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">count</span> <span class="o">-=</span> <span class="mi">1</span><span class="p">];</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">stack_alloc</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">o</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span><span class="p">(</span><span class="n">o</span><span class="o">--</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">stack_push</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="p">(</span><span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span><span class="p">)</span> <span class="nf">alloc_ind</span><span class="p">(</span><span class="nb">NULL</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">stack_pack</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">n</span><span class="p">,</span> <span class="kt">int8_t</span> <span class="n">t</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">assert</span><span class="p">(</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">count</span> <span class="o">&gt;=</span> <span class="n">n</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_base</span><span class="o">**</span> <span class="n">data</span> <span class="o">=</span> <span class="nf">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="o">*</span><span class="n">data</span><span class="p">)</span> <span class="o">*</span> <span class="n">n</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">assert</span><span class="p">(</span><span class="n">data</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">memcpy</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">[</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">count</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">-</span> <span class="n">n</span><span class="p">],</span> <span class="n">n</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="o">*</span><span class="n">data</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_data</span><span class="o">*</span> <span class="n">new_node</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">node_data</span><span class="o">*</span><span class="p">)</span> <span class="nf">alloc_node</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">new_node</span><span class="o">-&gt;</span><span class="n">array</span> <span class="o">=</span> <span class="n">data</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">new_node</span><span class="o">-&gt;</span><span class="n">base</span><span class="p">.</span><span class="n">tag</span> <span class="o">=</span> <span class="n">NODE_DATA</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">new_node</span><span class="o">-&gt;</span><span class="n">tag</span> <span class="o">=</span> <span class="n">t</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="nf">stack_popn</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">n</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">stack_push</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="p">(</span><span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span><span class="p">)</span> <span class="n">new_node</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">stack_split</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_data</span><span class="o">*</span> <span class="n">node</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">node_data</span><span class="o">*</span><span class="p">)</span> <span class="nf">stack_pop</span><span class="p">(</span><span class="n">s</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nf">stack_push</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">node</span><span class="o">-&gt;</span><span class="n">array</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>Let&rsquo;s now talk about how this will connect to the code we generate. To get a quick example, consider the <code>node_global</code> struct that we have declared above. It has a member <code>function</code>, which is a <strong>function pointer</strong> to a function that takes a stack and returns void.</p> <p>When we finally generate machine code for each of the functions we have in our program, it will be made up of sequences of G-machine operations expressed using assembly instructions. These instructions will still have to manipulate the G-machine stack (they still represent G-machine operations!), and thus, the resulting assembly subroutine will take as parameter a stack. It will then construct the function&rsquo;s graph on that stack, as we&rsquo;ve already seen. Thus, we express a compiled top-level function as a subroutine that takes a stack, and returns void. A global node holds in it the pointer to the function that it will call.</p> <p>When our program will start, it will assume that there exists a top-level function <code>f_main</code> that takes 0 parameters. It will take that function, call it to produce the initial graph, and then let the unwind loop take care of the evaluation.</p> <p>Thus, our program will initially look like this: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/07/runtime.c" data-first-line="154" data-last-line="159"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/07/runtime.c#L154-L159">runtime.c</a>, lines 154 through 159</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">154 </span><span class="lnt">155 </span><span class="lnt">156 </span><span class="lnt">157 </span><span class="lnt">158 </span><span class="lnt">159 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="k">extern</span> <span class="kt">void</span> <span class="nf">f_main</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span><span class="o">**</span> <span class="n">argv</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_global</span><span class="o">*</span> <span class="n">first_node</span> <span class="o">=</span> <span class="nf">alloc_global</span><span class="p">(</span><span class="n">f_main</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span> <span class="n">result</span> <span class="o">=</span> <span class="nf">eval</span><span class="p">((</span><span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span><span class="p">)</span> <span class="n">first_node</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>As we said, we expect an externally-declared subroutine <code>f_main</code>. We construct a global node for <code>f_main</code> with arity 0, and then start the execution using a function <code>eval</code>. What&rsquo;s <code>eval</code>, though? It&rsquo;s the function that will take care of creating a new stack, and evaluating the node that is passed to it using our unwind loop. <code>eval</code> itself is pretty terse:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/07/runtime.c" data-first-line="144" data-last-line="152"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/07/runtime.c#L144-L152">runtime.c</a>, lines 144 through 152</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">144 </span><span class="lnt">145 </span><span class="lnt">146 </span><span class="lnt">147 </span><span class="lnt">148 </span><span class="lnt">149 </span><span class="lnt">150 </span><span class="lnt">151 </span><span class="lnt">152 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span> <span class="nf">eval</span><span class="p">(</span><span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">stack</span> <span class="n">program_stack</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">stack_init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">program_stack</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">stack_push</span><span class="p">(</span><span class="o">&amp;</span><span class="n">program_stack</span><span class="p">,</span> <span class="n">n</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">unwind</span><span class="p">(</span><span class="o">&amp;</span><span class="n">program_stack</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span> <span class="n">result</span> <span class="o">=</span> <span class="nf">stack_pop</span><span class="p">(</span><span class="o">&amp;</span><span class="n">program_stack</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">stack_free</span><span class="p">(</span><span class="o">&amp;</span><span class="n">program_stack</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">result</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We create a fresh program stack, start it off with whatever node we want to evaluate, and have <code>unwind</code> take care of the rest.</p> <p><code>unwind</code> is a direct implementation of the rules from Part 5:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/07/runtime.c" data-first-line="118" data-last-line="142"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/07/runtime.c#L118-L142">runtime.c</a>, lines 118 through 142</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">118 </span><span class="lnt">119 </span><span class="lnt">120 </span><span class="lnt">121 </span><span class="lnt">122 </span><span class="lnt">123 </span><span class="lnt">124 </span><span class="lnt">125 </span><span class="lnt">126 </span><span class="lnt">127 </span><span class="lnt">128 </span><span class="lnt">129 </span><span class="lnt">130 </span><span class="lnt">131 </span><span class="lnt">132 </span><span class="lnt">133 </span><span class="lnt">134 </span><span class="lnt">135 </span><span class="lnt">136 </span><span class="lnt">137 </span><span class="lnt">138 </span><span class="lnt">139 </span><span class="lnt">140 </span><span class="lnt">141 </span><span class="lnt">142 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">unwind</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">while</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span> <span class="n">peek</span> <span class="o">=</span> <span class="nf">stack_peek</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">(</span><span class="n">peek</span><span class="o">-&gt;</span><span class="n">tag</span> <span class="o">==</span> <span class="n">NODE_APP</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_app</span><span class="o">*</span> <span class="n">n</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">node_app</span><span class="o">*</span><span class="p">)</span> <span class="n">peek</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">stack_push</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">n</span><span class="o">-&gt;</span><span class="n">left</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="n">peek</span><span class="o">-&gt;</span><span class="n">tag</span> <span class="o">==</span> <span class="n">NODE_GLOBAL</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_global</span><span class="o">*</span> <span class="n">n</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">node_global</span><span class="o">*</span><span class="p">)</span> <span class="n">peek</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">assert</span><span class="p">(</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">count</span> <span class="o">&gt;</span> <span class="n">n</span><span class="o">-&gt;</span><span class="n">arity</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;=</span> <span class="n">n</span><span class="o">-&gt;</span><span class="n">arity</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">s</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">[</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">count</span> <span class="o">-</span> <span class="n">i</span><span class="p">]</span> </span></span><span class="line"><span class="cl"> <span class="o">=</span> <span class="p">((</span><span class="k">struct</span> <span class="n">node_app</span><span class="o">*</span><span class="p">)</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">[</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">count</span> <span class="o">-</span> <span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="p">])</span><span class="o">-&gt;</span><span class="n">right</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">n</span><span class="o">-&gt;</span><span class="nf">function</span><span class="p">(</span><span class="n">s</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="n">peek</span><span class="o">-&gt;</span><span class="n">tag</span> <span class="o">==</span> <span class="n">NODE_IND</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_ind</span><span class="o">*</span> <span class="n">n</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">node_ind</span><span class="o">*</span><span class="p">)</span> <span class="n">peek</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nf">stack_pop</span><span class="p">(</span><span class="n">s</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">stack_push</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">n</span><span class="o">-&gt;</span><span class="n">next</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">break</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></td></tr></table> </div> </div> </div> <p>We can now come up with some simple programs. Let&rsquo;s try writing out, by hand, <code>main = { 320 + 6 }</code>. We end up with:</p> <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/07/examples/runtime1.c"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/07/examples/runtime1.c">runtime1.c</a>, entire file</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span><span class="lnt">21 </span><span class="lnt">22 </span><span class="lnt">23 </span><span class="lnt">24 </span><span class="lnt">25 </span><span class="lnt">26 </span><span class="lnt">27 </span><span class="lnt">28 </span><span class="lnt">29 </span><span class="lnt">30 </span><span class="lnt">31 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;../runtime.h&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">f_add</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_num</span><span class="o">*</span> <span class="n">left</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">node_num</span><span class="o">*</span><span class="p">)</span> <span class="nf">eval</span><span class="p">(</span><span class="nf">stack_peek</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="mi">0</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_num</span><span class="o">*</span> <span class="n">right</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">node_num</span><span class="o">*</span><span class="p">)</span> <span class="nf">eval</span><span class="p">(</span><span class="nf">stack_peek</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="mi">1</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> <span class="nf">stack_push</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="p">(</span><span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span><span class="p">)</span> <span class="nf">alloc_num</span><span class="p">(</span><span class="n">left</span><span class="o">-&gt;</span><span class="n">value</span> <span class="o">+</span> <span class="n">right</span><span class="o">-&gt;</span><span class="n">value</span><span class="p">));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">f_main</span><span class="p">(</span><span class="k">struct</span> <span class="n">stack</span><span class="o">*</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">// PushInt 320 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">stack_push</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="p">(</span><span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span><span class="p">)</span> <span class="nf">alloc_num</span><span class="p">(</span><span class="mi">320</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1">// PushInt 6 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">stack_push</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="p">(</span><span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span><span class="p">)</span> <span class="nf">alloc_num</span><span class="p">(</span><span class="mi">6</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1">// PushGlobal f_add (the function for +) </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">stack_push</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="p">(</span><span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span><span class="p">)</span> <span class="nf">alloc_global</span><span class="p">(</span><span class="n">f_add</span><span class="p">,</span> <span class="mi">2</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span> <span class="n">left</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span> <span class="n">right</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1">// MkApp </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">left</span> <span class="o">=</span> <span class="nf">stack_pop</span><span class="p">(</span><span class="n">s</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">right</span> <span class="o">=</span> <span class="nf">stack_pop</span><span class="p">(</span><span class="n">s</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">stack_push</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="p">(</span><span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span><span class="p">)</span> <span class="nf">alloc_app</span><span class="p">(</span><span class="n">left</span><span class="p">,</span> <span class="n">right</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c1">// MkApp </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">left</span> <span class="o">=</span> <span class="nf">stack_pop</span><span class="p">(</span><span class="n">s</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="n">right</span> <span class="o">=</span> <span class="nf">stack_pop</span><span class="p">(</span><span class="n">s</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="nf">stack_push</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="p">(</span><span class="k">struct</span> <span class="n">node_base</span><span class="o">*</span><span class="p">)</span> <span class="nf">alloc_app</span><span class="p">(</span><span class="n">left</span><span class="p">,</span> <span class="n">right</span><span class="p">));</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div> </div> <p>If we add to the bottom of our <code>main</code> the following code:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%d</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="p">((</span><span class="k">struct</span> <span class="n">node_num</span><span class="o">*</span><span class="p">)</span> <span class="n">result</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">value</span><span class="p">);</span> </span></span></code></pre></div><p>And compile and run our code:</p> <pre tabindex="0"><code>gcc runtime.c examples/runtime1.c ./a.out </code></pre><p>We get the output <code>326</code>, which is exactly correct!</p> <p>We now have a common set of functions and declarations that serve to support the code we generate from our compiler. Although this time, we wrote out <code>f_main</code> by hand, we will soon use LLVM to generate code for <code>f_main</code> and more. Once we get that going, we be able to compile our code!</p> <p>Next time, we will start work on converting our G-machine instructions into machine code. We will set up LLVM and get our very first fully functional compiled programs in <a href="https://danilafe.com/blog/08_compiler_llvm/">Part 8 - LLVM</a>.</p> Switching to a Static Site Generator https://danilafe.com/blog/static_site/ Mon, 05 Aug 2019 01:13:58 -0700 https://danilafe.com/blog/static_site/ <p>A long time ago, I decided to try out Jekyll for my website. However, it all felt too convoluted: the ruby gems I needed to install, the configuration options for the site, the theming. I was given a template that already made several design decisions for me, and I didn&rsquo;t like it. So I wrote my own back end and front end, and was happy with it for a few years, periodically updating the look of the site. I even made a post on the site, called &ldquo;Ditching Jekyll&rdquo;, with the glorious contents:</p> <blockquote> <p>This morning, my guilt finally took over and I ditched the idea of using Jekyll. For the better, probably. I sat down to code, and got up several hours later, victorious! Thanks to a handy tutorial on Python and Flask written by Miguel Grinberg, found here [&ldquo;here&rdquo; is not a link], I wrote a bare bones website to host my silly blog posts and projects.</p> </blockquote> <p>Recently, however, I started to do a little more writing for the blog. My posts grew fairly long, and started to include nontrivial pieces of code, ones that I couldn&rsquo;t write and be sure without checking that they worked. Additionally, I felt an increasing need for version control. This was all very difficult through a custom backend, which doesn&rsquo;t come for free with features such as tracking changes and including code snippets. My workflow boiled town to the following:</p> <ol> <li>Write the skeleton post in a local Markdown file</li> <li>Write the relevant source code in a proper source file</li> <li>Copy-paste the new source code into the Markdown post.</li> <li>Copy-paste the full post into my custom website&rsquo;s input box.</li> </ol> <p>There are two steps where parts of a writing project can become out of sync with each other! Changing the source code means that you eventually have to update the Markdown file with the changes, and modifying the Markdown file also means that eventually, you&rsquo;ll have upload the new file to the website. It&rsquo;s inefficient, and there&rsquo;s a lot of redundancy.</p> <p>With this in mind, I think there are many advantages for using a static site generator for a personal website / blog. They are as follows:</p> <ol> <li>Content is first class. Your posts are files you can edit in any way you like, rather than opaque entities stored in a database. This also has the added benefit of allowing you to properly use version control with your posts.</li> <li>A static site generator can support complex multi-file setups. It took me about half an hour to configure my posts to pull code snippets from other files on disk, thus freeing me from having to copy-paste source into my posts.</li> <li>A custom back end is overcomplicating things. You write your own code to render posts, your own code to log the user in. This way you&rsquo;re not only spending more time than you need, but you&rsquo;re also adding more surface area for possible errors and bugs.</li> <li>A static site generator allows for the re-use of ideas and code. Rather than reinventing your own solution for tagging, rendering Markdown, syntax highlighting, and the like, you get these solutions out of the box, with a body of online questions and answers that have been generated over these very subjects.</li> </ol> <p>Having come to this conclusion, I will be switching my website to <a href="https://gohugo.io/"class="external-link">Hugo<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, which seems to be easier to configure and customize, without making too many assumptions about what you want in your website.</p> Compiling a Functional Language Using C++, Part 0 - Intro https://danilafe.com/blog/00_compiler_intro/ Sat, 03 Aug 2019 01:02:30 -0700 https://danilafe.com/blog/00_compiler_intro/ <p>During my last academic term, I was enrolled in a compilers course. We had a final project - develop a compiler for a basic Python subset, using LLVM. It was a little boring - virtually nothing about the compiler was <strong>not</strong> covered in class, and it felt more like putting two puzzle pieces together than building a real project.</p> <p>Instead, I chose to implement a compiler for a functional programming language, based on a wonderful book by Simon Peyton Jones, <em>Implementing functional languages: a tutorial</em>. Since the class was requiring the use of tools based on C++, that&rsquo;s what I used for my compiler. It was neat little project, and I wanted to share with everyone else how one might go about writing their own functional language.</p> <a href="#motivation"> <h3 id="motivation">Motivation</h3> </a> <p>There are two main motivating factors for this series.</p> <p>First, whenever I stumble on a compiler implementation tutorial, the language created is always imperative, inspired by C, C++, JavaScript, or Python. There are many interesting things about compiling such a language. However, I also think that the compilation of a functional language (including features like lazy evaluation) is interesting enough, and rarely covered.</p> <p>Second, I&rsquo;m inspired by books such as <em>Software Foundations</em> that use source code as their text. The entire content of <em>Software Foundations</em>, for instance, is written as comments in Coq source file. This means that you can not only read the book, but also run the code and interact with it. This makes it very engaging to read. Because of this, I want to provide for each post a &ldquo;snapshot&rdquo; of the project code. All the code in the posts will directly mirror that snapshot. The code you&rsquo;ll be reading will be runnable and open.</p> <a href="#overview"> <h3 id="overview">Overview</h3> </a> <p>Let&rsquo;s go over some preliminary information before we embark on this journey.</p> <a href="#the-classic-stages-of-a-compiler"> <h4 id="the-classic-stages-of-a-compiler">The &ldquo;classic&rdquo; stages of a compiler</h4> </a> <p>Let&rsquo;s take a look at the high level overview of what a compiler does. Conceptually, the components of a compiler are pretty cleanly separated. They are as follows:</p> <ol> <li>Tokenizing / lexical analysis</li> <li>Parsing</li> <li>Analysis / optimization</li> <li>Code Generation</li> </ol> <p>There are many variations on this structure. Some compilers don&rsquo;t optimize at all, some translate the program text into an intermediate representation, an alternative way of representing the program that isn&rsquo;t machine code. In some compilers, the stages of parsing and analysis can overlap. In short, just like the pirate&rsquo;s code, it&rsquo;s more of a guideline than a rule.</p> <a href="#what-we-will-cover"> <h4 id="what-we-will-cover">What we will cover</h4> </a> <p>We&rsquo;ll go through the stages of a compiler, starting from scratch and building up our project. We&rsquo;ll cover:</p> <ul> <li>Tokenizing using regular expressions and Flex.</li> <li>Parsing using context free grammars and Bison.</li> <li>Monomorphic type checking (including typing rules).</li> <li>Evaluation using graph reduction and the G-Machine.</li> <li>Compiling G-Machine instructions to machine code using LLVM.</li> </ul> <p>We&rsquo;ll be creating a <strong>lazily evaluated</strong>, <strong>functional</strong> language.</p> <a href="#what-we-wont-cover"> <h4 id="what-we-wont-cover">What we won&rsquo;t cover</h4> </a> <p>Surely a guide written by one person can&rsquo;t be comprehensive. Not only do I have a finite amount of time to share this informatiom with you, but I also don&rsquo;t want to dilute the content of the posts in this series. Furthermore, many things that we&rsquo;ll be using for this tutorial are taught by numerous other sources, and those sources do a better job than I would. So, here are some things that you might want to know for this series, which won&rsquo;t be covered by the series itself:</p> <ul> <li><a href="https://en.wikipedia.org/wiki/Theory_of_computation"class="external-link">Theory of computation<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, or, more specifically, <a href="https://en.wikipedia.org/wiki/Automata_theory"class="external-link">automata theory<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. Deterministic and nondeterministic finite automata are briefly mentioned during tokenizing, and context free grammars are used in our parser. However, I don&rsquo;t provide a real explanation for either of those things.</li> <li><a href="https://en.wikipedia.org/wiki/Functional_programming"class="external-link">Functional programming<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, with a touch of <a href="https://en.wikipedia.org/wiki/Lambda_calculus"class="external-link">lambda calculus<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. We jump straight into implementing the concepts from these.</li> <li>C++. I do my best to use correct and modern C++, but I&rsquo;m not an expert. I will not explain the syntax / semantics of C++ code included in these posts, but I will explain what my code does in the context of compilers.</li> </ul> <a href="#the-syntax-of-our-language"> <h4 id="the-syntax-of-our-language">The syntax of our language</h4> </a> <p>Simon Peyton Jones, in his two works regarding compiling functional languages, remarks that most functional languages are very similar, and vary largely in syntax. That&rsquo;s our main degree of freedom. We want to represent the following things, for sure:</p> <ul> <li>Defining functions</li> <li>Applying functions</li> <li>Arithmetic</li> <li>Algebraic data types (to represent lists, pairs, and the like)</li> <li>Pattern matching (to operate on data types)</li> </ul> <p>We can additionally support anonymous (lambda) functions, but compiling those is actually a bit trickier, so we will skip those for now. Arithmetic is the simplest to define - let&rsquo;s define it as we would expect: <code>3</code> is a number, <code>3+2*6</code> evaluates to 15. Function application isn&rsquo;t much more difficult - <code>f x</code> means &ldquo;apply f to x&rdquo;, and <code>f x + g x</code> means sum the result of applying f to x and g to x. That is, function application has higher precedence, or <strong>binds tighter</strong> than binary operators like plus.</p> <p>Next, let&rsquo;s define the syntax for declaring a function. Why not:</p> <pre tabindex="0"><code>defn f x = { x + x } </code></pre><p>As for declaring data types:</p> <pre tabindex="0"><code>data List = { Nil, Cons Int List } </code></pre><p>Notice that we are avoiding polymorphism here.</p> <p>Let&rsquo;s also define a syntax for pattern matching:</p> <pre tabindex="0"><code>case l of { Nil -&gt; { 0 } Cons x xs -&gt; { x } } </code></pre><p>The above means &ldquo;if the list <code>l</code> is <code>Nil</code>, then return 0, otherwise, if it&rsquo;s constructed from an integer and another list (as defined in our <code>data</code> example), return the integer&rdquo;.</p> <p>That&rsquo;s it for the introduction! In the next post, we&rsquo;ll cover tokenizng, which is the first step in converting source code into an executable program.</p> <a href="#navigation"> <h3 id="navigation">Navigation</h3> </a> <p>Here are the posts that I&rsquo;ve written so far for this series:</p> <ul> <li><a href="https://danilafe.com/blog/01_compiler_tokenizing/">Tokenizing</a></li> <li><a href="https://danilafe.com/blog/02_compiler_parsing/">Parsing</a></li> <li><a href="https://danilafe.com/blog/03_compiler_typechecking/">Typechecking</a></li> <li><a href="https://danilafe.com/blog/04_compiler_improvements/">Small Improvements</a></li> <li><a href="https://danilafe.com/blog/05_compiler_execution/">Execution</a></li> <li><a href="https://danilafe.com/blog/06_compiler_compilation/">Compilation</a></li> <li><a href="https://danilafe.com/blog/07_compiler_runtime/">Runtime</a></li> <li><a href="https://danilafe.com/blog/08_compiler_llvm/">LLVM</a></li> <li><a href="https://danilafe.com/blog/09_compiler_garbage_collection/">Garbage Collection</a></li> <li><a href="https://danilafe.com/blog/10_compiler_polymorphism/">Polymorphism</a></li> <li><a href="https://danilafe.com/blog/11_compiler_polymorphic_data_types/">Polymorphic Data Types</a></li> <li><a href="https://danilafe.com/blog/12_compiler_let_in_lambda/">Let/In and Lambdas</a></li> <li><a href="https://danilafe.com/blog/13_compiler_cleanup/">Cleanup</a></li> </ul> Compiling a Functional Language Using C++, Part 1 - Tokenizing https://danilafe.com/blog/01_compiler_tokenizing/ Sat, 03 Aug 2019 01:02:30 -0700 https://danilafe.com/blog/01_compiler_tokenizing/ <p>It makes sense to build a compiler bit by bit, following the stages we outlined in the first post of the series. This is because these stages are essentially a pipeline, with program text coming in one end, and the final program coming out of the other. So as we build up our pipeline, we&rsquo;ll be able to push program text further and further, until eventually we get something that we can run on our machine.</p> <p>This is how most tutorials go about building a compiler, too. The result is that there are a <strong>lot</strong> of tutorials covering tokenizing and parsing. Nonetheless, I will cover the steps required to tokenize and parse our little functional language. Before we start, it might help to refresh your memory about the syntax of the language, which we outlined in the <a href="https://danilafe.com/blog/00_compiler_intro/">previous post</a>.</p> <p>When we first get our program text, it&rsquo;s in a representation difficult for us to make sense of. If we look at how it&rsquo;s represented in C++, we see that it&rsquo;s just an array of characters (potentially hundreds, thousands, or millions in length). We <strong>could</strong> jump straight to parsing the text (which involves creating a tree structure, known as an <strong>abstract syntax tree</strong>; more on that later). There&rsquo;s nothing wrong with this approach - in fact, in functional languages, tokenizing is frequently skipped. However, in our closer-to-metal language (C++), it happens to be more convenient to first break the input text into a bunch of distinct segments (tokens).</p> <p>For example, consider the string &ldquo;320+6&rdquo;. If we skip tokenizing and go straight into parsing, we&rsquo;d feed our parser the sequence of characters <code>['3', '2', '6', '+', '6', '\0']</code>. On the other hand, if we run a tokenizing step on the string first, we&rsquo;d be feeding our parser three tokens, <code>(&quot;320&quot;, NUMBER)</code>, <code>(&quot;+&quot;, OPERATOR)</code>, and <code>(&quot;6&quot;, NUMBER)</code>. To us, this is a bit more clear - we&rsquo;ve partitioned the string into logical segments. Our parser, then, won&rsquo;t have to care about recognizing a number - it will just know that a number is next in the string, and do with that information what it needs.</p> <a href="#the-theory"> <h3 id="the-theory">The Theory</h3> </a> <p>How do we go about breaking up a string into tokens? We need to come up with a way to compare some characters in a string against a set of rules. But &ldquo;rules&rdquo; is a very general term - we could, for instance, define a particular token that is a fibonacci number - 1, 2, 3, 5, and so on would be marked as a &ldquo;fibonacci number&rdquo;, while the other numbers will be marked as just a regular number. To support that, our rules would get pretty complex. And equally complex will become our checking of these rules for particular strings.</p> <p>Fortunately, we&rsquo;re not insane. We observe that the rules for tokens in practice are fairly simple - one or more digits is an integer, a few letters together are a variable name. In order to be able to efficiently break text up into such tokens, we restrict ourselves to <strong>regular languages</strong>. A language is defined as a set of strings (potentially infinite), and a regular language is one for which we can write a <strong>regular expression</strong> to check if a string is in the set. Regular expressions are a way of representing patterns that a string has to match. We define regular expressions as follows:</p> <ul> <li>Any character is a regular expression that matches that character. Thus, \(a\) is a regular expression (from now shortened to regex) that matches the character &lsquo;a&rsquo;, and nothing else.</li> <li>\(r_1r_2\), or the concatenation of \(r_1\) and \(r_2\), is a regular expression that matches anything matched by \(r_1\), followed by anything that matches \(r_2\). For instance, \(ab\), matches the character &lsquo;a&rsquo; followed by the character &lsquo;b&rsquo; (thus matching &ldquo;ab&rdquo;).</li> <li>\(r_1|r_2\) matches anything that is either matched by \(r_1\) or \(r_2\). Thus, \(a|b\) matches the character &lsquo;a&rsquo; or the character &lsquo;b&rsquo;.</li> <li>\(r_1?\) matches either an empty string, or anything matched by \(r_1\).</li> <li>\(r_1+\) matches one or more things matched by \(r_1\). So, \(a+\) matches &ldquo;a&rdquo;, &ldquo;aa&rdquo;, &ldquo;aaa&rdquo;, and so on.</li> <li>\((r_1)\) matches anything that matches \(r_1\). This is mostly used to group things together in more complicated expressions.</li> <li>\(.\) matches any character.</li> </ul> <p>More powerful variations of regex also include an &ldquo;any of&rdquo; operator, \([c_1c_2c_3]\), which is equivalent to \(c_1|c_2|c_3\), and a &ldquo;range&rdquo; operator, \([c_1-c_n]\), which matches all characters in the range between \(c_1\) and \(c_n\), inclusive.</p> <p>Let&rsquo;s see some examples. An integer, such as 326, can be represented with \([0-9]+\). This means, one or more characters between 0 or 9. Some (most) regex implementations have a special symbol for \([0-9]\), written as \(\setminus d\). A variable, starting with a lowercase letter and containing lowercase or uppercase letters after it, can be written as \([a-z]([a-zA-Z]+)?\). Again, most regex implementations provide a special operator for \((r_1+)?\), written as \(r_1*\).</p> <p>So how does one go about checking if a regular expression matches a string? An efficient way is to first construct a <a href="https://en.wikipedia.org/wiki/Finite-state_machine"class="external-link">state machine<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. A type of state machine can be constructed from a regular expression by literally translating each part of it to a series of states, one-to-one. This machine is called a <strong>Nondeterministic Finite Automaton</strong>, or NFA for short. The &ldquo;Finite&rdquo; means that the number of states in the state machine is, well, finite. For us, this means that we can store such a machine on disk. The &ldquo;Nondeterministic&rdquo; part, though, is more complex: given a particular character and a particular state, it&rsquo;s possible that an NFA has the option of transitioning into more than one other state. Well, which state <strong>should</strong> it pick? No easy way to tell. Each time we can transition to more than one state, we exponentially increase the number of possible states that we can be in. This isn&rsquo;t good - we were going for efficiency, remember?</p> <p>What we can do is convert our NFA into another kind of state machine, in which for every character, only one possible state transition is possible. This machine is called a <strong>Deterministic Finite Automaton</strong>, or DFA for short. There&rsquo;s an algorithm to convert an NFA into a DFA, which I won&rsquo;t explain here.</p> <p>Since both the conversion of a regex into an NFA and a conversion of an NFA into a DFA is done by following an algorithm, we&rsquo;re always going to get the same DFA for the same regex we put in. If we come up with the rules for our tokens once, we don&rsquo;t want to be building a DFA each time our tokenizer is run - the result will always be the same! Even worse, translating a regular expression all the way into a DFA is the inefficient part of the whole process. The solution is to generate a state machine, and convert it into code to simulate that state machine. Then, we include that code as part of our compiler. This way, we have a state machine &ldquo;hardcoded&rdquo; into our tokenizer, and no conversion of regex to DFAs needs to be done at runtime.</p> <a href="#the-practice"> <h3 id="the-practice">The Practice</h3> </a> <p>Creating an NFA, and then a DFA, and then generating C++ code are all cumbersome. If we had to write code to do this every time we made a compiler, it would get very repetitive, very fast. Fortunately, there exists a tool that does exactly this for us - it&rsquo;s called <code>flex</code>. Flex takes regular expressions, and generates code that matches a string against those regular expressions. It does one more thing in addition to that - for each regular expression it matches, flex runs a user-defined action (which we write in C++). We can use this to convert strings that represent numbers directly into numbers, and do other small tasks.</p> <p>So, what tokens do we have? From our arithmetic definition, we see that we have integers. Let&rsquo;s use the regex <code>[0-9]+</code> for those. We also have the operators <code>+</code>, <code>-</code>, <code>*</code>, and <code>/</code>. The regex for <code>-</code> is simple enough: it&rsquo;s just <code>-</code>. However, we need to preface our <code>/</code>, <code>+</code> and <code>*</code> with a backslash, since they happen to also be modifiers in flex&rsquo;s regular expressions: <code>\/</code>, <code>\+</code>, <code>\*</code>.</p> <p>Let&rsquo;s also represent some reserved keywords. We&rsquo;ll say that <code>defn</code>, <code>data</code>, <code>case</code>, and <code>of</code> are reserved. Their regular expressions are just their names. We also want to tokenize <code>=</code>, <code>-&gt;</code>, <code>{</code>, <code>}</code>, <code>,</code>, <code>(</code> and <code>)</code>. Finally, we want to represent identifiers, like <code>f</code>, <code>x</code>, <code>Nil</code>, and <code>Cons</code>. We will actually make a distinction between lowercase identifiers and uppercase identifiers, as we will follow Haskell&rsquo;s convention of representing data type constructors with uppercase letters, and functions and variables with lowercase ones. So, our two regular expressions will be <code>[a-z][a-zA-Z]*</code> for the lowercase variables, and <code>[A-Z][a-zA-Z]*</code> for uppercase variables. Let&rsquo;s make a tokenizer in flex with all this. To do this, we create a new file, <code>scanner.l</code>, in which we write a mix of regular expressions and C++ code. Here&rsquo;s the whole thing:</p> <div class="highlight-group" > <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/01/scanner.l">scanner.l</a>, entire file</div><pre><code>%option noyywrap %{ #include &lt;iostream&gt; %} %% [ \n]+ {} \+ { std::cout &lt;&lt; &#34;PLUS&#34; &lt;&lt; std::endl; } \* { std::cout &lt;&lt; &#34;TIMES&#34; &lt;&lt; std::endl; } - { std::cout &lt;&lt; &#34;MINUS&#34; &lt;&lt; std::endl; } \/ { std::cout &lt;&lt; &#34;DIVIDE&#34; &lt;&lt; std::endl; } [0-9]+ { std::cout &lt;&lt; &#34;NUMBER: &#34; &lt;&lt; yytext &lt;&lt; std::endl; } defn { std::cout &lt;&lt; &#34;KEYWORD: defn&#34; &lt;&lt; std::endl; } data { std::cout &lt;&lt; &#34;KEYWORD: data&#34; &lt;&lt; std::endl; } case { std::cout &lt;&lt; &#34;KEYWORD: case&#34; &lt;&lt; std::endl; } of { std::cout &lt;&lt; &#34;KEYWORD: of&#34; &lt;&lt; std::endl; } \{ { std::cout &lt;&lt; &#34;OPEN CURLY&#34; &lt;&lt; std::endl; } \} { std::cout &lt;&lt; &#34;CLOSED CURLY&#34; &lt;&lt; std::endl; } \( { std::cout &lt;&lt; &#34;OPEN PARENTH&#34; &lt;&lt; std::endl; } \) { std::cout &lt;&lt; &#34;CLOSE PARENTH&#34; &lt;&lt; std::endl; } , { std::cout &lt;&lt; &#34;COMMA&#34; &lt;&lt; std::endl; } -&gt; { std::cout &lt;&lt; &#34;PATTERN ARROW&#34; &lt;&lt; std::endl; } = { std::cout &lt;&lt; &#34;EQUAL&#34; &lt;&lt; std::endl; } [a-z][a-zA-Z]* { std::cout &lt;&lt; &#34;LOWERCASE IDENTIFIER: &#34; &lt;&lt; yytext &lt;&lt; std::endl; } [A-Z][a-zA-Z]* { std::cout &lt;&lt; &#34;UPPERCASE IDENTIFIER: &#34; &lt;&lt; yytext &lt;&lt; std::endl; } %% int main() { yylex(); } </code></pre> </div> <p>A flex file starts with options. I set the <code>noyywrap</code> option, which disables a particular feature of flex that we won&rsquo;t use, and which causes linker errors. Next up, flex allows us to put some C++ code that we want at the top of our generated code. I simply include <code>iostream</code>, so that we can use <code>cout</code> to print out our tokens. Next, <code>%%</code>, and after that, the meat of our tokenizer: regular expressions, followed by C++ code that should be executed when the regular expression is matched.</p> <p>The first token: whitespace. This includes the space character, and the newline character. We ignore it, so its rule is empty. After that, we have the regular expressions for the tokens we&rsquo;ve talked about. For each, I just print a description of the token that matched. This will change when we hook this up to a parser, but for now, this works fine. Notice that the variable <code>yytext</code> contains the string matched by our regular expression. This variable is set by the code flex generates, and we can use it to get the extract text that matched a regex. This is useful, for instance, to print the variable name that we matched. After all of our tokens, another <code>%%</code>, and more C++ code. For this simple example, I declare a <code>main</code> function, which just calls <code>yylex</code>, a function flex generates for us. Let&rsquo;s generate the C++ code, and compile it:</p> <pre tabindex="0"><code>flex -o scanner.cpp scanner.l g++ -o scanner scanner.cpp </code></pre><p>Now, let&rsquo;s feed it an expression:</p> <pre tabindex="0"><code>echo &#34;3+2*6&#34; | ./scanner </code></pre><p>We get the output:</p> <pre tabindex="0"><code>NUMBER: 3 PLUS NUMBER: 2 TIMES NUMBER: 6 </code></pre><p>Hooray! We have tokenizing.</p> <p>With our text neatly divided into meaningful chunks, we can continue on to <a href="https://danilafe.com/blog/02_compiler_parsing/">Part 2 - Parsing</a>.</p> Compiling a Functional Language Using C++, Part 2 - Parsing https://danilafe.com/blog/02_compiler_parsing/ Sat, 03 Aug 2019 01:02:30 -0700 https://danilafe.com/blog/02_compiler_parsing/ <p>In the previous post, we covered tokenizing. We learned how to convert an input string into logical segments, and even wrote up a tokenizer to do it according to the rules of our language. Now, it&rsquo;s time to make sense of the tokens, and parse our language.</p> <a href="#the-theory"> <h3 id="the-theory">The Theory</h3> </a> <p>The rules to parse a language are more complicated than the rules for recognizing tokens. For instance, consider a simple language of a matching number of open and closed parentheses, like <code>()</code> and <code>((()))</code>. You can&rsquo;t write a regular expression for it! We resort to a wider class of languages, called <strong>context free languages</strong>. These languages are ones that are matched by <strong>context free grammars</strong>. A context free grammar is a list of rules in the form of \(S \rightarrow \alpha\), where \(S\) is a <strong>nonterminal</strong> (conceptualy, a thing that expands into other things), and \(\alpha\) is a sequence of nonterminals and terminals (a terminal is a thing that doesn&rsquo;t expand into other things; for us, this is a token).</p> <p>Let&rsquo;s write a context free grammar (CFG from now on) to match our parenthesis language:</p> $$ \begin{aligned} S &amp;amp; \rightarrow ( S ) \\ S &amp;amp; \rightarrow () \end{aligned} $$ <p>So, how does this work? We start with a &ldquo;start symbol&rdquo; nonterminal, which we usually denote as \(S\). Then, to get a desired string, we replace a nonterminal with the sequence of terminals and nonterminals on the right of one of its rules. For instance, to get <code>()</code>, we start with \(S\) and replace it with the body of the second one of its rules. This gives us <code>()</code> right away. To get <code>((()))</code>, we have to do a little more work:</p> $$ S \rightarrow (S) \rightarrow ((S)) \rightarrow ((())) $$ <p>In practice, there are many ways of using a CFG to parse a programming language. Various parsing algorithms support various subsets of context free languages. For instance, top down parsers follow nearly exactly the structure that we had. They try to parse a nonterminal by trying to match each symbol in its body. In the rule \(S \rightarrow \alpha \beta \gamma\), it will first try to match \(\alpha\), then \(\beta\), and so on. If one of the three contains a nonterminal, it will attempt to parse that nonterminal following the same strategy. However, this leaves a flaw - For instance, consider the grammar</p> $$ \begin{aligned} S &amp;amp; \rightarrow Sa \\ S &amp;amp; \rightarrow a \end{aligned} $$ <p>A top down parser will start with \(S\). It will then try the first rule, which starts with \(S\). So, dutifully, it will try to parse <strong>that</strong> \(S\). And to do that, it will once again try the first rule, and find that it starts with another \(S\)&hellip; This will never end, and the parser will get stuck. A grammar in which a nonterminal can appear in the beginning of one of its rules <strong>left recursive</strong>, and top-down parsers aren&rsquo;t able to handle those grammars.</p> <p>We <strong>could</strong> rewrite our grammar without using left-recursion, but we don&rsquo;t want to. Instead, we&rsquo;ll use a <strong>bottom up</strong> parser, using specifically the LALR(1) parsing algorithm. Here&rsquo;s an example of how it works, using our left-recursive grammar. We start with our goal string, and a &ldquo;dot&rdquo; indicating where we are. At first, the dot is behind all the characters:</p> $$ .aaa $$ <p>We see nothing interesting on the left side of the dot, so we move (or <strong>shift</strong>) the dot forward by one character:</p> $$ a.aa $$ <p>Now, on the left side of the dot, we see something! In particular, we see the body of one of the rules for \(S\) (the second one). So we <strong>reduce</strong> the thing on the left side of the dot, by replacing it with the left hand side of the rule (\(S\)):</p> $$ S.aa $$ <p>There&rsquo;s nothing else we can do with the left side, so we shift again:</p> $$ Sa.a $$ <p>Great, we see another body on the left of the dot. We reduce it:</p> $$ S.a $$ <p>Just like before, we shift over the dot, and again, we reduce. We end up with our start symbol, and nothing on the right of the dot, so we&rsquo;re done!</p> <a href="#the-practice"> <h3 id="the-practice">The Practice</h3> </a> <p>In practice, we don&rsquo;t want to just match a grammar. That would be like saying &ldquo;yup, this is our language&rdquo;. Instead, we want to create something called an <strong>abstract syntax tree</strong>, or AST for short. This tree captures the structure of our language, and is easier to work with than its textual representation. The structure of the tree we build will often mimic the structure of our grammar: a rule in the form \(S \rightarrow A B\) will result in a tree named &ldquo;S&rdquo;, with two children corresponding the trees built for A and B. Since an AST captures the structure of the language, we&rsquo;ll be able to toss away some punctuation like <code>,</code> and <code>(</code>. These tokens will appear in our grammar, but we will tweak our parser to simply throw them away. Additionally, we will write our grammar ignoring whitespace, since our tokenizer conveniently throws that into the trash.</p> <p>The grammar for arithmetic actually involves more effort than it would appear at first. We want to make sure that our parser respects the order of operations. This way, when we have our tree, it will immediately have the structure in which multiplication is done before addition. We do this by creating separate &ldquo;levels&rdquo; in our grammar, with one nonterminal matching addition and subtraction, while another nonterminal matches multiplication and division. We want the operation that has the least precedence to be <strong>higher</strong> in our tree than one of higher precedence. For instance, for <code>3+2*6</code>, we want our tree to have <code>+</code> as the root, <code>3</code> as the left child, and the tree for <code>2*6</code> as the right child. Why? Because this tree represents &ldquo;the addition of 3 and the result of multiplying 2 by 6&rdquo;. If we had <code>*</code> be the root, we&rsquo;d have a tree representing &ldquo;the multiplication of the result of adding 3 to 2 and 6&rdquo;, which is <strong>not</strong> what our expression means.</p> <p>So, with this in mind, we want our rule for <strong>addition</strong> (represented with the nonterminal \(A_{add}\), to be matched first, and for its children to be trees created by the multiplication rule, \(A_{mult}\). So we write the following rules:</p> $$ \begin{aligned} A_{add} &amp;amp; \rightarrow A_{add}&#43;A_{mult} \\ A_{add} &amp;amp; \rightarrow A_{add}-A_{mult} \\ A_{add} &amp;amp; \rightarrow A_{mult} \end{aligned} $$ <p>The first rule matches another addition, added to the result of a multiplication. Similarly, the second rule matches another addition, from which the result of a multiplication is then subtracted. We use the \(A_{add}\) on the left side of \(+\) and \(-\) in the body because we want to be able to parse strings like <code>1+2+3+4</code>, which we want to view as <code>((1+2)+3)+4</code> (mostly because subtraction is <a href="https://en.wikipedia.org/wiki/Operator_associativity"class="external-link">left-associative<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>). So, we want the top level of the tree to be the rightmost <code>+</code> or <code>-</code>, since that means it will be the &ldquo;last&rdquo; operation. You may be asking,</p> <blockquote> <p>You define addition in terms of addition; how will parsing end? What if there&rsquo;s no addition at all, like <code>2*6</code>?</p> </blockquote> <p>This is the purpose of the third rule, which serves to say &ldquo;an addition expression can just be a multiplication, without any plusses or minuses.&rdquo; Our rules for multiplication are very similar:</p> $$ \begin{aligned} A_{mult} &amp;amp; \rightarrow A_{mult}*P \\ A_{mult} &amp;amp; \rightarrow A_{mult}/P \\ A_{mult} &amp;amp; \rightarrow P \end{aligned} $$ <p>P, in this case, is an application (remember, application has higher precedence than any binary operator). Once again, if there&rsquo;s no <code>*</code> or <code>\</code>, we simply fall through to a \(P\) nonterminal, representing application.</p> <p>Application is refreshingly simple:</p> $$ \begin{aligned} P &amp;amp; \rightarrow P B \\ P &amp;amp; \rightarrow B \end{aligned} $$ <p>An application is either only one &ldquo;thing&rdquo; (represented with \(B\), for base), such as a number or an identifier, or another application followed by a thing.</p> <p>We now need to define what a &ldquo;thing&rdquo; is. As we said before, it&rsquo;s a number, or an identifier. We also make a parenthesized arithmetic expression a &ldquo;thing&rdquo;, allowing us to wrap right back around and recognize anything inside parentheses:</p> $$ \begin{aligned} B &amp;amp; \rightarrow \text{num} \\ B &amp;amp; \rightarrow \text{lowerVar} \\ B &amp;amp; \rightarrow \text{upperVar} \\ B &amp;amp; \rightarrow ( A_{add} ) \\ B &amp;amp; \rightarrow C \end{aligned} $$ <p>What&rsquo;s the last \(C\)? We also want a &ldquo;thing&rdquo; to be a case expression. Here are the rules for that:</p> $$ \begin{aligned} C &amp;amp; \rightarrow \text{case} \; A_{add} \; \text{of} \; \{ L_B\} \\ L_B &amp;amp; \rightarrow R \; L_B \\ L_B &amp;amp; \rightarrow R \\ R &amp;amp; \rightarrow N \; \text{arrow} \; \{ A_{add} \} \\ N &amp;amp; \rightarrow \text{lowerVar} \\ N &amp;amp; \rightarrow \text{upperVar} \; L_L \\ L_L &amp;amp; \rightarrow \text{lowerVar} \; L_L \\ L_L &amp;amp; \rightarrow \epsilon \end{aligned} $$ <p>\(L_B\) is the list of branches in our case expression. \(R\) is a single branch, which is in the form <code>Pattern -&gt; Expression</code>. \(N\) is a pattern, which we will for now define to be either a variable name (\(\text{lowerVar}\)), or a constructor with some arguments. The arguments of a constructor will be lowercase names, and a list of those arguments will be represented with \(L_L\). One of the bodies of this nonterminal is just the character \(\epsilon\), which just means &ldquo;nothing&rdquo;. We use this because a constructor can have no arguments (like Nil).</p> <p>We can use these grammar rules to represent any expression we want. For instance, let&rsquo;s try <code>3+(multiply 2 6)</code>, where multiply is a function that, well, multiplies. We start with \(A_{add}\):</p> $$ \begin{aligned} &amp;amp; A_{add} \\ &amp;amp; \rightarrow A_{add} &#43; A_{mult} \\ &amp;amp; \rightarrow A_{mult} &#43; A_{mult} \\ &amp;amp; \rightarrow P &#43; A_{mult} \\ &amp;amp; \rightarrow B &#43; A_{mult} \\ &amp;amp; \rightarrow \text{num(3)} &#43; A_{mult} \\ &amp;amp; \rightarrow \text{num(3)} &#43; P \\ &amp;amp; \rightarrow \text{num(3)} &#43; B \\ &amp;amp; \rightarrow \text{num(3)} &#43; (A_{add}) \\ &amp;amp; \rightarrow \text{num(3)} &#43; (A_{mult}) \\ &amp;amp; \rightarrow \text{num(3)} &#43; (P) \\ &amp;amp; \rightarrow \text{num(3)} &#43; (P \; \text{num(6)}) \\ &amp;amp; \rightarrow \text{num(3)} &#43; (P \; \text{num(2)} \; \text{num(6)}) \\ &amp;amp; \rightarrow \text{num(3)} &#43; (\text{lowerVar(multiply)} \; \text{num(2)} \; \text{num(6)}) \\ \end{aligned} $$ <p>We&rsquo;re almost there. We now want a rule for a &ldquo;something that can appear at the top level of a program&rdquo;, like a function or data type declaration. We make a new set of rules:</p> $$ \begin{aligned} T &amp;amp; \rightarrow \text{defn} \; \text{lowerVar} \; L_P =\{ A_{add} \} \\ T &amp;amp; \rightarrow \text{data} \; \text{upperVar} = \{ L_D \} \\ L_D &amp;amp; \rightarrow D \; , \; L_D \\ L_D &amp;amp; \rightarrow D \\ L_P &amp;amp; \rightarrow \text{lowerVar} \; L_P \\ L_P &amp;amp; \rightarrow \epsilon \\ D &amp;amp; \rightarrow \text{upperVar} \; L_U \\ L_U &amp;amp; \rightarrow \text{upperVar} \; L_U \\ L_U &amp;amp; \rightarrow \epsilon \end{aligned} $$ <p>That&rsquo;s a lot of rules! \(T\) is the &ldquo;top-level declaration rule. It matches either a function or a data definition. A function definition consists of the keyword &ldquo;defn&rdquo;, followed by a function name (starting with a lowercase letter), followed by a list of parameters, represented by \(L_P\).</p> <p>A data type definition consists of the name of the data type (starting with an uppercase letter), and a list \(L_D\) of data constructors \(D\). There must be at least one data constructor in this list, so we don&rsquo;t use the empty string rule here. A data constructor is simply an uppercase variable representing a constructor of the data type, followed by a list \(L_U\) of zero or more uppercase variables (representing the types of the arguments of the constructor).</p> <p>Finally, we want one or more of these declarations in a valid program: $$ \begin{aligned} G &amp;amp; \rightarrow T \; G \\ G &amp;amp; \rightarrow T \end{aligned} $$ </p> <p>Just like with tokenizing, there exists a piece of software that will generate a bottom-up parser for us, given our grammar. It&rsquo;s called Bison, and it is frequently used with Flex. Before we get to bison, though, we need to pay a debt we&rsquo;ve already incurred - the implementation of our AST. Such a tree is language-specific, so Bison doesn&rsquo;t generate it for us. Here&rsquo;s what I came up with: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/02/ast.hpp"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/02/ast.hpp">ast.hpp</a>, entire file</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt"> 10 </span><span class="lnt"> 11 </span><span class="lnt"> 12 </span><span class="lnt"> 13 </span><span class="lnt"> 14 </span><span class="lnt"> 15 </span><span class="lnt"> 16 </span><span class="lnt"> 17 </span><span class="lnt"> 18 </span><span class="lnt"> 19 </span><span class="lnt"> 20 </span><span class="lnt"> 21 </span><span class="lnt"> 22 </span><span class="lnt"> 23 </span><span class="lnt"> 24 </span><span class="lnt"> 25 </span><span class="lnt"> 26 </span><span class="lnt"> 27 </span><span class="lnt"> 28 </span><span class="lnt"> 29 </span><span class="lnt"> 30 </span><span class="lnt"> 31 </span><span class="lnt"> 32 </span><span class="lnt"> 33 </span><span class="lnt"> 34 </span><span class="lnt"> 35 </span><span class="lnt"> 36 </span><span class="lnt"> 37 </span><span class="lnt"> 38 </span><span class="lnt"> 39 </span><span class="lnt"> 40 </span><span class="lnt"> 41 </span><span class="lnt"> 42 </span><span class="lnt"> 43 </span><span class="lnt"> 44 </span><span class="lnt"> 45 </span><span class="lnt"> 46 </span><span class="lnt"> 47 </span><span class="lnt"> 48 </span><span class="lnt"> 49 </span><span class="lnt"> 50 </span><span class="lnt"> 51 </span><span class="lnt"> 52 </span><span class="lnt"> 53 </span><span class="lnt"> 54 </span><span class="lnt"> 55 </span><span class="lnt"> 56 </span><span class="lnt"> 57 </span><span class="lnt"> 58 </span><span class="lnt"> 59 </span><span class="lnt"> 60 </span><span class="lnt"> 61 </span><span class="lnt"> 62 </span><span class="lnt"> 63 </span><span class="lnt"> 64 </span><span class="lnt"> 65 </span><span class="lnt"> 66 </span><span class="lnt"> 67 </span><span class="lnt"> 68 </span><span class="lnt"> 69 </span><span class="lnt"> 70 </span><span class="lnt"> 71 </span><span class="lnt"> 72 </span><span class="lnt"> 73 </span><span class="lnt"> 74 </span><span class="lnt"> 75 </span><span class="lnt"> 76 </span><span class="lnt"> 77 </span><span class="lnt"> 78 </span><span class="lnt"> 79 </span><span class="lnt"> 80 </span><span class="lnt"> 81 </span><span class="lnt"> 82 </span><span class="lnt"> 83 </span><span class="lnt"> 84 </span><span class="lnt"> 85 </span><span class="lnt"> 86 </span><span class="lnt"> 87 </span><span class="lnt"> 88 </span><span class="lnt"> 89 </span><span class="lnt"> 90 </span><span class="lnt"> 91 </span><span class="lnt"> 92 </span><span class="lnt"> 93 </span><span class="lnt"> 94 </span><span class="lnt"> 95 </span><span class="lnt"> 96 </span><span class="lnt"> 97 </span><span class="lnt"> 98 </span><span class="lnt"> 99 </span><span class="lnt">100 </span><span class="lnt">101 </span><span class="lnt">102 </span><span class="lnt">103 </span><span class="lnt">104 </span><span class="lnt">105 </span><span class="lnt">106 </span><span class="lnt">107 </span><span class="lnt">108 </span><span class="lnt">109 </span><span class="lnt">110 </span><span class="lnt">111 </span><span class="lnt">112 </span><span class="lnt">113 </span><span class="lnt">114 </span><span class="lnt">115 </span><span class="lnt">116 </span><span class="lnt">117 </span><span class="lnt">118 </span><span class="lnt">119 </span><span class="lnt">120 </span><span class="lnt">121 </span><span class="lnt">122 </span><span class="lnt">123 </span><span class="lnt">124 </span><span class="lnt">125 </span><span class="lnt">126 </span><span class="lnt">127 </span><span class="lnt">128 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="cp">#pragma once </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;memory&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;vector&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">ast</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">virtual</span> <span class="o">~</span><span class="n">ast</span><span class="p">()</span> <span class="o">=</span> <span class="k">default</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="n">ast_ptr</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">unique_ptr</span><span class="o">&lt;</span><span class="n">ast</span><span class="o">&gt;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">pattern</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">virtual</span> <span class="o">~</span><span class="n">pattern</span><span class="p">()</span> <span class="o">=</span> <span class="k">default</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="n">pattern_ptr</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">unique_ptr</span><span class="o">&lt;</span><span class="n">pattern</span><span class="o">&gt;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">branch</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">pattern_ptr</span> <span class="n">pat</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">ast_ptr</span> <span class="n">expr</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">branch</span><span class="p">(</span><span class="n">pattern_ptr</span> <span class="n">p</span><span class="p">,</span> <span class="n">ast_ptr</span> <span class="n">a</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">pat</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">p</span><span class="p">)),</span> <span class="n">expr</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">a</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="n">branch_ptr</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">unique_ptr</span><span class="o">&lt;</span><span class="n">branch</span><span class="o">&gt;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">constructor</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">types</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">constructor</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">n</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">ts</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">name</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">n</span><span class="p">)),</span> <span class="n">types</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">ts</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="n">constructor_ptr</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">unique_ptr</span><span class="o">&lt;</span><span class="n">constructor</span><span class="o">&gt;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">definition</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">virtual</span> <span class="o">~</span><span class="n">definition</span><span class="p">()</span> <span class="o">=</span> <span class="k">default</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="n">definition_ptr</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">unique_ptr</span><span class="o">&lt;</span><span class="n">definition</span><span class="o">&gt;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">enum</span> <span class="nc">binop</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">PLUS</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">MINUS</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">TIMES</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="n">DIVIDE</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">ast_int</span> <span class="o">:</span> <span class="k">public</span> <span class="n">ast</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">value</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">explicit</span> <span class="nf">ast_int</span><span class="p">(</span><span class="kt">int</span> <span class="n">v</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">value</span><span class="p">(</span><span class="n">v</span><span class="p">)</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">ast_lid</span> <span class="o">:</span> <span class="k">public</span> <span class="n">ast</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">id</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">explicit</span> <span class="nf">ast_lid</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">i</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">id</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">i</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">ast_uid</span> <span class="o">:</span> <span class="k">public</span> <span class="n">ast</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">id</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">explicit</span> <span class="nf">ast_uid</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">i</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">id</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">i</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">ast_binop</span> <span class="o">:</span> <span class="k">public</span> <span class="n">ast</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">binop</span> <span class="n">op</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">ast_ptr</span> <span class="n">left</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">ast_ptr</span> <span class="n">right</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">ast_binop</span><span class="p">(</span><span class="n">binop</span> <span class="n">o</span><span class="p">,</span> <span class="n">ast_ptr</span> <span class="n">l</span><span class="p">,</span> <span class="n">ast_ptr</span> <span class="n">r</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">op</span><span class="p">(</span><span class="n">o</span><span class="p">),</span> <span class="n">left</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">l</span><span class="p">)),</span> <span class="n">right</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">r</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">ast_app</span> <span class="o">:</span> <span class="k">public</span> <span class="n">ast</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">ast_ptr</span> <span class="n">left</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">ast_ptr</span> <span class="n">right</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">ast_app</span><span class="p">(</span><span class="n">ast_ptr</span> <span class="n">l</span><span class="p">,</span> <span class="n">ast_ptr</span> <span class="n">r</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">left</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">l</span><span class="p">)),</span> <span class="n">right</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">r</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">ast_case</span> <span class="o">:</span> <span class="k">public</span> <span class="n">ast</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">ast_ptr</span> <span class="n">of</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">branch_ptr</span><span class="o">&gt;</span> <span class="n">branches</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">ast_case</span><span class="p">(</span><span class="n">ast_ptr</span> <span class="n">o</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">branch_ptr</span><span class="o">&gt;</span> <span class="n">b</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">of</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">o</span><span class="p">)),</span> <span class="n">branches</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">b</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">pattern_var</span> <span class="o">:</span> <span class="k">public</span> <span class="n">pattern</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">var</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">pattern_var</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">v</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">var</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">v</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">pattern_constr</span> <span class="o">:</span> <span class="k">public</span> <span class="n">pattern</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">constr</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">params</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">pattern_constr</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">c</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">p</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">constr</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">c</span><span class="p">)),</span> <span class="n">params</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">p</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">definition_defn</span> <span class="o">:</span> <span class="k">public</span> <span class="n">definition</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">params</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">ast_ptr</span> <span class="n">body</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">definition_defn</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">n</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">p</span><span class="p">,</span> <span class="n">ast_ptr</span> <span class="n">b</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">name</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">n</span><span class="p">)),</span> <span class="n">params</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">p</span><span class="p">)),</span> <span class="n">body</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">b</span><span class="p">))</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">definition_data</span> <span class="o">:</span> <span class="k">public</span> <span class="n">definition</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">constructor_ptr</span><span class="o">&gt;</span> <span class="n">constructors</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="n">definition_data</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">n</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">constructor_ptr</span><span class="o">&gt;</span> <span class="n">cs</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="o">:</span> <span class="n">name</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">n</span><span class="p">)),</span> <span class="n">constructors</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">cs</span><span class="p">))</span> <span class="p">{}</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span></code></pre></td></tr></table> </div> </div> </div> We create a base class for an expression tree, which we call <code>ast</code>. Then, for each possible syntactic construct in our language (a number, a variable, a binary operation, a case expression) we create a subclass of <code>ast</code>. The <code>ast_case</code> subclass is the most complex, since it must contain a list of case expression branches, which are a combination of a <code>pattern</code> and another expression.</p> <p>Finally, we get to writing our Bison file, <code>parser.y</code>. Here&rsquo;s what I come up with: <div class="highlight-group" > <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/02/parser.y">parser.y</a>, entire file</div><pre><code>%{ #include &lt;string&gt; #include &lt;iostream&gt; #include &#34;ast.hpp&#34; #include &#34;parser.hpp&#34; std::vector&lt;definition_ptr&gt; program; extern yy::parser::symbol_type yylex(); %} %token PLUS %token TIMES %token MINUS %token DIVIDE %token &lt;int&gt; INT %token DEFN %token DATA %token CASE %token OF %token OCURLY %token CCURLY %token OPAREN %token CPAREN %token COMMA %token ARROW %token EQUAL %token &lt;std::string&gt; LID %token &lt;std::string&gt; UID %language &#34;c++&#34; %define api.value.type variant %define api.token.constructor %type &lt;std::vector&lt;std::string&gt;&gt; lowercaseParams uppercaseParams %type &lt;std::vector&lt;definition_ptr&gt;&gt; program definitions %type &lt;std::vector&lt;branch_ptr&gt;&gt; branches %type &lt;std::vector&lt;constructor_ptr&gt;&gt; constructors %type &lt;ast_ptr&gt; aAdd aMul case app appBase %type &lt;definition_ptr&gt; definition defn data %type &lt;branch_ptr&gt; branch %type &lt;pattern_ptr&gt; pattern %type &lt;constructor_ptr&gt; constructor %start program %% program : definitions { program = std::move($1); } ; definitions : definitions definition { $$ = std::move($1); $$.push_back(std::move($2)); } | definition { $$ = std::vector&lt;definition_ptr&gt;(); $$.push_back(std::move($1)); } ; definition : defn { $$ = std::move($1); } | data { $$ = std::move($1); } ; defn : DEFN LID lowercaseParams EQUAL OCURLY aAdd CCURLY { $$ = definition_ptr( new definition_defn(std::move($2), std::move($3), std::move($6))); } ; lowercaseParams : %empty { $$ = std::vector&lt;std::string&gt;(); } | lowercaseParams LID { $$ = std::move($1); $$.push_back(std::move($2)); } ; uppercaseParams : %empty { $$ = std::vector&lt;std::string&gt;(); } | uppercaseParams UID { $$ = std::move($1); $$.push_back(std::move($2)); } ; aAdd : aAdd PLUS aMul { $$ = ast_ptr(new ast_binop(PLUS, std::move($1), std::move($3))); } | aAdd MINUS aMul { $$ = ast_ptr(new ast_binop(MINUS, std::move($1), std::move($3))); } | aMul { $$ = std::move($1); } ; aMul : aMul TIMES app { $$ = ast_ptr(new ast_binop(TIMES, std::move($1), std::move($3))); } | aMul DIVIDE app { $$ = ast_ptr(new ast_binop(DIVIDE, std::move($1), std::move($3))); } | app { $$ = std::move($1); } ; app : app appBase { $$ = ast_ptr(new ast_app(std::move($1), std::move($2))); } | appBase { $$ = std::move($1); } ; appBase : INT { $$ = ast_ptr(new ast_int($1)); } | LID { $$ = ast_ptr(new ast_lid(std::move($1))); } | UID { $$ = ast_ptr(new ast_uid(std::move($1))); } | OPAREN aAdd CPAREN { $$ = std::move($2); } | case { $$ = std::move($1); } ; case : CASE aAdd OF OCURLY branches CCURLY { $$ = ast_ptr(new ast_case(std::move($2), std::move($5))); } ; branches : branches branch { $$ = std::move($1); $1.push_back(std::move($2)); } | branch { $$ = std::vector&lt;branch_ptr&gt;(); $$.push_back(std::move($1));} ; branch : pattern ARROW OCURLY aAdd CCURLY { $$ = branch_ptr(new branch(std::move($1), std::move($4))); } ; pattern : LID { $$ = pattern_ptr(new pattern_var(std::move($1))); } | UID lowercaseParams { $$ = pattern_ptr(new pattern_constr(std::move($1), std::move($2))); } ; data : DATA UID EQUAL OCURLY constructors CCURLY { $$ = definition_ptr(new definition_data(std::move($2), std::move($5))); } ; constructors : constructors COMMA constructor { $$ = std::move($1); $$.push_back(std::move($3)); } | constructor { $$ = std::vector&lt;constructor_ptr&gt;(); $$.push_back(std::move($1)); } ; constructor : UID uppercaseParams { $$ = constructor_ptr(new constructor(std::move($1), std::move($2))); } ; </code></pre> </div> </p> <p>There&rsquo;s a few things to note here. First of all, the <strong>parser</strong> is the &ldquo;source of truth&rdquo; regarding what tokens exist in our language. We have a list of <code>%token</code> declarations, each of which corresponds to a regular expression in our scanner.</p> <p>Next, observe that there&rsquo;s a certain symmetry between our parser and our scanner. In our scanner, we mixed the theoretical idea of a regular expression with an <strong>action</strong>, a C++ code snippet to be executed when a regular expression is matched. This same idea is present in the parser, too. Each rule can produce a value, which we call a <strong>semantic value</strong>. For type safety, we allow each nonterminal and terminal to produce only one type of semantic value. For instance, all rules for \(A_{add}\) must produce an expression. We specify the type of each nonterminal and using <code>%type</code> directives. The types of terminals are specified when they&rsquo;re declared.</p> <p>Next, we must recognize that Bison was originally made for C, rather than C++. In order to allow the parser to store and operate on semantic values of various types, the canonical solution back in those times was to use a C <code>union</code>. Unions are great, but for C++, they&rsquo;re more trouble than they&rsquo;re worth: unions don&rsquo;t allow for non-trivial constructors! This means that stuff like <code>std::unique_ptr</code> and <code>std::string</code> is off limits as a semantic value. But we&rsquo;d really much rather use them! The solution is to:</p> <ol> <li>Specify the language to be C++, rather than C.</li> <li>Enable the <code>variant</code> API feature, which uses a lightweight <code>std::variant</code> alternative in place of a union.</li> <li>Enable the creation of token constructors, which we will use in Flex.</li> </ol> <p>In order to be able to use the variant-based API, we also need to change the Flex <code>yylex</code> function to return <code>yy::parser::symbol_type</code>. You can see it in our forward declaration of <code>yylex</code>.</p> <p>Now that we made these changes, it&rsquo;s time to hook up Flex to all this. Here&rsquo;s a new version of the Flex scanner, with all necessary modifications: <div class="highlight-group" > <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/02/scanner.l">scanner.l</a>, entire file</div><pre><code>%option noyywrap %{ #include &lt;iostream&gt; #include &#34;ast.hpp&#34; #include &#34;parser.hpp&#34; #define YY_DECL yy::parser::symbol_type yylex() %} %% [ \n]+ {} \+ { return yy::parser::make_PLUS(); } \* { return yy::parser::make_TIMES(); } - { return yy::parser::make_MINUS(); } \/ { return yy::parser::make_DIVIDE(); } [0-9]+ { return yy::parser::make_INT(atoi(yytext)); } defn { return yy::parser::make_DEFN(); } data { return yy::parser::make_DATA(); } case { return yy::parser::make_CASE(); } of { return yy::parser::make_OF(); } \{ { return yy::parser::make_OCURLY(); } \} { return yy::parser::make_CCURLY(); } \( { return yy::parser::make_OPAREN(); } \) { return yy::parser::make_CPAREN(); } , { return yy::parser::make_COMMA(); } -&gt; { return yy::parser::make_ARROW(); } = { return yy::parser::make_EQUAL(); } [a-z][a-zA-Z]* { return yy::parser::make_LID(std::string(yytext)); } [A-Z][a-zA-Z]* { return yy::parser::make_UID(std::string(yytext)); } %% </code></pre> </div> </p> <p>The key two ideas are that we overrode the default signature of <code>yylex</code> by changing the <code>YY_DECL</code> preprocessor variable, and used the <code>yy::parser::make_&lt;TOKEN&gt;</code> functions to return the <code>symbol_type</code> rather than <code>int</code>.</p> <p>Finally, let&rsquo;s get a main function so that we can at least check for segmentation faults and other obvious mistakes: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/02/main.cpp"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/02/main.cpp">main.cpp</a>, entire file</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-C++" data-lang="C++"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;ast.hpp&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;parser.hpp&#34;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp"></span> </span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="n">yy</span><span class="o">::</span><span class="n">parser</span><span class="o">::</span><span class="n">error</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">msg</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;An error occured: &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">msg</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="k">extern</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">definition_ptr</span><span class="o">&gt;</span> <span class="n">program</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">yy</span><span class="o">::</span><span class="n">parser</span> <span class="n">parser</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">parser</span><span class="p">.</span><span class="n">parse</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">program</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></td></tr></table> </div> </div> </div> </p> <p>Now, we can compile and run the code: <div class="highlight-group" data-base-path="compiler" data-file-path="compiler/02/compile.sh"> <div class="highlight-label">From <a href="https://dev.danilafe.com/DanilaFe/bloglang/src/commit/137455b0f4365ba3fd11c45ce49781cdbe829ec3/02/compile.sh">compile.sh</a>, entire file</div><div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt">1 </span><span class="lnt">2 </span><span class="lnt">3 </span><span class="lnt">4 </span><span class="lnt">5 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">bison -o parser.cpp -d parser.y </span></span><span class="line"><span class="cl">flex -o scanner.cpp scanner.l </span></span><span class="line"><span class="cl">g++ -c -o scanner.o scanner.cpp </span></span><span class="line"><span class="cl">g++ -c -o parser.o parser.cpp </span></span><span class="line"><span class="cl">g++ main.cpp parser.o scanner.o </span></span></code></pre></td></tr></table> </div> </div> </div> We used the <code>-d</code> option for Bison to generate the <code>compiler_parser.hpp</code> header file, which exports our token declarations and token creation functions, allowing us to use them in Flex.</p> <p>At last, we can feed some code to the parser from <code>stdin</code>. Let&rsquo;s try it:</p> <pre tabindex="0"><code>./a.out defn main = { add 320 6 } defn add x y = { x + y } </code></pre><p>The program prints <code>2</code>, indicating two declarations were made. Let&rsquo;s try something obviously wrong:</p> <pre tabindex="0"><code>./a.out }{ </code></pre><p>We are told an error occured. Excellent! We&rsquo;re not really sure what our tree looks like, though. We just know there&rsquo;s <strong>stuff</strong> in the list of definitions. Having read our source code into a structure we&rsquo;re more equipped to handle, we can now try to verify that the code makes sense in <a href="https://danilafe.com/blog/03_compiler_typechecking/">Part 3 - Type Checking</a>.</p> Local Development Environment for JOS and CS 444 https://danilafe.com/blog/jos_local/ Sun, 07 Apr 2019 06:01:00 +0000 https://danilafe.com/blog/jos_local/ <p>Working with the instructor&rsquo;s dotfiles on the server is great and all, but there&rsquo;s a few things you can do loccally that you can&rsquo;t do on the school server:</p> <ol> <li><strong>Use QEMU&rsquo;s X environment to test VGA.</strong> If you&rsquo;re like me, and want to get color working in JOS, you want to use QEMU&rsquo;s X display. But to my knowlege, there is no easy way to do that from the server.</li> <li><strong>Work offline.</strong> What if you don&rsquo;t have internet available? You won&rsquo;t have a way to connect to the shcool server, and therefore work on the lab.</li> <li><strong>Work with no latency.</strong> In my experience, the school server is fairly laggy and inconvenient to use. It&rsquo;s much nicer to develop locally, where your keys don&rsquo;t take a noticeable amount of time to appear on the screen.</li> <li><strong>Use your own editor.</strong> vim is nice, and it&rsquo;s my editor of choice, but some others prefer VScode, Atom, and the like.</li> </ol> <a href="#getting-started"> <h3 id="getting-started">Getting Started</h3> </a> <p>By default, most distributions of <code>gcc</code> do not provide the <code>i386</code> architecture. We need <code>i386</code> since that&rsquo;s the archtecture of our virtual target CPU. The JOS makefile looks for a compiler that does support the architecture - but usually doesn&rsquo;t find it. So, we need a version of that compiler that does. Before we get started, <strong>make sure you need to do this</strong>. If <code>make</code> inside the JOS directory works out of the box, you don&rsquo;t!</p> <p>Let&rsquo;s first make a directory where we will put all the programs that will be needed to compile for <code>i386</code>.</p> <pre tabindex="0"><code>mkdir ~/.i386-toolchain </code></pre><p>Additionally, let&rsquo;s make a directory where we can download and extract various tarballs, if only for a while. I usually consider my &ldquo;downloads&rdquo; folder a workspace where that can be done.</p> <pre tabindex="0"><code>cd ~/Downloads </code></pre><p>Let&rsquo;s start by downloading and building GNU <code>binutils</code>, followed by <code>gcc</code>.</p> <a href="#building-binutils"> <h3 id="building-binutils">Building binutils</h3> </a> <p>Let&rsquo;s download <code>binutils</code> v2.32 (latest at the time of writing):</p> <pre tabindex="0"><code>curl -O http://ftp.gnu.org/gnu/binutils/binutils-2.32.tar.gz tar -xzf binutils-2.32.tar.gz cd binutils-2.32 </code></pre><p>Now, we will run the <code>configure</code> script to generate a working <code>Makefile</code>. We can&rsquo;t just run configure with no parameters - we need to specify a custom install location (our <code>i386-toolchain</code> folder), and the target architecture that we want to build for (<code>i386</code>, of course). It&rsquo;s easiest to keep these in variables:</p> <pre tabindex="0"><code>export TARGET=i386-elf export PREFIX=~/.i386-toolchain </code></pre><p>We&rsquo;re ready to run the configure script:</p> <pre tabindex="0"><code>./configure --prefix=$PREFIX --target=$TARGET --disable-nls --enable=languages=c </code></pre><p>This generates a <code>Makefile</code>, which we will promptly use:</p> <pre tabindex="0"><code>make -j8 &amp;&amp; make install </code></pre><p>This should install <code>binutils</code> to the toolchain directory.</p> <a href="#building-gcc"> <h3 id="building-gcc">Building gcc</h3> </a> <p>The latest version of <code>gcc</code> at the time of writing is v8.3.0. Let&rsquo;s similarly download and untar this:</p> <pre tabindex="0"><code>curl -O ftp://ftp.gnu.org/gnu/gcc/gcc-8.3.0/gcc-8.3.0.tar.gz tar -xzf gcc-8.3.0.tar.gz cd gcc-8.3.0 </code></pre><p>I&rsquo;ve run into issues compiling <code>gcc</code> without using a separate build directory, so I recommend you do that:</p> <pre tabindex="0"><code>mkdir build &amp;&amp; cd build </code></pre><p>We will now run a similar <code>configure</code> script, with one addition: we specify the languages we want to use.</p> <pre tabindex="0"><code>../configure --prefix=$PREFIX --target=$TARGET --disable-nls --enable-languages=c </code></pre><p>Ah, but we don&rsquo;t want to just run <code>make</code> - we just want the compiler and the standard library (<code>libgcc</code>, which <code>gcc</code> tries to link to JOS and everything else). Thus, our make command is as follows:</p> <pre tabindex="0"><code>make all-gcc all-target-libgcc -j8 make install-gcc install-target-libgcc </code></pre><p>Finally, we can move into our local copy of JOS. I assume you have it cloned - I&rsquo;m sure you don&rsquo;t need my help to do that. There&rsquo;s a configuration setting that we have to set to make sure the <code>Makefile</code> uses the correct compiler. This is inside <code>conf/env.mk</code>:</p> <pre tabindex="0"><code>GCCPREFIX=&#39;~/.i386-toolchain/bin/i386-elf-&#39; </code></pre><p>If you run <code>make</code> inside of the JOS directory, the kernel should compile!</p> <a href="#dotfiles"> <h3 id="dotfiles">dotfiles</h3> </a> <p>A lot of the dotfiles the professor provides are very nice. The one I find most useful is the <code>.gdbinit</code> file and the dashboard it comes with. Fortunately, even without having access to the script Dr. Jang provides in the server&rsquo;s network filesystem, we can set up most of his dotfiles. It&rsquo;s easy enough - they&rsquo;re hosted on his GitHub! For simplicity, let&rsquo;s clone these files in the same location as the canonical script does:</p> <pre tabindex="0"><code>git clone https://github.com/blue9057/peda ~/.cs444 </code></pre><p>Personally, I only use the <code>gdbinit</code> script. If you don&rsquo;t have an existing one, we can link it in:</p> <pre tabindex="0"><code>cd ~ ln -sf ~/.cs444/gdbinit .gdbinit </code></pre><p>That should do it!</p> <a href="#conclusion"> <h3 id="conclusion">Conclusion</h3> </a> <p>With this, we&rsquo;ve set up a compiler for <code>i386</code> and pulled some dotfiles recommended by the professor. I hope this makes for easier development - with whatever tools you prefer, locally!</p> Lambda Calculus and Church Encoded Integers https://danilafe.com/blog/lambda_calculus_integers/ Fri, 29 Mar 2019 05:21:26 +0000 https://danilafe.com/blog/lambda_calculus_integers/ <p>You always hear the words &ldquo;turning complete&rdquo; thrown around in various contexts. Powerpoint is Turing complete, CSS is Turing complete. Turing completeness effectively describes a property of a language of system to perform any computation that can be performed on a computer (or by a human with a piece of paper). However, there&rsquo;s nothing special about Turing completeness (or Turing machines) that makes them <strong>the</strong> way to represent that property. In fact, two other representations of computation were devised before the introduction of Turing machines - lambda calculus (proposed by Alonso Church) and general recursive functions (proposed by Kurt Gödel). These ways to represent computation can be shown to be equivalent to Turning machines. In this post I will talk about what lambda calculus is, as well as a way to use it to represent natural numbers.</p> <p>Side note: apparently, &ldquo;natural numbers&rdquo; can mean either positive integers, or just nonnegative integers. In this case, we will pick the latter definition.</p> <a href="#abstraction-and-application"> <h3 id="abstraction-and-application">Abstraction and Application</h3> </a> <p>The book <em>Structure and Interpretation of Computer Programs</em> provides this list of 3 essential mechanisms present in any powerful language:</p> <ul> <li><strong>primitive expressions</strong>, which represent the simplest entities the language is concerned with,</li> <li><strong>means of combination</strong>, by which compound elements are built from simpler ones, and</li> <li><strong>means of abstraction</strong>, by which compound elements can be named and manipulated as units.</li> </ul> <p>Abstraction refers to generalizing some operation. Usually, this is done by taking concrete values on which a computation is performed and replacing them with variables. For instance, looking at <code>3+3</code>, <code>1+2</code>, and <code>1+4</code>, we can see that in each of these, two numbers are being added. Thus, we write can write a more abstract representation of this as <code>x+y</code>, where <code>x</code> and <code>y</code> are both numbers. In most languages, we represent such a generalization using a function. In C:</p> <pre tabindex="0"><code>int add(int l, int r) { return l + r; } </code></pre><p>In Haskell:</p> <pre tabindex="0"><code>add l r = l + r </code></pre><p>Lambda calculus is all about abstraction into functions, and the application of these functions. In lambda calculus, abstraction looks like this:</p> $$ \lambda \ x \ . \ t $$ <p>This reads: a function that, when given a variable <code>x</code>, evaluates to <code>t</code>. Usually, <code>t</code> is an expression using <code>x</code>. For example, suppose that there exists a function <code>double</code>. If we wanted to create a function that multiplies a number by 4, we can write it as follows:</p> $$ \lambda \ x \ . \ \text{double} \ (\text{double} \ x) $$ <p>Here, we see function application: the function <code>double</code>, which takes a number and returns a number, is given the parameter <code>x</code>. The result of evaluating <code>double x</code> is then given as a parameter to <code>double</code> again, thereby multiplying <code>x</code> by 4. Let&rsquo;s use this new fuction to multiply some number y by 4:</p> $$ (\lambda \ x . \ \text{double} \ (\text{double} \ x)) \ y $$ <p>Here, we see both abstraction and application: we abstract the process of multiplying a number by 4, and then apply that process to a variable y. Application works by simply replacing the variable before the dot in the abstraction with the value that&rsquo;s being passed in. This is called <strong>binding</strong>. In our case, we&rsquo;re binding the variable <code>x</code> to the value of <code>y</code>. This results in the following:</p> $$ \text{double} \ (\text{double} \ y) $$ <p>Since we have no idea what the body (the expression after the dot) of <code>double</code> looks like, we cannot simplify this any further.</p> <a href="#currying"> <h3 id="currying">Currying</h3> </a> <p>This is nice and all, but what about functions of more than one variable? What if we want to add numbers, like in our original example of abstraction? If lambda abstraction has only one variable on the left of the dot, how can we represent such functions? Surely there has to be a way, since we claim this little language can represent any computation that can be done on a computer. The answer is fairly simple. We use abstraction twice:</p> $$ \lambda \ x \ . \ \lambda \ y \ . \ x &#43; y $$ <p>The second abstraction is inside the first. It can be equivalently written:</p> $$ \lambda \ x \ . \ (\lambda \ y \ . \ x &#43; y) $$ <p>I&rsquo;ve done something here that you might&rsquo;ve accepted without questioning, but that I can&rsquo;t ignore without feeling guilty. I&rsquo;ve assumed that our lambda calculus has numbers and addition. This <strong>isn&rsquo;t needed</strong> for the language to be Turing complete. However, it makes for much nicer examples, so we&rsquo;ll stick with it here. Let&rsquo;s move on to an example of using our function to add two numbers:</p> $$ (\lambda \ x \ . \ \lambda \ y \ . \ x &#43; y) \ 1 \ 2 $$ <p>Function application is left associative. This is equivalently written as:</p> $$ ((\lambda \ x \ . \ \lambda \ y \ . \ x &#43; y) \ 1) \ 2 $$ <p>First, we bind <code>x</code> to 1:</p> $$ (\lambda \ y \ . \ 1 &#43; y) \ 2 $$ <p>Then, we bind <code>y</code> in the resulting expression to 2:</p> $$ 1 &#43; 2 $$ <p>Well, there it is. We can extend this logic to functions of 3, 4, and more variables.</p> <a href="#partial-function-application"> <h3 id="partial-function-application">Partial Function Application</h3> </a> <p>Our way of defining functions of more than one variable leads to a curious effect. To highlight this effect, let&rsquo;s imagine calling the <code>add</code> function with less arguments than it takes in a language such as C:</p> <pre tabindex="0"><code>add(1); </code></pre><p>Oh no! The compiler won&rsquo;t like this; <code>add</code> takes two parameters, and we&rsquo;ve only given it one. However, when we try to do the same thing in lambda calculus:</p> $$ (\lambda \ x \ . \ \lambda \ y \ . \ x &#43; y) 1 $$ <p>As before, we can bind <code>x</code> to 1:</p> $$ \lambda \ y \ . \ 1 &#43; y $$ <p>This is not an error! All we&rsquo;ve done is produce a function that takes the remaining parameter, <code>y</code>, and adds it to the parameter we&rsquo;ve already passed in. This is an example of a partially applied function. Effectively, this new function we&rsquo;ve produce is waiting for the remaining parameters we haven&rsquo;t yet given it. Once we do, it&rsquo;ll behave just like it would&rsquo;ve if we passed all the parameters in together.</p> <a href="#first-class-functions"> <h3 id="first-class-functions">First Class Functions</h3> </a> <p>So far, we&rsquo;ve only been applying our functions to numbers. But it doesn&rsquo;t have to be this way! Functions can also serve as parameters to other functions. For example, we can imagine a function that takes another function <code>f</code>, which transforms a number somehow, and then applies it to a number two times. The first parameter of such a function would be <code>f</code>, and the other would be some number <code>x</code>. In the body, we will apply <code>f</code> to the result of applying <code>f</code> to <code>x</code>:</p> $$ \lambda \ f \ . \ \lambda \ x \ . \ f (f \ x) $$ <p>This might look kind of familiar! Let&rsquo;s apply this function to our previously mentioned <code>double</code> function:</p> $$ (\lambda \ f . \ \lambda \ x \ . \ f (f \ x)) \ double $$ <p>Just like in our previous examples, we simply replace <code>f</code> with <code>double</code> during application:</p> $$ \lambda \ x \ . double \ (double \ x) $$ <p>We&rsquo;ve now created our multiply-by-four function! We can create other functions the same way. For instance, if we had a function <code>halve</code>, we could create a function to divide a number by 4 by applying our &ldquo;apply-twice&rdquo; function to it.</p> <a href="#church-encoded-integers"> <h3 id="church-encoded-integers">Church Encoded Integers</h3> </a> <p>We can represent numbers using just abstraction and application. This is called Church encoding. In church encoding, a number is a function that takes another function, and applies it that many times to a value. 0 would take a function and a value, and apply it no times, returning the value it was given. 1 would take a function <code>f</code> and a value <code>v</code>, and return the result of applying that function <code>f</code> one time: <code>f v</code>. We&rsquo;ve already seen 2! It&rsquo;s a function that applies a function to a value twice, <code>f (f v)</code>. Let&rsquo;s write these numbers in lambda calculus: 0 becomes \(\lambda \ f \ . \ \lambda \ x \ . \ x\), 1 becomes \(\lambda \ f \ . \ \lambda \ x \ . \ f \ x\), and 2 becomes the familiar \(\lambda \ f \ . \lambda \ x \ . \ f \ (f \ x)\).</p> <p>Now, let&rsquo;s try represent addition. Addition of two numbers <code>a</code> and <code>b</code> would be done by taking a function <code>f</code> and applying it the first number of times, and then applying it the second number more times. Since addition must take in numbers <code>a</code> and <code>b</code>, which are functions of two variables, and return a number, we will end up with something like this:</p> $$ \lambda \ a \ . \ \lambda \ b \ . \ ?? $$ <p>What goes in the body of the second lambda? We know that a number is a function which takes a function and a variable to apply the function to:</p> $$ \lambda \ f \ . \ \lambda \ v \ . \ ?? $$ <p>Since the addition of a and b must return a number, we then have the following:</p> $$ \lambda \ a \ . \ \lambda \ b \ . \ \lambda \ f \ . \ \lambda \ v \ . \ ?? $$ <p>So how do we apply <code>f</code> the first number of times, and then apply it the second number more times? Well, <code>a f</code> is a partially applied function that, when given a variable, will apply <code>f</code> to that variable the first number of times. Similarly, <code>b f</code> is a partially applied function that will apply <code>f</code> the second number of times. We feed <code>v</code> into <code>a f</code>, which applies <code>f</code> the first number of times and returns the result. We then feed that into <code>b f</code>. This results in:</p> $$ \lambda \ a \ . \ \lambda \ b \ . \ \lambda \ f \ . \ \lambda \ v \ . \ b \ f \ (a \ f \ v) $$ <p>Phew! Now let&rsquo;s try multiplication. The product of two numbers would apply the function the first number of times, the second number of times. As we already know, <code>a f</code> will get us a function that fulfills the first part of this requirement. Then we can apply <em>that function</em> the second number of times, which would give us what we want:</p> $$ \lambda \ a \ . \ \lambda \ b \ . \ \lambda \ f \ . \ \lambda \ v \ . \ b \ (a \ f) \ v $$ <p>Neat!</p> <a href="#conclusion"> <h3 id="conclusion">Conclusion</h3> </a> <p>Today, we learned about lambda calculus, a representation of computation in terms of pure functions rather than an infinite tape and a machine dutifully churning away. This little language lies at the heart of many bigger programming languages such as Haskell. I hope you enjoyed!</p> Proof of Inductive Palindrome Definition in Coq https://danilafe.com/blog/coq_palindrome/ Thu, 28 Mar 2019 06:55:01 +0000 https://danilafe.com/blog/coq_palindrome/ <p>Going through the Software Foundations textbook, I found an exercise which I really enjoyed solving. This exercise was to define an inductive property that holds iff a list is a palindrome. A subsequent exercise asks to prove that if a list fits the inductive definition we wrote, then it is its own reverse. A more difficult exercise asks to show that a list being its own inverse means that it matches our definition of a palindrome.</p> <a href="#the-inductive-definition"> <h3 id="the-inductive-definition">The inductive definition</h3> </a> <p>I wasn’t sure at first why the problem said that the inductive definition should have three constructors. I came up with two ways to construct a palindrome:</p> <ul> <li>An empty list is a palindrome.</li> <li>Adding the same value to the back and the front of another palindrome makes a longer palindrome.</li> </ul> <p>Of course, my definition led to only having palindromes of even length. In order to allow for palindromes of odd length, one more rule is needed (another base case):</p> <ul> <li>A list with a single value is a palindrome.</li> </ul> <p>This translates readily into Coq:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Inductive</span> <span class="n">palindrome</span> <span class="o">{</span> <span class="n">X</span> <span class="o">:</span> <span class="kt">Type</span> <span class="o">}</span> <span class="o">:</span> <span class="kt">list</span> <span class="n">X</span> <span class="o">-&gt;</span> <span class="kt">Prop</span> <span class="o">:=</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">palindrome_nil</span> <span class="o">:</span> <span class="n">palindrome</span> <span class="bp">[]</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">palindrome_x</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">x</span> <span class="o">:</span> <span class="n">X</span><span class="o">),</span> <span class="n">palindrome</span> <span class="o">[</span><span class="n">x</span><span class="o">]</span> </span></span><span class="line"><span class="cl"> <span class="o">|</span> <span class="n">palindrome_xs</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">x</span> <span class="o">:</span> <span class="n">X</span><span class="o">)</span> <span class="o">(</span><span class="n">l</span> <span class="o">:</span> <span class="kt">list</span> <span class="n">X</span><span class="o">),</span> </span></span><span class="line"><span class="cl"> <span class="n">palindrome</span> <span class="n">l</span> <span class="o">-&gt;</span> <span class="n">palindrome</span> <span class="o">(</span><span class="n">x</span><span class="o">::</span><span class="n">l</span> <span class="o">++</span> <span class="o">[</span><span class="n">x</span><span class="o">]).</span> </span></span></code></pre></div><a href="#the-first-direction"> <h3 id="the-first-direction">The first direction</h3> </a> <p>It’s nearly trivial to show that a palindrome is its own inverse:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Theorem</span> <span class="n">pal_rev</span> <span class="o">{</span> <span class="n">X</span> <span class="o">:</span> <span class="kt">Type</span> <span class="o">}</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">l</span> <span class="o">:</span> <span class="kt">list</span> <span class="n">X</span><span class="o">),</span> <span class="n">palindrome</span> <span class="n">l</span> <span class="o">-&gt;</span> <span class="n">l</span> <span class="o">=</span> <span class="n">rev</span> <span class="n">l</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Proof</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">intros</span> <span class="n">l</span> <span class="n">H</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">induction</span> <span class="n">H</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="kp">reflexivity</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="kp">reflexivity</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">simpl</span><span class="o">.</span> <span class="kn">Search</span> <span class="n">rev</span><span class="o">.</span> <span class="k">rewrite</span> <span class="n">rev_app_distr</span><span class="o">.</span> <span class="k">simpl</span><span class="o">.</span> <span class="k">rewrite</span> <span class="o">&lt;-</span> <span class="n">IHpalindrome</span><span class="o">.</span> <span class="kp">reflexivity</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Qed</span><span class="o">.</span> </span></span></code></pre></div><p>This follows from the quasi-distributive property of reverse over append.</p> <a href="#the-other-direction"> <h3 id="the-other-direction">The other direction</h3> </a> <p>The proof in the other direction took me a significant amount of time. It was clear that a proof by induction was necessary, but that’s where I got stuck. Structural induction using the definition of list doesn’t work. It’s trivial to prove the base case; However, the inductive step states that if some list is a palindrome, prepending any x to the front of that list will create another palindrome. This is blatantly wrong; we can come up with numerous cases (<code>[1; 1]</code> prepended with <code>2</code>, for instance) in which prepending a value to a palindrome does <strong>not</strong> create a new palindrome. The inductive definition of list did not work well with this proof.</p> <a href="#a-different-kind-of-induction"> <h4 id="a-different-kind-of-induction">A different kind of induction</h4> </a> <p>Coq’s basic induction wouldn’t do. But structural induction doesn’t limit us to a particular definition of a list; it just states that if:</p> <ul> <li>We can prove that a proposition holds for the primitive values of a type</li> <li>We can prove that if the proposition holds for the building blocks of a value of the type then the proposition holds for the value built of those blocks</li> </ul> <p>Then, the proposition holds for all values of that type. This can be easily seen in the regular induction on lists: we show that a proposition holds on <code>[]</code>, and then that if it holds for <code>l</code>, it also holds for <code>x::l</code>. But this is just one way of constructing a list, and structural induction doesn’t dictate how we construct a value.</p> <p>As I was working with my definition of a palindrome, it dawned on me that generalizing a single variable in that definition would create another inductive definition of a list. If we change the inductive rule in the palindrome definition from saying “surrounding another palindrome with x on both sides” to “surrounding another list with some x on one side and some y on the other”, we can effectively construct any list. We can then state a different structural induction principle:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Theorem</span> <span class="n">alt_induction</span> <span class="o">{</span> <span class="n">X</span> <span class="o">:</span> <span class="kt">Type</span> <span class="o">}</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">P</span> <span class="o">:</span> <span class="kt">list</span> <span class="n">X</span> <span class="o">-&gt;</span> <span class="kt">Prop</span><span class="o">),</span> </span></span><span class="line"><span class="cl"> <span class="n">P</span> <span class="bp">[]</span> <span class="o">-&gt;</span> </span></span><span class="line"><span class="cl"> <span class="o">(</span><span class="k">forall</span> <span class="o">(</span><span class="n">x</span> <span class="o">:</span> <span class="n">X</span><span class="o">),</span> <span class="n">P</span> <span class="o">[</span><span class="n">x</span><span class="o">])</span> <span class="o">-&gt;</span> </span></span><span class="line"><span class="cl"> <span class="o">(</span><span class="k">forall</span> <span class="o">(</span><span class="n">l</span> <span class="o">:</span> <span class="kt">list</span> <span class="n">X</span><span class="o">),</span> <span class="n">P</span> <span class="n">l</span> <span class="o">-&gt;</span> <span class="k">forall</span> <span class="o">(</span><span class="n">x</span> <span class="n">y</span> <span class="o">:</span> <span class="n">X</span><span class="o">),</span> <span class="n">P</span> <span class="o">(</span><span class="n">x</span><span class="o">::</span><span class="n">l</span><span class="o">++[</span><span class="n">y</span><span class="o">]))</span> <span class="o">-&gt;</span> </span></span><span class="line"><span class="cl"> <span class="k">forall</span> <span class="o">(</span><span class="n">ln</span> <span class="o">:</span> <span class="kt">list</span> <span class="n">X</span><span class="o">),</span> <span class="n">P</span> <span class="n">ln</span><span class="o">.</span> </span></span></code></pre></div><p>But how would we go about proving this? If we try to just use induction on <code>ln</code>, we get stuck the same way we get stuck in the palindrome proof. What’s missing?</p> <a href="#proving-equivalence"> <h4 id="proving-equivalence">Proving Equivalence</h4> </a> <p>What’s missing is that there’s nothing as yet stating that appending values to the back and the front of another list is an equivalent way to appending a value only to the front of a list. We need to state this explicitly; but how? I decided that the easiest way would be by defining a new inductive property, <code>list_alt</code> which holds for all lists that can be constructed by adding things to the back and the front. Then, we can show that it holds for all lists, and, therefore, that any list can be constructed this way. I defined the property as such:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Inductive</span> <span class="n">list_alt</span> <span class="o">{</span> <span class="n">X</span> <span class="o">:</span> <span class="kt">Type</span> <span class="o">}</span> <span class="o">:</span> <span class="kt">list</span> <span class="n">X</span> <span class="o">-&gt;</span> <span class="kt">Prop</span> <span class="o">:=</span> </span></span><span class="line"><span class="cl"><span class="o">|</span> <span class="n">list_alt_base</span> <span class="o">:</span> <span class="n">list_alt</span> <span class="bp">[]</span> </span></span><span class="line"><span class="cl"><span class="o">|</span> <span class="n">list_alt_x</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">x</span> <span class="o">:</span> <span class="n">X</span><span class="o">),</span> <span class="n">list_alt</span> <span class="o">[</span><span class="n">x</span><span class="o">]</span> </span></span><span class="line"><span class="cl"><span class="o">|</span> <span class="n">list_alt_xy</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">x</span> <span class="n">y</span> <span class="o">:</span> <span class="n">X</span><span class="o">)</span> <span class="o">(</span><span class="n">l</span> <span class="o">:</span> <span class="kt">list</span> <span class="n">X</span><span class="o">),</span> <span class="n">list_alt</span> <span class="n">l</span> <span class="o">-&gt;</span> <span class="n">list_alt</span> <span class="o">(</span><span class="n">x</span><span class="o">::</span><span class="n">l</span><span class="o">++[</span><span class="n">y</span><span class="o">]).</span> </span></span></code></pre></div><p>Then, to prove it true for all lists, I first showed what is effectively the inductive hypothesis of a proof by induction on the structure of a list:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Theorem</span> <span class="n">list_alt_cons</span> <span class="o">{</span> <span class="n">X</span> <span class="o">:</span> <span class="kt">Type</span> <span class="o">}</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">x</span> <span class="o">:</span> <span class="n">X</span><span class="o">)</span> <span class="o">(</span><span class="n">l</span> <span class="o">:</span> <span class="kt">list</span> <span class="n">X</span><span class="o">),</span> </span></span><span class="line"><span class="cl"> <span class="n">list_alt</span> <span class="n">l</span> <span class="o">-&gt;</span> <span class="n">list_alt</span> <span class="o">(</span><span class="n">x</span><span class="o">::</span><span class="n">l</span><span class="o">).</span> </span></span><span class="line"><span class="cl"><span class="kn">Proof</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">intros</span> <span class="n">x</span> <span class="n">l</span> <span class="n">H</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">induction</span> <span class="n">H</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">apply</span> <span class="n">list_alt_x</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">assert</span> <span class="o">(</span><span class="n">I</span> <span class="o">:</span> <span class="o">[</span><span class="n">x</span><span class="o">;</span><span class="n">x0</span><span class="o">]</span> <span class="o">=</span> <span class="o">[</span><span class="n">x</span><span class="o">]</span> <span class="o">++</span> <span class="bp">[]</span> <span class="o">++</span> <span class="o">[</span><span class="n">x0</span><span class="o">]).</span> </span></span><span class="line"><span class="cl"> <span class="o">{</span> <span class="kp">reflexivity</span><span class="o">.</span> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="k">rewrite</span> <span class="n">I</span><span class="o">.</span> <span class="k">apply</span> <span class="n">list_alt_xy</span><span class="o">.</span> <span class="k">apply</span> <span class="n">list_alt_base</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">inversion</span> <span class="n">IHlist_alt</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="k">simpl</span><span class="o">.</span> <span class="k">assert</span> <span class="o">(</span><span class="n">I</span> <span class="o">:</span> <span class="o">[</span><span class="n">x</span><span class="o">;</span> <span class="n">x0</span><span class="o">;</span> <span class="n">y</span><span class="o">]</span> <span class="o">=</span> <span class="o">[</span><span class="n">x</span><span class="o">]</span> <span class="o">++</span> <span class="o">[</span><span class="n">x0</span><span class="o">]</span> <span class="o">++</span> <span class="o">[</span><span class="n">y</span><span class="o">]).</span> </span></span><span class="line"><span class="cl"> <span class="o">{</span> <span class="kp">reflexivity</span><span class="o">.</span> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="k">rewrite</span> <span class="n">I</span><span class="o">.</span> <span class="k">apply</span> <span class="n">list_alt_xy</span><span class="o">.</span> <span class="k">apply</span> <span class="n">list_alt_x</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="k">assert</span> <span class="o">(</span><span class="n">I</span> <span class="o">:</span> <span class="n">x0</span><span class="o">::(</span><span class="n">l0</span> <span class="o">++</span> <span class="o">[</span><span class="n">y0</span><span class="o">])</span> <span class="o">++</span> <span class="o">[</span><span class="n">y</span><span class="o">]</span> <span class="o">=</span> <span class="o">(</span><span class="n">x0</span><span class="o">::(</span><span class="n">l0</span><span class="o">++[</span><span class="n">y0</span><span class="o">]))++[</span><span class="n">y</span><span class="o">]).</span> </span></span><span class="line"><span class="cl"> <span class="o">{</span> <span class="kp">reflexivity</span><span class="o">.</span> <span class="o">}</span> </span></span><span class="line"><span class="cl"> <span class="k">rewrite</span> <span class="n">I</span><span class="o">.</span> <span class="k">apply</span> <span class="n">list_alt_xy</span><span class="o">.</span> <span class="k">apply</span> <span class="n">list_alt_xy</span><span class="o">.</span> <span class="k">apply</span> <span class="n">H1</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Qed</span><span class="o">.</span> </span></span></code></pre></div><p>Next, the actual proof of equivalence simply used the previous theorem:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Theorem</span> <span class="n">list_alt_correct</span> <span class="o">{</span> <span class="n">X</span> <span class="o">:</span> <span class="kt">Type</span> <span class="o">}</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">l</span> <span class="o">:</span> <span class="kt">list</span> <span class="n">X</span><span class="o">),</span> <span class="n">list_alt</span> <span class="n">l</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Proof</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">induction</span> <span class="n">l</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">apply</span> <span class="n">list_alt_base</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">apply</span> <span class="n">list_alt_cons</span><span class="o">.</span> <span class="k">apply</span> <span class="n">IHl</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Qed</span><span class="o">.</span> </span></span></code></pre></div><p>Finally, we can prove our custom principle of induction (by induction (!) on the property list_alt, which we now know holds for all lists):</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Theorem</span> <span class="n">alt_induction</span> <span class="o">{</span> <span class="n">X</span> <span class="o">:</span> <span class="kt">Type</span> <span class="o">}</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">P</span> <span class="o">:</span> <span class="kt">list</span> <span class="n">X</span> <span class="o">-&gt;</span> <span class="kt">Prop</span><span class="o">),</span> </span></span><span class="line"><span class="cl"> <span class="n">P</span> <span class="bp">[]</span> <span class="o">-&gt;</span> </span></span><span class="line"><span class="cl"> <span class="o">(</span><span class="k">forall</span> <span class="o">(</span><span class="n">x</span> <span class="o">:</span> <span class="n">X</span><span class="o">),</span> <span class="n">P</span> <span class="o">[</span><span class="n">x</span><span class="o">])</span> <span class="o">-&gt;</span> </span></span><span class="line"><span class="cl"> <span class="o">(</span><span class="k">forall</span> <span class="o">(</span><span class="n">l</span> <span class="o">:</span> <span class="kt">list</span> <span class="n">X</span><span class="o">),</span> <span class="n">P</span> <span class="n">l</span> <span class="o">-&gt;</span> <span class="k">forall</span> <span class="o">(</span><span class="n">x</span> <span class="n">y</span> <span class="o">:</span> <span class="n">X</span><span class="o">),</span> <span class="n">P</span> <span class="o">(</span><span class="n">x</span><span class="o">::</span><span class="n">l</span><span class="o">++[</span><span class="n">y</span><span class="o">]))</span> <span class="o">-&gt;</span> </span></span><span class="line"><span class="cl"> <span class="k">forall</span> <span class="o">(</span><span class="n">ln</span> <span class="o">:</span> <span class="kt">list</span> <span class="n">X</span><span class="o">),</span> <span class="n">P</span> <span class="n">ln</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Proof</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">intros</span> <span class="n">P</span> <span class="n">Hb1</span> <span class="n">Hb2</span> <span class="n">Hss</span> <span class="n">ln</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">induction</span> <span class="o">(</span><span class="n">list_alt_correct</span> <span class="n">ln</span><span class="o">).</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">apply</span> <span class="n">Hb1</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">apply</span> <span class="n">Hb2</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="k">apply</span> <span class="n">Hss</span><span class="o">.</span> <span class="k">apply</span> <span class="n">IHl</span><span class="o">.</span> <span class="kn">Qed</span><span class="o">.</span> </span></span></code></pre></div><p>Finally, the palindrome proof can simply use this principle. Coq turns out to have a mechanism to use a custom induction principle via <code>induction … using …</code>:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Coq" data-lang="Coq"><span class="line"><span class="cl"><span class="kn">Theorem</span> <span class="n">rev_pal&#39;</span> <span class="o">{</span> <span class="n">X</span> <span class="o">:</span> <span class="kt">Type</span> <span class="o">}</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="n">l</span> <span class="o">:</span> <span class="kt">list</span> <span class="n">X</span><span class="o">),</span> <span class="n">l</span> <span class="o">=</span> <span class="n">rev</span> <span class="n">l</span> <span class="o">-&gt;</span> <span class="n">palindrome</span> <span class="n">l</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Proof</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">intros</span> <span class="n">l</span> <span class="n">H</span><span class="o">.</span> <span class="k">induction</span> <span class="n">l</span> <span class="k">using</span> <span class="n">alt_induction</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="c">(* Base case 1; empty list is palindrome by definition. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">apply</span> <span class="n">palindrome_nil</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="c">(* Base case 2; singleton list is palindrome by definition. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">apply</span> <span class="n">palindrome_x</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="o">-</span> <span class="c">(* Inductive step (and interesting case.) *)</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c">(* Milk the reverse property to show that we end and begin with the same thing. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">assert</span> <span class="o">(</span><span class="n">Hi1</span> <span class="o">:</span> <span class="n">x</span><span class="o">::</span><span class="n">l</span> <span class="o">++</span> <span class="o">[</span><span class="n">y</span><span class="o">]</span> <span class="o">=</span> <span class="o">(</span><span class="n">x</span><span class="o">::</span><span class="n">l</span><span class="o">)</span> <span class="o">++</span> <span class="o">[</span><span class="n">y</span><span class="o">]).</span> <span class="kp">reflexivity</span><span class="o">.</span> <span class="k">rewrite</span> <span class="n">Hi1</span> <span class="k">in</span> <span class="n">H</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> <span class="k">rewrite</span> <span class="n">rev_app_distr</span> <span class="k">in</span> <span class="n">H</span><span class="o">.</span> <span class="k">simpl</span> <span class="k">in</span> <span class="n">H</span><span class="o">.</span> <span class="k">injection</span> <span class="n">H</span> <span class="k">as</span> <span class="n">Heq1</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c">(* Do the same again; This time, we also find that rev l = l. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">apply</span> <span class="n">rev_injective</span> <span class="k">in</span> <span class="n">H</span><span class="o">.</span> <span class="kr">repeat</span> <span class="o">(</span><span class="k">rewrite</span> <span class="n">rev_app_distr</span> <span class="k">in</span> <span class="n">H</span><span class="o">).</span> <span class="k">simpl</span> <span class="k">in</span> <span class="n">H</span><span class="o">.</span> <span class="k">injection</span> <span class="n">H</span> <span class="k">as</span> <span class="n">Heq2</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c">(* Use the induction hypothesis to deduce that l is a palindrome. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">rewrite</span> <span class="n">rev_involutive</span> <span class="k">in</span> <span class="n">H</span><span class="o">.</span> <span class="k">symmetry</span> <span class="k">in</span> <span class="n">H</span><span class="o">.</span> <span class="k">apply</span> <span class="n">IHl</span> <span class="k">in</span> <span class="n">H</span><span class="o">.</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="c">(* We know we start and end with the same thing; Use the third way to construct a palindrome. </span></span></span><span class="line"><span class="cl"><span class="c"> Since l is a palindrome, we are done. *)</span> </span></span><span class="line"><span class="cl"> <span class="k">rewrite</span> <span class="n">Heq1</span><span class="o">.</span> <span class="k">apply</span> <span class="n">palindrome_xs</span><span class="o">.</span> <span class="k">apply</span> <span class="n">H</span><span class="o">.</span> </span></span><span class="line"><span class="cl"><span class="kn">Qed</span><span class="o">.</span> </span></span></code></pre></div><a href="#conclusion"> <h3 id="conclusion">Conclusion</h3> </a> <p>Although I’m proud to have figured this out, I’m not sure if my solution can be considered “good” by the experts. I took advantage of the fact that Coq allows proofs by induction on propositions, which may be less direct than otherwise possible. However, the solution works, and the resulting proofs are quite elegant. I should also note that it’s possible to use <code>list_alt</code> to prove <code>rev_pal</code> wihout using induction. It’s not much longer, but perhaps less clean.</p> Setting Up Crystal on ARM https://danilafe.com/blog/crystal_on_arm/ Sat, 02 Mar 2019 06:47:50 +0000 https://danilafe.com/blog/crystal_on_arm/ <p>Having found myself using a Chromebook with Arch Linux, I acutely felt the lack of official <a href="https://crystal-lang.org"class="external-link">Crystal<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> builds for ARM. After spending an hour or so looking online for a comprehensive guide to getting a working compiler on ARM, I think I now know the general idea. I will lay it out here, for myself, as well as for others who may be in the same situation.</p> <a href="#to-compile-crystal-you-need-crystal"> <h3 id="to-compile-crystal-you-need-crystal">To compile Crystal, you need Crystal</h3> </a> <p>To compile Crystal without bootstrapping it from older versions, you need a different machine which is capable of cross-compilation. Fortunately for me, my friends and I have previously set up a server hosting several x86_64 virtual machines, one of which we dedicated to cross-compiling various programs.</p> <p>To get started, I had to download the compiler on that machine:</p> <pre tabindex="0"><code>sudo pacman -S crystal </code></pre><p>Note that this isn&rsquo;t the ARM Chromebook. Running this command on the Chromebook would not work.</p> <a href="#building-on-the-x86_64-machine"> <h3 id="building-on-the-x86_64-machine">Building on the x86_64 Machine</h3> </a> <p>After getting the compiler, I also needed to download the compiler source. This was done using git:</p> <pre tabindex="0"><code>git clone https://github.com/crystal-lang/crystal.git </code></pre><p>I also installed <code>llvm6</code>, which is required on both the machine that&rsquo;s building the compiler and the machine for which the compiler is being built:</p> <pre tabindex="0"><code>sudo pacman -S llvm6 </code></pre><p>From here on in, I ran commands from inside the directory that was downloaded via <code>git clone</code>:</p> <pre tabindex="0"><code>cd crystal </code></pre><p>Finally, I didn&rsquo;t want to compile the &ldquo;master&rdquo; version of the compiler. It wasn&rsquo;t a release! To check out the latest release (0.27.2 at the time of writing), I used git:</p> <pre tabindex="0"><code>git checkout 0.27.2 </code></pre><p>Now, I had the compiler and the source. I was ready to compile the source to get myself a nice ARM Crystal compiler. But how? The official guide specified two options for cross compilation:</p> <ul> <li><code>--cross-compile</code> - This option is basically a flag. You just add it to the command to enable cross compilation.</li> <li><code>--target=&lt;llvm target triple&gt;</code> - This specifies the target architecture you&rsquo;re building for.</li> </ul> <p>In order to get the second option right, I had to know the LLVM target triple for my target machine. To find it, I ran the following command on that machine:</p> <pre tabindex="0"><code>gcc -dumpmachine </code></pre><p>This produced the output <code>armv7l-unknown-linux-gnueabihf</code>. This was exactly what I needed to know!</p> <p>Finally, looking through the Makefile in the repository, I found three more flags that are used by default in the process:</p> <ul> <li><code>-D without_openssl</code></li> <li><code>-D without_zlib</code></li> <li><code>--release</code> - for a faster compiler</li> </ul> <p>To compile the compiler, I had to compile the <code>src/compiler/crystal.cr</code> file. With all these options, the command came out to be:</p> <pre tabindex="0"><code>crystal build src/compiler/crystal.cr --cross-compile --target=armv7l-unknown-linux-gnueabihf -D without_openssl -D without_zlib --release </code></pre><p>There is only one more trick to cross-compiling Crystal: although the official guide specifies the options <code>--cross-compile</code> and <code>--target=...</code>, and although you can just attempt to use the <code>crystal</code> command on the source file, <strong>this won&rsquo;t work</strong>. You need to use the wrapper script that Crystal provides. I had to replace <code>crystal</code> with <code>./bin/crystal</code>:</p> <pre tabindex="0"><code>./bin/crystal build src/compiler/crystal.cr --cross-compile --target=armv7l-unknown-linux-gnueabihf -D without_openssl -D without_zlib --release </code></pre><p>With this, I finally obtained a <code>crystal.o</code> file. After downloading this onto my target machine, <strong>and making sure to copy the command the compiler printed out</strong>, I was ready to proceed.</p> <a href="#compiling-on-the-target-arm-machine"> <h3 id="compiling-on-the-target-arm-machine">Compiling on the Target ARM Machine</h3> </a> <p>Just like with the x86_64 machine, I needed llvm6:</p> <pre tabindex="0"><code>sudo pacman -S llvm6 </code></pre><p>I also needed the Crystal source, again!</p> <pre tabindex="0"><code>git clone https://github.com/crystal-lang/crystal.git &amp;&amp; cd crystal git checkout 0.27.2 </code></pre><p>Finally, I needed a few more libraries. These are <code>gc</code> (the Garbage Collector Crystal uses) and <code>libevent</code>:</p> <pre tabindex="0"><code>sudo pacman -S gc libevent </code></pre><p>With these dependencies installed, I could compile the last two files needed to build a working compiler:</p> <pre tabindex="0"><code>make deps </code></pre><p>After this, the command I noted down from earlier (on the x86_64 machine) was all that was left (note that I placed <code>crystal.o</code> in the clone of the Crystal repository):</p> <pre tabindex="0"><code>cc &#39;crystal.o&#39; -o &#39;crystal&#39; -rdynamic src/llvm/ext/llvm_ext.o `/usr/bin/llvm-config --libs --system-libs --ldflags 2&gt; /dev/null` -lstdc++ -lpcre -lm -lgc -lpthread src/ext/libcrystal.a -levent -lrt -ldl -L/usr/lib -L/usr/local/lib </code></pre><p>This produced a fresh, new <code>crystal</code> executable!</p> <p>I was not done. The executable couldn&rsquo;t compile a basic &ldquo;Hello, world&rdquo;! This is because I once again needed the wrapper script. This script searches the <code>.build</code> directory for the Crystal executable, and so I placed it there:</p> <pre tabindex="0"><code>mkdir .build mv crystal .build </code></pre><p>Finally, I made sure to add the <code>./bin</code> directory to my PATH.</p> <a href="#shards"> <h3 id="shards">Shards</h3> </a> <p>Crystal is not complete without its package manager, shards. This program doesn&rsquo;t need to be cross compiled, but it does come separately from the compiler. First, I cloned the repository:</p> <pre tabindex="0"><code>git clone https://github.com/crystal-lang/shards.git </code></pre><p>Then, I installed <code>libyaml</code>, which is necessary to compile shards:</p> <pre tabindex="0"><code>sudo pacman -S libyaml </code></pre><p>And finally, I ran the Makefile provided in the repository:</p> <pre tabindex="0"><code>make </code></pre><p>I once again added the <code>./bin</code> directory to my path. And finally, a working Crystal environment!</p> Haskell Error Checking and Autocompletion With LSP https://danilafe.com/blog/haskell_language_server/ Wed, 16 Jan 2019 16:15:37 +0000 https://danilafe.com/blog/haskell_language_server/ <p>For Oregon State University&rsquo;s CS 381, Programming Language Fundamentals, Haskell is the language of choice. While it has an excellent REPL, and an excellent compiler, a piece of the puzzle is missing when writing Haskell code: on-the-fly error checking. Although there are many IDEs for more popular languages, such as Java and C++, that check your code as you type, this is not the case for Haskell. However, thanks to the magic of the <em>Language Server Protocol</em>, you can get code error checking and linting in your favorite editor. After we&rsquo;re done, your IDE / Text Editor / etc. should be able to display errors in a way similar to the image below:</p> <p><img src="https://i.imgur.com/mPlPeYd.png" alt="Imgur"></p> <a href="#prelude-language-server-protocol"> <h3 id="prelude-language-server-protocol">Prelude: Language Server Protocol</h3> </a> <p>Language Server Protocol (LSP) is an attempt to simplify the code analysis feature in various IDEs. Before LSP, each individual IDE had to provide its own functionality for analyzing user code, and couldn&rsquo;t re-use the code analysis features of another IDE. Thus, for every IDE, for every language, a new code analyzer had to be developed. For \(m\) IDEs and \(n\) languages, there would need to be \(m \times n\) code analyzers. This is less than ideal. LSP solves this problem by moving the task of analyzing code into an external process, called a <em>Language Server</em>. The language server examines code, finds errors and warnings, and figures out the names of the various variables and functions that the source file contains. It then communicates this information to a <em>Language Client</em>, which is usually the IDE. The language client is responsible for presenting the information to the user in any way it deems fit.</p> <p>Because LSP is a <em>protocol</em>, any server is able to communicate with any client. Once someone has written a language client for, say, Haskell, this client can be used by any IDE that supports LSP, which means that IDE instantly gains Haskell support. Similarly, once an IDE works as an LSP client, it can use any language server, and thus is immediately able to support all languages for which there is a language client.</p> <a href="#lsp-for-haskell-haskell-ide-engine-hie"> <h3 id="lsp-for-haskell-haskell-ide-engine-hie">LSP for Haskell: Haskell IDE Engine (HIE)</h3> </a> <p>It so happens that Haskell <em>does</em> have a language server, called <a href="https://github.com/haskell/haskell-ide-engine"class="external-link">HIE<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. We&rsquo;ll be using it to get all the neat error checking and autocompletion features. As explained above, a language server is an external program: we have to install it separately from an IDE, and then configure the IDE to communicate with it. So, how are we going to install this external program? We will compile HIE from source, using the latest Haskell release (<code>8.6.3</code> at the time of writing). To do so, you must have a working Haskell compiler (GHC), as well as Stack.</p> <p><em>Note: It has come to my attention that on Windows, the GHC compiler is <a href="https://github.com/haskell/haskell-ide-engine/issues/1008"class="external-link">broken<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> for version 8.6.3, leading to the package <code>fclabels</code> taking an infinite amount of time to compile. If you&rsquo;re running Windows, I suggest using 8.4.4 instead of 8.6.3. It is not yet clear if HIE can handle versions of Haskell higher than itself (which would happen because your compiler will most likely be 8.6.3 no matter what). I will update this post as more information becomes available.</em></p> <p>First things first, we need to clone the repository:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git clone https://github.com/haskell/haskell-ide-engine.git </span></span><span class="line"><span class="cl"><span class="nb">cd</span> haskell-ide-engine </span></span><span class="line"><span class="cl">git checkout 0.5.0.0 </span></span></code></pre></div><p>Notice that we checked out the <code>0.5.0.0</code> release. At the time of writing, this is the latest release. I have attempted to compile the master branch of HIE directly, and was not successful; It&rsquo;s possible that it is not completely stable.</p> <p><em>Note: At the time of this update (June 2019), the compilation process does not seem to require you to check out a particular branch or tag. If you&rsquo;ve already checked it out, run <code>git checkout master</code> to undo that action. Instead, it seems like you should run the following command:</em></p> <pre tabindex="0"><code>git submodule update --init </code></pre><p>Next, we will compile HIE. This is a fairly time consuming step: on my machine, the full compilation process took around 10 minutes. At the very least, the project repo contains a Makefile and a PowerShell file, so all we have to do is run the script. From here, different steps need to be taken depending on your operating system: choose your adventure!</p> <a href="#update-june-2019-installation-using-stack"> <h4 id="update-june-2019-installation-using-stack">Update (June 2019): Installation Using Stack</h4> </a> <p>While Haskell IDE Engine version 0.5.0.0 contained a Makefile and PowerShell script to help compile the program, these are no longer present. It seems like the installation process is similar on all platforms. The AUR repository for HIE uses the following command to build the IDE engine:</p> <pre tabindex="0"><code>stack --stack-yaml=stack-8.6.5.yaml build </code></pre><p>Replace <code>8.6.5</code> with <code>8.4.4</code> if you want the Haskell LTS release. Once that&rsquo;s done, you can install the created executable using the following command:</p> <pre tabindex="0"><code>stack --stack-yaml=stack-8.6.5.yaml install </code></pre><p>Please see the below, OS-dependent sections for how to add the program to your PATH (skip the <code>make</code> or PowerShell instructions, since we already built and installed HIE).</p> <a href="#installation-of-v0500-unix-like-systems"> <h4 id="installation-of-v0500-unix-like-systems">Installation of v0.5.0.0: UNIX-like Systems</h4> </a> <p>On UNIX systems, we can use the provided Makefile to run the compilation script:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">make hie-8.6.3 </span></span></code></pre></div><p>After the command completes, you need to add the executable to your path. You do this by modifying your <code>.bashrc</code> file. First, find the directory:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">stack path --local-bin </span></span></code></pre></div><p>Copy the output of that command, and add a new line to the bottom of your <code>~/.bashrc</code> file (not including the square brackets):</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">PATH</span><span class="o">=</span><span class="nv">$PATH</span>:<span class="o">[</span>output of stack path<span class="o">]</span> </span></span></code></pre></div><p>Reload your RC file by running</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">source</span> ~/.bashrc </span></span></code></pre></div><p>You are now ready to verify your installation.</p> <a href="#installation-of-v0500-windows-systems"> <h4 id="installation-of-v0500-windows-systems">Installation of v0.5.0.0: Windows Systems</h4> </a> <p>To run the compilation process on Windows, you need to use the provided PowerShell script:</p> <pre tabindex="0"><code>powershell -ExecutionPolicy RemoteSigned -c .\build-all.ps1 8.6.3 </code></pre><p>Once that completes, you need to add the location of the compiled executable to your Path environment variable. To do so, you first, you need to find the directory that you want to add to the variable:</p> <pre tabindex="0"><code>stack path --local-bin </code></pre><p>Next, you need to locate the Path variable itself. It is accessed through a Control Panel setting, which is found under <em>System and Security &gt; System &gt; Change settings &gt; Advanced &gt; Environment Variables</em>. Select the line that says &ldquo;Path&rdquo; and click <em>Edit</em>. If this brings up a single text box, add</p> <pre tabindex="0"><code>;[output of stack path] </code></pre><p>to the end of the textbox (do <em>not</em> delete the text that was previously in the box; only add to it). If the <em>Edit</em> button brings up a dialog box with a list of paths, simply click <em>New</em> and paste the output <code>stack path</code> there before pressing Enter.</p> <p>You are now ready to verify your installation.</p> <a href="#verifying-installation"> <h4 id="verifying-installation">Verifying Installation</h4> </a> <p>After you have added HIE to your path, you should be able to start it with the command:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">hie </span></span></code></pre></div><p>This should produce some output along the lines of &ldquo;Run entered for HIE(hie) Version 0.5.0.0&rdquo;. Once this is done, HIE is installed and you can now configure your favorite editor.</p> <a href="#setting-up-a-language-client"> <h3 id="setting-up-a-language-client">Setting Up a Language Client</h3> </a> <a href="#atom"> <h4 id="atom">Atom</h4> </a> <p>In Atom, you need to install <code>atom-ide-ui</code>, <code>ide-haskell-hie</code>, and <code>language-haskell</code>. From there, opening a Haskell file in the IDE should enable autocompletion.</p> <p>If autocompletion and error checking do <em>not</em> work, chances are, it cannot find the HIE executable we built in the previous steps. Locate it by going to <em>Settings &gt; Packages &gt; (Search ide-haskell-hie) &gt; Settings (on ide-haskell-hie package)</em>. From there, paste the output of <code>stack path --local-bin</code> into <em>Absolute path of hie executable</em>. To the end of that line, add <code>/hie</code> (or <code>\hie</code>, if you&rsquo;re on Windows): the output of the command gives the folder in which the language server is installed. Adding the <code>hie</code> to the end makes it point to a file.</p> <a href="#visual-studio-code"> <h4 id="visual-studio-code">Visual Studio Code</h4> </a> <p>The package <code>Haskell Language Server</code> provides language client support to Visual Studio Code.</p> <a href="#neovim"> <h4 id="neovim">Neovim</h4> </a> <p>First, set up <a href="https://github.com/junegunn/vim-plug"class="external-link">vim-plug<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> as per the GitHub instructions, most likely by dropping the <code>plug.vim</code> file into your Neovim&rsquo;s <code>autoload</code> directory. From there, edit your <code>init.vim</code> file to include the following:</p> <pre tabindex="0"><code>call plug#begin(&#39;~/.config/nvim/plugged&#39;) Plug &#39;roxma/nvim-yarp&#39; Plug &#39;ncm2/ncm2&#39; Plug &#39;autozimu/LanguageClient-Neovim&#39;, {&#39;branch&#39;: &#39;next&#39;, &#39;do&#39;: &#39;bash install.sh&#39; } call plug#end() </code></pre><p>After that&rsquo;s done, restart Neovim, then execute the <code>:PlugInstall</code> command. Restart Neovim again to make sure the plugins have loaded.</p> <p><em>Note: If you already use a different plugin manager, just install <code>roxma/nvim-yarp</code>, <code>ncm2/ncm2</code> and <code>autozimu/LanguageClient-Neovim</code></em></p> <p>Additionally, you need to add the following options (also in <code>init.vim</code>):</p> <pre tabindex="0"><code>autocmd BufEnter * call ncm2#enable_for_buffer() &#34; Enable ncm2 for all buffers set completeopt=noinsert,menuone </code></pre><p>The above code installs the three plugins, <code>nvim-yarp</code>, <code>ncm2</code> and <code>LanguageClient-Neovim</code>. While it&rsquo;s pretty clear what the last one does, the first two are less obvious. <code>nvim-yarp</code> is a dependency for ncm2, an allows for Neovim to communicate with Python. ncm2 is a plugin that improves autocompletion. The options we set are recommended by the ncm2 installation guide.</p> <p>Finally, we need to configure the LanguageClient:</p> <pre tabindex="0"><code>let g:LanguageClient_serverCommands = { \ &#39;haskell&#39;: [&#39;hie&#39;, &#39;--lsp&#39;], \ } </code></pre><p>After we&rsquo;re done, restart Neovim. You should now have autocompletion and error checking. Some settings that I also use with this setup are as follows:</p> <pre tabindex="0"><code>let g:LanguageClient_diagnosticsDisplay = {1: {&#34;name&#34;: &#34;Error&#34;,&#34;texthl&#34;: &#34;ALEError&#34;,&#34;signText&#34;: &#34;&gt;&gt;&#34;,&#34;signTexthl&#34;: &#34;ALEErrorSign&#34;,},2: {&#34;name&#34;: &#34;Warning&#34;,&#34;texthl&#34;: &#34;ALEWarning&#34;,&#34;signText&#34;: &#34;&gt;&gt;&#34;,&#34;signTexthl&#34;: &#34;ALEWarningSign&#34;,},3: {&#34;name&#34;: &#34;Information&#34;,&#34;texthl&#34;: &#34;ALEInfo&#34;,&#34;signText&#34;: &#34;&gt;&gt;&#34;,&#34;signTexthl&#34;: &#34;ALEInfoSign&#34;,},4: {&#34;name&#34;: &#34;Hint&#34;,&#34;texthl&#34;: &#34;ALEInfo&#34;,&#34;signText&#34;: &#34;&gt;&gt;&#34;,&#34;signTexthl&#34;: &#34;ALEInfoSign&#34;,},} set signcolumn=yes </code></pre><a href="#conclusion"> <h3 id="conclusion">Conclusion</h3> </a> <p>With the Language Server Protocol, you can have consistent and effective error checking and autocompletion in any editor that can act like a client. I hope that after reading this post, you you will have a more pleasant time with using Haskell.</p> A Look Into Starbound's File Formats https://danilafe.com/blog/starbound/ Wed, 17 May 2017 22:34:04 +0000 https://danilafe.com/blog/starbound/ <p>After playing a few hours of Starbound, I decided that I wanted to mess around with its file format, perhaps make a save manager/swapper, or something. There wasn&rsquo;t a really clear idea in my head as to what I was going to do, but I wanted to start writing <em>something</em> and see what happens. Looking up &ldquo;starbound file formats&rdquo; led me immediately <a href="http://seancode.com/galileo/format/"class="external-link">here<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. I tried to follow the outline with my own implementation, but things weren&rsquo;t working. At all. So, I cracked open a hex editor. According to the website, I was supposed to see:</p> <blockquote> <p>Byte[4] Signature &ldquo;BTDB&rdquo;</p> </blockquote> <p>However, this was not the case. Instead, a very similar <code>BTreeDB5</code> was what greeted me at the beginning of the file. Not good. The next links in my search results were <a href="https://github.com/blixt/py-starbound"class="external-link">py-starbound<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, and its friend, by the same author, <a href="https://github.com/blixt/go-starbound"class="external-link">go-starbound<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. Neither of these was in a language that I felt comfortable using for my project, so there was only one solution - to figure out the formats that they used, and try one more time to write my own.</p> <p><strong>py-starbound</strong>, nicely enough, actually has a file named <code>FORMATS.md</code>. This file proved very helpful, though slightly incomplete. One key piece of information was missing from it - how the <code>BTreeDB5</code> was organized beyond its header. The only thing in <code>FORMATs.md</code> on this subject was:</p> <blockquote> <p>This section will contain information on how to retrieve a value from a BTreeDB5 database.</p> </blockquote> <p>Not very helpful. Before I go into what I managed to determine from the code, we may first take a look at one thing that we already know about the world format - it is a <a href="https://en.wikipedia.org/wiki/B-tree"class="external-link">B-Tree<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>.</p> <a href="#binary-search-trees"> <h3 id="binary-search-trees">Binary Search Trees</h3> </a> <p>The B-Tree is a generalization of a Binary Search Tree, or BST for short. Binary Search trees (and B-Trees in general) operate on data that can be ordered consistently, the simplest example being numbers. For instance, as an example, I&rsquo;ll be using a BST that holds integers. A BST is made up of nodes, objects that actually hold the pieces of data that the tree itself organizes.</p> <p>In a BST, the nodes are organized in a simple way. Each node can have up to two <em>children</em> (sub-nodes), and each of those can have up to two children, etc. The children are generally classified as <em>right</em> and <em>left</em>. Conventionally, left children always have a value that is below (or comes before) the value of the node whose child they are (their <em>parent</em>), and right children have a bigger value.</p> <p>In order to find the data in the tree, a program would first examine the very first node of the BST, called the <em>root</em>. If the data it&rsquo;s looking for is equal to that of the root, the search ends there. If the data is less, it moves on to the left child, and performs the same operation there. Similarly, if the data is larger, it the search moves on to the right child. This continues until either a node with the same value is found, or until there is no child left to examine - the algorithm reached the end of the tree.</p> <p>The purpose of the binary search tree is to speed up lookups. Let&rsquo;s compare it to a simple list of values. To check if a value is there, a naive approach would go from the beginning to the end of the list, searching for the data. For instance, if we wanted to check whether the list <code>(1, 2, 3, 4, 5, 6, 7, 8, 9)</code> contained the number nine, we would have to look at the first element (one), then the second, all the way until the 9th. This is fine for small lists, but when the size of the lists become big enough, this operation becomes fairly slow. Let&rsquo;s now take a look at how the same operation would be performed in a binary tree.</p> <p>Here&rsquo;s an example of a valid binary tree with the numbers one through nine.</p> <pre tabindex="0"><code> (5) (2) (7) (1) (3) (6) (8) (4) (9) </code></pre><p>The root node of this tree is 5, its left child is 2, and its right child is 7. In order to check if the nine is in this tree, we only need to look at the five, then the seven, then the eight, and finally, the nine. We have, therefore, looked at only four of the numbers, instead of all nine. But let&rsquo;s look at another valid binary search tree.</p> <pre tabindex="0"><code>(1) (2) (3) (4) (5) ... </code></pre><p>In this tree, the root node is 1, and the right child is 2. None of the nodes have a left child. Looking through this tree would be as slow as looking through a list - we&rsquo;d have to look through all the numbers before we find 9. No good.</p> <p><strong>Although the average efficiency of a Binary Search Tree is \(O(\log n)\), meaning that for a tree with \(n\) nodes, it will only need to examine up to \(logn\) of these nodes, it can nonetheless be as inefficient as \(O(n)\), meaning that it will have to examine every node in the tree.</strong></p> <p>This isn&rsquo;t good enough, and many clever algorithms have been invented to speed up the lookup of the tree by making sure that it remains <em>balanced</em> - that is, it <em>isn&rsquo;t</em> arranged like a simple list. Some of these algorithms include <a href="https://en.wikipedia.org/wiki/Red%E2%80%93black_tree"class="external-link">Red-Black Trees<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, <a href="https://en.wikipedia.org/wiki/AVL_tree"class="external-link">AVL Trees<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, and, of course, B-Trees.</p> <a href="#b-trees"> <h3 id="b-trees">B-Trees</h3> </a> <p>B-Trees are a generalization of Binary Search Trees. That means that every Binary Search Tree is a B-Tree, but not all B-Trees are BSTs. The key difference lies in the fact that B-Trees&rsquo; nodes aren&rsquo;t limited to having only two child nodes, and can also have more than one value.</p> <p>Each B-Tree node is a sorted array of values. That is, instead of a single number like the BST that we&rsquo;ve looked at, it has multiple, and these numbers <em>must</em> be sorted. Below are some examples of B-Tree nodes:</p> <pre tabindex="0"><code>(1) (1, 2) (2, 3, 5) </code></pre><p>This raises the question: How do we find the next node to examine if the node doesn&rsquo;t contain our child? Before, we only had two choices, but now, our data can be bigger than one of the values and smaller than the other&hellip;what do we do?</p> <p>This is solved using another property of B-Trees - the number of children of a node is always one more than the number of values in it. Thus, a node with one value will have two children, and a node with two will have three children. Thus, to find the next node we want to look for, we find the child that falls &ldquo;between&rdquo; the two values that our data lies between. To clarify, let&rsquo;s look at a simple B-Tree:</p> <pre tabindex="0"><code> (10, 20, 30) (1st) (2nd) (3rd) (4th) </code></pre><p>If we were looking for the number 15, we&rsquo;d look between the 10 and the 20, examining the 2nd node, and if we were looking for 45 we&rsquo;d look past the 30, at the 4th node.</p> <a href="#starbound-b-trees-and-btreedb5"> <h3 id="starbound-b-trees-and-btreedb5">Starbound B-Trees and BTreeDB5</h3> </a> <p>The BTreeDB5 data structure uses something other than integers for its keys - it uses sequences of bytes. These bytes are compared in a very similar fashion to integers. The game first looks at the first number in the sequence of bytes (like the largest digit in an integer), and if that&rsquo;s the same, moves on to the next one. Also, Starbound B-Trees not only have the values, or <em>keys</em>, that they use to find data, but the data itself.</p> <p>The &ldquo;nodes&rdquo; in the BTreeDB are called &ldquo;blocks&rdquo; and are one of three types - &ldquo;index&rdquo;, &ldquo;leaf&rdquo;, and &ldquo;free&rdquo; nodes. &ldquo;Index&rdquo; nodes are like the <code>(10, 20, 30)</code> node in the above example - they point to other nodes, but actually store no data themselves. The &ldquo;leaf&rdquo; nodes actually contain the data, and, if that data is longer than the maximum block size, &ldquo;leaf&rdquo; nodes contain the index of the next leaf node where the user might continue to read the data. The &ldquo;free&rdquo; nodes are simply free data, empty and ready for Starbound to fill them with something useful.</p> <p>To be able to read a Starbound BTree, the first thing that needs to be done is to read the general information about the tree. For this, we read the very beginning of the file, called the <em>header</em>. <a href="https://github.com/blixt/py-starbound/blob/master/FORMATS.md#btreedb5"class="external-link">GitHub user blixt has made an excellent table of values that make up the header<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. The ones that matter to us are <code>Whether to use root node #2 instead</code> (hereby referred to as &ldquo;swap&rdquo;, <code>Root node #1 block index</code> and <code>Root node #2 block index</code>. We also need to know the key size and block size of the B-Tree to be able to jump around in the file.</p> <p>Once the header has been parsed (this is an exercise left up to the reader), the next step is to find the root node. This is following exactly the general lookup algorithm for a B-Tree. The index in the file (by bytes) of a block is \(headerSize + blockSize \times n\), where \(n\) is the block number. Depending on the value of <code>swap</code> (whether to use the second root node), we use either the index of root node 1 or 2 for \(n\), and move to that position.</p> <p>Next, we proceed to identify the node that we&rsquo;ve found. The first two bytes in that node are either the ASCII values of &lsquo;F&rsquo;, &lsquo;L&rsquo;, or &lsquo;I&rsquo;, representing, you guessed it, &ldquo;Free&rdquo;, &ldquo;Leaf&rdquo;, or &ldquo;Index&rdquo;. If the node is an index node, we need to search it for the next node we need to examine. To do so, we first read two values from the node, two 32-bit integers. The first is the number of keys in the block, and the next is the block number of the first child node.</p> <p>The key -&gt; child data is stored in the node as follows:</p> <pre tabindex="0"><code>(1st child)(1st key)(2nd child)(2nd key) ... (nth child) </code></pre><p>(The key is of the length given in the header, and the child number is a 32-bit integer)</p> <p>As you can see, the number of children is one more than the number of keys, and it&rsquo;s easier to treat the first child block number as a separate value, and the rest as key -&gt; child pairs, like:</p> <pre tabindex="0"><code>(1st child) (1st key, 2nd child) ... ((n-1)th key, nth child) </code></pre><p>Simply, if the value we&rsquo;re searching for is bigger than the first key only, we go to the second child, if it&rsquo;s bigger than the second key, we go to the third child, etc. If our value is not bigger than any of the keys, we go to the 1st child. After we move to the index of the new child, we once again examine its type, and if it&rsquo;s still &ldquo;II&rdquo;, we repeat the process.</p> <p>Once we reach a &ldquo;leaf&rdquo; node, we&rsquo;re very close. After the two ASCII characters describing its type, the leaf node will contain a 32-bit integer describing the number of key-data pairs it has. Each key-data pair is made up of the key, a variable integer describing the length of the data, and the data itself. We examine one pair after another, carefully making sure that we don&rsquo;t exceed the end of the block, located at \(headerSize + blockSize \times (n + 1) - 4\). The reason that we subtract four from this equation is to save space for the address of the next block. As I mentioned above, leaf nodes, if their data is bigger than the block size, contain the block number of the next leaf node to which we can continue if we reach the end of the current leaf. If we do reach the end of the current leaf, we read the 32-bit integer telling us the number of the next block, jump to its index, and, after reading the first two bytes to ensure it&rsquo;s actually a leaf, continue reading our data. Once that&rsquo;s done, voila! We have our bytes.</p> <p>If we&rsquo;re using this method to read a Starbound world file, we need to also know that the data is zlib-compressed. I won&rsquo;t go far into the details, as it appears like both Python and Java provide libraries for the decompression of such data.</p> <p>This concludes today&rsquo;s post. I might make another post later about my implementation of the BTreeDB search, written in either Kotlin or Java. In the meantime, if you&rsquo;re interested, check out the following <a href="https://github.com/blixt/py-starbound"class="external-link">Python<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, <a href="https://github.com/blixt/go-starbound"class="external-link">Go<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> and <a href="https://github.com/DanilaFe/kt-starbound"class="external-link">Kotlin (my own)<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> implementations of parsers for BTreeDBs and other Starbound file formats.</p> New Look, New Features! https://danilafe.com/blog/new_look/ Wed, 23 Nov 2016 23:29:13 +0000 https://danilafe.com/blog/new_look/ <p>I&rsquo;ve recently taken the time to redesign my website from scratch, on both the back-end and the front-end! On the back-end, this means the addition of <a href="https://danilafe.com/tags">tags</a>, which allow me to categorize the not-so-plentiful content on the site. On the front-end, this means a new design written completely from scratch. This new front-end also allows me to render mathematics: </p> $$A=\int_{a}^{b}f(x)\,dx$$<p><br> Phew!</p> <p>The only downside to this is that I chose to migrate the lazy way. That is, I simply copy pasted all the post data from one database to the other. Boo! This means the dates will say all three previous posts were made today. Oh well!</p> Learning Emulation, Part 2.5 - Implementation https://danilafe.com/blog/03_learning_emulation/ Thu, 30 Jun 2016 00:00:00 +0000 https://danilafe.com/blog/03_learning_emulation/ <p><em>This is the third post in a series I&rsquo;m writing about Chip-8 emulation. If you want to see the first one, head <a href="https://danilafe.com/blog/01_learning_emulation/">here</a>.</em></p> <p>In the previous part of this tutorial, we created a type to represent a basic Chip-8 machine. However, we&rsquo;ve done nothing to make it behave like one! Let&rsquo;s start working on that.</p> <a href="#initializing"> <h3 id="initializing">Initializing</h3> </a> <p>If you&rsquo;re writing your emulator in C / C++, simply stating that we have a variable such as <code>stackp</code> (stack pointer) will not give you a clean value. Memory for these variables is allocated and never cleaned up, so they can (and do) contain gibberish. For some parts of our program, it&rsquo;s necessary to initialize everything we have to 0 or another meaningful value. A prime example is that of the timers, especially the sound timer. The sound timer beeps until its value is 0, and if it starts with a value of a few million, the game will make a sound without having asked for any. The stack pointer also needs to be set to 0. As it&rsquo;s proper style to initialize everything, we&rsquo;ll do just that.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"> <span class="n">chip</span><span class="o">-&gt;</span><span class="n">pc</span> <span class="o">=</span> <span class="mh">0x200</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">chip</span><span class="o">-&gt;</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">chip</span><span class="o">-&gt;</span><span class="n">stackp</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">chip</span><span class="o">-&gt;</span><span class="n">delay_timer</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">chip</span><span class="o">-&gt;</span><span class="n">sound_timer</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">16</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">chip</span><span class="o">-&gt;</span><span class="n">v</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">4096</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">chip</span><span class="o">-&gt;</span><span class="n">memory</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">16</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">chip</span><span class="o">-&gt;</span><span class="n">stack</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="p">(</span><span class="mi">64</span> <span class="o">*</span><span class="mi">32</span><span class="p">);</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">chip</span><span class="o">-&gt;</span><span class="n">display</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span></code></pre></div><p>I set to program counter to <code>0x200</code> because, according to the specification on the Wiki page for Chip-8,</p> <blockquote> <p>most programs written for the original system begin at memory location 512 (0x200)</p> </blockquote> <a href="#the-first-steps"> <h3 id="the-first-steps">The First Steps</h3> </a> <p>We are now ready to start stepping the emulator. I&rsquo;m going to omit loading a file into memory and assume that one is already loaded. However, I will remind the reader to load the programs into memory starting at address <code>0x200</code> when they write their file loading code.</p> <p>According to the Chip-8 Wiki,</p> <blockquote> <p>CHIP-8 has 35 opcodes, which are all two bytes long and stored big-endian.</p> </blockquote> <p>So, the first thing we want to do in our step code is to combine the next two bytes at the program counter into a single short.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">unsigned</span> <span class="kt">short</span> <span class="kt">int</span> <span class="n">opcode</span> <span class="o">=</span> <span class="p">(</span><span class="n">chip</span><span class="o">-&gt;</span><span class="n">memory</span><span class="p">[</span><span class="n">chip</span><span class="o">-&gt;</span><span class="n">pc</span><span class="p">]</span> <span class="o">&lt;&lt;</span> <span class="mi">8</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="n">chip</span><span class="o">-&gt;</span><span class="n">memory</span><span class="p">[</span><span class="n">chip</span><span class="o">-&gt;</span><span class="n">pc</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]);</span> </span></span></code></pre></div><p>In this piece of code, we take the byte at the program counter, and shift it to the left 8 bits (the size of a byte). We then use binary OR to combine it with the next byte.</p> <p>Now that we have an opcode, we need to figure out how to decode it. Most opcodes are discerned by the first hexadecimal digit that makes them up (for example, the <code>F</code> in <code>0xFABC</code>), so let&rsquo;s first isolate that first digit. We can do that by using the binary AND operation on the program counter, with the second operand being <code>0xF000</code>.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">unsigned</span> <span class="kt">short</span> <span class="kt">int</span> <span class="n">head</span> <span class="o">=</span> <span class="n">opcode</span> <span class="o">&amp;</span> <span class="mh">0xf000</span><span class="p">;</span> </span></span></code></pre></div><p>If we had an opcode with a value such as <code>0x1234</code>, running <code>0x1234 &amp; 0xF000</code> will give us <code>0x1000</code>. This is exactly what we want to tell each opcode apart. We can now start implementing the instructions!</p> <p>The first instruction listed on the Wiki page is:</p> <blockquote> <p><code>0x00E0</code>: Clears the screen.</p> </blockquote> <p>So, in our step code, we need to check if the opcode starts with a 0. If it does, then the whole head variable will be a 0.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="n">head</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"><span class="p">...</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></div><p>Next, though, we have a problem. There are two codes on the Wiki page that start with 0:</p> <blockquote> <p><code>0x00E0</code>: Clears the screen.</p> </blockquote> <blockquote> <p><code>0x00EE</code>: Returns from a subroutine.</p> </blockquote> <p>So now, we need to check if the ending of the instruction is <code>0xE0</code> and not <code>0xEE</code>.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">if</span><span class="p">((</span><span class="n">opcode</span> <span class="o">&amp;</span> <span class="mh">0x00ff</span><span class="p">)</span> <span class="o">==</span> <span class="mh">0xe0</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"><span class="p">...</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></div><p>Now, we can just clear the screen the way we did when we initialized.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="p">(</span><span class="mi">64</span> <span class="o">*</span><span class="mi">32</span><span class="p">);</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">chip</span><span class="o">-&gt;</span><span class="n">display</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span></code></pre></div><p>All together, our code ends up being:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Get the Opcode </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">unsigned</span> <span class="kt">short</span> <span class="kt">int</span> <span class="n">opcode</span> <span class="o">=</span> <span class="p">(</span><span class="n">chip</span><span class="o">-&gt;</span><span class="n">memory</span><span class="p">[</span><span class="n">chip</span><span class="o">-&gt;</span><span class="n">pc</span><span class="p">]</span> <span class="o">&lt;&lt;</span> <span class="mi">8</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="n">chip</span><span class="o">-&gt;</span><span class="n">memory</span><span class="p">[</span><span class="n">chip</span><span class="o">-&gt;</span><span class="n">pc</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1">// Decode opcode </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">if</span><span class="p">(</span><span class="n">head</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">if</span><span class="p">((</span><span class="n">opcode</span> <span class="o">&amp;</span> <span class="mh">0x00ff</span><span class="p">)</span> <span class="o">==</span> <span class="mh">0xe0</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="p">(</span><span class="mi">64</span> <span class="o">*</span><span class="mi">32</span><span class="p">);</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">chip</span><span class="o">-&gt;</span><span class="n">display</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="c1">// Increment the program counter; We don&#39;t want to execute the same thing twice. </span></span></span><span class="line"><span class="cl"><span class="c1">// We increment it by two because a single memory location is one byte, and an instruction is two bytes. </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">chip</span><span class="o">-&gt;</span><span class="n">pc</span> <span class="o">+=</span> <span class="mi">2</span><span class="p">;</span> </span></span></code></pre></div><p>From here, we should be able to implement most of the instructions, as they are pretty basic. It&rsquo;s important that there is some independence here - though emulating a very popular thing like Chip-8 has plenty of tutorials, if we want to emulate something new, we need to be able to read the documentation and work from it alone. So, I leave the rest of the instructions up to you to implement. I might post another update with a guide on how to implement the draw instruction, as I had some trouble with it, but the documentation for Chip-8 is more than enough to help you with the opcodes on your own.</p> Learning Emulation, Part 2 https://danilafe.com/blog/02_learning_emulation/ Wed, 29 Jun 2016 00:00:00 +0000 https://danilafe.com/blog/02_learning_emulation/ <p><em>This is the second post in a series I&rsquo;m writing about Chip-8 emulation. If you want to see the first one, head <a href="https://danilafe.com/blog/01_learning_emulation/">here</a>.</em></p> <p>Now that we have an understanding of the physical capabilities of a Chip-8 system, we can write code that will represent such a system on our computer. In this post we&rsquo;ll start writing some basic code - be prepared.</p> <a href="#a-simple-representation"> <h3 id="a-simple-representation">A Simple Representation</h3> </a> <p>We&rsquo;ll start with the memory. Chip-8 has, as we remember, 4096 memory locations, all of which are bytes. We will create an array of 4096 bytes to match that.</p> <pre tabindex="0"><code>unsigned char memory[4096]; </code></pre><p>If we have instructions in memory, we also need what&rsquo;s called a program counter, a variable that points to the next instruction in memory we have to execute. This variable would never need to be negative, and hold a value of up to 4095. The type we use for the program counter would thus have to be bigger than a <code>char</code> (max unsigned value of 256). The next biggest type we can have would be a <code>short</code>.</p> <pre tabindex="0"><code>unsigned short int pc; </code></pre><p>Next up, the registers. They all hold an 8-bit value, which is always positive. We can represent them with a 16-long array.</p> <pre tabindex="0"><code>unsigned char v[16]; </code></pre><p><em>Note: I used <code>v</code> for the variable name as the registers are named <code>V0</code>-<code>VF</code></em> in the Chip-8 specs.</p> <p>We also need to create the <code>I</code> register. As it&rsquo;s used to handle memory locations, it needs to be able to hold values up to 4095, just like the program counter. So we write it as a short integer.</p> <pre tabindex="0"><code>unsigned short int i; </code></pre><p>Now, the stack. The stack is, unsurprisingly, a <a href="https://en.wikipedia.org/wiki/Stack_%28abstract_data_type%29"class="external-link">stack datastructure<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. According to the Wiki page on Chip-8,</p> <blockquote> <p>modern implementations [of the stack] normally have at least 16 levels</p> </blockquote> <p>Because of that, we&rsquo;re going to represent the stack as a 16-long array of short integers. These integers will represent locations in memory at which a jump was made to a subroutine. If this sounds confusing, it&rsquo;ll be explained when we implement it. Additionally, we&rsquo;d need a stack pointer, which holds a number up to 15 and gives the index in the stack of the latest value that was added.</p> <pre tabindex="0"><code>unsigned short int stack[16]; unsigned char stackp; </code></pre><p>We&rsquo;re almost there. We now need to add the timers. I do not remember seeing the size of these timers on the Wiki, so we&rsquo;ll represent them with generous integers.</p> <pre tabindex="0"><code>unsigned int delay_timer; unsigned int sound_timer; </code></pre><p>Finally, we need a variable to hold the display pixels (which are true or false values). My code is in C, and C did not have a primitive boolean type out of the box unti C99. Because I&rsquo;m mildly crazy, I will avoid this new feature and use, instead, a char. If your language of choice supports booleans (and / or you don&rsquo;t exhibit compulsive behaviors), by all means use them. Anyway, since the display is 64x32, we can make a display variable like this:</p> <pre tabindex="0"><code>unsigned char display[64 * 32]; </code></pre><p>If we were to use booleans, this might look something like:</p> <pre tabindex="0"><code>bool display[64 * 32]; </code></pre><p>Those are all the variables that we have to keep track of! Let&rsquo;s group them under a single chip <code>struct</code>.</p> <pre tabindex="0"><code>struct chip_s { unsigned char memory[4096]; unsigned short int pc; unsigned char v[16]; unsigned short int i; unsigned short int stack[16]; unsigned char stackp; unsigned int delay_timer; unsigned int sound_timer; unsigned char display[32 * 64]; }; </code></pre><p>Since this is C, if we were to just use a <code>struct</code> we&rsquo;d have to use the <code>struct</code> keyword every time. To avoid doing that, I&rsquo;ll use a <code>typedef</code> to define a <code>chip_t</code> type:</p> <pre tabindex="0"><code>typedef struct chip_s chip_t; </code></pre><p>That&rsquo;s it for today! By the end of this post you should have a data type representing a simple Chip-8 machine. I think the next two posts will be independent of each other: one for basic rendering with GLFW, and one for actual Chip-8 code.</p> Learning Emulation, Part 1 https://danilafe.com/blog/01_learning_emulation/ Mon, 27 Jun 2016 00:00:00 +0000 https://danilafe.com/blog/01_learning_emulation/ <p>I&rsquo;ve decided that the purpose of a blog is to actually use it every once in a while. So, to fill up this blank space, I&rsquo;ll be documenting my own experience of starting to learn how emulation works. I&rsquo;d like to say right now that my main goal was not to learn emulation. Rather, I needed to emulate to refresh my skills for a different subject area. However, emulation turned out fun enough to write about.</p> <a href="#choosing-a-target"> <h3 id="choosing-a-target">Choosing a Target</h3> </a> <p>To emulate is to mimic the behavior of something. In terms of video games, emulation attempts to run games made for a console - without the console. To do so, one must read and execute the game&rsquo;s code, which is written as machine code for the processor inside the console. To be able to execute the code, we must know exactly how the processor would do so.</p> <p>At this point, you can probably imagine that emulating really complex processors would be, if nothing else, very tedious. They have huge amounts of different instructions and these instructions would need to be each implemented in our code. So, jumping too far ahead and trying to emulate something complex like a PS2 may make you overwhelmed. When it comes to starting out, small, rewarding, steps are the best.</p> <p>As many people who have just started to learn about emulation, I picked Chip-8 as the first thing I&rsquo;m going to emulate. Chip-8 isn&rsquo;t actually a console by itself. It&rsquo;s an interpreted programming language which is intended to be run by a processor. However, Chip-8 is very low-level and running it will give us a good taste of what console emulation is like.</p> <a href="#doing-some-reading"> <h3 id="doing-some-reading">Doing Some Reading</h3> </a> <p>Remember how I said we&rsquo;d need to know exactly how the processor executes the game code? I wasn&rsquo;t lying. If we want to emulate Chip-8, we need to understand exactly how it was intended to be run. Fortunately, <a href="https://en.wikipedia.org/wiki/CHIP-8"class="external-link">its Wikipedia page<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> contains lots of information about it. Since Chip-8 isn&rsquo;t run on a console - it&rsquo;s an interpreted language - it&rsquo;s instead run in a &ldquo;virtual machine&rdquo;. A virtual machine we can think of, for now, as an equivalent to a console that only exists in our imagination. So, without further ado, let&rsquo;s start examining this virtual machine.</p> <p>According to the Wiki page, Chip-8 was commonly implemented on machines with <strong>4096 memory locations</strong> all of which are <strong>a byte</strong>. However, since on these machines the &ldquo;interpreter&rdquo; - what actually reads and executes the Chip-8 code - occupied the first 512 bytes, <strong>programs typically start right after 512 bytes</strong>. The last 256 + 96 bytes are used &ldquo;internally&rdquo;, so they&rsquo;re not accessed either. Chip-8 has <strong>16 1-byte registers, named V0 through VF</strong> and the last register, <strong>VF, is used as a carry flag</strong>. Another register, I, is present in Chip-8 and is twice as big as the others. This is so that it can be <strong>used in operations involving memory</strong>. Chip-8 also <strong>has a stack with a maximum length of 16</strong>, used only for subroutines as well as <strong>two timers counting down at 60hz</strong>. The display is <strong>64x32</strong> with pixels being either &ldquo;on&rdquo; or &ldquo;off&rdquo;, and input is done through a <strong>hex keyboard with 16 keys</strong>.</p> <p>Phew, that was a lot of information. Whoever might read this will probably need to take time to process this much information, and I&rsquo;ll need to take time to write more. So long, for now!</p> About https://danilafe.com/about/ Mon, 01 Jan 0001 00:00:00 +0000 https://danilafe.com/about/ <p>I&rsquo;m Daniel. I currently work at HPE on the <a href="https://chapel-lang.org"class="external-link">Chapel<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a> language team. Previously, I got my Master&rsquo;s Degree in Programming Languages at Oregon State University. Due to my initial interest in calculators and compilers, I got involved in the Programming Language Theory research group in my first year of college, and ended up gaining same experience in formal verification, domain specific language, and explainable computing.</p> <p>For work, school, and hobby projects, I use a variety of programming languages, most commonly C/C++, Haskell, <a href="https://crystal-lang.org/"class="external-link">Crystal<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>, and <a href="https://elm-lang.org/"class="external-link">Elm<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. I also have experience with Java, Python, Haxe, and JavaScript.</p> <p>A few notes about me or this site:</p> <ul> <li><strong>Correctness</strong>: I mostly write technical content. Even though I proofread my articles, there&rsquo;s always a fairly good chance that I&rsquo;m wrong. You should always use your best judgement when reading anything on this site &ndash; if something seems wrong, it may very well be. I&rsquo;m far from an expert.</li> <li><strong>Schedule</strong>: I do not have a set post schedule. There are many reasons for this: schoolwork, personal life, lack of inspiration. It also takes a <em>very</em> long time for me to write a single article. My article on <a href="https://danilafe.com/blog/10_compiler_polymorphism/">polymorphic type checking</a> is around 8,000 words long; besides writing it, I have to edit it, link up all the code references, and proofread the final result. And of course, I need to write the code and occasionally do some research.</li> <li><strong>Design</strong>: I am doing my best to keep this website accessible and easy on the eyes. I&rsquo;m also doing my best to avoid any and all uses of JavaScript. I used to use a lot of uMatrix, and most of the websites I browsed during this time were broken. Similarly, a lot of websites were unusable on my weaker machines. So, I&rsquo;m doing my part and making this site usable without any JavaScript, and, as it seems to me, even without any CSS.</li> <li><strong>Source code</strong>: This blog is open source, but not on GitHub. Instead, you can find the code on my <a href="https://dev.danilafe.com/Web-Projects/blog-static"class="external-link">Gitea instance<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. If you use this code for your own site, I would prefer that you don&rsquo;t copy the theme.</li> </ul> Content Graph https://danilafe.com/graph/ Mon, 01 Jan 0001 00:00:00 +0000 https://danilafe.com/graph/ <p>Here you can see a visualization of the posts on this blog using <a href="https://visjs.org/"class="external-link">Vis.js<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>. In the below graph, each node is an article, and each edge between nodes is a reference from one article to another. Each article is sized to be roughly proportional to its word count (file size is used as a quick metric for this purpose). You can hover over a node to see the title of the article it represents, and double-click to go to that article.</p> Favorites https://danilafe.com/favorites/ Mon, 01 Jan 0001 00:00:00 +0000 https://danilafe.com/favorites/ <p>The amount of content on this blog is monotonically increasing. Thus, as time goes on, it&rsquo;s becoming harder and harder to see at a glance what kind of articles I write. To address this, I&rsquo;ve curated a small selection of articles from this site that I&rsquo;ve particularly enjoyed writing, or that I think turned out especially well. They&rsquo;re listed below, most recent first.</p> Search https://danilafe.com/search/ Mon, 01 Jan 0001 00:00:00 +0000 https://danilafe.com/search/ <p>Here&rsquo;s a <a href="https://github.com/jameslittle230/stork"class="external-link">Stork<svg class="feather" style="display: none;"> <use xlink:href="https://danilafe.com/feather-sprite.svg#external-link"/> </svg></a>-powered search for all articles on this site. Stork takes some time to load on slower devices, which is why this isn&rsquo;t on every page (or even on the index page). Because the LaTeX rendering occurs <em>after</em> the search indexing, you may see raw LaTeX code in the content of the presented articles, like <code>\beta</code>. This does, however, also mean that you can search for mathematical symbols using only the English alphabet!</p> <p>If you&rsquo;re just browsing, you could alternatively check out <a href="https://danilafe.com/blog">all posts</a>, or perhaps my <a href="https://danilafe.com/favorites">favorite articles</a> from this blog.</p>