On 28-mar-10, at 22:34, so wrote:

On Mon, 29 Mar 2010 00:46:20 +0400, Don <nos...@nospam.com> wrote:

so wrote:
Hello Don, finally!
It is hard to explain yourself when you don't know the people you talk have numeric coding background. Since we know you have done much numeric and generic coding, it will be enough for me if you understand what i mean/ask/want, and say what i am talking is ridiculous, or think that i am trolling, just say so and i shut up! :)

You're definitely not trolling! I'm not 100% sure of which issue you're referring too, but I'm aware of a few, for example:

(1) Converting a floating point literal into a double literal is usually not lossless. 0.5f, 0.5, and 0.5L are all exactly the same number, since they are exactly representable.
But 0.1 is not the same as 0.1L.
So it's a bit odd that this silent lossless conversion is taking place.
It does have a very strong precedent from C, however.

(2) The interaction between implicit casting and template parameters is quite poor. Eg, the fact that '0' is an int, not a floating point type, means that something simple like:
 add(T)(T x) if (isFloatingPoint!(T))
doesn't work properly. It is not the same as:
add(real x)
since it won't allow add(0).

Which is pretty annoying. Why can't 0 just mean zero???

At the beginning the question in my mind was, why can't we do this the way it works in math :

[Real numbers] contains [Integers] contains [Natural numbers]

So, when a compiler see 0, which is the element of all above, it should classify it as :
1st degree. [floating point type, integer, natural number]
When it encounters say, -3, it will be :
2nd degree. [floating point type and integer]
And finally when it encounters something like 4.0, it will be :
3rd degree. [floating point type]

With these in mind, the prototype :

T foo(T)(T m) {}, should be able to take all three degrees of types above.

We go abit further and see what it actually is :

T foo(T)(T m) {
T n = m * 5 - 4 * 7 / 2; // numbers belongs all the systems above, this line should work for every T
        T k = n / 20; // same.
        return n + k; // same.
}

"T n = m * 5 - 4 * 7 / 2;"
m is here in the type of T,
* operator has a generic operand 5, convert it to T, if you can't, pop error. - operator has a generic operand (4 * 7 / 2), expect it in the and keep parsing.

Again, in the code above, none of the constants is a native type, as in 5 is not int, it is a generic constant which belongs all systems above.
What this means?

calling foo(0.0f) is actually :
float foo(float m) {
        float n = m * 5.0f - 4.0f * 7.0f / 2.0f;
        float k = n / 20.0f;
        return n + k;
}

m is here float and operation * has a generic operand

There wasn't a single implicit cast and the code was perfectly generic.

why do you see implicit cast as bad? for me the change from 5 to 5.0f that you have above *is* an implicit cast, and is exactly what you want. For 4 * 7 / 2, well that should either be calculated as integer or as float, it is unlikely that both are correct. Some languages decided to use a different symbol for integer division and float division. without that the thing is kind of ambiguous, and D chooses integer division, and only later implicit conversion to float. What I do agree with is that writing generic float literals is cumbersome (using cast(T)x).

Implicit cast is what happens to stuff that can also be seen as a type X so natural number -> integers,... implicit cast should not discard precision, and should maintain the value, and is a *good* thing in general (if the rules are sensible).

calling foo(0) will pass the "if(isFloatingPoint!(T))" contract, since 0 belongs to all, but compiler will be unable to resolve the final type which is to be instantiated.

that gets easily very annoying, even aldor, that basically did all what you wanted, and complained about unclear overload did an exception for integers, so that they would default to Int....

Still I find D approach reasonable

Reply via email to