www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - The "opCast" overloading?

reply Arlen Albert Keshabyan <arlen.albert gmail.com> writes:
Hello Walter!
The D language is just a miracle. Sure, you already know about that :).
I know the problem of "opCast" cannot be overloaded so far. But I'm sure there
must be a way to do that somehow. That's the question of a semantic. Right?
May we hope it's going to happen soon? I'm asking you the question because
you're the Father of D.
Nov 21 2006
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Arlen Albert Keshabyan" <arlen.albert gmail.com> wrote in message 
news:ejvjgp$2hgk$1 digitaldaemon.com...
 Hello Walter!
 The D language is just a miracle. Sure, you already know about that :).
 I know the problem of "opCast" cannot be overloaded so far. But I'm sure 
 there
 must be a way to do that somehow. That's the question of a semantic. 
 Right?
 May we hope it's going to happen soon? I'm asking you the question because
 you're the Father of D.

Many people have suggested it, by i.e. void opCast(out int x) { // put the cast result in x rather than returning it } Which would allow for overloading by type, but also has the disadvantage that you wouldn't be able to use cast(int)Object as a temporary. :S Frankly, I don't see the need for it. D isn't trying to be C++, where you can define types which are just as "integrated" into the language as the standard types. You can't have opAssign, or opDeref or opAddressOf, and so the "boundary" that D classes would have to cross to get to the kind of integration of C++ classes is so large, that allowing overloading of opCast just doesn't seem like it'd do much. (And to tell you the truth, I wouldn't really want to see it either!) The convention in D is instead to have "toType" methods which take the place of a cast. I.e. toString, toInt, toOtherClassType etc.
Nov 21 2006
next sibling parent reply Burton Radons <burton-radons smocky.com> writes:
Jarrett Billingsley wrote:
 "Arlen Albert Keshabyan" <arlen.albert gmail.com> wrote in message 
 news:ejvjgp$2hgk$1 digitaldaemon.com...
 Hello Walter!
 The D language is just a miracle. Sure, you already know about that :).
 I know the problem of "opCast" cannot be overloaded so far. But I'm sure 
 there
 must be a way to do that somehow. That's the question of a semantic. 
 Right?
 May we hope it's going to happen soon? I'm asking you the question because
 you're the Father of D.

Many people have suggested it, by i.e. void opCast(out int x) { // put the cast result in x rather than returning it } Which would allow for overloading by type, but also has the disadvantage that you wouldn't be able to use cast(int)Object as a temporary. :S

That's no issue, the compiler already juggles temporary stack variables. For example, you can call a method on a struct value result of another function call. Maybe you're thinking of something else. I think the current design was chosen either because it was easy (in the then-present compiler) or because it was limited, which are both terrible reasons to do it. C++ is absolutely rife with features where they tried to implement something but they couldn't do it uniformly so the standard's chock full of caveats that will cause innocent-looking code (to people who don't fully understand the standard - which is to say, everyone except for a few hundred people on the planet) to suddenly fail compilation, usually for reasons that make for flatly incomprehensible error codes. This is analogous, except instead of not having any way to correctly implement the feature, the correct, simple, and obvious way is being spurned! (Note to Walter: I do not want the compiler to solve cast chains at this time, but to select the one direct cast which works. Cast overloads in every language I know of incorrectly consider all casts to be equivalent, so the shortest-path solution is frequently highly ambiguous and could severely strip a value if you're not very careful, which you can only be (at best) if you control all the source, and so is unsuitable for most any collaborative environment. There should be discrimination between up casts (casts which add or do not change the amount of information in the value) and down casts (casts which strip information); one path is better than another if it has fewer down casts, fewer up casts on a match of down casts, and failing that is ambiguous. An extension would be that an implicit cast is the shortest path containing only up casts, which allows us to exactly reproduce all casting behaviour in the current language and neatly fit in new types without worrying about spastic behaviour or the compiler rejecting code. As far as I can tell this is a flawless solution to the casting problem as while I can theoretically think that there might be two paths with the same number of up casts as down casts while having both, I can't think of any actual types where that would occur unless if they're attempting some silly things like unevenly-applied shortcut casts. Ambiguous matches should be VERY rare.)
 Frankly, I don't see the need for it.  D isn't trying to be C++, where you 
 can define types which are just as "integrated" into the language as the 
 standard types.  You can't have opAssign, or opDeref or opAddressOf, and so 
 the "boundary" that D classes would have to cross to get to the kind of 
 integration of C++ classes is so large, that allowing overloading of opCast 
 just doesn't seem like it'd do much.  (And to tell you the truth, I wouldn't 
 really want to see it either!)
 
 The convention in D is instead to have "toType" methods which take the place 
 of a cast.  I.e. toString, toInt, toOtherClassType etc. 

I don't think that's really well-justified. If instead of "foo.toString" we could use "cast (char []) foo", that allows us much more flexibility in templating, helps to decrease the special nature of certain types, allows the programmer more options in how his type is used, and eliminates one more place where we have to adorn a function name with irrelevant type information. It's all win. There is absolutely no reason to follow a "way" if it's the wrong way, or if we were only doing that because there was an unintuitive language limitation. As it is I've never used opCast and in fact it seems very specifically useless. Its function is exclusively as syntax sugar; it grants no advantages to simply having a method with the same semantics. Worse, it confuses the casting issue in objects, because while a cast between class types normally results in a reference which is still referring to the original object, the opCast function implies (but neither enforces nor suggests) that the result will be a completely different object. However, the method for casting the object is the same. There's an analogous situation in value types where explicitly casting float to int (or int to float, which might happen in many templating scenarios) is considered equivalent to explicitly casting int to void*, so that you cannot automatically discriminate between controllably harmful or completely harmless casts casts like the formers and uncontrollably /dangerous/ casts like the latter. This is one of the few cases where C++ got something absolutely god damned right, and even worse, where every other language I know of got it completely wrong. If two casts do something different or have different levels of operation, they MUST have different names or different methods of access. So there are different casts for when you want to strip the constness from a value, for when you want to change the lexical type of a value (things like int to string) which might be irreversible as compared to a reversible cast, and so on. Other languages either overload cast with multiple operations (which is an incorrect use of overloading) or neuter casting so that you can't do anything interesting. D miraculously manages to do both simultaneously. Uh, I've kind of thought a lot about casting, sorry.
Nov 22 2006
parent Reiner Pope <reiner.pope ggmmaaiill.com> writes:
I don't exactly have much to say; I'm lending my support to an insightful post.

== Quote from Burton Radons (burton-radons smocky.com)'s article
 (Note to Walter: I do not want the compiler to solve cast chains at this
 time, but to select the one direct cast which works. Cast overloads in
 every language I know of incorrectly consider all casts to be
 equivalent, so the shortest-path solution is frequently highly ambiguous
 and could severely strip a value if you're not very careful, which you
 can only be (at best) if you control all the source, and so is
 unsuitable for most any collaborative environment. There should be
 discrimination between up casts (casts which add or do not change the
 amount of information in the value) and down casts (casts which strip
 information); one path is better than another if it has fewer down
 casts, fewer up casts on a match of down casts, and failing that is
 ambiguous. An extension would be that an implicit cast is the shortest
 path containing only up casts, which allows us to exactly reproduce all
 casting behaviour in the current language and neatly fit in new types
 without worrying about spastic behaviour or the compiler rejecting code.
 As far as I can tell this is a flawless solution to the casting problem
 as while I can theoretically think that there might be two paths with
 the same number of up casts as down casts while having both, I can't
 think of any actual types where that would occur unless if they're
 attempting some silly things like unevenly-applied shortcut casts.
 Ambiguous matches should be VERY rare.)

idea. As does being able to support implicit casting in user-defined types (Walter himself has commented on the annoyance of having to write explicit up casts in other languages like Pascal). I don't understand, though: you don't seem to want chained casts, yet you describe how they should work. Why? Also, what is the motivation for chained casts? Wouldn't they cause more surprising behaviour than help?
 Frankly, I don't see the need for it.  D isn't trying to be C++, where you
 can define types which are just as "integrated" into the language as the
 standard types.  You can't have opAssign, or opDeref or opAddressOf, and so
 the "boundary" that D classes would have to cross to get to the kind of
 integration of C++ classes is so large, that allowing overloading of opCast
 just doesn't seem like it'd do much.  (And to tell you the truth, I wouldn't
 really want to see it either!)

 The convention in D is instead to have "toType" methods which take the place
 of a cast.  I.e. toString, toInt, toOtherClassType etc.

we could use "cast (char []) foo", that allows us much more flexibility in templating, helps to decrease the special nature of certain types, allows the programmer more options in how his type is used, and eliminates one more place where we have to adorn a function name with irrelevant type information. It's all win. There is absolutely no reason to follow a "way" if it's the wrong way, or if we were only doing that because there was an unintuitive language limitation.

[Other good stuff about casting snipped] Cheers, Reiner
Nov 22 2006
prev sibling parent reply "Unknown W. Brackets" <unknown simplemachines.org> writes:
I think the problem with that is simply:

class C
{
	void opCast(out long l);
	void opCast(out int i);
}

C c = new C();
short s = cast(short) c;

What happens?  I would personally say, an error.  But someone will 
always be mad about that.

-[Unknown]


 "Arlen Albert Keshabyan" <arlen.albert gmail.com> wrote in message 
 news:ejvjgp$2hgk$1 digitaldaemon.com...
 Hello Walter!
 The D language is just a miracle. Sure, you already know about that :).
 I know the problem of "opCast" cannot be overloaded so far. But I'm sure 
 there
 must be a way to do that somehow. That's the question of a semantic. 
 Right?
 May we hope it's going to happen soon? I'm asking you the question because
 you're the Father of D.

Many people have suggested it, by i.e. void opCast(out int x) { // put the cast result in x rather than returning it } Which would allow for overloading by type, but also has the disadvantage that you wouldn't be able to use cast(int)Object as a temporary. :S Frankly, I don't see the need for it. D isn't trying to be C++, where you can define types which are just as "integrated" into the language as the standard types. You can't have opAssign, or opDeref or opAddressOf, and so the "boundary" that D classes would have to cross to get to the kind of integration of C++ classes is so large, that allowing overloading of opCast just doesn't seem like it'd do much. (And to tell you the truth, I wouldn't really want to see it either!) The convention in D is instead to have "toType" methods which take the place of a cast. I.e. toString, toInt, toOtherClassType etc.

Nov 22 2006
next sibling parent Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Unknown W. Brackets wrote:
 I think the problem with that is simply:
 
 class C
 {
     void opCast(out long l);
     void opCast(out int i);
 }
 
 C c = new C();
 short s = cast(short) c;
 
 What happens?  I would personally say, an error.  But someone will 
 always be mad about that.
 
 -[Unknown]

Well, I'd say an error makes some sense. Except, this is an explicit cast so one assumes the user has some clue -- and if not, they soon will. So... I would say the int version is used, and that possibly a warning about possible data loss is generated when -w is given. Its the same concerns as: # int foo = 0xf0f0f0f0; # short bar = cast(short)foo; Casting always has its dangers. -- Chris Nicholson-Sauls
Nov 22 2006
prev sibling parent Georg Wrede <georg.wrede nospam.org> writes:
Unknown W. Brackets wrote:
 "Arlen Albert Keshabyan" <arlen.albert gmail.com> wrote in message 
 news:ejvjgp$2hgk$1 digitaldaemon.com...

 Hello Walter!
 The D language is just a miracle. Sure, you already know about that :).
 I know the problem of "opCast" cannot be overloaded so far. But I'm 
 sure there
 must be a way to do that somehow. That's the question of a semantic. 
 Right?
 May we hope it's going to happen soon? I'm asking you the question 
 because
 you're the Father of D.

Many people have suggested it, by i.e. void opCast(out int x) { // put the cast result in x rather than returning it } Which would allow for overloading by type, but also has the disadvantage that you wouldn't be able to use cast(int)Object as a temporary. :S Frankly, I don't see the need for it. D isn't trying to be C++, where you can define types which are just as "integrated" into the language as the standard types. You can't have opAssign, or opDeref or opAddressOf, and so the "boundary" that D classes would have to cross to get to the kind of integration of C++ classes is so large, that allowing overloading of opCast just doesn't seem like it'd do much. (And to tell you the truth, I wouldn't really want to see it either!) The convention in D is instead to have "toType" methods which take the place of a cast. I.e. toString, toInt, toOtherClassType etc.

I think the problem with that is simply: class C { void opCast(out long l); void opCast(out int i); } C c = new C(); short s = cast(short) c; What happens? I would personally say, an error. But someone will always be mad about that.

Thinking more broadly about this, there are two kinds of cast situations, implicit casts (those that are not written in the source code) and explicit casts. If we look at them separately for a while, it might give some insight. Explicit casts of classes, like the class C example above, raise some questions. Usually, when a cast is required, say from int to real, the result is outside of the realm of the castee. In other words, precisely because the int here has no notion of exponents or fractions we need to do the cast. Following this thought, one might ask whether it is reasonable to demand of this class C to be able to generate the results of its various casts. This would also mean that either the original programmer should somehow be aware of all the possible situations that might later arise, or else there would be a massive subclassing going on so we can slap in a new opCast method each time one is needed. In trivial cases, like when returning an int, wouldn't it be more practical to instead have an int getValue() method? A cast here seems overkill. And with the non-trivial cases, one starts to wonder what the whole point of the casting is in the first place. Explicit casting is an ugly hack to begin with. Its original purpose was to cheat and lie, to pretend a pointer is an int, and such. This was excusable with the performance issues of the day. But today, casts are used liberally, automatically (implicitly), and at times for purposes that do not stand up to rational scrutiny. And often just because of plain sloppiness in workmanship or cognition. Where a non-trivial class needs to be "converted" to another, it might be more logical to have the "receiving" class have the method of conversion. This would allow for an unlimited number of receiving classes invented as and when needed, without the original class needing to know about them. (And a more universal solution would of course be to have a factory class that receives as input the castee in a serialized form.) But, all this discussion ignores private variables and access rights. However you look at it, this whole issue seems error prone, ill conceived, and simply a bad idea. Classes (or actually, instances of classes) should never be explicitly cast to other classes. Period. Implicit casts of classes, are simply needed for polymorphism, so they should be allowed. But that, too, should be restricted to assigning to variables of type <ancestor to the class>, or to type <interface of the class or its ancestor>. Nothing else. class Ancestor {} class Child: Ancestor {} Ancestor a = new Child(); Child c = a; Ancestor a2 = c; No explicit casts should be needed here. (Of course this needs a runtime check at "c = a;" to see that the current instance that a refers to, is assignable to c. But hey, we have bounds checking too in D!) I think casting oranges to footballs is bad practice. Whenever one finds himself even considering it, one should trash the blueprints and start over. And if, for "unavoidable external reasons", one does really need the contents of an orange transferred to a football, then I'd suggest an intermediate class SuckAppleAndBlowFootball. --- D is a language that makes a difference between built-in types and user types (not least by not having an overloadable assignment operator). And that is OK, IMHO. And since this is the case, the above reasoning about restricting casting of classes should not sound all that bad. It also means that we have no obligation to keep the implicit casting rules for built-ins similar to those of classes.
Nov 23 2006