notes-computer-programming-programmingLanguageDesign-prosAndCons-autoTypeCoercion

hmm.. transitive implicit coercion might be confusing if you have a zillion clever/nonobvious ways to coerce types into each other which are not all defined in one place.. otoh it sucks to have to write 'a = sqrt(32); print str(a)' instead of just 'print a'.. what's the answer?

i think scala has full-out transitive implicit coercion (not sure tho) but ppl say it may be confusing. C has only explicit coercion.

is 'non-transitive implicit coercion' the answer (and if the compiler does it automatically and does type inference then you have to work to make it nontransitive, b/c even if it only goes one coercion per operation, if something is passed thru a chain of operations that would normally lead to a chained coercion, unless you explicitly represent whether it has already been implicitly coerced at least once)? is a universal 'coerce here if necessary' operator the answer?

scala uses coercion instead of moneypatching/open classes:

" However, it's not as nice as we would like it to be, e.g. "nice" * 3. But how can we get there, because obviously we can't go and put the '*' method to String class. Well, the first thing to do is to create a class StringRepeated? that has that method.

class StringRepeated?(original: String){ def *(times: Int) = { def multiply(times: Int, accumulated: String): String = { if(times > 1) multiply(times - 1, accumulated + original) else original + accumulated } multiply(times, "") } }

Now if we create a StringRepeated?, we can use the '*' like this:

val repeated = (new StringRepeated?("nice")) * 3 == "nicenicenice"

But we can't do this on normal Strings directly. The implicit conversion is what comes to rescue; it makes the conversion from String to StringRepeated?.

implicit def string2repeated(x: String) = new StringRepeated?(x)

That's it. Now when compiler sees a "nice" * 3, it first concludes that there is no '*' method defined in String. Then it looks for implicit conversion that would give a new object that has a '*' with correct parameter type. If there is no ambiguouty in choosing what implicit conversion to apply, the compiler will insert the conversion.

So "nice" * 3 will be statically translated to something like (new StringRepeated?("nice")) * 3.

Now as this small example shows, implicit conversion can be somewhat handy. For additional example, I have an application that prints text that is partly colorized or otherwise styled. I have taken advantage of implicit conversion so that I can write printWith("Hi,"

"
"this" * Bold " is " * Fore(RED)"cool."). It will print sequentially "Hi, this is cool."

but the same page shows that this interacts badly with other features:

http://scalada.blogspot.com/2008/03/implicit-conversions-magical-and.html

" In Scala == is structural equality test method, unlike in Java, where it is reference equality test. It takes one argument type of Any, which means you can compare your object against any object. I mean any, and compiler will allow you that. Doesn't sound too bad, you say? Well, if you have lots of inheritance, you might really want that ability, so that you can reveal whether two objects are the same even if their static type is not the same. But usually I just want to know two statically equivalently typed objects to be tested against each other, not arbitrary objects.

I have had problems with this simply, because you don't always compare things you should. Humans do mistakes (I sometimes wonder, am I ever doing something *right*). For example, I might have a tuple of Ints, which has name 'boom'. And i have another tuple 'doom', and I'd like to compare their first elements, i.e. boom._1 == doom._1. But I might be a bit sleepy, and write "boom == doom._1", i.e. comparing a tuple against an Int. Compiler won't say that you are comparing completely different objects. It just sings logically incorrect bytecode. It might be very hard to notice that the comparison isn't ever going to give a true value just by testing it few times.

Ok, that's bad enough as it is, but when we mix implicit conversions with this magical soup, we are really doomed. An example: String has no reverse method. Therefore it has been extended with RichString? in the same kind of way I described earlier for RepeatedString?. When you say "yay" reverse, you get a RichString?, not String. Guess what, "yay".reverse == "yay" won't give you true. Happy debugging times ahead, my friend. It isn't helping that with type inference in use, even with intermediate variables (val x = "yay" reverse) you might think you're dealing with a normal String. That might happen if you just forgot that there ever was a RichString? class, and thought that reverse was defined in String. "

another solution might be to have only the 'end-consumer' say whether coercion is allowed. e.g. 'print' can say 'i allow coercions via str() in my first argument'. This is no better than saying 'print needs to be defined like: def print(s): s = str(s); ..do stuff..'. Note also that in Python's scheme, it is the class of the value being coerced that decided how it gets coerced (via the __str__ method, or the __repr__ method if that's absent). There's no chained coercion.

it seems to me that we dont want people using implicit coercion in the way it was used in that blog post. A Haskell typeclass instance would be a cleaner way to do that. However, this still seems to me like a case of ad-hoc polymorphism for something 'ad-hoc', e.g. it's not being used to add e.g. read and write to a file, it's being used to add multiplication to a string. maybe if you added multiplication to anything which had an append operation, i'd feel better.

hmm.. perhaps that's it.. on the one hand, there is the implementation of an interface for a specific implementation. On the other hand, there is saying, 'if something supports interface A and interface B, then here is how to make it support interface C'. Haskell allows the latter, also via the typeclass interface mechanism:

" instance (Eq a) => Eq (Tree a) where Leaf a == Leaf b = a == b (Branch l1 r1) == (Branch l2 r2) = (l1==l2) && (r1==r2) _ == _ = False " -- http://www.haskell.org/tutorial/classes.htm