Powers vs Properties: Examples Compendium

One of my favorite articles about programming philosophy is Ted Kaminski’s The one ring problem: abstraction and our quest for power where he describes how adding more power to languages (e.g. adding macros) always comes at the cost of (provable) properties.

image

This article is meant as a personal compendium of various examples of this tradeoff that I have encountered during my career. Expect the list to grow over time.

Normed Division Algebras

John Baez, in his article on Octonions, describes this exact phenomena for the four normed division algebras, where increasing “power” comes with the loss of property:

Compiler Intermediate Representations

When compiling high-level programming languages to low-level assembly languages, compilers typically go through a series of intermediate representations. Imperative languages use SSA based representations, while functional languages use lambda-calculus based representations such as System F.

image

Properties

Here are main properties that IRs lose as they go down from high-level programming languages to assembly languages:

IR Hierarchy

  1. High-Level Language → Abstract Syntax Tree (AST)
    • Properties Preserved: Semantic meaning, Type information, Variable scope relationships, Control flow structure
    • Properties Lost: Comments and documentation, Specific syntax choices (coding style), Whitespace and formatting, Programmer intent beyond what’s explicit in code
  2. AST → High-Level IR (e.g., Rust’s HIR, Swift’s SIL)
    • Properties Preserved: Control flow structure, Type relationships, Function boundaries, Variable scope information
    • Properties Lost: Language-specific abstractions, Some type system nuances, Complex inheritance relationships, Syntactic sugar
  3. High-Level IR → Mid-Level IR (e.g., LLVM IR, GIMPLE)
    • Properties Preserved: Basic control flow, Function boundaries, Core type information (simplified), Data flow relationships
    • Properties Lost:
      • High-level type system features (generics, traits)
      • Object-oriented relationships
      • Exception handling semantics (translated to control flow)
      • Abstract data types (transformed to primitives)
  4. Mid-Level IR → Low-Level IR (e.g., RTL, LLVM MachineIR)
    • Properties Preserved: Basic operations, Simplified control flow, Memory access patterns, Data dependencies
    • Properties Lost: Hardware independence, Most remaining type information, Variable abstractions (everything becomes registers or memory), Some optimization opportunities tied to high-level semantics
  5. Low-Level IR → Assembly Language
    • Properties Preserved: Machine operations, Memory addressing, Register allocation, Basic blocks structure
    • Properties Lost: Machine independence, Most optimization opportunities, Remaining abstract data types, Register allocation flexibility, Variable lifetime management
  6. Assembly → Machine Code
    • Properties Preserved: Direct hardware operations, Memory layout
    • Properties Lost: Human readability, Symbolic references, Label names, Meaningful structure beyond instructions

References

Leaving a list of references for those interested in digging into this topic further: