Explain the core tradeoff between monomorphization and type erasure in terms of runtime performance and binary size.
Think about your answer, then reveal below.
Model answer: Monomorphization generates a separate, fully specialized copy of the generic code for each concrete type used. This enables zero-cost abstraction — the compiler can optimize each copy as if it were hand-written for that type — but multiplies code size with every new type instantiation, potentially bloating the binary and pressuring instruction caches. Type erasure produces a single shared implementation operating on a uniform representation (typically object references), keeping binary size small regardless of how many types are used, but at the cost of indirection, boxing of primitives, and loss of type-specific optimization opportunities. The choice encodes a tradeoff between 'fast at runtime' and 'small in memory.'
Neither approach is universally better — the right choice depends on the language's goals and typical use cases. Systems languages (Rust, C++) favor monomorphization because performance is paramount and generics are often used with a small, fixed set of types. Application languages (Java, C#) favor erasure or hybrid approaches because developer productivity, binary size, and startup time matter more than peak throughput. Modern languages like Swift and C# explore intermediate strategies (specialization for critical types, sharing for others) to capture benefits of both.