Oh, yes of course. Switching to String and it works as I expected.
I thought the builtins were defined in core and reexported by the prelude (they are defined in core, they're just implicitly in scope anyway).
But I still think expected behaviour is that builtins should have the same precedence as the prelude.