Are you saying that using equations to add a level of indirection
prevents optimization?  I still don't see it - discarding x doesn't
change the semantics, so a good compiler /should/ do this.  How is
this different from optimizing out application of a constant function?

No, no compiler should change "getChar >>= \x -> getChar" to just getChar, because it's just wrong. The first will try to read in two values of type Char, the second will only try to read one. Side effects aren't THAT hated!