Wed, 7 Feb 2001 13:04:12 +0100 (MET), Bjorn Lisper <lisper@it.kth.se> pisze:
A natural principle to adopt is that an already typeable expression should not be transformed. This will for instance resolve the ambiguity in the list of list example: if l :: [[a]] then length l is already well-typed and should not be transformed into map length l.
So there are ways to interpret an expression which are not chosen only because some other way is a better match? This is very dangerous in principle. Two interpretations of a code are "correct", but one is "more correct" than the other. Say there is a code which relies on the implicit fmap, and it's slightly changed by replacing the function with a more general function, which has the same result on this instance. Then suddenly without a warning the code has a different meaning, because it is now applied in a different way (different placement of implicit fmaps). Also, what is the inferred type of, for example f x y = x + length y ? It can be Int -> [a] -> Int [Int] -> [a] -> [Int] and neither is more general than the other. And this is a simple function.
Again, Haskell does not have subtyping. It is not compatible with type inference - it can only work in poor languages which require an operation to be fully applied where it is used, and either don't have static types or require them to be specified explicitly.
I am not so sure about this. Could you exemplify?
Sorry, I don't have a concrete example in mind. How to infer types when implicit conversions are possible anywhere? The above function f can be applied even to two numbers (because the second would be promoted to a list of length = 1), so what is its inferred most general type? Assuming that Ints can be implicitly converted to Doubles, is the function f :: Int -> Int -> Double -> Double f x y z = x + y + z ambiguous? Because there are two interpretations: f x y z = realToFrac x + realToFrac y + z f x y z = realToFrac (x + y) + z Making this and similar case ambiguous means inserting lots of explicit type signatures to disambiguate subexpressions. Again, arbitrarily choosing one of the alternatives basing on some set of weighting rules is dangerous, because a programmer might mean the other alternative - there is no simple way to ensure that the compiler interprets it in the same way as I wanted. It's not enough to check that all types match modulo conversions - I must carefully check that no "better" interpretation is possible.
Note that you can do some of this overloading already within Haskell's class system.
But it's quite rigorous: all uses of an identifier must be at a type which is an instance of a single generic type. There is enough type information to disambiguate the meaning in most cases - *without* rejecting an interpretation because another was better (except defaulting of numeric types - yes, it's ugly). Another advantage of the Haskell's class system is that no code relies on absence of something. Adding an instance does not make previously working code ambiguous or otherwise incorrect! Except when the instance conflicts with another one - this is the only kind of "negative constraint". -- __("< Marcin Kowalczyk * qrczak@knm.org.pl http://qrczak.ids.net.pl/ \__/ ^^ SYGNATURA ZASTÊPCZA QRCZAK