www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Is opCast need, we have to!

reply Jesse Phillips <jessekphillips+D gmail.com> writes:
I'm not going to say it should be removed, but I'm just wondering if this is
really needed anymore, or maybe to! should instead make use of it.

opCast provides a means to convert an object/struct of one type to another
through an explicit cast operation. This is good as it allows generic code to
perform conversions through a standard call, and the result is a usable type.

I would like to advocate the use of to!() more often in D code than cast(). It
is much more robust, though has slightly different behavior (throws an
exception on class conversions error).

The main issue I see is that we can now convert classes in two forms. To will
make use of both on opCast function and a to function that is found in the
class. (opCast taking precedence in to!). The behavior for using these are
quite different, and be a reason to keep them distinct. In which case this is
just an informative message about their behavior and I'll add it to a wiki.

The example below shows 3 things:

 * order of precedence is: opCast, cast, to
 * defining opCast can disable casting to any type (its existence prevents
normal casting operations from happening)
 * the declared to!() function is not used when an opCast exists but doesn't
match.

Maybe the last one should be fixed.

Here is the code:

import std.conv;

void main() {
    B b = new B;
    b.i = 5;
    A ab = b;

    B bcast = cast(B) ab;
    assert(b.i == bcast.i); // Fail: used opCast function

    B bto   = to!B(ab);
    assert(b.i == bto.i); // Fail: used opCast function

    C c = new C;
    c.i = 5;
    A ac = c;
    //C ccast = cast(C) ac; // Doesn't compile: uses opCast
    //C cto   = to!C(ac); // Doesn't compile: uses opCast
    //
    // test.d(13): Error: template instance opCast!(C) 
    //             does not match template declaration opCast(T) if (is(T == B))
    //
    //assert(c.i == ccast.i);
    //assert(c.i == cto.i);

    E e = new E;
    e.i = 5;
    D de = e;

    E ecast = cast(E) de;
    assert(e.i == ecast.i); // Pass: uses cast

    E eto   = to!E(de);
    assert(e.i == eto.i); // Pass: uses cast 
}

class A {
    B opCast(T)() if(is(T == B)) {
        return new B;
    }

    C to(T)() if(is(T == C)) {
        auto b = new B;
        b.i = 5;
        return b;
    }
}

class B : A {
    int i;
}

class C : A {
    int i;
}

class D {
    E to(T)() if(is(T == E)) {
        return new E;
    }
}

class E : D {
    int i;
}
Dec 01 2010
next sibling parent reply foobar <foo bar.com> writes:
Jesse Phillips Wrote:

 I'm not going to say it should be removed, but I'm just wondering if this is
really needed anymore, or maybe to! should instead make use of it.
 
 opCast provides a means to convert an object/struct of one type to another
through an explicit cast operation. This is good as it allows generic code to
perform conversions through a standard call, and the result is a usable type.
 
 I would like to advocate the use of to!() more often in D code than cast(). It
is much more robust, though has slightly different behavior (throws an
exception on class conversions error).
 
 The main issue I see is that we can now convert classes in two forms. To will
make use of both on opCast function and a to function that is found in the
class. (opCast taking precedence in to!). The behavior for using these are
quite different, and be a reason to keep them distinct. In which case this is
just an informative message about their behavior and I'll add it to a wiki.
 
 The example below shows 3 things:
 
  * order of precedence is: opCast, cast, to
  * defining opCast can disable casting to any type (its existence prevents
normal casting operations from happening)
  * the declared to!() function is not used when an opCast exists but doesn't
match.
 
 Maybe the last one should be fixed.
 
 Here is the code:
 
 import std.conv;
 
 void main() {
     B b = new B;
     b.i = 5;
     A ab = b;
 
     B bcast = cast(B) ab;
     assert(b.i == bcast.i); // Fail: used opCast function
 
     B bto   = to!B(ab);
     assert(b.i == bto.i); // Fail: used opCast function
 
     C c = new C;
     c.i = 5;
     A ac = c;
     //C ccast = cast(C) ac; // Doesn't compile: uses opCast
     //C cto   = to!C(ac); // Doesn't compile: uses opCast
     //
     // test.d(13): Error: template instance opCast!(C) 
     //             does not match template declaration opCast(T) if (is(T ==
B))
     //
     //assert(c.i == ccast.i);
     //assert(c.i == cto.i);
 
     E e = new E;
     e.i = 5;
     D de = e;
 
     E ecast = cast(E) de;
     assert(e.i == ecast.i); // Pass: uses cast
 
     E eto   = to!E(de);
     assert(e.i == eto.i); // Pass: uses cast 
 }
 
 class A {
     B opCast(T)() if(is(T == B)) {
         return new B;
     }
 
     C to(T)() if(is(T == C)) {
         auto b = new B;
         b.i = 5;
         return b;
     }
 }
 
 class B : A {
     int i;
 }
 
 class C : A {
     int i;
 }
 
 class D {
     E to(T)() if(is(T == E)) {
         return new E;
     }
 }
 
 class E : D {
     int i;
 }
 
IMHO, coercions in D should be redesigned. They are a tiny bit better than C but C is a weekly (and poorly) typed language. I personally prefer the properly strongly typed ML family of languages. My preference is as follows: 1. static_cast: a. Remove ALL implicit coercions inherited from c such as double -> int, b. I don't see the need for an operator for conversions since they can have different parameters, e.g.: - converting to string can have formatting specified - converting string to numeric types with optional base parameter - converting integer to floating point specifies round/floor/ceiling/etc.. 2. const_cast: should be a _separate_ operator in order to prevent removing const by mistake. const Base obj1 = new Derived(); auto obj2 = cast(Derived)(obj1); // oops: meant to only down cast 3. dynamic_cast: the language should provide a down cast operator for OO. 4. reinterpret_cast: unsafe and should be restricted as much as possible (Not available in safe code) maybe not provide it at all since it's implementable via union. A restricted library solution for when you really want to play with bits and bytes? the above means that: double pi = 3.14; int p = pi; // compile-time error int p = floor(pi); // ok and also: int x = ??; // some number double y = x; //compile-time error double z = double(x); // explicit double r = 5 / 2; // compile error choose either: a. double r1 = double(5/2); // 2.0 b. double r2 = double (5) / 2; // 2.5
Dec 01 2010
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
foobar:

 1. static_cast: 
   a. Remove ALL implicit coercions inherited from c such as double -> int,
   b. I don't see the need for an operator for conversions since they can have
different parameters, e.g.:
   - converting to string can have formatting specified
   - converting string to numeric types with optional base parameter
   - converting integer to floating point specifies round/floor/ceiling/etc..
 2. const_cast: should be a _separate_ operator in order to prevent removing
const by mistake.
   const Base obj1 = new Derived();
   auto obj2 = cast(Derived)(obj1); // oops: meant to only down cast
 3. dynamic_cast: the language should provide a down cast operator for OO. 
 4. reinterpret_cast: unsafe and should be restricted as much as possible (Not
available in  safe code) maybe not provide it at all since it's implementable
via union. A restricted library solution for when you really want to play with
bits and bytes? 
There are some ideas here, like the separation from const_cast, dynamic cast, and other casts. In past I too have asked for something similar, but I think Walter was not interested. I guess the idea of having a single cast is to keep the language simple. But those _are_ different kinds of casts, they have different semantics. Lumping different semantics into the same syntax is not a good way to simplify a language, in my opinion. It just creates obscurity and maybe some other troubles too. Bye, bearophile
Dec 01 2010
next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Wednesday, December 01, 2010 14:32:12 bearophile wrote:
 foobar:
 1. static_cast:
   a. Remove ALL implicit coercions inherited from c such as double ->
   int, b. I don't see the need for an operator for conversions since
   they can have different parameters, e.g.: - converting to string can
   have formatting specified
   - converting string to numeric types with optional base parameter
   - converting integer to floating point specifies
   round/floor/ceiling/etc..
 
 2. const_cast: should be a _separate_ operator in order to prevent
 removing const by mistake.
 
   const Base obj1 = new Derived();
   auto obj2 = cast(Derived)(obj1); // oops: meant to only down cast
 
 3. dynamic_cast: the language should provide a down cast operator for OO.
 4. reinterpret_cast: unsafe and should be restricted as much as possible
 (Not available in  safe code) maybe not provide it at all since it's
 implementable via union. A restricted library solution for when you
 really want to play with bits and bytes?
There are some ideas here, like the separation from const_cast, dynamic cast, and other casts. In past I too have asked for something similar, but I think Walter was not interested. I guess the idea of having a single cast is to keep the language simple. But those _are_ different kinds of casts, they have different semantics. Lumping different semantics into the same syntax is not a good way to simplify a language, in my opinion. It just creates obscurity and maybe some other troubles too.
And how many programmers do you know who actually, really know the differences between the 4 C++ cast types. _I_'m not sure that _I_ know, and I've studied it. And honestly, in most cases - if not in _all_ cases - as far as I can tell, the compiler should be able to determine the correct cast type. So, forcing that on the programmer is just stupid. If you actually _need_ separate cast types, then we should have them, but if we can avoid that, we definitely should. I think that the fact that C++ has so many types of casts is horrible. I try and use them correctly, but I don't think that there are very many programmers (percentage- wise at least) who do. I know plenty of programmers who just use C-style casts everywhere. - Jonathan M Davis
Dec 01 2010
prev sibling parent Sclytrack <sclyrack idiot.com> writes:
== Quote from bearophile (bearophileHUGS lycos.com)'s article
 foobar:
 1. static_cast:
   a. Remove ALL implicit coercions inherited from c such as double -> int,
   b. I don't see the need for an operator for conversions since they can have
different parameters, e.g.:
   - converting to string can have formatting specified
   - converting string to numeric types with optional base parameter
   - converting integer to floating point specifies round/floor/ceiling/etc..
 2. const_cast: should be a _separate_ operator in order to prevent removing
const by mistake.
   const Base obj1 = new Derived();
   auto obj2 = cast(Derived)(obj1); // oops: meant to only down cast
 3. dynamic_cast: the language should provide a down cast operator for OO.
 4. reinterpret_cast: unsafe and should be restricted as much as possible (Not
available in safe code) maybe not provide it at all since it's implementable via union. A restricted library solution for when you really want to play with bits and bytes?
 There are some ideas here, like the separation from const_cast, dynamic cast,
and other casts. In past I too have asked for something similar, but I think Walter was not interested.
 I guess the idea of having a single cast is to keep the language simple. But
those _are_ different kinds of casts, they have different semantics. Lumping different semantics into the same syntax is not a good way to simplify a language, in my opinion. It just creates obscurity and maybe some other troubles too.
 Bye,
 bearophile
How about the following. T1 a; T2 b; b = cast(T2, T1) a; The difference between the two types would be clearly visible.
Dec 01 2010
prev sibling parent reply Jesse Phillips <jessekphillips+D gmail.com> writes:
foobar Wrote:

 IMHO, coercions in D should be redesigned. They are a tiny bit better than C
but C is a weekly (and poorly) typed language. I personally prefer the properly
strongly typed ML family of languages.
 
 My preference is as follows:
 1. static_cast: 
   a. Remove ALL implicit coercions inherited from c such as double -> int,
Done
   b. I don't see the need for an operator for conversions since they can have
different parameters, e.g.:
   - converting to string can have formatting specified
   - converting string to numeric types with optional base parameter
   - converting integer to floating point specifies round/floor/ceiling/etc..
This was kind of my point, to! already specifies how to generically (an important part) to other types. And opCast overrides everything (which I don't wish to claim is incorrect, just asking).
 2. const_cast: should be a _separate_ operator in order to prevent removing
const by mistake.
   const Base obj1 = new Derived();
   auto obj2 = cast(Derived)(obj1); // oops: meant to only down cast
I think to! should be changed such that this would be a ConvException.
 3. dynamic_cast: the language should provide a down cast operator for OO. 
Then the example above is invalid, though still valid when casting const double to int...
 4. reinterpret_cast: unsafe and should be restricted as much as possible (Not
available in  safe code) maybe not provide it at all since it's implementable
via union. A restricted library solution for when you really want to play with
bits and bytes? 
 
 the above means that:
 double pi = 3.14; 
 int p = pi; // compile-time error
Currently is an error.
 int p = floor(pi); // ok
Maybe std.math.floor should return an int, since they are implicitly converted to real...
 and also:
 int x = ??; // some number
 double y = x; //compile-time error
 double z = double(x); // explicit
Other than overflow, which isn't fixed if made explicit, I don't see an issue with it being implicit.
 double r = 5 / 2; // compile error
Well this might be one, if 5 and 2 are variables.
 choose either:
 a.  double r1 = double(5/2); // 2.0
 b. double r2 = double (5) / 2; // 2.5
Assuming variables here, wouldn't that need to be: double r2 = cast(double) 5 / cast(double) 2; // not implicit casting remember.
Dec 01 2010
parent foobar <foo bar.com> writes:
Jesse Phillips Wrote:

 foobar Wrote:
 
 IMHO, coercions in D should be redesigned. They are a tiny bit better than C
but C is a weekly (and poorly) typed language. I personally prefer the properly
strongly typed ML family of languages.
 
 My preference is as follows:
 1. static_cast: 
   a. Remove ALL implicit coercions inherited from c such as double -> int,
Done
   b. I don't see the need for an operator for conversions since they can have
different parameters, e.g.:
   - converting to string can have formatting specified
   - converting string to numeric types with optional base parameter
   - converting integer to floating point specifies round/floor/ceiling/etc..
This was kind of my point, to! already specifies how to generically (an important part) to other types. And opCast overrides everything (which I don't wish to claim is incorrect, just asking).
I don't follow as to how this is important to have a generic interface. Can I specify parameters for conversion with to! as in my examples? I'd appreciate an example
 2. const_cast: should be a _separate_ operator in order to prevent removing
const by mistake.
   const Base obj1 = new Derived();
   auto obj2 = cast(Derived)(obj1); // oops: meant to only down cast
I think to! should be changed such that this would be a ConvException.
This should not even compile. a run-time exception is better than current D but is still very weak.
 
 3. dynamic_cast: the language should provide a down cast operator for OO. 
Then the example above is invalid, though still valid when casting const double to int...
a down cast should _only_ perform dynamic down casts in the OO sense. so: Base foo = new Derived; Derived bar = downCast(foo); // compiles. performed at run-time [const] double -> int is not a down cast, it is a conversion.
 
 4. reinterpret_cast: unsafe and should be restricted as much as possible (Not
available in  safe code) maybe not provide it at all since it's implementable
via union. A restricted library solution for when you really want to play with
bits and bytes? 
 
 the above means that:
 double pi = 3.14; 
 int p = pi; // compile-time error
Currently is an error.
 int p = floor(pi); // ok
Maybe std.math.floor should return an int, since they are implicitly converted to real...
 and also:
 int x = ??; // some number
 double y = x; //compile-time error
 double z = double(x); // explicit
Other than overflow, which isn't fixed if made explicit, I don't see an issue with it being implicit.
besides the overflow issue you have mentioned, I also don't want special cases. No implicit conversions should be applied equally everywhere.
 
 double r = 5 / 2; // compile error
Well this might be one, if 5 and 2 are variables.
 choose either:
 a.  double r1 = double(5/2); // 2.0
 b. double r2 = double (5) / 2; // 2.5
Assuming variables here, wouldn't that need to be: double r2 = cast(double) 5 / cast(double) 2; // not implicit casting remember.
This depends on the definition of the "/" operator. (double / int) always returns a double so no casting is required.
Dec 02 2010
prev sibling next sibling parent Jesse Phillips <jessekphillips+D gmail.com> writes:
Looking at the code it seems the Doc is actually in need of updating[1].

But I feel that using to! should be a much safer choice and have thus suggested
an improvement along with a patch[2].

1. http://d.puremagic.com/issues/show_bug.cgi?id=5308
2. http://d.puremagic.com/issues/show_bug.cgi?id=5307
Dec 01 2010
prev sibling next sibling parent reply foobar <foo bar.com> writes:
Jonathan M Davis Wrote:

 On Wednesday, December 01, 2010 14:32:12 bearophile wrote:
 foobar:
 1. static_cast:
   a. Remove ALL implicit coercions inherited from c such as double ->
   int, b. I don't see the need for an operator for conversions since
   they can have different parameters, e.g.: - converting to string can
   have formatting specified
   - converting string to numeric types with optional base parameter
   - converting integer to floating point specifies
   round/floor/ceiling/etc..
 
 2. const_cast: should be a _separate_ operator in order to prevent
 removing const by mistake.
 
   const Base obj1 = new Derived();
   auto obj2 = cast(Derived)(obj1); // oops: meant to only down cast
 
 3. dynamic_cast: the language should provide a down cast operator for OO.
 4. reinterpret_cast: unsafe and should be restricted as much as possible
 (Not available in  safe code) maybe not provide it at all since it's
 implementable via union. A restricted library solution for when you
 really want to play with bits and bytes?
There are some ideas here, like the separation from const_cast, dynamic cast, and other casts. In past I too have asked for something similar, but I think Walter was not interested. I guess the idea of having a single cast is to keep the language simple. But those _are_ different kinds of casts, they have different semantics. Lumping different semantics into the same syntax is not a good way to simplify a language, in my opinion. It just creates obscurity and maybe some other troubles too.
And how many programmers do you know who actually, really know the differences between the 4 C++ cast types. _I_'m not sure that _I_ know, and I've studied it. And honestly, in most cases - if not in _all_ cases - as far as I can tell, the compiler should be able to determine the correct cast type. So, forcing that on the programmer is just stupid. If you actually _need_ separate cast types, then we should have them, but if we can avoid that, we definitely should. I think that the fact that C++ has so many types of casts is horrible. I try and use them correctly, but I don't think that there are very many programmers (percentage- wise at least) who do. I know plenty of programmers who just use C-style casts everywhere. - Jonathan M Davis
My suggestion is much simpler than c++. the _language_ needs only to provide two operators: down cast operator and const cast operator. interpret cast is a corner case that can also be implemented as a library utility. conversions can and should be done with regular D code (functions). which is not far from current D idioms (to!).
Dec 02 2010
next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Thursday, December 02, 2010 02:23:23 foobar wrote:
 My suggestion is much simpler than c++.
 the _language_ needs only to provide two operators:
 down cast operator and const cast operator.
 interpret cast is a corner case that can also be implemented as a library
 utility. conversions can and should be done with regular D code
 (functions). which is not far from current D idioms (to!).
Personally, I think that const_cast is _the_ most pointless cast operator of the lot. Simply cast, and if const is part of the new type and wasn't part of the old one, then it's adding const-ness. If const isn't part of the new type and it was part of the old one, then it's removing constness. Adding the extra complexity just to make sure that you didn't accidentally add or remove const seems to be totally overkill to me. And since casting away const in D is technically undefined behavior anyway, does it even make sense to add a specific operator for it? - Jonathan M Davis
Dec 02 2010
prev sibling parent Brad Roberts <braddr puremagic.com> writes:
On 12/2/2010 10:35 AM, Jonathan M Davis wrote:
 On Thursday, December 02, 2010 02:23:23 foobar wrote:
 My suggestion is much simpler than c++.
 the _language_ needs only to provide two operators:
 down cast operator and const cast operator.
 interpret cast is a corner case that can also be implemented as a library
 utility. conversions can and should be done with regular D code
 (functions). which is not far from current D idioms (to!).
Personally, I think that const_cast is _the_ most pointless cast operator of the lot. Simply cast, and if const is part of the new type and wasn't part of the old one, then it's adding const-ness. If const isn't part of the new type and it was part of the old one, then it's removing constness. Adding the extra complexity just to make sure that you didn't accidentally add or remove const seems to be totally overkill to me. And since casting away const in D is technically undefined behavior anyway, does it even make sense to add a specific operator for it? - Jonathan M Davis
The problem is that it's unsafe across type changes. Consider: const T x; void foo(T); foo(cast(T)x); // drop const, it happens to be safe for some reason What happens when the type of x changes? Finding all casts of x across all the code that might reference x is problematic. When all you want to do is manipulate the constness, being forced to do so via repeating the type means your code is more fragile than it needs to be. Later, Brad
Dec 02 2010
prev sibling parent reply Jesse Phillips <jessekphillips+D gmail.com> writes:
foobar Wrote:


   - converting to string can have formatting specified
   - converting string to numeric types with optional base parameter
   - converting integer to floating point specifies round/floor/ceiling/etc..
This was kind of my point, to! already specifies how to generically (an important part) to other types. And opCast overrides everything (which I don't wish to claim is incorrect, just asking).
I don't follow as to how this is important to have a generic interface. Can I specify parameters for conversion with to! as in my examples? I'd appreciate an example
Oops, sorry, though I don't see why it can't be specified in to!.
 2. const_cast: should be a _separate_ operator in order to prevent removing
const by mistake.
   const Base obj1 = new Derived();
   auto obj2 = cast(Derived)(obj1); // oops: meant to only down cast
I think to! should be changed such that this would be a ConvException.
This should not even compile. a run-time exception is better than current D but is still very weak.
See my bug report, http://d.puremagic.com/issues/show_bug.cgi?id=5307 After making it a runtime error, I made it just not compile. (Error message sucks though.)
 3. dynamic_cast: the language should provide a down cast operator for OO. 
Then the example above is invalid, though still valid when casting const double to int...
a down cast should _only_ perform dynamic down casts in the OO sense. so: Base foo = new Derived; Derived bar = downCast(foo); // compiles. performed at run-time [const] double -> int is not a down cast, it is a conversion.
I was referring to the need for const_cast
 Maybe std.math.floor should return an int, since they are implicitly converted
to real...
 
 and also:
 int x = ??; // some number
 double y = x; //compile-time error
 double z = double(x); // explicit
Other than overflow, which isn't fixed if made explicit, I don't see an issue with it being implicit.
besides the overflow issue you have mentioned, I also don't want special cases. No implicit conversions should be applied equally everywhere.
Then be explicit in all of _your_ code. That won't stop others from using implicit conversion, but you can just assume they are of the same type and be fine.
Dec 02 2010
next sibling parent Jesse Phillips <jessekphillips+D gmail.com> writes:
Jesse Phillips Wrote:

 besides the overflow issue you have mentioned, I also don't want special
cases. No implicit conversions should be applied equally everywhere. 
Then be explicit in all of _your_ code. That won't stop others from using implicit conversion, but you can just assume they are of the same type and be fine.
Now that I'm clearer on my own stance I should state my view on this. D has some unique rules for implicit conversion. You'll notice many of your complaints were _not_ valid in D. D allows for more implicit conversions then reason I think experience from other languages can not dictate that implicit conversion is bad/unsafe in D. The desire to specify specific explicit conversions is understandable, as it prevents conversions that weren't expected. Cast should be avoided, D's implicit conversion rules help _prevent_ miss use of this drastically unsafe tool as you can just make assignments and issues are compiler errors which bring to your attention the problem. This is one of the reasons I want to restrict what to! allows for conversions. So we have: Safe conversions: implicit Type conversion: to!() Unsafe/forced/qualifier conversions: cast Even qualifier conversions have some library support to avoid using cast: std.exception.assumeUnique!() (maybe that should be in std.conv? Made sense in std.contract but not really exception).
Dec 02 2010
prev sibling parent reply foobar <foo bar.com> writes:
Jesse Phillips Wrote:

 foobar Wrote:
 
 
   - converting to string can have formatting specified
   - converting string to numeric types with optional base parameter
   - converting integer to floating point specifies round/floor/ceiling/etc..
This was kind of my point, to! already specifies how to generically (an important part) to other types. And opCast overrides everything (which I don't wish to claim is incorrect, just asking).
I don't follow as to how this is important to have a generic interface. Can I specify parameters for conversion with to! as in my examples? I'd appreciate an example
Oops, sorry, though I don't see why it can't be specified in to!.
how would the to! usage look like with these additions? I suspect at that stage the benefits of to! genericity will be lost. to!(int)("1010101", 2); / base 2 ? to!(int)(3.14, ..); // how do you specify a floor strategy here, enum? this quickly gets messy, ugly and redundant. int a = floor(3.14); // KISS - much better
 2. const_cast: should be a _separate_ operator in order to prevent removing
const by mistake.
   const Base obj1 = new Derived();
   auto obj2 = cast(Derived)(obj1); // oops: meant to only down cast
I think to! should be changed such that this would be a ConvException.
This should not even compile. a run-time exception is better than current D but is still very weak.
See my bug report, http://d.puremagic.com/issues/show_bug.cgi?id=5307 After making it a runtime error, I made it just not compile. (Error message sucks though.)
good :)
 
 3. dynamic_cast: the language should provide a down cast operator for OO. 
Then the example above is invalid, though still valid when casting const double to int...
a down cast should _only_ perform dynamic down casts in the OO sense. so: Base foo = new Derived; Derived bar = downCast(foo); // compiles. performed at run-time [const] double -> int is not a down cast, it is a conversion.
I was referring to the need for const_cast
Sorry, I lost you here. what are you talking about here?
  
 Maybe std.math.floor should return an int, since they are implicitly converted
to real...
 
 and also:
 int x = ??; // some number
 double y = x; //compile-time error
 double z = double(x); // explicit
Other than overflow, which isn't fixed if made explicit, I don't see an issue with it being implicit.
besides the overflow issue you have mentioned, I also don't want special cases. No implicit conversions should be applied equally everywhere.
Then be explicit in all of _your_ code. That won't stop others from using implicit conversion, but you can just assume they are of the same type and be fine.
since the entire point is to prevent bugs by having compiler checks, I don't see how the above conclusion helps me at all. I want the compiler to prevent me from getting the kinds of bugs previously shown.
Dec 02 2010
parent reply Jesse Phillips <jessekphillips+D gmail.com> writes:
foobar Wrote:

 how would the to! usage look like with these additions? I suspect at that
stage the benefits of to! genericity will be lost.
 
 to!(int)("1010101", 2); / base 2 ? 
to!(int)(base!(2)("1010101")); base!() returns a struct which can be created with strings/int/... and to! can change it to the different representations?
 to!(int)(3.14, ..); // how do you specify a floor strategy here, enum? 
 this quickly gets messy, ugly and redundant. 
to!(int)(floor(3.14));
 int a = floor(3.14); // KISS - much better
floor is not a type conversion! I could agree the type of floor should be an int, but it is not converting a real to an int. double a = cast(double) floor(3.14); Do you really want that?
 a down cast should _only_ perform dynamic down casts in the OO sense. so:
 Base foo = new Derived; 
 Derived bar = downCast(foo); // compiles. performed at run-time
 
 [const] double -> int is not a down cast, it is a conversion.
I was referring to the need for const_cast
Sorry, I lost you here. what are you talking about here?
Sorry, I am trying to shortcut on explanation. What I mean is, if you have a dynamic_cast operator you would not have the issue of casting away const when you meant to downcast. "const Base obj1 = new Derived(); auto obj2 = cast(Derived)(obj1); // oops: meant to only down cast" If you had dynamic cast, you won't use cast to change downcast. The implied question was, why do you need a const_cast then? But you are asking for only having const_cast and dynamic_cast, so it makes sense, oh and static_cast. To me, I don't see a need to distinguish between dynamic and static cast in D. These are not concepts you are going to confuse and in general don't even compile together: auto a = cast(double) b; If b is a class it won't compile (with the exception of opCast, which implies it is safe to cast to double anyway). But I think the implicit casting of D is important. It provides a way to do "safe" conversions allowing code to look cleaner. You shouldn't need to question if an assignment is performing a conversion that is causing an issue in your code, when using D. The type you are assigning too might be an issue, but this not a bug introduced by the conversion. For example you can assign a real to a float implicitly. This could result in a loss of precision. The bug did not come from an implicit conversion to float, it came because the precision of real was needed but float was used. My initial thought was it was a bad idea, but I came up with that reasoning to make myself feel good about D :D
 Then be explicit in all of _your_ code. That won't stop others from using
implicit conversion, but you can just assume they are of the same type and be
fine.
 
since the entire point is to prevent bugs by having compiler checks, I don't see how the above conclusion helps me at all. I want the compiler to prevent me from getting the kinds of bugs previously shown.
The compiler _is_ checking the code. There is nothing inherently wrong with converting an int to a double, except larger numbers can be held in an int (but making the conversion explicit will not resolve this, and won't result in people considering it). The example of: double d = 5 / 2; Is one place I agree could use an explicit conversion to double. I think this is the only major logic bug solved by forcing type conversions. But I also believe when the compiler complains the solution to fix it will be: double d = cast(double) 5 / 2; Even though it needs to wrap the five/two. That is because a complaining compiler does not make a thinking programmer, and the more it complains the less thinking. If you want say that the programmer sucks if they don't think about the changes they are making then, they also suck when they don't think 5 / 2 returns an int.
Dec 02 2010
next sibling parent Don <nospam nospam.com> writes:
Jesse Phillips wrote:
 foobar Wrote:
 
 how would the to! usage look like with these additions? I suspect at that
stage the benefits of to! genericity will be lost.

 to!(int)("1010101", 2); / base 2 ? 
to!(int)(base!(2)("1010101")); base!() returns a struct which can be created with strings/int/... and to! can change it to the different representations?
 to!(int)(3.14, ..); // how do you specify a floor strategy here, enum? 
 this quickly gets messy, ugly and redundant. 
to!(int)(floor(3.14));
 int a = floor(3.14); // KISS - much better
floor is not a type conversion! I could agree the type of floor should be an int, but it is not converting a real to an int. double a = cast(double) floor(3.14); Do you really want that?
 a down cast should _only_ perform dynamic down casts in the OO sense. so:
 Base foo = new Derived; 
 Derived bar = downCast(foo); // compiles. performed at run-time

 [const] double -> int is not a down cast, it is a conversion.
I was referring to the need for const_cast
Sorry, I lost you here. what are you talking about here?
Sorry, I am trying to shortcut on explanation. What I mean is, if you have a dynamic_cast operator you would not have the issue of casting away const when you meant to downcast. "const Base obj1 = new Derived(); auto obj2 = cast(Derived)(obj1); // oops: meant to only down cast" If you had dynamic cast, you won't use cast to change downcast. The implied question was, why do you need a const_cast then? But you are asking for only having const_cast and dynamic_cast, so it makes sense, oh and static_cast. To me, I don't see a need to distinguish between dynamic and static cast in D. These are not concepts you are going to confuse and in general don't even compile together: auto a = cast(double) b; If b is a class it won't compile (with the exception of opCast, which implies it is safe to cast to double anyway). But I think the implicit casting of D is important. It provides a way to do "safe" conversions allowing code to look cleaner. You shouldn't need to question if an assignment is performing a conversion that is causing an issue in your code, when using D. The type you are assigning too might be an issue, but this not a bug introduced by the conversion. For example you can assign a real to a float implicitly. This could result in a loss of precision. The bug did not come from an implicit conversion to float, it came because the precision of real was needed but float was used. My initial thought was it was a bad idea, but I came up with that reasoning to make myself feel good about D :D
 Then be explicit in all of _your_ code. That won't stop others from using
implicit conversion, but you can just assume they are of the same type and be
fine.
since the entire point is to prevent bugs by having compiler checks, I don't see how the above conclusion helps me at all. I want the compiler to prevent me from getting the kinds of bugs previously shown.
The compiler _is_ checking the code. There is nothing inherently wrong with converting an int to a double, except larger numbers can be held in an int (but making the conversion explicit will not resolve this, and won't result in people considering it). The example of: double d = 5 / 2; Is one place I agree could use an explicit conversion to double. I think this is the only major logic bug solved by forcing type conversions. But I also believe when the compiler complains the solution to fix it will be: double d = cast(double) 5 / 2; Even though it needs to wrap the five/two. That is because a complaining compiler does not make a thinking programmer, and the more it complains the less thinking. If you want say that the programmer sucks if they don't think about the changes they are making then, they also suck when they don't think 5 / 2 returns an int.
I did a language proposal on this some time back, which was accepted, but which I haven't got around to making a patch for yet. The idea is that integer division (5 / 2) involves a discarding of the fractional part. The fact can be tracked as part of the integer range propagation (it is an int with a discarded fraction). An int with a discarded fraction cannot be implicitly converted to double. It can give a very informative error message too. That will catch this particular bug (which is a moderately common one).
Dec 02 2010
prev sibling parent reply foobar <foo bar.com> writes:
Jesse Phillips Wrote:

 foobar Wrote:
 
 how would the to! usage look like with these additions? I suspect at that
stage the benefits of to! genericity will be lost.
 
 to!(int)("1010101", 2); / base 2 ? 
to!(int)(base!(2)("1010101"));
semantically this is what i wanted but i still feel that a plain function would be easier on the eyes. for instance: int r = parseInt("101010", 2); // second parameter is optional and defaults to decimal base
 
 base!() returns a struct which can be created with strings/int/... and to! can
change it to the different representations?
 
 to!(int)(3.14, ..); // how do you specify a floor strategy here, enum? 
 this quickly gets messy, ugly and redundant. 
to!(int)(floor(3.14));
 int a = floor(3.14); // KISS - much better
floor is not a type conversion! I could agree the type of floor should be an int, but it is not converting a real to an int. double a = cast(double) floor(3.14);
you're right, point taken. to fix my previous example: int a = integer(floor(3.14)); // still simpler than the unified template syntax
 
 Do you really want that?
 
 a down cast should _only_ perform dynamic down casts in the OO sense. so:
 Base foo = new Derived; 
 Derived bar = downCast(foo); // compiles. performed at run-time
 
 [const] double -> int is not a down cast, it is a conversion.
I was referring to the need for const_cast
Sorry, I lost you here. what are you talking about here?
Sorry, I am trying to shortcut on explanation. What I mean is, if you have a dynamic_cast operator you would not have the issue of casting away const when you meant to downcast. "const Base obj1 = new Derived(); auto obj2 = cast(Derived)(obj1); // oops: meant to only down cast" If you had dynamic cast, you won't use cast to change downcast. The implied question was, why do you need a const_cast then? But you are asking for only having const_cast and dynamic_cast, so it makes sense, oh and static_cast.
Thanks for explaining. As you said, I want only const_cast and down_cast operators to be provided by the language.
 
 To me, I don't see a need to distinguish between dynamic and static cast in D.
These are not concepts you are going to confuse and in general don't even
compile together:
 
 auto a = cast(double) b;
 
 If b is a class it won't compile (with the exception of opCast, which implies
it is safe to cast to double anyway).
IMO, there is a need to distinguish the two because I may want to provide a conversion of my class type to a different class. contrived example: class Person {}; class Kid : Person {}; class Adult : Person {}; I want to be able to convert a Kid instance into an Adult instance when said kid turns 18.
 
 But I think the implicit casting of D is important. It provides a way to do
"safe" conversions allowing code to look cleaner. You shouldn't need to
question if an assignment is performing a conversion that is causing an issue
in your code, when using D.
 
 The type you are assigning too might be an issue, but this not a bug
introduced by the conversion. For example you can assign a real to a float
implicitly. This could result in a loss of precision. The bug did not come from
an implicit conversion to float, it came because the precision of real was
needed but float was used.
 
 My initial thought was it was a bad idea, but I came up with that reasoning to
make myself feel good about D :D
 
My opinion - this is a misfeature inherited from C. Assignment of incorrect type (e.g. a double value to an int) should be a compile-time error.
 Then be explicit in all of _your_ code. That won't stop others from using
implicit conversion, but you can just assume they are of the same type and be
fine.
 
since the entire point is to prevent bugs by having compiler checks, I don't see how the above conclusion helps me at all. I want the compiler to prevent me from getting the kinds of bugs previously shown.
The compiler _is_ checking the code. There is nothing inherently wrong with converting an int to a double, except larger numbers can be held in an int (but making the conversion explicit will not resolve this, and won't result in people considering it). The example of:
because of differences in representation this is also unsafe. Not all integral values can be represented accurately in a floating point type and this has nothing to do with size of int. I myself had such a bug where I expected a value of 2.0 as a result of some calculation but got 1.99998.. instead.
 
 double d = 5 / 2;
 
 Is one place I agree could use an explicit conversion to double. I think this
is the only major logic bug solved by forcing type conversions. But I also
believe when the compiler complains the solution to fix it will be:
 
 double d = cast(double) 5 / 2;
 
 Even though it needs to wrap the five/two. That is because a complaining
compiler does not make a thinking programmer, and the more it complains the
less thinking. If you want say that the programmer sucks if they don't think
about the changes they are making then, they also suck when they don't think 5
/ 2 returns an int.
i disagree with this logic. Take a look at ML family of languages to see that more checks do not make worse programmers.
Dec 02 2010
parent reply Jesse Phillips <jessekphillips+D gmail.com> writes:
foobar Wrote:

 Jesse Phillips Wrote:
 
 to!(int)(base!(2)("1010101"));
semantically this is what i wanted but i still feel that a plain function would be easier on the eyes. for instance: int r = parseInt("101010", 2); // second parameter is optional and defaults to decimal base
But then the logic for base conversion is spread out in: parseInt, parseDouble, toString, and what ever else a base conversion might be converted to/from. Though a helper function would be used by all. And how do you handle generic code? static if(is(T == int) parseInt... else static if(is(T == double)) parseDouble...
 you're right, point taken. to fix my previous example:
 int a = integer(floor(3.14)); // still simpler than the unified template syntax
 
 
 Do you really want that?
Yes, yes you do. Well you are in luck: int a = floor(3.14); // does not compile in D I think returning integer would be correct, but doesn't bother me to much? (Anyone else reading with an opinion?... numeric coders?)
 IMO, there is a need to distinguish the two because I may want to provide a
conversion of my class type to a different class.
 contrived example:
 class Person {};
 class Kid : Person {};
 class Adult : Person {}; 
 I want to be able to convert a Kid instance into an Adult instance when said
kid turns 18. 
Currently you can provide the conversion in the manner you desire Adult grownup = kid.canSmokeNow(); And by _not_ providing an opCast function, casting from Kid to adult will fail! to compile. Now of the cast types I do not know where this one falls, but dynamic cast is not it (I would think static cast).
 The type you are assigning too might be an issue, but this not a bug
introduced by the conversion. For example you can assign a real to a float
implicitly. This could result in a loss of precision. The bug did not come from
an implicit conversion to float, it came because the precision of real was
needed but float was used.
 
 My initial thought was it was a bad idea, but I came up with that reasoning to
make myself feel good about D :D
 
My opinion - this is a misfeature inherited from C. Assignment of incorrect type (e.g. a double value to an int) should be a compile-time error.
Could we please get one thing straight, D does not implicitly convert double/float/real to int. Or int to byte. You can select from these examples: int -> uint real -> double -> float int -> double... byte -> int and other similar combinations But stop using a completely invalid conversion example.
 The compiler _is_ checking the code. There is nothing inherently wrong with
converting an int to a double, except larger numbers can be held in an int (but
making the conversion explicit will not resolve this, and won't result in
people considering it). The example of:
because of differences in representation this is also unsafe. Not all integral values can be represented accurately in a floating point type and this has nothing to do with size of int. I myself had such a bug where I expected a value of 2.0 as a result of some calculation but got 1.99998.. instead.
I have never heard this. Not all values are representable in floating point, but I do not know of any issue representing integers. http://ideone.com/e8x5L If you received a value of 1.99998 from a calculation, it is an inherit issue with using double and not a conversion error from converting an int to a double.
 double d = 5 / 2;
 
 Is one place I agree could use an explicit conversion to double. I think this
is the only major logic bug solved by forcing type conversions. But I also
believe when the compiler complains the solution to fix it will be:
 
 double d = cast(double) 5 / 2;
 
 Even though it needs to wrap the five/two. That is because a complaining
compiler does not make a thinking programmer, and the more it complains the
less thinking. If you want say that the programmer sucks if they don't think
about the changes they are making then, they also suck when they don't think 5
/ 2 returns an int.
i disagree with this logic. Take a look at ML family of languages to see that more checks do not make worse programmers.
I said nothing about making the programmer worse due to checks, but do to excessive complaining. I do not think a programmer sucks for either mistake; I do think both are very easy mistakes to make. As Don pointed out there is a plan to fix this issue (not sure if it applies to when variables are used too). [OT] Why nagging people makes them numb. I believe you understand the premise of this. If something continually asks or tells you to do something, the answer becomes involuntary. Like dialogs that say do you wish to continue? Well I might not, but by the time I realize that I've already clicked yes (actually I might realize it 5 mins later and has nothing to do with being asked the question). But what I think you want is for the required action to be a safer action. For example, write now the solution is to insert a cast() which handles many different things and could perform one you weren't expecting. With many cast/conversion methods then you insert the appropriate method and it will complain that it can't do that. And now that I am seeing your view a little better I don't see this at play because: double d = double(5/2); would induce thinking, but so would: double d = cast(double)(5/2); (want an ! in there :P) I do not wish to argue against making things explicit, but I do want to figure out if there really is an issue in the way D does implicit conversions. So far I am seeing an opinion based on experience with C, but not D and an unwillingness to see that implicit conversion isn't harmful when done right.
Dec 02 2010
parent reply foobar <foo bar.com> writes:
Jesse Phillips Wrote:

 foobar Wrote:
 
 Jesse Phillips Wrote:
 
 to!(int)(base!(2)("1010101"));
semantically this is what i wanted but i still feel that a plain function would be easier on the eyes. for instance: int r = parseInt("101010", 2); // second parameter is optional and defaults to decimal base
But then the logic for base conversion is spread out in: parseInt, parseDouble, toString, and what ever else a base conversion might be converted to/from. Though a helper function would be used by all. And how do you handle generic code? static if(is(T == int) parseInt... else static if(is(T == double)) parseDouble...
I still haven't seen one example of generic code that uses a generic conversion. I remain unconvinced about this point and would appreciate an example. this is however a minor point in the discussion (stylistic issue) so let's move on to the more important parts.
 you're right, point taken. to fix my previous example:
 int a = integer(floor(3.14)); // still simpler than the unified template syntax
 
 
 Do you really want that?
Yes, yes you do. Well you are in luck: int a = floor(3.14); // does not compile in D I think returning integer would be correct, but doesn't bother me to much? (Anyone else reading with an opinion?... numeric coders?)
 IMO, there is a need to distinguish the two because I may want to provide a
conversion of my class type to a different class.
 contrived example:
 class Person {};
 class Kid : Person {};
 class Adult : Person {}; 
 I want to be able to convert a Kid instance into an Adult instance when said
kid turns 18. 
Currently you can provide the conversion in the manner you desire Adult grownup = kid.canSmokeNow(); And by _not_ providing an opCast function, casting from Kid to adult will fail! to compile. Now of the cast types I do not know where this one falls, but dynamic cast is not it (I would think static cast).
Exactly. I want to be required to write something like the above and _not_ allowed to use an opCast for this case. As you said, this is indeed a static cast and it should be separate from the down cast provided by cast().
 The type you are assigning too might be an issue, but this not a bug
introduced by the conversion. For example you can assign a real to a float
implicitly. This could result in a loss of precision. The bug did not come from
an implicit conversion to float, it came because the precision of real was
needed but float was used.
 
 My initial thought was it was a bad idea, but I came up with that reasoning to
make myself feel good about D :D
 
My opinion - this is a misfeature inherited from C. Assignment of incorrect type (e.g. a double value to an int) should be a compile-time error.
Could we please get one thing straight, D does not implicitly convert double/float/real to int. Or int to byte. You can select from these examples: int -> uint real -> double -> float int -> double... byte -> int and other similar combinations But stop using a completely invalid conversion example.
That was just one example. Let's discuss another one from your list then: uint foo = -1; // remember that this can be a result of a long calculation should the above compile in your opinion?
 The compiler _is_ checking the code. There is nothing inherently wrong with
converting an int to a double, except larger numbers can be held in an int (but
making the conversion explicit will not resolve this, and won't result in
people considering it). The example of:
because of differences in representation this is also unsafe. Not all integral values can be represented accurately in a floating point type and this has nothing to do with size of int. I myself had such a bug where I expected a value of 2.0 as a result of some calculation but got 1.99998.. instead.
I have never heard this. Not all values are representable in floating point, but I do not know of any issue representing integers. http://ideone.com/e8x5L
http://ideone.com/ksQDV
 If you received a value of 1.99998 from a calculation, it is an inherit issue
with using double and not a conversion error from converting an int to a double.
 
 double d = 5 / 2;
 
 Is one place I agree could use an explicit conversion to double. I think this
is the only major logic bug solved by forcing type conversions. But I also
believe when the compiler complains the solution to fix it will be:
 
 double d = cast(double) 5 / 2;
 
 Even though it needs to wrap the five/two. That is because a complaining
compiler does not make a thinking programmer, and the more it complains the
less thinking. If you want say that the programmer sucks if they don't think
about the changes they are making then, they also suck when they don't think 5
/ 2 returns an int.
i disagree with this logic. Take a look at ML family of languages to see that more checks do not make worse programmers.
I said nothing about making the programmer worse due to checks, but do to excessive complaining. I do not think a programmer sucks for either mistake; I do think both are very easy mistakes to make. As Don pointed out there is a plan to fix this issue (not sure if it applies to when variables are used too). [OT] Why nagging people makes them numb. I believe you understand the premise of this. If something continually asks or tells you to do something, the answer becomes involuntary. Like dialogs that say do you wish to continue? Well I might not, but by the time I realize that I've already clicked yes (actually I might realize it 5 mins later and has nothing to do with being asked the question). But what I think you want is for the required action to be a safer action. For example, write now the solution is to insert a cast() which handles many different things and could perform one you weren't expecting. With many cast/conversion methods then you insert the appropriate method and it will complain that it can't do that.
I understand what you mean about the confirmation dialog but as you said yourself above, I'm not asking for a confirmation dialog but rather using safer actions. I think we agree on this.
 And now that I am seeing your view a little better I don't see this at play
because:
 
 double d = double(5/2);
 
 would induce thinking, but so would:
 
 double d = cast(double)(5/2);
 
 (want an ! in there :P)
BUT, in the latter case cast(double) can do additional things that I don't need like remove constancy. cast() is problematic cause it overloads several semantics actions onto the same syntactic operator. It's also easier to read / understand code when you see "cast" and you know that it always means one specific operation (e.g. down cast). You don't need to read what's inside the cast to figure out if it removes constancy, does a static type conversion or perhaps a run-time down cast.
 
 I do not wish to argue against making things explicit, but I do want to figure
out if there really is an issue in the way D does implicit conversions. So far
I am seeing an opinion based on experience with C, but not D and an
unwillingness to see that implicit conversion isn't harmful when done right.
Dec 03 2010
next sibling parent Jesse Phillips <jessekphillips+D gmail.com> writes:
foobar Wrote:

 I still haven't seen one example of generic code that uses a generic
conversion. I remain unconvinced about this point and would appreciate an
example.
 this is however a minor point in the discussion (stylistic issue) so let's
move on to the more important parts.
Good point. I don't have one from experience. I can build on your example: class Elderly : Person { Elderly to(Person p) { Adult adult = to!Adult(p); return new Elderly(adult); } } The above would throw an exception if p was a Kid. Note that your solution does not provide compile time checking because you'd still need a dynamic cast to retrieve an Adult from your person. Providing a generic abstraction so when someone does find a use is a major style of Phobos.
 That was just one example. Let's discuss another one from your list then:
 uint foo = -1; // remember that this can be a result of a long calculation
 should the above compile in your opinion? 
No, I think int to uint should not be implicit. Maybe there is a reason other than C inheritance, I'm sure bearophile has a feature request for it.
 http://ideone.com/ksQDV 
I have an account so I get 15sec of runtime: http://ideone.com/9ZvNL Ok, I'm surprised this is successful. So I made another test that fails on my local machine, but strangely enough, succeeds on ideone... http://ideone.com/gOtYc Any way this demonstration is showing an issue with overflow. I have already stated that explicit casts do not solve this and got no disagreement from you.
 I understand what you mean about the confirmation dialog but as you said
yourself above, I'm not asking for a confirmation dialog but rather using safer
actions. 
 I think we agree on this. 
Yes, we do agree. What we don't agree on is the best way to make things safer. I am saying that implicit casts are not bad when data is not being lost. And to! is a good candidate for providing safe conversions (possible data lost but qualifiers remain). And that mixing conversions that loose data with ones that transform it (class conversion) is not an issue.
Dec 03 2010
prev sibling parent Adam Ruppe <destructionator gmail.com> writes:
foobar wrote:
 I still haven't seen one example of generic code that uses a generic
conversion.
 I remain unconvinced about this point and would appreciate an example.
Like half the generic code I've written does that. A direct example for this is my web.d - it makes automatic wrappers for D functions that are callable through a cgi interface. D: int myFunction(float a, string b) { .... } User's browser goes to: myprogram/my-function?a=1.5&b=something And sees the return value. The way the code works is: (pseudocode) foreach(member; thing.allMembers) { member.arguments args; foreach(i, arg; member.arguments) { args[i] = to!(typeof(arg))(cgi.get[arg.name]); } cgi.write(to!string(member(args))); } Everything in the web interface is string based on transport, so generic conversions are done to and from all the D types used. I did something similar with the command line version and with my D2 dmdscript port.
Dec 03 2010