I would suggest that a good programming language should have strict rules, which implementations would be expected to enforce consistently, but the rules should be written in such fashion so as to be helpful. I would further suggest that one should consider designing a language to avoid cases where the "Hamming distance" between two substantially-different programs is only one. Obviously one can't achieve such a thing with numeric or string literals (if a programmer who meant 123 instead types 1223 or 13, the compiler can't very well know what the program meant). On the other hand, if language were to use :=
for assignment and ==
for equality comparison, and not use a single =
for any legal purpose, then would greatly reduce the possibilities both for accidental assignments which were supposed to be comparisons, and accidental do-nothing comparisons which were supposed to be assignments.
I would suggest that while there are places where it is useful for compilers to infer things, such inference is often most valuable in the simplest cases, and less valuable in the more complicated cases. For example, allowing the replacement of:
Dictionary<complicatedType1,complicatedType2> item =
new Dictionary<complicatedType1, complicatedType2()>;
with
var item = new Dictionary<complicatedType1, complicatedType2()>;
does not require any complicated type inference, but makes the code vastly more readable (among other things, using the more verbose syntax only in scenarios where it's needed, e.g. because the type of the storage location doesn't precisely match the type of the expression creating it, will help call extra attention to places that may require it).
One major difficulty of attempting more sophisticated type inference is that ambiguous situations may arise; I would suggest that a good language should allow a programmer to include information to the compiler could use to either resolve such ambiguities (e.g. by regarding some typecasts as preferable to others), determine that they don't matter (e.g. because even though two possible overloads may execute different code, the programmer has indicated that they should behave identically in those cases where either could be used), or flag those (and only those) which cannot be handled in either of the above ways.