Because of the "D UFCS anti-pattern" (as dubbed by Steven Schveighoffer):
http://forum.dlang.org/thread/op.xeuot6g2eav7ka@stevens-macbook-pro-2.local
https://issues.dlang.org/show_bug.cgi?id=9074
https://issues.dlang.org/show_bug.cgi?id=12583#c3

Long story short, if `r` has only has `put(string)`, then `r.put('.')` will fail to compile. On the other hand, `put(r, '.')` will call `std.range.put`, which will do what must be done to make it work. Ditto the other way around: If `r` has `put(char)`, then `r.put("hello")` will fail, whereas `put(r, "hello")` will succeed.

We don't usually "see" the issue much in phobos because:
1. We always use Appender, which has all flavors of put built into it, so has no need for `std.range.put` trans-coding.
2. Up until now, `put`'s transcoding was not very well implemented, and anything other than `Appender` failled sooner or later.