It is time for Cone to get a proper module system. This design space is complex and rife with historical missteps. In order to distill the topography of the landscape and clarify the key requirements, I felt it necessary to begin the journey with cross-language research and contemplation. I wound up pursuing three rabbit-chasing adventures in module wonderland: What do programmers want from modularity? You can read about this adventure in my earlier post on modularity, which captures what we want from modularity, and summarizes how the three modularity capabilities are surfaced across different layers of programming language features (and program decomposition).
Cone’s module system has been on my mind of late. The best design for modules is neither easy or obvious, as evidenced by how much modules vary from one language to the next. To guide my approach for Cone, I went back to basics: What is the role (and benefit) that modularity plays in programming (languages)? What role do modules play within this larger picture. This post synthesizes my findings.
Inheritance makes it easier than any other mechanism (e.g. generics, macros, composition/delegation) to define a type that reuses the state and some methods of other types. After reading my inheritance posts, I hope you are convinced that simplifying inheritance to a namespace-based mechanism ensures we obtain this convenient reuse capability, while avoiding most of the complexity and coupling dangers of traditional inheritance. However, you might still wonder whether real-world code needs inheritance’s reuse capability.
After removing the interface, inversion of control, and protected access capabilities from traditional inheritance, what do we have left (besides composition)? This is what we have: placing a few extra tokens on a derived class causes all named fields and methods of one or more base classes to be absorbed as if explicitly incorporated. Further, certain inherited methods can be customized (overridden) with their own implementation. The primary selling point for inheritance has always been this sort of code reuse.
Anti-inheritance advocates are likely to enthusiastically support this post. It promotes the most useful feature of traditional inheritance (Interfaces), turning it into a more valuable abstraction that is largely independent of inheritance. It discards two other traditional features of inheritance, Inversion of Control and Protected Access, as both unnecessary and dangerous. Let’s examine each in turn… Interfaces An interface is “an abstract type that contains no data but defines behaviors as method signatures.
I need to decide what sort of inheritance capability Cone will offer. None! I can hear some of you insist. “Inheritance has recently fallen out of favor as a programming design solution,” claims the Rust language book. “Favor object composition over class inheritance,” recommends the Design Patterns book in 1994. “Inheritance is Evil.” insists Nicolò Pignatelli. It is not hard to find cogent, hard-hitting critiques of inheritance, complaining about costs incurred from fragile base classes, excessive coupling, and broken encapsulation.