The use of traits here is really cool. It looks like the entire language implementation is defined as a trait, which allows for language backends to be swapped out while maintaining the same outward API.
Thanks for catching this. That is spot on. These APIs in the `Symbol` backend [0] show this concept off really well I think.
The `Symbol` data structure knows how to, e.g. render its `Symbol#inspect` implementation [1]. To extract the underlying bytes associated with the `Symbol`, however, it needs an interpreter. But not a whole interpreter or any particular interpreter, just one that implements `Intern` [2].
All of these generics get monomorphized so there are no vtables or other indirection. Because it's unlikely you'll have more than one interpreter implementation in your program, there is no code bloat either.