Front Matter

Preface

This book grew out of a frustration with having to choose. The literature we learn from is generous and deep, but it is split down the middle. One shelf teaches us to make illegal states unrepresentable, to parse rather than validate, to let the compiler carry the weight — and is largely silent on how a system so tightly closed is ever supposed to grow. The other shelf teaches combinators, interpreters, generic operations, computation under partial information — and is largely silent on how you keep a system so open from quietly going wrong. Each shelf is right about the problem it fights. Neither says much about living with both at once, which is the only situation a working programmer is ever actually in.

The aim here is to hold the two disciplines in one hand. The point is not that constraint is better than flexibility, or the reverse, but that they belong in different places — a core you constrain, an edge you leave open, and a contract on the boundary between them so each side can trust the other without seeing inside it. That is the whole of it, said in one breath. The rest of what follows is what it takes to mean it: where the line goes, how the boundary holds, and what each discipline looks like when it is doing its own job and not the other's.

What follows is not about a language. The ideas here should carry to any language with a real type system and a well-defined way to state contracts. But they are shown in Nex, and that choice is not incidental: Nex puts Design by Contract — require, ensure, invariant, variant — in the ordinary grammar of the language rather than off to the side in a testing library or a comment. That is exactly what a case for the boundary between constraint and flexibility needs, because it lets the membrane between them appear on the page in the same syntax as everything else. If Nex is unfamiliar, Appendix A is a short reading guide; you do not need to know the language to follow the argument, only to read the listings, and that takes a page or two.

This book is written for the programmer who has felt software get hard and wants a principled account of what to do about it — not for the type theorist or the metaprogramming specialist, though both may find the placement debatable. You do not need category theory or a background in language design. You need to have shipped something, watched it calcify or misbehave, and wondered whether the two problems really had to be traded off against each other. They do not, and showing why is the point.

These chapters are meant to be read in order, because the order is the point: we take up contracts first, constrain the core, then compose the trustworthy parts, and only then — deliberately, after you have seen what safety costs and buys — step outside the type system to open the frontier. Reading straight through, you watch a single running system, an order-and-fulfillment domain with a pluggable pricing engine, earn its flexibility without ever loosening its core. But it does not have to be read that way. If you have a decision in front of you right now, Chapter 14 is the framework for where to draw the line, Chapter 15 assembles the whole system end to end, and the glossary in Appendix D collects the vocabulary — some of it coined here — with a pointer to where each term is developed.

— Vijay Mathew