www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - The purpose of opCast?

reply Derek <derek psych.ward> writes:
I know this is a "squeaky wheel", but has anyone found a good reason to use
the current opCast overloading? To me it still seems like a half-baked idea
that does nobody any good yet. I suspect its just that I don't understand
what it was designed to do (read: what is the problem it is trying to
solve).

-- 
Derek
Melbourne, Australia
Feb 18 2005
next sibling parent reply John Demme <me teqdruid.com> writes:
Although I haven't found a good use for it yet, it's a matter of 
symmetry.  In D, I can overload *all* of the operators that primitives 
can use, and thusly replace a primitive with a class with very little 
effort.  Since primitives can be cast, there should be an operator 
overload for it.

Or at least that's my opinion.

John

Derek wrote:
 I know this is a "squeaky wheel", but has anyone found a good reason to use
 the current opCast overloading? To me it still seems like a half-baked idea
 that does nobody any good yet. I suspect its just that I don't understand
 what it was designed to do (read: what is the problem it is trying to
 solve).
 
Feb 18 2005
parent reply Norbert Nemec <Norbert Nemec-online.de> writes:
John Demme schrieb:
 Although I haven't found a good use for it yet, it's a matter of 
 symmetry.  In D, I can overload *all* of the operators that primitives 
 can use, and thusly replace a primitive with a class with very little 
 effort.  Since primitives can be cast, there should be an operator 
 overload for it.
But at best, it is a half-hearted attempt to cover up the asymmetry without really solving it. Primitives can be cast to various types. opCast can be overloaded only for one target type. Primitives can be casted implicitely. opCast only works for explicit casts. The easiest solution for the first restriction could be learned from C++: change the specs in such a way that opCast takes one dummy argument of the same type as the return type. ... cast(sometype)(someobject) ... would then be equivalent to sometype dummyvar; ... someobject.opCast(dummyvar) ... For the second restriction the biggest hurdle might be to convince Walter that implicit casting is essential for the extensibility of the language. So far, he seems to be strictly against implicit casting (except for the builtin types, of course.)
Feb 19 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Sat, 19 Feb 2005 11:53:02 +0100, Norbert Nemec  
<Norbert Nemec-online.de> wrote:
 John Demme schrieb:
 Although I haven't found a good use for it yet, it's a matter of  
 symmetry.  In D, I can overload *all* of the operators that primitives  
 can use, and thusly replace a primitive with a class with very little  
 effort.  Since primitives can be cast, there should be an operator  
 overload for it.
But at best, it is a half-hearted attempt to cover up the asymmetry without really solving it. Primitives can be cast to various types. opCast can be overloaded only for one target type. Primitives can be casted implicitely. opCast only works for explicit casts.
Some can, to some types, this seems to have been decided on a case by case basis. I vaguely remember that some of these cases bothered me, but I can't remember which ones exactly.. maybe someone else can?
 The easiest solution for the first restriction could be learned from  
 C++: change the specs in such a way that opCast takes one dummy argument  
 of the same type as the return type.
 	... cast(sometype)(someobject) ...
 would then be equivalent to
 	sometype dummyvar;
 	... someobject.opCast(dummyvar) ...
IMO this is a 'hack', surely we can do better, that is, if we want to do this at all. I understand that overloading by return type has 'issues' of complexity, however, if the cast was always explicit... I realise people don't like 'special-case' behaviour i.e. making opCast the only thing that overloads by return type... Does it make sense, is it possible, to allow overloads by return type for everything, giving an error where it's indeterminate and requiring and explicit cast.
 For the second restriction the biggest hurdle might be to convince  
 Walter that implicit casting is essential for the extensibility of the  
 language. So far, he seems to be strictly against implicit casting  
 (except for the builtin types, of course.)
It's the classic trade off, implicit vs explicit, ease-of-use non-obvious behaviour vs verbose obvious behaviour. Everyone draws the line in a different place. The difference between a class and primitives is that Walter can define casts that should be implicit and ones that shouldn't for primitives. He has no way of doing the same with classes, so takes the safest option making them all explicit. All FWICS and IMO of course. Regan
Feb 20 2005
parent reply Norbert Nemec <Norbert Nemec-online.de> writes:
Regan Heath schrieb:
 Does it make sense, is it  possible, to allow overloads by return type for
everything, giving 
 an  error where it's indeterminate and requiring and explicit cast.
I think, it would make parsing a mess. Currently, the type of an expression can always be determined bottom up: determine the types of the leaves and from there construct the type of the whole expression deterministically. If you break this up, the language becomes a mess. (C/C++ had a few exceptions which were cleaned out in D)
 For the second restriction the biggest hurdle might be to convince  
 Walter that implicit casting is essential for the extensibility of 
 the  language. So far, he seems to be strictly against implicit 
 casting  (except for the builtin types, of course.)
It's the classic trade off, implicit vs explicit, ease-of-use non-obvious behaviour vs verbose obvious behaviour. Everyone draws the line in a different place.
Of course. Language design consists of drawing lines and making decisions. Neither language design nor any other art mix too well with democracy. Still, since the artwork 'D' is still in progress, the lines that have been drawn before might slowly evolve...
Feb 20 2005
parent reply Georg Wrede <georg.wrede nospam.org> writes:
Norbert Nemec wrote:
 Regan Heath schrieb:
 
 Does it make sense, is it  possible, to allow overloads by return type 
 for everything, giving an  error where it's indeterminate and 
 requiring and explicit cast.
Interesting thought. At first sight, the compiler could surely use the return type as well as argument types in overload resolution. Or could it? Are there any strong reasons for not doing this? OTOH, is it really needed? Or what reasons are there against it?
Feb 21 2005
parent reply Derek <derek psych.ward> writes:
On Mon, 21 Feb 2005 12:16:13 +0200, Georg Wrede wrote:

 Norbert Nemec wrote:
 Regan Heath schrieb:
 
 Does it make sense, is it  possible, to allow overloads by return type 
 for everything, giving an  error where it's indeterminate and 
 requiring and explicit cast.
Interesting thought. At first sight, the compiler could surely use the return type as well as argument types in overload resolution. Or could it? Are there any strong reasons for not doing this? OTOH, is it really needed? Or what reasons are there against it?
The main one seems to be, what would happen if someone coded a function call but did not assign the return to anything? How would the compiler know which signature to use if it had to use return-type as a determinate? eg. int foo(char a) { . . . return 1;} real foo(char a) { . . . return 1.0; } void bar(int x) { . . . } void bar(real x) { . . . } foo('x'); // Does this return an int or real? bar( foo('y') ); // Which 'bar' is to be called? To counter this, one could make the rule that every call to a function must either assign the result or indicate to the compiler which return type is being ignored/required. This would help make programs more robust and help readers know the coder's intentions better. For example... cast(int)foo('x'); // Call the 'int' version and ignore the result. bar( cast(real)foo('y') ); // Call the 'real' version of foo and bar. -- Derek Melbourne, Australia
Feb 21 2005
parent reply xs0 <xs0 xs0.com> writes:
 The main one seems to be, what would happen if someone coded a function
 call but did not assign the return to anything? How would the compiler know
 which signature to use if it had to use return-type as a determinate?
Well, that's not the only "main" issue, imho.. For example, consider: int mysqrt(double a) { return floor(sqrt(a)); } double eval_some_cost(...) { double cost=sqrt(..); if (something) cost*=1.3; return cost; } Now, this works fine and all. But, I (or my co-worker) can decide sometime in the future that there should also be a double mysqrt(double) returning the unrounded square root (or even something completely different, but that is bad style, I guess). The problem is that without any change in either the int version or in eval_some_cost(), the code will produce different results. This is most probably the reason why ambiguous function calls are not allowed and you need to be explicit which version you want to use.
 For example...
    cast(int)foo('x');  // Call the 'int' version and ignore the result.
    bar( cast(real)foo('y') ); // Call the 'real' version of foo and bar.
Hmm, but there's no casting, so why use the cast() operator? I'd like better something along the lines of foo:int('x'); bar(foo:real('y')); And cast(type)obj can almost be directly translated to obj.opCast:type() (almost because implicit casts should still be allowed when there's no ambiguity). Ambiguity should be defined differently for return types, though - the above problem is still not solved, even if there is an exact match (actually, precisely because of the exact match). So, I'd say that if there are return types A and B, and either can be implicitly casted to the other, the caller should be forced to specify which is the right one when using either. That's ok, though; even if you write A a=func(), it actually doesn't mean that func() should return something of type A, it just means that you'd like the compiler to implicitly cast it to A. This logic is actually the same as with double a=2/3 -- you'll still get 0, because the casting gets done on assignment, not evaluation.. xs0
Feb 21 2005
next sibling parent reply xs0 <xs0 xs0.com> writes:
     double cost=sqrt(..);
should of course be double cost=mysqrt(..); :) xs0
Feb 21 2005
parent "Regan Heath" <regan netwin.co.nz> writes:
On Mon, 21 Feb 2005 14:04:00 +0100, xs0 <xs0 xs0.com> wrote:
     double cost=sqrt(..);
should of course be double cost=mysqrt(..); :)
I really should read all threads before answering.. regardless that wont compile. Regan
Feb 21 2005
prev sibling next sibling parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Mon, 21 Feb 2005 14:03:04 +0100, xs0 <xs0 xs0.com> wrote:
 The main one seems to be, what would happen if someone coded a function
 call but did not assign the return to anything? How would the compiler  
 know
 which signature to use if it had to use return-type as a determinate?
Well, that's not the only "main" issue, imho.. For example, consider: int mysqrt(double a) { return floor(sqrt(a)); } double eval_some_cost(...) { double cost=sqrt(..);
I assume the above line is supposed to be double cost=mysqrt(..); ? if so, it doesn't compile, return type is wrong.
      if (something)
          cost*=1.3;

      return cost;
 }

 Now, this works fine and all.
Can you give an example which compile, so I can mess with it :)
 But, I (or my co-worker) can decide sometime in the future that there  
 should also be a double mysqrt(double) returning the unrounded square  
 root (or even something completely different, but that is bad style, I  
 guess). The problem is that without any change in either the int version  
 or in eval_some_cost(), the code will produce different results. This is  
 most probably the reason why ambiguous function calls are not allowed  
 and you need to be explicit which version you want to use.
Consider this, existing, similar problem: class Parent { void foo(long a) { printf("parent: foo: long\n"); } } class Child : Parent { //uncomment this line //void foo(int a) { printf("child: foo: int\n"); } } void main() { Child c = new Child(); long a = 5; c.foo(a); } This issue is caused by name resolution matching the child function with implicit conversion. In other words, implicit conversion causes the problem, and it's a seperate issue to solve, adding this new explicit function selection will not add new sources of bugs, they're already there.
 For example...
    cast(int)foo('x');  // Call the 'int' version and ignore the result.
    bar( cast(real)foo('y') ); // Call the 'real' version of foo and bar.
Hmm, but there's no casting, so why use the cast() operator?
Because it has basically the same meaning.
 I'd like better something along the lines of

 foo:int('x');
 bar(foo:real('y'));
I think we can avoid a 'new' syntax by using an existing one, I think cast(x) will be commonly understood.
 And cast(type)obj can almost be directly translated to obj.opCast:type()  
 (almost because implicit casts should still be allowed when there's no  
 ambiguity). Ambiguity should be defined differently for return types,  
 though - the above problem is still not solved, even if there is an  
 exact match (actually, precisely because of the exact match).
 So, I'd say that if there are return types A and B, and either can be  
 implicitly casted to the other, the caller should be forced to specify  
 which is the right one when using either.
Agreed, but it's not the way D works currently.
 That's ok, though; even if you write A a=func(), it actually doesn't  
 mean that func() should return something of type A, it just means that  
 you'd like the compiler to implicitly cast it to A.
Yep, this is how D works currently.
 This logic is actually the same as with double a=2/3 -- you'll still get  
 0, because the casting gets done on assignment, not evaluation..
Yep, if this was an error the bug would be spotted. This whole issue comes down to where you draw the line, where you balance convenience vs pedanticism. (if that aint a word it should be). Walter has drawn it in one place, almost everyone disagrees in some way, large or small. Regan
Feb 21 2005
next sibling parent reply xs0 <xs0 xs0.com> writes:
 Can you give an example which compile, so I can mess with it :)
The point wasn't to compile it :) If you really want to, you can probably just return (int)sqrt(..) or something..
 The problem is that without any change in either the int version  
 or in eval_some_cost(), the code will produce different
 results. This is  most probably the reason why ambiguous function 
 calls are not allowed  and you need to be explicit which version you 
 want to use.
Consider this, existing, similar problem: class Parent { void foo(long a) { printf("parent: foo: long\n"); } } class Child : Parent { //uncomment this line //void foo(int a) { printf("child: foo: int\n"); } } void main() { Child c = new Child(); long a = 5; c.foo(a); }
Yup, it is similar, but I think that in this case, Parent.foo will still be called (because long can't be implicitly cast to int?). And even if you switched int and long, it's not the quite the same - Child is a new class with a new contract and whatnot.. But yes, in any case, overloading methods with implicitly castable types (both the return type and the parameters) is asking for trouble. Still, overloading parameter types is an issue that's present in other languages as well (like C++ and Java), so I guess people (i.e. me) are more used to it, while overloading on return type is far less common..
 Hmm, but there's no casting, so why use the cast() operator?
Because it has basically the same meaning.
How does it have the same meaning? Normally, you cast() something to make it one type from another type. If you select some specific return type, you'd already get that type as the result, so you wouldn't want to cast it. I'd certainly read cast(int)func(params) as func _not_ returning int.
 Agreed, but it's not the way D works currently.
Yup, currently you can't overload just on return types :)
 This whole issue comes down to where you draw the line, where you 
 balance  convenience vs pedanticism. (if that aint a word it should be). 
 Walter has  drawn it in one place, almost everyone disagrees in some 
 way, large or  small.
I certainly don't disagree with Walter; actually I think D is the best language I've ever seen (at least in the compiled-to-native-code category). Well, considering how opCast is the only problem where not having return-type-overloads is really an issue, perhaps it could just get special treatment and the problem'd be solved? class A { cast(T : int) { return ...; } cast(T : double) { return ...; } } or something.. looks obvious to me :) (T is just dummy, of course, but the syntax indicates specialization of cast for different types, much like with templates) xs0
Feb 21 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Tue, 22 Feb 2005 01:26:44 +0100, xs0 <xs0 xs0.com> wrote:
 Can you give an example which compile, so I can mess with it :)
The point wasn't to compile it :) If you really want to, you can probably just return (int)sqrt(..) or something..
If it doesn't compile and you're trying to show something that 'could' happen then it's an invalid example.
 The problem is that without any change in either the int version
>> or in eval_some_cost(), the code will produce different
 results. This is  most probably the reason why ambiguous function  
 calls are not allowed  and you need to be explicit which version you  
 want to use.
Consider this, existing, similar problem: class Parent { void foo(long a) { printf("parent: foo: long\n"); } } class Child : Parent { //uncomment this line //void foo(int a) { printf("child: foo: int\n"); } } void main() { Child c = new Child(); long a = 5; c.foo(a); }
Yup, it is similar, but I think that in this case, Parent.foo will still be called (because long can't be implicitly cast to int?).
Wrong.
 And even if you switched int and long, it's not the quite the same -  
 Child is a new class with a new contract and whatnot.. But yes, in any  
 case, overloading methods with implicitly castable types (both the  
 return type and the parameters) is asking for trouble. Still,  
 overloading parameter types is an issue that's present in other  
 languages as well (like C++ and Java), so I guess people (i.e. me) are  
 more used to it, while overloading on return type is far less common..


 Hmm, but there's no casting, so why use the cast() operator?
Because it has basically the same meaning.
How does it have the same meaning? Normally, you cast() something to make it one type from another type. If you select some specific return type, you'd already get that type as the result, so you wouldn't want to cast it. I'd certainly read cast(int)func(params) as func _not_ returning int.
It has the same meaning as casting a parameter to force the compiler to select the correct overload. Sure, casting a parameter does actually cast the parameter, but so does casting a return value. The only difference is the order it happens in.
 Agreed, but it's not the way D works currently.
Yup, currently you can't overload just on return types :)
That isn't what I meant.
 This whole issue comes down to where you draw the line, where you  
 balance  convenience vs pedanticism. (if that aint a word it should  
 be). Walter has  drawn it in one place, almost everyone disagrees in  
 some way, large or  small.
I certainly don't disagree with Walter; actually I think D is the best language I've ever seen (at least in the compiled-to-native-code category). Well, considering how opCast is the only problem where not having return-type-overloads is really an issue, perhaps it could just get special treatment and the problem'd be solved?
Perhaps. A more generic soln would be more useful however.
 class A {
     cast(T : int) { return ...; }
     cast(T : double) { return ...; }
 }

 or something.. looks obvious to me :) (T is just dummy, of course, but  
 the syntax indicates specialization of cast for different types, much  
 like with templates)
This is a hack. I think we can do better. Regan
Feb 21 2005
next sibling parent reply xs0 <xs0 xs0.com> writes:
 If it doesn't compile and you're trying to show something that 'could'  
 happen then it's an invalid example.
Well, I didn't specify which floor() and sqrt() get called, so one could argue that the example was valid? :)
 Yup, it is similar, but I think that in this case, Parent.foo will 
 still  be called (because long can't be implicitly cast to int?).
Wrong.
My mistake then.. Although I think it should be the case, according to http://www.digitalmars.com/d/type.html (which doesn't list long in integer promotions, true, but I was extrapolating the int case).
 It has the same meaning as casting a parameter to force the compiler to  
 select the correct overload. Sure, casting a parameter does actually 
 cast  the parameter, but so does casting a return value. The only 
 difference is  the order it happens in.
Well, you make it sound as if you're trying to work around a bug in the compiler (which would be its complaining), but you're really just producing an exact match. Casting a return value, otoh, happens _after_ the function returns, so it can't be used for the same purpose... And consider the case where you'd want the double version, but you'd like to also cast it to int: cast(int)(cast(double)func(3)) ?
 Yup, currently you can't overload just on return types :)
That isn't what I meant.
So what did you mean?
 Well, considering how opCast is the only problem where not having  
 return-type-overloads is really an issue, perhaps it could just get  
 special treatment and the problem'd be solved?
Perhaps. A more generic soln would be more useful however.
Well, it depends on what you consider useful, I guess. I find it rather useful if the language doesn't contain features which complicate things without any real benefit.. I mean, what's the real difference between cast(int)something and something.asInt()? The only reasonable case I can think of are templates, so you can use the same code both for classes that are something, and classes than can cast themselves as that same something (like a List or whatever). And in that respect, having multiple possible casts might be a good idea..
 This is a hack. I think we can do better.
Well, what is your better suggestion then? Your suggestion (cast(type)func) is imho much more of a hack. BTW, I wasn't suggesting a dummy parameter, which you already proclaimed a hack, merely a dummy type alias, which could possibly even be used with a mixin or something, so it wouldn't even be a dummy anymore. As in: cast(T: int) { return Something!(T).convert(this); } cast(T: double) { return Something!(T).convert(this); } xs0
Feb 21 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Tue, 22 Feb 2005 02:12:27 +0100, xs0 <xs0 xs0.com> wrote:
 If it doesn't compile and you're trying to show something that 'could'   
 happen then it's an invalid example.
Well, I didn't specify which floor() and sqrt() get called, so one could argue that the example was valid? :)
If you had specified them, would it have compiled? If so, post the example here, if not...
 Yup, it is similar, but I think that in this case, Parent.foo will  
 still  be called (because long can't be implicitly cast to int?).
Wrong.
My mistake then.. Although I think it should be the case, according to http://www.digitalmars.com/d/type.html (which doesn't list long in integer promotions, true, but I was extrapolating the int case).
It doesn't say exactly what it does on that page, it probably should. I notice you don't try examples which are posted, I think you should, before commenting on them, it's a waste of time guessing what's going to happen.
 It has the same meaning as casting a parameter to force the compiler  
 to  select the correct overload. Sure, casting a parameter does  
 actually cast  the parameter, but so does casting a return value. The  
 only difference is  the order it happens in.
Well, you make it sound as if you're trying to work around a bug in the compiler (which would be its complaining), but you're really just producing an exact match.
True, however I'm also specifying the overload I want, because the compiler cannot read my mind and I doesn't guess (thank Bob).
 Casting a return value, otoh, happens _after_ the function returns, so  
 it can't be used for the same purpose...
I disagree. As I said, I'm explicitly specifying the overload I mean.
 And consider the case where you'd want the double version, but you'd  
 like to also cast it to int:

 cast(int)(cast(double)func(3))
So you're saying you have: int func(int a) {} double func(int a) {} void main() { int a; a = func(); } and you want to call the double version, right? If so, then yes, your syntax looks fine.
 Yup, currently you can't overload just on return types :)
That isn't what I meant.
So what did you mean?
Your original comment was "So, I'd say that if there are return types A and B, and either can be implicitly casted to the other, the caller should be forced to specify which is the right one when using either." I replied: "Agreed, but it's not the way D works currently." Turns out, I was wrong, D does work this way currently. Your comment: "Yup, currently you can't overload just on return types :)" has no bearing on this however, and wasn't what I meant, so I said that.
 Well, considering how opCast is the only problem where not having   
 return-type-overloads is really an issue, perhaps it could just get   
 special treatment and the problem'd be solved?
Perhaps. A more generic soln would be more useful however.
Well, it depends on what you consider useful, I guess. I find it rather useful if the language doesn't contain features which complicate things without any real benefit.. I mean, what's the real difference between cast(int)something and something.asInt()?
As I said above, when casting to cause an exact match I am explicitly specifying the overload I want to use, because the compiler cannot read my mind and doesn't guess. The other is converting to an int, sure, the cast does this, but, when used to select an overload the intent is different, the fact that it's converting (which it might not be, consider int->long) is secondary.
 The only reasonable case I can think of are templates, so you can use  
 the same code both for classes that are something, and classes than can  
 cast themselves as that same something (like a List or whatever). And in  
 that respect, having multiple possible casts might be a good idea..
Classes can be implicitly cast to their parent types, I believe. Is this what you're referring to? eg. class A {} class B : A {} void foo(A a) {} void main() { B b = new B(); foo(b); } no cast is required, even if you add... class A {} class B : A {} void foo(A a) { printf("A"); } void foo(B b) { printf("B"); } void main() { B b = new B(); A a = new A(); foo(a); foo(b); }
 This is a hack. I think we can do better.
Well, what is your better suggestion then?
cast(type)
 Your suggestion (cast(type)func) is imho much more of a hack.
You're welcome to think so. In the end Walter decides anyway.
 BTW, I wasn't suggesting a dummy parameter, which you already proclaimed  
 a hack, merely a dummy type alias, which could possibly even be used  
 with a mixin or something, so it wouldn't even be a dummy anymore. As in:

 cast(T: int)    { return Something!(T).convert(this); }
 cast(T: double) { return Something!(T).convert(this); }
That syntax does not seem obvious to me. Which is why I prefer cast(type), it has an obvious commonly understood meaning. Regan
Feb 21 2005
parent reply xs0 <xs0 xs0.com> writes:
 Well, I didn't specify which floor() and sqrt() get called, so one 
 could  argue that the example was valid? :)
If you had specified them, would it have compiled? If so, post the example here, if not...
What kind of questions is that? Are you suggesting you're not sure whether "return floor(sqrt(a))" can ever be a valid function body? And I can't produce a compilable example, because the example included an overload on return type, which is not possible...
 I notice you don't try examples which are posted, I think you should,  
 before commenting on them, it's a waste of time guessing what's going 
 to  happen.
Like I said, I can't produce a compilable example (which I thought would be somewhat obvious from the nature of this thread, but I guess it isn't).
 True, however I'm also specifying the overload I want, because the  
 compiler cannot read my mind and I doesn't guess (thank Bob).
Maybe, but you're specifying the overload you want merely as a side-effect of casting parameters.
 Casting a return value, otoh, happens _after_ the function returns, 
 so  it can't be used for the same purpose...
I disagree. As I said, I'm explicitly specifying the overload I mean.
No, you're not.. You're just casting the return type. You can cast the return type now, so casting obviously doesn't mean you're trying to select an overload..
 So you're saying you have:
 
 int func(int a) {}
 double func(int a) {}
 
 void main() {
   int a;
   a = func();
 }
 
 and you want to call the double version, right?
 If so, then yes, your syntax looks fine.
Since you were so smart about compilable examples, I'm surprised you wrote an example that can't be compiled :P (Sorry, couldn't resist) I think you're not clear on how evaluation of expressions works, and that is the reason you think using cast for this would be fine. Let me try to explain. Let's focus just on ints and doubles. When you type cast(int)expression it's basically the same as if you called some compiler-provided function named, for example, cast_to_int(), so the above translates to cast_to_int(expression). That function has several overloads, in this case two (because I limited the example to two types): int cast_to_int(int value) { return value; } int cast_to_int(double value) { return ...; // the largest integer not less than value } Same goes for cast(double). Now, when you type f(cast(int)a, cast(double)b) it gets translated to f(cast_to_int(a), cast_to_double(b)) and NOT to f(a, b); // OY, COMPILER, I'M TALKING TO YOU! I WANT f(int,double) So, before the func() gets the parameters, they're already cast to the right types. Incidentally, that makes an exact match with one of the overloads of func(), so the compiler knows which one you meant. OTOH, if you write cast(int)func(a,b), it gets translated to cast_to_int(func(a,b)) From that it's obvious that casting is done _after_ the function is evaluated, so casting can't have an effect on which function is selected. Got it? (now don't start arguing I made this up; this is how it actually works (I wrote a parser or two in my life, so I know this))
 As I said above, when casting to cause an exact match I am explicitly  
 specifying the overload I want to use, because the compiler cannot read 
 my  mind and doesn't guess.
No, you're not specifying the overload, you're just casting the arguments, possibly producing an exact overload match..
 The other is converting to an int, sure, the cast does this, but, when  
 used to select an overload the intent is different, the fact that it's  
 converting (which it might not be, consider int->long) is secondary.
no, it isn't secondary, just the opposite (and casting int to long is converting, the first is 32 bits, the other is 64 bits). the only cast that doesn't do anything is actually casting a type to the same type.. (even if two types are the same length and casting doesn't change one bit, it will change the semantics of expressions; for example, if you cast int 3 to uint 3, you'll get exactly the memory contents; however, if you add 3000000000, you'll get a negative value in the first case and a positive value in the second case)
 The only reasonable case I can think of are templates, so you can use  
 the same code both for classes that are something, and classes than 
 can  cast themselves as that same something (like a List or whatever). 
 And in  that respect, having multiple possible casts might be a good 
 idea..
Classes can be implicitly cast to their parent types, I believe. Is this what you're referring to? eg.
Nope, I'm referring to something like this: class SomeClass(T) { void doSomething(T param) { List a=cast(List)param; // now do something with a } } This template will currently work for both Ts that are themselves Lists, and Ts that have opCast() that returns a List. But, opCast() can't be overloaded, and if you want it for something else, you have a problem..
 Well, what is your better suggestion then?
cast(type)
That's impossible (really, cast is not overload-selection-operator, it's casting-operator). Any other suggestions?
 cast(T: int)    { return Something!(T).convert(this); }
 cast(T: double) { return Something!(T).convert(this); }
That syntax does not seem obvious to me.
Why not? How would you suggest overloading opCast() if return-type overloading is not allowed (which is the case this suggestion is trying to cover, and I was just trying to show how T may not always be dummy)?
 Which is why I prefer cast(type), it has an obvious commonly understood  
 meaning.
Yes, I agree completely, it's just not what you understand it to be... xs0
Feb 22 2005
next sibling parent Georg Wrede <georg.wrede nospam.org> writes:
xs0 wrote:
 That's impossible (really, cast is not overload-selection-operator, it's 
 casting-operator). Any other suggestions?
If we had overloading on return type, then in some situations we'd want some way to choose which return type to use. Using cast for this would seem natural. Of course, changing the semantics of cast may, for all I know, raise other issues.
Feb 22 2005
prev sibling parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Tue, 22 Feb 2005 10:20:33 +0100, xs0 <xs0 xs0.com> wrote:
 Well, I didn't specify which floor() and sqrt() get called, so one  
 could  argue that the example was valid? :)
If you had specified them, would it have compiled? If so, post the example here, if not...
What kind of questions is that? Are you suggesting you're not sure whether "return floor(sqrt(a))" can ever be a valid function body?
No.
 And I can't produce a compilable example, because the example included  
 an overload on return type, which is not possible...
No, your example didn't show one, you described one being added later. All I want is an example that compiles. Which should be possible.
 I notice you don't try examples which are posted, I think you should,   
 before commenting on them, it's a waste of time guessing what's going  
 to  happen.
Like I said, I can't produce a compilable example (which I thought would be somewhat obvious from the nature of this thread, but I guess it isn't).
See above.
 True, however I'm also specifying the overload I want, because the   
 compiler cannot read my mind and I doesn't guess (thank Bob).
Maybe, but you're specifying the overload you want merely as a side-effect of casting parameters.
No. If the compiler errors because it cannot pick the overload, then, by adding a cast I am selecting an overload, the fact that it casts/converts is the side effect. In other words the point/purpose of the cast changes depending on what it's used for. I think that, because the cast comes first, you're assuming it's the purpose, because the overload selection comes second it's a side effect. A side effect can occur first, just as the purpose can occur second.
 Casting a return value, otoh, happens _after_ the function returns,  
 so  it can't be used for the same purpose...
I disagree. As I said, I'm explicitly specifying the overload I mean.
No, you're not.. You're just casting the return type. You can cast the return type now, so casting obviously doesn't mean you're trying to select an overload..
Yes, you can cast a return type now, now the purpose is to cast. Were it possible to cast to select an overload, then doing so would be the purpose. The purpose of cast now, is not always to convert, but sometimes to select an overload, the purpose changes depending on where it's used and why (by definition).
 So you're saying you have:
  int func(int a) {}
 double func(int a) {}
  void main() {
   int a;
   a = func();
 }
  and you want to call the double version, right?
 If so, then yes, your syntax looks fine.
Since you were so smart about compilable examples, I'm surprised you wrote an example that can't be compiled :P (Sorry, couldn't resist)
I find your comment petty. The example above cannot be compiled because it is an error, I was giving an example of an error case which your syntax would solve. The same is not true of your example, which was an example of 'before' the collision was added.
 I think you're not clear on how evaluation of expressions works, and  
 that is the reason you think using cast for this would be fine. Let me  
 try to explain.
I am not the only one, see Georg's reply.
 Let's focus just on ints and doubles. When you type cast(int)expression  
 it's basically the same as if you called some compiler-provided function  
 named, for example, cast_to_int(), so the above translates to  
 cast_to_int(expression). That function has several overloads, in this  
 case two (because I limited the example to two types):

 int cast_to_int(int value) {
     return value;
 }
 int cast_to_int(double value) {
     return ...; // the largest integer not less than value
 }

 Same goes for cast(double). Now, when you type

 f(cast(int)a, cast(double)b)

 it gets translated to

 f(cast_to_int(a), cast_to_double(b))

 and NOT to

 f(a, b); // OY, COMPILER, I'M TALKING TO YOU! I WANT f(int,double)

 So, before the func() gets the parameters, they're already cast to the  
 right types. Incidentally, that makes an exact match with one of the  
 overloads of func(), so the compiler knows which one you meant.

 OTOH, if you write cast(int)func(a,b), it gets translated to

 cast_to_int(func(a,b))

  From that it's obvious that casting is done _after_ the function is  
 evaluated, so casting can't have an effect on which function is  
 selected. Got it? (now don't start arguing I made this up; this is how  
 it actually works (I wrote a parser or two in my life, so I know this))
I understand everything you've said above, and I agree it works as you've said. But, the point you're missing is that the 'intent' of a statement is not necessarily the same as the effect generated by the compiler. In this case using a cast to "select an overload" is the intent of the programmer. The effect is that the "variable is converted", but, that is a side effect. This behaviour/method is generally accepted and understood by programmers, and extending it to include return values will be the same, generally accepted and understood. Further it requires no additional syntax and is easily parsable.
 As I said above, when casting to cause an exact match I am explicitly   
 specifying the overload I want to use, because the compiler cannot read  
 my  mind and doesn't guess.
No, you're not specifying the overload, you're just casting the arguments,
This is not my 'intent' it is simply the effect.
 possibly producing an exact overload match..
This is the intent.
 The other is converting to an int, sure, the cast does this, but, when   
 used to select an overload the intent is different, the fact that it's   
 converting (which it might not be, consider int->long) is secondary.
no, it isn't secondary, just the opposite (and casting int to long is converting, the first is 32 bits, the other is 64 bits).
It's not converting per-se, more extending and setting bits to 0.
 the only cast that doesn't do anything is actually casting a type to the  
 same type..
int -> uint makes no changes to the memory.
 (even if two types are the same length and casting doesn't change one  
 bit, it will change the semantics of expressions; for example, if you  
 cast int 3 to uint 3, you'll get exactly the memory contents; however,  
 if you add 3000000000, you'll get a negative value in the first case and  
 a positive value in the second case)
Sure, but as you and I have said, it makes no change to the memory. Regardless, this is beside the point, except to say that if a programmer uses a cast to select an overload they need to consider the effect the conversion will have, but, again, the conversion is secondary to the intent which is to select the overload.
 The only reasonable case I can think of are templates, so you can use   
 the same code both for classes that are something, and classes than  
 can  cast themselves as that same something (like a List or whatever).  
 And in  that respect, having multiple possible casts might be a good  
 idea..
Classes can be implicitly cast to their parent types, I believe. Is this what you're referring to? eg.
Nope, I'm referring to something like this: class SomeClass(T) { void doSomething(T param) { List a=cast(List)param; // now do something with a } } This template will currently work for both Ts that are themselves Lists, and Ts that have opCast() that returns a List. But, opCast() can't be overloaded, and if you want it for something else, you have a problem..
Ok.
 Well, what is your better suggestion then?
cast(type)
That's impossible (really, cast is not overload-selection-operator, it's casting-operator). Any other suggestions?
No, this is the best method/soln I've heard so far.
 cast(T: int)    { return Something!(T).convert(this); }
 cast(T: double) { return Something!(T).convert(this); }
That syntax does not seem obvious to me.
Why not?
Well.. it looks like a function, returning nothing, taking something called T which must be an int, if that's the case why not just go: cast(int) { .. } in which case it looks just like the c++ hack to me. So in short your example seems to be the c++ hack, with extra characters making it less clear (to me).
 How would you suggest overloading opCast() if return-type overloading is  
 not allowed
No idea, I'll think about this, if, return type overloading is officially "not going to happen, ever".
 (which is the case this suggestion is trying to cover, and
Ahh, then we're talking at cross puposes. I am suggesting overload on return values, using a cast. I think it's the best solution. If denied that, then I'll try and think of another solution.
 I was just trying to show how T may not always be dummy)?
I dont think it's any different to using a dummy, and using typeof(param) on it.
 Which is why I prefer cast(type), it has an obvious commonly  
 understood  meaning.
Yes, I agree completely, it's just not what you understand it to be...
It appears Georg agrees with me. Regan
Feb 22 2005
next sibling parent reply brad domain.invalid writes:
 
 If the compiler errors because it cannot pick the overload, then, by  
 adding a cast I am selecting an overload, the fact that it 
 casts/converts  is the side effect. In other words the point/purpose of 
 the cast changes  depending on what it's used for.
 
I haven't read the whole thread, but I think I get the general gist. IMHO, you have just shot yourself in the foot with "In other words the point/purpose of the cast changes depending on what it's used for" I think that D's philosophy is to be clear and simple where possible, having situations where the behaviour of cast changes would be against that philosophy. Also, what happens in the situation where: int foo(int i) {}; <--- I really want this one called char foo (int i) {}; char c = cast(char)(cast(int)foo(10)); Ugly, no? Brad
Feb 22 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Wed, 23 Feb 2005 10:41:05 +1300, <brad domain.invalid> wrote:
  If the compiler errors because it cannot pick the overload, then, by   
 adding a cast I am selecting an overload, the fact that it  
 casts/converts  is the side effect. In other words the point/purpose of  
 the cast changes  depending on what it's used for.
I haven't read the whole thread, but I think I get the general gist. IMHO, you have just shot yourself in the foot with "In other words the point/purpose of the cast changes depending on what it's used for" I think that D's philosophy is to be clear and simple where possible, having situations where the behaviour of cast changes would be against that philosophy.
No, you missunderstand. The behaviour isn't changing at all. Cast still does what cast does, the intent of the programmer is what changes.
 Also, what happens in the situation where:
 int foo(int i) {};	<--- I really want this one called
 char foo (int i) {};

 char c = cast(char)(cast(int)foo(10));

 Ugly, no?
Perhaps, matter of opinion. Your example seems engineered to look bad however. Why do you have a "char foo" function, yet want to use the "int foo" one, to get a char? It makes little or no sense to me. Regan
Feb 22 2005
parent reply brad domain.invalid writes:
Regan Heath wrote:

 
 No, you missunderstand. The behaviour isn't changing at all. Cast still  
 does what cast does, the intent of the programmer is what changes.
cast currently takes one type an converts it to another type. What you are suggesting would make cast select a different function based on the return type. Surely that changes the role of cast? Instead of simply being a type conversion mechanism, it is now a mechanism for resolving ambiguity.
 
 Also, what happens in the situation where:
 int foo(int i) {};    <--- I really want this one called
 char foo (int i) {};

 char c = cast(char)(cast(int)foo(10));

 Ugly, no?
Perhaps, matter of opinion. Your example seems engineered to look bad however. Why do you have a "char foo" function, yet want to use the "int foo" one, to get a char? It makes little or no sense to me. Regan
It is engineered, yes - but that doesn't mean that it couldn't happen in the real world. Brad
Feb 22 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Wed, 23 Feb 2005 11:51:22 +1300, <brad domain.invalid> wrote:
 Regan Heath wrote:

  No, you missunderstand. The behaviour isn't changing at all. Cast  
 still  does what cast does, the intent of the programmer is what  
 changes.
cast currently takes one type an converts it to another type. What you are suggesting would make cast select a different function based on the return type. Surely that changes the role of cast?
Nope. It's comparable to casting a parameter. eg. void foo(int a) {} void foo(float a) {} foo(cast(int)5); cast still converts the type, which is it's role/function, but, the intent (of the programmer) here is to select the overload and resolve the ambiguity. The only difference is the order in which things happen, I suspect people (you?) think the thing that happens first must be the intent, I disagree.
 Instead of simply being a type conversion mechanism, it is now a  
 mechanism for resolving ambiguity.
It already is, that's part of my point, it's already used to resolve ambiguity, see the above example.
 Also, what happens in the situation where:
 int foo(int i) {};    <--- I really want this one called
 char foo (int i) {};

 char c = cast(char)(cast(int)foo(10));

 Ugly, no?
Perhaps, matter of opinion. Your example seems engineered to look bad however. Why do you have a "char foo" function, yet want to use the "int foo" one, to get a char? It makes little or no sense to me. Regan
It is engineered, yes - but that doesn't mean that it couldn't happen in the real world.
True. I'd argue that if it did you have a bigger 'design' problem. Regan
Feb 22 2005
next sibling parent reply brad domain.invalid writes:
Regan Heath wrote:

 on the  return type.  Surely that changes the role of cast?
Nope. It's comparable to casting a parameter. eg. void foo(int a) {} void foo(float a) {} foo(cast(int)5); cast still converts the type, which is it's role/function, but, the intent (of the programmer) here is to select the overload and resolve the ambiguity.
Fair enough, though I can't say I've personally used cast as a method for selecting a particular overload. I usually use it when assigning a variable that is not quite of the right type. I think that overloading on return type just smells wrong. I get the feeling that it would lead to murky areas, and much of what D is all about getting rid of the murk. For example int foo (int i) {} uint foo (int i) {} char c = foo(3); // which foo, and how are the implicit cast rules defined for different architectures? foo(3); // which foo? Is there a blindingly obvious example of why we might like to have return type overloading? I can't think of a language that has it - but I am willing to learn :)
 It is engineered, yes - but that doesn't mean that it couldn't happen 
 in  the real world.
True. I'd argue that if it did you have a bigger 'design' problem.
The problem is, in the real world with maintenance on a deadline you don't always get to redesign the project, and often what can be done quick and dirty gets done. Brad
Feb 22 2005
parent "Regan Heath" <regan netwin.co.nz> writes:
On Wed, 23 Feb 2005 14:06:05 +1300, <brad domain.invalid> wrote:
 Regan Heath wrote:

 on the  return type.  Surely that changes the role of cast?
Nope. It's comparable to casting a parameter. eg. void foo(int a) {} void foo(float a) {} foo(cast(int)5); cast still converts the type, which is it's role/function, but, the intent (of the programmer) here is to select the overload and resolve the ambiguity.
Fair enough, though I can't say I've personally used cast as a method for selecting a particular overload. I usually use it when assigning a variable that is not quite of the right type.
Each to thier own.
 I think that overloading on return type just smells wrong.  I get the  
 feeling that it would lead to murky areas, and much of what D is all  
 about getting rid of the murk.  For example

 int foo (int i) {}
 uint foo (int i) {}

 char c = foo(3); // which foo
Neither, D would give an error, same as it does for. void foo(int i); void foo(uint i); char c = 3; foo(c);
 , and how are the implicit cast rules defined for different  
 architectures?
No implicit cast rules. All explicit, as required.
 foo(3); // which foo?

 Is there a blindingly obvious example of why we might like to have  
 return type overloading?  I can't think of a language that has it - but  
 I am willing to learn :)
C++ has it, with a hack, with it's cast operator. The problem with the C++ one, according to Walter is it's implicit nature, meaning it can cause hidden bugs. I agree, which is why I think it should be explicit. For a few examples see my other post to "Georg Wrede" in another branch of this thread.
 It is engineered, yes - but that doesn't mean that it couldn't happen  
 in  the real world.
True. I'd argue that if it did you have a bigger 'design' problem.
The problem is, in the real world with maintenance on a deadline you don't always get to redesign the project, and often what can be done quick and dirty gets done.
I know. I believe this behaviour cause more problems than it solves. But that's my personal philosophy. Regan
Feb 22 2005
prev sibling parent reply Charles Hixson <charleshixsn earthlink.net> writes:
Regan Heath wrote:
 On Wed, 23 Feb 2005 11:51:22 +1300, <brad domain.invalid> wrote:
 
 Regan Heath wrote:

  No, you missunderstand. The behaviour isn't changing at all. Cast  
 still  does what cast does, the intent of the programmer is what  
 changes.
cast currently takes one type an converts it to another type. What you are suggesting would make cast select a different function based on the return type. Surely that changes the role of cast?
Nope. It's comparable to casting a parameter. eg.
...> Regan I would argue that what is being casted is the functions return value (since that's what you are using to chose between the routines). Personally, in that context I prefer to append :type to the function name. (Logically it should be to the parenthesized parameter list, but that gets ugly and unreadable very quickly.) Thus one would have: mysqrt:int(arg); rather than: mysqrt(arg):int; The second form is more elegant in simple cases, but the first version is more readable when the expressions get complex. In fact, one could even have: real x; x = mysqrt:int(arg); which would run the integer version of mysqrt before converting it into a real. I feel that this would be a worthy addition to D, but I don't think it's crucially important. Nice, but not necessary. And not fundamental in the way that the earlier thread about vector operations was.
Feb 23 2005
next sibling parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Wed, 23 Feb 2005 17:03:41 -0800, Charles Hixson  
<charleshixsn earthlink.net> wrote:
 Regan Heath wrote:
 On Wed, 23 Feb 2005 11:51:22 +1300, <brad domain.invalid> wrote:

 Regan Heath wrote:

  No, you missunderstand. The behaviour isn't changing at all. Cast   
 still  does what cast does, the intent of the programmer is what   
 changes.
cast currently takes one type an converts it to another type. What you are suggesting would make cast select a different function based on the return type. Surely that changes the role of cast?
Nope. It's comparable to casting a parameter. eg.
...> Regan I would argue that what is being casted is the functions return value (since that's what you are using to chose between the routines).
Well, yes, currently that's exactly what is happening. Imagine... void foo(long a){} void foo(float a){} void main() { foo(5); } The compiler gives the error: ex4.d(5): function ex4.foo overloads void(long a) and void(float a) both match argument list for foo And to solve it, you go: foo(cast(int)5); or foo(cast(float)5); depending on what one you want, or alternately you realise there is a bug and fix it in another way. The above is the commonly accepted method of resolving an overload conflict, in D (and other languages) I think it's a natural extension that if you had: long foo(int a){} float foo(int a){} void main() { int a; a = foo(5); } and the compiler gave the error: ex4.d(5): function ex4.foo overloads long(int a) and float(int a) both match return type for foo the programmers response, if they have encountered the first example above, would be to use cast again eg. a = cast(long)foo(5); a = cast(float)foo(5);
 Personally, in that context I prefer to append :type to the function  
 name.  (Logically it should be to the parenthesized parameter list, but  
 that gets ugly and unreadable very quickly.)   Thus one would have:
    mysqrt:int(arg);
 rather than:
    mysqrt(arg):int;

 The second form is more elegant in simple cases, but the first version  
 is more readable when the expressions get complex.  In fact, one could  
 even have:
    real x;
    x = mysqrt:int(arg);
 which would run the integer version of mysqrt before converting it into  
 a real.
They both work, I just prefer cast(type).
 I feel that this would be a worthy addition to D, but I don't think it's  
 crucially important. Nice, but not necessary.
I agree, but I like to discuss the options.
 And not fundamental in the way that the earlier thread about vector  
 operations was.
Each to thier own, I have no use for vector operations myself. Regan
Feb 23 2005
parent reply Charles Hixson <charleshixsn earthlink.net> writes:
Regan Heath wrote:
 On Wed, 23 Feb 2005 17:03:41 -0800, Charles Hixson  
 <charleshixsn earthlink.net> wrote:
 
 Regan Heath wrote:

 On Wed, 23 Feb 2005 11:51:22 +1300, <brad domain.invalid> wrote:

 Regan Heath wrote:

  No, you missunderstand. The behaviour isn't changing at all. 
 Cast   still  does what cast does, the intent of the programmer is 
 what   changes.
cast currently takes one type an converts it to another type. What you are suggesting would make cast select a different function based on the return type. Surely that changes the role of cast?
Nope. It's comparable to casting a parameter. eg.
...> Regan I would argue that what is being casted is the functions return value (since that's what you are using to chose between the routines).
Well, yes, currently that's exactly what is happening. Imagine... void foo(long a){} void foo(float a){} void main() { foo(5); } The compiler gives the error: ex4.d(5): function ex4.foo overloads void(long a) and void(float a) both match argument list for foo And to solve it, you go: foo(cast(int)5); or foo(cast(float)5); depending on what one you want, or alternately you realise there is a bug and fix it in another way. The above is the commonly accepted method of resolving an overload conflict, in D (and other languages) I think it's a natural extension that if you had: long foo(int a){} float foo(int a){} void main() { int a; a = foo(5); } and the compiler gave the error: ex4.d(5): function ex4.foo overloads long(int a) and float(int a) both match return type for foo the programmers response, if they have encountered the first example above, would be to use cast again eg. a = cast(long)foo(5); a = cast(float)foo(5);
 Personally, in that context I prefer to append :type to the function  
 name.  (Logically it should be to the parenthesized parameter list, 
 but  that gets ugly and unreadable very quickly.)   Thus one would have:
    mysqrt:int(arg);
 rather than:
    mysqrt(arg):int;

 The second form is more elegant in simple cases, but the first 
 version  is more readable when the expressions get complex.  In fact, 
 one could  even have:
    real x;
    x = mysqrt:int(arg);
 which would run the integer version of mysqrt before converting it 
 into  a real.
They both work, I just prefer cast(type).
 I feel that this would be a worthy addition to D, but I don't think 
 it's  crucially important. Nice, but not necessary.
I agree, but I like to discuss the options.
 And not fundamental in the way that the earlier thread about vector  
 operations was.
Each to thier own, I have no use for vector operations myself. Regan
The problem is, it isn't REALLY a cast that is wanted, it's a selection. The type of the return value is being used to select the method used to do the calculation. A pure decomposition would look sort of like: foo(int, real) returns float::foo(i, x) but that is intensely laborous, and overwhelmingly verbose. And normally one can tell the types of the arguments (though one might argue this about literal strings...char[]? dchar[]? wchar[]?)). Usually one can also determine the return type, but not always. So to be really safe one would specify the type separately from the arguments/return values. But this isn't usually needed. Anyway one can cast the arguments into the appropriate type is that isn't obvious. This, however, disguises that what is significant here is the SELECTION of the routine version. The casting is a way of accomplishing this. For a returned value from a function, I would argue that one might well want to specify it separate from the way that one needed to cast the result. The only obvious example that occurs to me has to do with polar vs. cartesian coordinates, and it's not critical. Still, I would consider being able to select the version of a routine by specifying the type of returned argument (NOT a cast, and hence confusing if specified as if it were one). Because of this I prefer a separate syntax, and my proposed function name decorator subscript seems to me quite reasonable, though an argument could easily be made for many other forms (e.g., a function name decorator prescript: int:foo(x) rather than foo:int(x) ). And again, though I would find this a nice feature, it's acutal need would only be occasional. But with the current system one does wonder how: x = cast(int)cast(real)foo(x); would be interpreted. Still, one can suppose that another layer of parenthesis would clarify things: x = cast(int)(cast(real)foo(x));
Feb 25 2005
next sibling parent Georg Wrede <georg.wrede nospam.org> writes:
Charles Hixson wrote:
 Regan Heath wrote:
 
 On Wed, 23 Feb 2005 17:03:41 -0800, Charles Hixson  
 <charleshixsn earthlink.net> wrote:

 Regan Heath wrote:

 On Wed, 23 Feb 2005 11:51:22 +1300, <brad domain.invalid> wrote:

 Regan Heath wrote:
 Still, I would consider being able to select the version of a routine by 
 specifying the type of returned argument (NOT a cast, and hence 
 confusing if specified as if it were one).
 
 Because of this I prefer a separate syntax, and my proposed function 
 name decorator subscript seems to me quite reasonable, though an 
 argument could easily be made for many other forms (e.g., a function 
 name decorator prescript:  int:foo(x) rather than foo:int(x) ).
 
 And again, though I would find this a nice feature, it's acutal need 
 would only be occasional.  But with the current system one does wonder how:
 x = cast(int)cast(real)foo(x);
 would be interpreted.  Still, one can suppose that another layer of 
 parenthesis would clarify things:
 x = cast(int)(cast(real)foo(x));
int:foo(x) looks indeed nicer than foo:int(x). My worry is that both notations "pollute the grammar space". (That is, having either of these notations, may later lead to unability to express some more general and useful new thing in the language.) So, my vote (remember, though, that I'm still not for or against this concept per se) would be, either just use cast for this too or create a new word (retsel, prefer, fncast, castfn, ...) for the purpose. (The syntax being the same as for cast!) Syntax would stay the same, semantics of cast itself would not be altered, (if that really is that important), and it is only about one single word more.
Feb 26 2005
prev sibling parent "Regan Heath" <regan netwin.co.nz> writes:
On Fri, 25 Feb 2005 14:03:40 -0800, Charles Hixson  
<charleshixsn earthlink.net> wrote:
 Still, I would consider being able to select the version of a routine by  
 specifying the type of returned argument (NOT a cast, and hence  
 confusing if specified as if it were one).
This is a good point. One way to look at: cast(int)foo(); is "call foo, and give me an 'int' result". This can be achieved by calling the version of foo that returns an int (if it exists) or calling the version of foo that returns an x, then casting that to an int. Either way, you get the result you want, unless.. there are multiple versions of a function called "foo", doing _different_ things, which if you ask me, is a problem with function naming and/or design. This new way of looking at it, might be the D way, instead of the C/C++ way. Just a thought. I understand other people might not look at it this way.
 Because of this I prefer a separate syntax, and my proposed function  
 name decorator subscript seems to me quite reasonable, though an  
 argument could easily be made for many other forms (e.g., a function  
 name decorator prescript:  int:foo(x) rather than foo:int(x) ).
If I had to choose between the above, I'd pick int:foo(x) but, it does look a bit like a c++ calling syntax, doesn't it?
 And again, though I would find this a nice feature, it's acutal need  
 would only be occasional.  But with the current system one does wonder  
 how:
 x = cast(int)cast(real)foo(x);
 would be interpreted.
Currently, it would cast the return of foo to a real, then an int. If my idea of cast(type)func was implemented then either: - it would be an error requiring parenthesis (not sure) - it would call the function returning 'real' then cast the result to 'int'
 Still, one can suppose that another layer of parenthesis would clarify  
 things:
 x = cast(int)(cast(real)foo(x));
I'm not sure what the current system thinks of this, for my idea I don't think it's "required" but some programmers might find it easier on the eyes, perhaps. Regan
Feb 27 2005
prev sibling parent Georg Wrede <georg.wrede nospam.org> writes:
Xsss Zffff wrote:
 Yiii Wrrrr wrote:
 
No, you missunderstand. The behaviour isn't changing at all. Cast still does what cast does, the intent of the programmer is what changes. ..... cast currently takes one type an converts it to another type. What you are suggesting would make cast select a different function based on the return type. Surely that changes the role of cast? <These, and a few tens of other posts -- averaging four to five times the length, per post, of some of the most heavy-weight posts here.> Please, guys. Please! You are bright, talented, and smart. But we all know that already, even if you don't behave like elks on a snowy field in March. Attackin every comma and splitting every hair in the other guy's post is not getting us forward. And, like everyone else here, I presume you have the future of D as the main objective, right. Not showing the rest of us who's got the last word.
Feb 23 2005
prev sibling parent reply xs0 <xs0 xs0.com> writes:
 And I can't produce a compilable example, because the example 
 included  an overload on return type, which is not possible...
No, your example didn't show one, you described one being added later. All I want is an example that compiles. Which should be possible.
Bah, my example was obviously both parts of it. Furthermore, I don't see what you could possibly gain by compiling the first part, as the code was trivial. I also already wrote what will make it compile (cast(int) instead of floor()). Finally, it's not possible, because both parts of the example were from the parallel universe in which return overloads in D exist. I'm assuming you don't have access to the compiler from that universe? :)
 No.
 
 If the compiler errors because it cannot pick the overload, then, by  
 adding a cast I am selecting an overload, the fact that it 
 casts/converts  is the side effect. In other words the point/purpose of 
 the cast changes  depending on what it's used for.
The compiler doesn't error because it cannot pick the overload, it errors, because Walter said it should, and for good reasons. There's a large difference there. As for the purpose, if you say the purpose of casting can be overload selection, then you'll also have to admit that there are also a large number of other possible purposes to casting. But then, you can't argue that it is the best syntax for doing so, because all those other purposes are just as valid, and casting is not somehow the natural choice. And it's certainly not the only choice..
 I think that, because the cast comes first, you're assuming it's the  
 purpose, because the overload selection comes second it's a side effect. 
 A  side effect can occur first, just as the purpose can occur second.
No, I think that because cast by itself has the functionality it has, its purpose is to change/convert types. The fact that that functionality can help you resolve overloads is definitely the side-effect; if there were no overloads, cast's purpose in the language would still remain the same.
 Yes, you can cast a return type now, now the purpose is to cast. Were 
 it  possible to cast to select an overload, then doing so would be the 
 purpose.
cast is not selecting an overload. The coder is the one selecting the overload by changing the type of arguments. If the coder uses the cast operator, it is just his choice, he could use other techniques (for example, instead of casting int to double you can also add 0.0 to it with the same result).
 The purpose of cast now, is not always to convert, but sometimes to 
 select  an overload, the purpose changes depending on where it's used 
 and why (by  definition).
That's just ridiculous. "Cast by definition" has a well-defined purpose and it is not selecting overloads.
 Since you were so smart about compilable examples, I'm surprised you  
 wrote an example that can't be compiled :P (Sorry, couldn't resist)
I find your comment petty.
Well, I find it petty that you proclaimed my example invalid just because you failed to see what its point was and wanted to compile it. At least I apologized..
 The example above cannot be compiled because it is an error, I was 
 giving  an example of an error case which your syntax would solve.
 The same is not true of your example, which was an example of 'before'
 the  collision was added.
Ahh, so you wanted to show something without it actually being compilable code. So did I, yet you keep being a smart-ass about it..
 I am not the only one, see Georg's reply.
I'm also not the only one, see Brad's reply. Now it's 2 vs 2. Let's see who wins. Man, this is fun :) (if you failed to notice, I'm trying to be sarcastic)
 I understand everything you've said above, and I agree it works as 
 you've  said.
 
 But, the point you're missing is that the 'intent' of a statement is 
 not  necessarily the same as the effect generated by the compiler.
The 'intent' of statements has nothing to do with the language (even more so outside computers). And even so, if I see int result=cast(int)func(...); I interpret the so-called intent as "call func(); because it doesn't return int, cast the return value to int". I'll also bet that that would be the interpretation of the vast majority of programmers. Start a poll or something, if you don't believe me.
 This behaviour/method is generally accepted and understood by 
 programmers,  and extending it to include return values will be the 
 same, generally  accepted and understood.
Well, I'd say that if someone understands cast as mainly being the thing to select overloads, he's a newbie (no offense meant to newbies; I was once a newbie too and also believed many wrong things).
 Further it requires no additional syntax and is easily parsable.
Why would no additional syntax for new functionality be a plus? And why would it be more easily parsable than any other syntax? If anything, it's harder - you need to figure out whether there is a function call involved, and if so, if it has overloaded return types and choose some completely different functionality based on the answers to those questions.
 no, it isn't secondary, just the opposite (and casting int to long is  
 converting, the first is 32 bits, the other is 64 bits).
It's not converting per-se, more extending and setting bits to 0.
Whatever.
 int -> uint makes no changes to the memory.
And the point is? That only the memory contents matter? What a weak argument. It's just electric charges in some silicon wafer without interpretation, so think about it a little (to start you of, the same memory bit pattern can be an int, a float, a single UTF32 character, 1-4 UTF8 characters, a processor op, a Java bytecode, and a gazillion other things). To make my own point clear - changing interpretation is far more important than changing memory contents.
 cast(T: int)    { return Something!(T).convert(this); }
 cast(T: double) { return Something!(T).convert(this); }
That syntax does not seem obvious to me.
Why not?
Well.. it looks like a function, returning nothing, taking something called T which must be an int, if that's the case why not just go: cast(int) { .. } in which case it looks just like the c++ hack to me.
well, to me it looks very similar to "template cast(T: int)" (which would mean "a specialization of cast for the type int"). It doesn't take T which must be an int, T is a type. I did consider cast(int) {..}, but that looks like you're trying to cast the code block, so I decided to propose something closer to templates. It doesn't have a return type, because it's already obvious what it is and you'd only have to type it twice.
 So in short your example seems to be the c++ hack, with extra 
 characters  making it less clear (to me).

 How would you suggest overloading opCast() if return-type overloading 
 is  not allowed
No idea, I'll think about this, if, return type overloading is officially "not going to happen, ever".
Well, my guess is that there's like 30% chance of overloading opcast in some completely new way (i.e. with some new syntax) and like 0.0001% chance of overloading return types, so you might as well start thinking about it.
 Ahh, then we're talking at cross puposes.
I clearly stated that I don't think overloading return types is a good idea, with the exception of opCast(). So why the ahh?
 I was just trying to show how T may not always be dummy)?
I dont think it's any different to using a dummy, and using typeof(param) on it.
Again, it's not a parameter, it's a Type.
 It appears Georg agrees with me.
Well, both my wife and my cousin agree with me, so what? For all I know it's just you with different newsreader settings.. Like I said, start a poll and we'll see how cast is understood.. xs0
Feb 22 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Wed, 23 Feb 2005 01:46:42 +0100, xs0 <xs0 xs0.com> wrote:
 And I can't produce a compilable example, because the example  
 included  an overload on return type, which is not possible...
No, your example didn't show one, you described one being added later. All I want is an example that compiles. Which should be possible.
Bah, my example was obviously both parts of it.
Obviously? I think not.
 Furthermore, I don't see what you could possibly gain by compiling the  
 first part
As I said, I want to fiddle with it.
 , as the code was trivial.
 I also already wrote what will make it compile (cast(int)
Can you post it then, so I can fiddle with it.
 instead of floor()). Finally, it's not possible, because both parts of  
 the example were from the parallel universe in which return overloads in  
 D exist.
I'm interested in the code, before these overloads are used.
 I'm assuming you don't have access to the compiler from that universe? :)
Now you're being silly.
 No.
  If the compiler errors because it cannot pick the overload, then, by   
 adding a cast I am selecting an overload, the fact that it  
 casts/converts  is the side effect. In other words the point/purpose of  
 the cast changes  depending on what it's used for.
The compiler doesn't error because it cannot pick the overload
Well, it could pick one at random.
 , it errors, because Walter said it should, and for good reasons.  
 There's a large difference there.
I don't think so, given that the compiler will never and should never pick one at random.
 As for the purpose, if you say the purpose of casting can be overload  
 selection, then you'll also have to admit that there are also a large  
 number of other possible purposes to casting.
Name one.
 But then, you can't argue that it is the best syntax for doing so,  
 because all those other purposes are just as valid, and casting is not  
 somehow the natural choice.
I believe it is. Georg would agree (it seems).
 And it's certainly not the only choice..
No, but as I said, I believe it's the best one.
 I think that, because the cast comes first, you're assuming it's the   
 purpose, because the overload selection comes second it's a side  
 effect. A  side effect can occur first, just as the purpose can occur  
 second.
No, I think that because cast by itself has the functionality it has, its purpose is to change/convert types. The fact that that functionality can help you resolve overloads is definitely the side-effect; if there were no overloads, cast's purpose in the language would still remain the same.
I give up, you're either ignoring my point, or you simply disagree, either way this is pointless.
 Yes, you can cast a return type now, now the purpose is to cast. Were  
 it  possible to cast to select an overload, then doing so would be the  
 purpose.
cast is not selecting an overload. The coder is the one selecting the overload by changing the type of arguments. If the coder uses the cast operator, it is just his choice, he could use other techniques (for example, instead of casting int to double you can also add 0.0 to it with the same result).
As above.
 The purpose of cast now, is not always to convert, but sometimes to  
 select  an overload, the purpose changes depending on where it's used  
 and why (by  definition).
That's just ridiculous. "Cast by definition" has a well-defined purpose and it is not selecting overloads.
As above.
 Since you were so smart about compilable examples, I'm surprised you   
 wrote an example that can't be compiled :P (Sorry, couldn't resist)
I find your comment petty.
Well, I find it petty that you proclaimed my example invalid just because you failed to see what its point was and wanted to compile it. At least I apologized..
I simply asked you to provide one that compiled, and told you why I wanted it.
 The example above cannot be compiled because it is an error, I was  
 giving  an example of an error case which your syntax would solve.
> The same is not true of your example, which was an example of 'before' > the collision was added. Ahh, so you wanted to show something without it actually being compilable code. So did I, yet you keep being a smart-ass about it..
Your code was not representing something that couldn't or shouldn't compile, as I've said it showed the code 'before' adding the fictional feature. My code shouldn't have compiled. If you can't see the difference...
 I am not the only one, see Georg's reply.
I'm also not the only one, see Brad's reply. Now it's 2 vs 2. Let's see who wins. Man, this is fun :) (if you failed to notice, I'm trying to be sarcastic)
I noticed, and am resisting the urge to be sarcastic in return.
 I understand everything you've said above, and I agree it works as  
 you've  said.
  But, the point you're missing is that the 'intent' of a statement is  
 not  necessarily the same as the effect generated by the compiler.
The 'intent' of statements has nothing to do with the language (even more so outside computers). And even so, if I see int result=cast(int)func(...); I interpret the so-called intent as "call func(); because it doesn't return int, cast the return value to int". I'll also bet that that would be the interpretation of the vast majority of programmers. Start a poll or something, if you don't believe me.
Sure, that's the literal meaning, and most people will agree, I do. But, the overall goal is to call a function and for it to result in an int. If cast(int)func causes the function that returns an int to be called then the overall goal has been achieved, just not in the exact manner you have described. As I said, it's the difference between effect and intent.
 This behaviour/method is generally accepted and understood by  
 programmers,  and extending it to include return values will be the  
 same, generally  accepted and understood.
Well, I'd say that if someone understands cast as mainly being the thing to select overloads, he's a newbie (no offense meant to newbies; I was once a newbie too and also believed many wrong things).
I never said "mainly".
 Further it requires no additional syntax and is easily parsable.
Why would no additional syntax for new functionality be a plus?
It depends on each situation, but generally speaking no new syntax means less to learn, less to parse etc.
 And why would it be more easily parsable than any other syntax?
Because it's already being parsed.
 If anything, it's harder - you need to figure out whether there is a  
 function call involved, and if so, if it has overloaded return types and  
 choose some completely different functionality based on the answers to  
 those questions.
That isn't parsing, it's process the results of parsing.
 no, it isn't secondary, just the opposite (and casting int to long is   
 converting, the first is 32 bits, the other is 64 bits).
It's not converting per-se, more extending and setting bits to 0.
Whatever.
I see you're no longer interested, so I'll stop now. Regan
Feb 22 2005
parent reply xs0 <xs0 xs0.com> writes:
  No, your example didn't show one, you described one being added 
 later.  All  I want is an example that compiles. Which should be 
 possible.
Bah, my example was obviously both parts of it.
Obviously? I think not.
It starts with "For example, consider:". It ends with "returning the unrounded square root" (plus the parenthesised comment). The next sentence is "The problem is that ...". I feel that it is very obvious that the example was both parts, but you seem to not agree. So be it.
 As I said, I want to fiddle with it.
Go ahead then. Fill what's missing, and fiddle with it. Why would anyone (including me) have to help you fiddle with something? You can hire me, of course, and I'll make you a really nice example to fiddle with. I'll even include several extra functions.
 I'm assuming you don't have access to the compiler from that universe? :)
Now you're being silly.
Yup, but so are you. I stated (in the next sentence; see above) that the example is an example of a problem with return type overloads.
 The compiler doesn't error because it cannot pick the overload
Well, it could pick one at random.
 , it errors, because Walter said it should, and for good reasons.  
 There's a large difference there.
I don't think so, given that the compiler will never and should never pick one at random.
I agree completely :) Now reread what I said..
 As for the purpose, if you say the purpose of casting can be overload  
 selection, then you'll also have to admit that there are also a large  
 number of other possible purposes to casting.
Name one.
Here's three: to round a number (cast(int)3.4) to just keep the lowest eight bits (cast(ubyte)12354) to decrease storage requirements (cast(float)3.4)
 But then, you can't argue that it is the best syntax for doing so,  
 because all those other purposes are just as valid, and casting is 
 not  somehow the natural choice.
I believe it is. Georg would agree (it seems).
Georg also said "Of course, changing the semantics of cast may, for all I know, raise other issues." But you conveniently ignored that.. Brad didn't agree, but you also conveniently ignored that..
 I give up, you're either ignoring my point, or you simply disagree, 
 either  way this is pointless.
Well, you're certainly ignoring a number of my points, so I agree that this is pointless.
 As above.
yup
 That's just ridiculous. "Cast by definition" has a well-defined 
 purpose  and it is not selecting overloads.
As above.
Now, this one still deserves a special attention, so I'll ask explicitly: do you or do you not agree that cast's only _defined_ purpose/functionality in this (or any other) language is that of type conversion?
 I simply asked you to provide one that compiled, and told you why I 
 wanted  it.
Well, like I said, you can hire me and then I can code whatever you want (as long as I'm able to, of course).
 Your code was not representing something that couldn't or shouldn't  
 compile, as I've said it showed the code 'before' adding the fictional  
 feature.
No, it wasn't "before" the fictional feature, it was "after" the fictional feature (I assume you mean overloaded return types). Only the first part was before the second part.
 Well, I'd say that if someone understands cast as mainly being the 
 thing  to select overloads, he's a newbie (no offense meant to 
 newbies; I was  once a newbie too and also believed many wrong things).
I never said "mainly".
No, you didn't use that word. But your argument is that cast is so often used to select overloads, that casting and overload selection are practically the same thing.
 Further it requires no additional syntax and is easily parsable.
Why would no additional syntax for new functionality be a plus?
It depends on each situation, but generally speaking no new syntax means less to learn, less to parse etc.
No, it means more to learn and harder to parse. You could also have all looping statements like this: loop {} (...); // do while statement loop (...) {} // while statement loop (...;...) {} // foreach statement loop (...;...;...) {} // for statement Are you claiming that is easier to learn?
 And why would it be more easily parsable than any other syntax?
Because it's already being parsed.
Not in this way.
 If anything, it's harder - you need to figure out whether there is a  
 function call involved, and if so, if it has overloaded return types 
 and  choose some completely different functionality based on the 
 answers to  those questions.
That isn't parsing, it's process the results of parsing.
Parse: To analyze a sentence or language statement. Parsing breaks down words into functional units that can be converted into machine language. For example, to parse the expression sum salary for title = "MANAGER" the word SUM must be identified as the primary command, FOR as a conditional search, TITLE as a field name and MANAGER as the data to be searched. So, obviously you need to know what that cast() does?
  It's not converting per-se, more extending and setting bits to 0.
Whatever.
I see you're no longer interested, so I'll stop now.
Well, considering how you failed to reply to the last part of my post, I'll assume you had no counter argument and that you agree.. xs0
Feb 22 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Wed, 23 Feb 2005 03:27:44 +0100, xs0 <xs0 xs0.com> wrote:
 As I said, I want to fiddle with it.
Go ahead then. Fill what's missing
I tried, I got to a point where, without knowing what you were trying to show, I couldn't decide what to use, which is why I asked you to do it.
 , and fiddle with it. Why would anyone (including me) have to help you  
 fiddle with something?
You don't 'have' to help. I figured you'd want to, to clarify your example for me. It appears I was wrong.
 The compiler doesn't error because it cannot pick the overload
Well, it could pick one at random.
 , it errors, because Walter said it should, and for good reasons.   
 There's a large difference there.
I don't think so, given that the compiler will never and should never pick one at random.
I agree completely :) Now reread what I said..
Done. I still don't agree that "there is a large difference there".
 As for the purpose, if you say the purpose of casting can be overload   
 selection, then you'll also have to admit that there are also a large   
 number of other possible purposes to casting.
Name one.
Here's three: to round a number (cast(int)3.4) to just keep the lowest eight bits (cast(ubyte)12354) to decrease storage requirements (cast(float)3.4)
Excellent. Thanks. I hadn't thought of any of them. So you agree that there is a purpose to casting, other than to simply change the format of the data?
 But then, you can't argue that it is the best syntax for doing so,   
 because all those other purposes are just as valid, and casting is  
 not  somehow the natural choice.
I believe it is. Georg would agree (it seems).
Georg also said "Of course, changing the semantics of cast may, for all I know, raise other issues." But you conveniently ignored that..
I'm trying to think of any and waiting for someone else to think of some.
 Brad didn't agree, but you also conveniently ignored that..
I am conversing with brad as we speak. How am I ignoring him?
 I give up, you're either ignoring my point, or you simply disagree,  
 either  way this is pointless.
Well, you're certainly ignoring a number of my points, so I agree that this is pointless.
I am disagreeing with several of your points.
 As above.
yup
 That's just ridiculous. "Cast by definition" has a well-defined  
 purpose  and it is not selecting overloads.
As above.
Now, this one still deserves a special attention, so I'll ask explicitly: do you or do you not agree that cast's only _defined_ purpose/functionality in this (or any other) language is that of type conversion?
Only purpose, disagree. It has several purposes, you've supplied some above. It's functionality, or effect.. agree. It does type conversion.
 I simply asked you to provide one that compiled, and told you why I  
 wanted  it.
Well, like I said, you can hire me and then I can code whatever you want (as long as I'm able to, of course).
So, you have no desire to provide an example to back up your statement, fine.
 Your code was not representing something that couldn't or shouldn't   
 compile, as I've said it showed the code 'before' adding the fictional   
 feature.
No, it wasn't "before" the fictional feature, it was "after" the fictional feature (I assume you mean overloaded return types). Only the first part was before the second part.
Just so we're talking about the same example, it went: int mysqrt(double a) { return floor(sqrt(a)); } double eval_some_cost(...) { double cost=sqrt(..); if (something) cost*=1.3; return cost; } where is the overloaded return type? where is the cast(type) or other syntax of this feature? What I am asking for is the above example to compile, either working, or showing the collision error similar to passing parameters. You also talk about adding another function, I'd like to see that function also, and explore how it can best be: 1. detected 2. solved.
 Well, I'd say that if someone understands cast as mainly being the  
 thing  to select overloads, he's a newbie (no offense meant to  
 newbies; I was  once a newbie too and also believed many wrong things).
I never said "mainly".
No, you didn't use that word. But your argument is that cast is so often used to select overloads, that casting and overload selection are practically the same thing.
No, that was not my argument. I never said "often".
 Further it requires no additional syntax and is easily parsable.
Why would no additional syntax for new functionality be a plus?
It depends on each situation, but generally speaking no new syntax means less to learn, less to parse etc.
No, it means more to learn and harder to parse.
Assuming you learn that to resolve an overload collision for parameters requires a cast, it's natural to apply the same learning to overload collision for return type. The parsing is identical, as the syntax is identical. The additional effort/code is in the name resoultion system.
 You could also have all looping statements like this:

 loop {} (...); // do while statement
 loop (...) {} // while statement
 loop (...;...) {} // foreach statement
 loop (...;...;...) {} // for statement

 Are you claiming that is easier to learn?
No.
 And why would it be more easily parsable than any other syntax?
Because it's already being parsed.
Not in this way.
I think we have a different definition of "parse". To me, parse means take the text and break it into tokens.
 If anything, it's harder - you need to figure out whether there is a   
 function call involved, and if so, if it has overloaded return types  
 and  choose some completely different functionality based on the  
 answers to  those questions.
That isn't parsing, it's process the results of parsing.
Parse: To analyze a sentence or language statement. Parsing breaks down words into functional units that can be converted into machine language. For example, to parse the expression sum salary for title = "MANAGER" the word SUM must be identified as the primary command, FOR as a conditional search, TITLE as a field name and MANAGER as the data to be searched.
Yep, that's my definition
 So, obviously you need to know what that cast() does?
No, you just need to identify it as a cast. Parsing "breaks down words into functional units", the phase that does the "converted into machine language" comes after the parsing.
  It's not converting per-se, more extending and setting bits to 0.
Whatever.
I see you're no longer interested, so I'll stop now.
Well, considering how you failed to reply to the last part of my post, I'll assume you had no counter argument and that you agree..
You could, that would be illogical. Regan
Feb 22 2005
parent reply xs0 <xs0 xs0.com> writes:
The depth of this thread is becoming rediculous. Maybe we should take 
this to e-mail?

 I tried, I got to a point where, without knowing what you were trying 
 to  show, I couldn't decide what to use, which is why I asked you to do it.
int a() { return 1; } double a() { return 1.5; } // this is the second part double b() { double tmp=a(); return a*2; } happy? :)
 Done. I still don't agree that "there is a large difference there".
There is. You said the compiler errors because it cannot pick an overload. I said that the compiler errors because it doesn't want to pick an overload.
 As for the purpose, if you say the purpose of casting can be 
 overload   selection, then you'll also have to admit that there are 
 also a large   number of other possible purposes to casting.
Name one.
Here's three: to round a number (cast(int)3.4) to just keep the lowest eight bits (cast(ubyte)12354) to decrease storage requirements (cast(float)3.4)
Excellent. Thanks. I hadn't thought of any of them. So you agree that there is a purpose to casting, other than to simply change the format of the data?
No, I don't agree those are purposes, those are side-effects. I said YOU'll have to agree that there are many purposes with reference to the next statement:
 But then, you can't argue that it is the best syntax for doing so,   
 because all those other purposes are just as valid, and casting is  
 not  somehow the natural choice.
 I'm trying to think of any and waiting for someone else to think of some.
Of course, whatever I write is not an issue, because you said so.
 Brad didn't agree, but you also conveniently ignored that..
I am conversing with brad as we speak. How am I ignoring him?
Well, in your summary of the current score, you only mentioned Georg but not Brad. And I never said you ignored Brad.
 Only purpose, disagree. It has several purposes, you've supplied some  
 above. It's functionality, or effect.. agree. It does type conversion.
You skipped DEFINED purpose, even though I emphasized it.
 Just so we're talking about the same example, it went:
 [snip]
 
 where is the overloaded return type?
 where is the cast(type) or other syntax of this feature?
The overloaded return type is in the statement following this code. And it was in reply to what Derek wrote:
 The main [issue] seems to be, what would happen if someone coded a function
 call but did not assign the return to anything? How would the compiler know
 which signature to use if it had to use return-type as a determinate? 
So I don't see what it has to do with cast or other syntax of this feature.
 No, that was not my argument. I never said "often".
Yeah, really funny. So, what exactly is your argument that makes cast() the best possible syntax for selecting an overloaded return type?
 No, it means more to learn and harder to parse.
Assuming you learn that to resolve an overload collision for parameters requires a cast, it's natural to apply the same learning to overload collision for return type.
Resolving an overload collision doesn't require a cast, it's just one way of doing it. Casting the result when there isn't an overload is natural, because casting works the same everywhere. What isn't natural is using cast syntax for something completely different.
 You could also have all looping statements like this:

 loop {} (...); // do while statement
 loop (...) {} // while statement
 loop (...;...) {} // foreach statement
 loop (...;...;...) {} // for statement

 Are you claiming that is easier to learn?
No.
So having the same keyword for looping constructs is not easier to learn, but having the same keyword for both casting types and selection of return-overloaded functions is easier to learn?
 I think we have a different definition of "parse".
 To me, parse means take the text and break it into tokens.
Nope, that's lexing (or lexical analysis, if you want to look it up).
 Parse: To analyze a sentence or language statement. Parsing breaks 
 down  words into functional units that can be converted into machine 
 language.  For example, to parse the expression sum salary for title = 
 "MANAGER"  the word SUM must be identified as the primary command, FOR 
 as a  conditional search, TITLE as a field name and MANAGER as the 
 data to be  searched.
Yep, that's my definition
No, it isn't. It says "Parsing breaks down words into functional units", what you're saying is "Parsing breaks input into words". Also notice how SUM is already identified as a command at this stage, even before machine language is produced.
 Whatever.
I see you're no longer interested, so I'll stop now.
Well, considering how you failed to reply to the last part of my post, I'll assume you had no counter argument and that you agree..
You could, that would be illogical.
It's also illogical to claim I was no longer interested (considering the amount of text after "Whatever"), but you still did.. xs0
Feb 22 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Wed, 23 Feb 2005 04:58:15 +0100, xs0 <xs0 xs0.com> wrote:
 The depth of this thread is becoming rediculous. Maybe we should take  
 this to e-mail?

 I tried, I got to a point where, without knowing what you were trying  
 to  show, I couldn't decide what to use, which is why I asked you to do  
 it.
int a() { return 1; } double a() { return 1.5; } // this is the second part double b() { double tmp=a(); return a*2; } happy? :)
Yes, thank you. Small modification: int a() { return 1; } double a() { return 1.5; } // this is the second part double c() { double tmp=a(); return tmp*2; } void main() { printf("%f\n",c()); } ok? The compiler gives: ex3.d(2): function ex3.a conflicts with ex3.a at ex3.d(1) (the file was called ex3.d) Weren't you arguing this would happen silently, and cause a bug? I expected/hoped it to complain, as it does. As long as it does error, then the programmer is forced to consider the problem, and add a solution. Following my example below, I believe the programmer would attempt to use cast().
 Done. I still don't agree that "there is a large difference there".
There is. You said the compiler errors because it cannot pick an overload. I said that the compiler errors because it doesn't want to pick an overload.
I agree, however, given that there is a approaching 0 chance of a compiler wanting to pick one based on no information, i.e. random, they're functionally equivalent, right? Regardless, this is a side-argument, and not relevant to the main argument, so lets just agree to disagree, ok?
 As for the purpose, if you say the purpose of casting can be  
 overload   selection, then you'll also have to admit that there are  
 also a large   number of other possible purposes to casting.
Name one.
Here's three: to round a number (cast(int)3.4) to just keep the lowest eight bits (cast(ubyte)12354) to decrease storage requirements (cast(float)3.4)
Excellent. Thanks. I hadn't thought of any of them. So you agree that there is a purpose to casting, other than to simply change the format of the data?
No, I don't agree those are purposes, those are side-effects. I said YOU'll have to agree that there are many purposes with reference to the next statement:
 But then, you can't argue that it is the best syntax for doing so,    
 because all those other purposes are just as valid, and casting is   
 not  somehow the natural choice.
 I'm trying to think of any and waiting for someone else to think of  
 some.
Of course, whatever I write is not an issue, because you said so.
No, I am listening to what you write, and replying, I just disagree, how am I ignroing you?
 Brad didn't agree, but you also conveniently ignored that..
I am conversing with brad as we speak. How am I ignoring him?
Well, in your summary of the current score, you only mentioned Georg but not Brad. And I never said you ignored Brad.
It wasn't a summary, I was simply responding to your statement that no-one would agree with me, I thought 1 example was enough.
 Only purpose, disagree. It has several purposes, you've supplied some   
 above. It's functionality, or effect.. agree. It does type conversion.
You skipped DEFINED purpose, even though I emphasized it.
I am not saying that cast's main, or defined purpose isn't to convert a variable. Let me re-state my argument, I don't think it was presented in a concise manner... What I'm saying is, that, for example, if you have: void foo(long a){} void foo(float a){} void main() { foo(5); } The compiler gives the error: ex4.d(5): function ex4.foo overloads void(long a) and void(float a) both match argument list for foo And to solve it, you go: foo(cast(int)5); or foo(cast(float)5); depending on what one you want, or alternately you realise there is a bug and fix it in another way. The above is the commonly accepted method of resolving an overload conflict, in D (and other languages) I think it's a natural extension that if you had: long foo(int a){} float foo(int a){} void main() { int a; a = foo(5); } and the compiler gave the error: ex4.d(5): function ex4.foo overloads long(int a) and float(int a) both match return type for foo (this exact error doesn't exist, because it doesn't consider return types, instead we get an error like your example showed above) the programmers natural response, if they have encountered the first example above, would be to use cast again eg. a = cast(long)foo(5); a = cast(float)foo(5);
 Just so we're talking about the same example, it went:
 [snip]
  where is the overloaded return type?
 where is the cast(type) or other syntax of this feature?
The overloaded return type is in the statement following this code. And it was in reply to what Derek wrote:
Right, I just wanted it in code, so I could see exactly what you meant, you've given me that now, I'm happy. :)
 The main [issue] seems to be, what would happen if someone coded a  
 function
 call but did not assign the return to anything? How would the compiler  
 know
 which signature to use if it had to use return-type as a determinate?
So I don't see what it has to do with cast or other syntax of this feature.
Who wrote the above? I'd say if the return-type was indeterminate it would be an error requiring cast(type) to resolve. eg. int foo() {} double foo() {} void main() { foo(); //error }
 No, that was not my argument. I never said "often".
Yeah, really funny. So, what exactly is your argument that makes cast() the best possible syntax for selecting an overloaded return type?
See above. I have restated it for clarity.
 No, it means more to learn and harder to parse.
Assuming you learn that to resolve an overload collision for parameters requires a cast, it's natural to apply the same learning to overload collision for return type.
Resolving an overload collision doesn't require a cast, it's just one way of doing it.
Sure, but it's the commonly accepted way for D.
 Casting the result when there isn't an overload is natural, because  
 casting works the same everywhere.
Sure, I'm not disagreeing here.
 What isn't natural is using cast syntax for something completely  
 different.
My argument is that it's not completely different, see my reasoning above with the example.
 You could also have all looping statements like this:

 loop {} (...); // do while statement
 loop (...) {} // while statement
 loop (...;...) {} // foreach statement
 loop (...;...;...) {} // for statement

 Are you claiming that is easier to learn?
No.
So having the same keyword for looping constructs is not easier to learn, but having the same keyword for both casting types and selection of return-overloaded functions is easier to learn?
The difference between cast and your loop example is that the syntax above changes depending on the purpose, the cast syntax doesn't, basically I don't think it's a fair analogy. Who knows, perhaps having only one keyword would have been easier? It would have made searching for loops easier, regardless, this is another argument entirely.
 I think we have a different definition of "parse".
 To me, parse means take the text and break it into tokens.
Nope, that's lexing (or lexical analysis, if you want to look it up).
It seems the same as the definition below, to me.
 Parse: To analyze a sentence or language statement. Parsing breaks  
 down  words into functional units that can be converted into machine  
 language.  For example, to parse the expression sum salary for title =  
 "MANAGER"  the word SUM must be identified as the primary command, FOR  
 as a  conditional search, TITLE as a field name and MANAGER as the  
 data to be  searched.
Yep, that's my definition
No, it isn't. It says "Parsing breaks down words into functional units", what you're saying is "Parsing breaks input into words".
I said "take the text and break it into tokens". text == words. tokens == functional units. as in, it splits the text, gets "cast", "(", "type", ")" and turns "cast" into tokCAST or whatever it uses internally to represent it.
 Also notice how SUM is already identified as a command at this stage,  
 even before machine language is produced.
Sure, just as cast would be, right? As in, it knows it's a cast, but doesn't necessarily know what it has to do about it yet, as that comes later, yes?
 Whatever.
I see you're no longer interested, so I'll stop now.
Well, considering how you failed to reply to the last part of my post, I'll assume you had no counter argument and that you agree..
You could, that would be illogical.
It's also illogical to claim I was no longer interested (considering the amount of text after "Whatever"), but you still did..
I was simply stating my impression, IMO the argument was degrading into a slagging match, and had drifted off point, I think we've fixed it, above. Regan
Feb 23 2005
parent reply xs0 <xs0 xs0.com> writes:
 Weren't you arguing this would happen silently, and cause a bug?
No.
 I agree, however, given that there is a approaching 0 chance of a 
 compiler  wanting to pick one based on no information, i.e. random, 
 they're  functionally equivalent, right?
No, there could be a large amount of rules to make the process totally deterministic (like in C++), but D's design says screw that, it's an error.
 Regardless, this is a side-argument, and not relevant to the main  
 argument, so lets just agree to disagree, ok?
Sure, whatever you want.
 It wasn't a summary, I was simply responding to your statement that 
 no-one  would agree with me, I thought 1 example was enough.
My statement was actually
 But then, you can't argue that it is the best syntax for doing
 so,  because all those other purposes are just as valid, and
 casting is not  somehow the natural choice.
Where does it say no-one would agree with you?
 Let me re-state my argument, I don't think it was presented in a 
 concise  manner...
 
 What I'm saying is, that, for example, if you have:
 [snip]
 
 And to solve it, you go:
   foo(cast(int)5);
 or
   foo(cast(float)5);
 
 depending on what one you want, or alternately you realise there is a 
 bug  and fix it in another way.
 
 The above is the commonly accepted method of resolving an overload  
 conflict, in D (and other languages)
Well, at least in Java that works just fine (foo(5), that is), and AFAIK in C++ as well, so forcing the user to resolve overload ambiguities is D-only. And I'd certainly resolve it with foo(5L) // or foo(5.0) not with cast()..
 I think it's a natural extension that if you had:
 
 long foo(int a){}
 float foo(int a){}
 void main() {int a=foo(5); }
 
 and the compiler gave the error:
 [snip]
 the programmers natural response, if they have encountered the first  
 example above, would be to use cast again eg.
 
 a = cast(long)foo(5);
 a = cast(float)foo(5);
Except that the second case wouldn't work (and the first one should't as well, because long shouldn't be implicitly castable to int), because when you say cast(float) you get a float, not an int, even if you managed to explain to the compiler which function you wanted to call. So even you made an error because of your syntax, showing that it's not a good syntax for this kind of functionality...
 So I don't see what it has to do with cast or other syntax of this  
 feature.
Who wrote the above?
Why do I need to repeat everything? Derek wrote that.
 I'd say if the return-type was indeterminate it 
 would  be an error requiring cast(type) to resolve. eg.
 
 int foo() {}
 double foo() {}
 
 void main() {
   foo();    //error
 }
This has nothing to do with anything. Go read what was said if you want to comment on it.
 What isn't natural is using cast syntax for something completely  
 different.
My argument is that it's not completely different, see my reasoning above with the example.
But your reasoning is wrong. The reason cast works for overload selection with parameters is because it changes the type of those parameters, that is what cast does and you agreed that that is what cast does. The compiler doesn't see cast(int) and figures out you're trying to call the int function, it sees a parameter of type int and decides there is no ambiguity (i.e. it doesn't care if you cast it or do anything else with it, it just cares about its type). void foo(int a) {} void foo(double a) {} void main() { int a=5; foo(a); } This works, because a is already of type int. Walter was just nice enough to handle constants slightly differently, because 5 is both an int and a double, until you specify what it is in some manner (by assigning it to a typed variable, or casting it, or whatnot). If you ask the compiler to select a function because there is a cast() in front of the call, you're not changing the type of anything and it is completely different.
 The difference between cast and your loop example is that the syntax 
 above  changes depending on the purpose, the cast syntax doesn't, 
 basically I  don't think it's a fair analogy.
I was merely replying to your claim that less syntax is easier to learn.
 Nope, that's lexing (or lexical analysis, if you want to look it up).
It seems the same as the definition below, to me.
Well, everyone else seems to think that there is a difference between lexical analysis and parsing..
 No, it isn't. It says "Parsing breaks down words into functional 
 units",  what you're saying is "Parsing breaks input into words".
I said "take the text and break it into tokens". text == words. tokens == functional units.
no, it's actually like this: input/text: "cast(type)(a+b+c)" | LEXING | V words/tokens = cast, (, "type", ), (, "a", +, "b", +, "c", ) | PARSING | V CAST_EXPR [ type: TYPE [ identifier: "type" ] value:SUM [ left-side: SUM [ left-side: VAR[identifier:"a"] right-side:VAR[identifier:"b"] ] right-side:VAR[identifier:"c"] ] ] (the latter is just one way of expressing what parsing produces, its typically a so-called abstract syntax tree (AST), but I don't have time to draw ASCII diagrams to represent it graphically). This is the first time it knows a cast is involved. It also knows that "type" is supposed to be a type identifier, that a sum will happen, and that variables a, b and c are referenced. What follows is usually semantic analysis, optimization (high-level), compilation, another optimization (on machine code) and linking. You see how "(" is not a functional unit? It's a token, and it even gets discarded in the parsing process, because it's not needed anymore. If you still don't believe me, go to dmd/src/dmd and check lexer.c and parse.c
 As in, it knows it's a cast, but doesn't necessarily know what it has 
 to  do about it yet, as that comes later, yes?
No, after just doing lexing it doesn't know it's a cast. xs0
Feb 23 2005
parent "Regan Heath" <regan netwin.co.nz> writes:
On Thu, 24 Feb 2005 02:20:42 +0100, xs0 <xs0 xs0.com> wrote:
 What isn't natural is using cast syntax for something completely   
 different.
My argument is that it's not completely different, see my reasoning above with the example.
But your reasoning is wrong.
I don't think so, and nothing you have said has convinced me. Sounds like it's time to agree to disagree as they say, we're getting nowhere. Regan
Feb 23 2005
prev sibling parent reply Georg Wrede <georg.wrede nospam.org> writes:
Regan Heath wrote:
 On Tue, 22 Feb 2005 01:26:44 +0100, xs0 <xs0 xs0.com> wrote:
 
 This whole issue comes down to where you draw the line, where you  
 balance  convenience vs pedanticism. (if that aint a word it should  
 be). Walter has  drawn it in one place, almost everyone disagrees in  
 some way, large or  small.
True. But then we might not always be able to see the real reason. After all, his CV looks better than ours! Also, some of the reasons might take a lot of explaining, especially if they're subtle. All that is time away from actually writing the D compiler. .-) And sometimes it may be just as simple as a feature demanding more research and writing from Walter, than what it's worth.
 I certainly don't disagree with Walter; actually I think D is the 
 best  language I've ever seen (at least in the 
 compiled-to-native-code  category).
Me too! And an ever increasing number of my friends here!
 Well, considering how opCast is the only problem where not having  
 return-type-overloads is really an issue, perhaps it could just get  
 special treatment and the problem'd be solved?
FTR: I still have no opinion for or against overloading on return type. Can we find examples where not having it produces cumbersome code compared with having RTO?
Feb 22 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Tue, 22 Feb 2005 10:23:16 +0200, Georg Wrede <georg.wrede nospam.org>  
wrote:
 Regan Heath wrote:
 On Tue, 22 Feb 2005 01:26:44 +0100, xs0 <xs0 xs0.com> wrote:

 This whole issue comes down to where you draw the line, where you   
 balance  convenience vs pedanticism. (if that aint a word it should   
 be). Walter has  drawn it in one place, almost everyone disagrees in   
 some way, large or  small.
True. But then we might not always be able to see the real reason. After all, his CV looks better than ours!
Well, in most cases I tend to agree with Walter, but, it's not due to his CV.
 Also, some of the reasons
 might take a lot of explaining, especially if they're subtle. All
 that is time away from actually writing the D compiler.   .-)
True, and we don't want that. :0)
 And sometimes it may be just as simple as a feature demanding more
 research and writing from Walter, than what it's worth.
True, which is why it's up to us to attempt to show how useful it can be, then he has some idea whether it's worth looking at.
 I certainly don't disagree with Walter; actually I think D is the  
 best  language I've ever seen (at least in the  
 compiled-to-native-code  category).
Me too! And an ever increasing number of my friends here!
I think most people here, are here for this very reason. Or they think D will become the best and want to follow it's evolution.
 Well, considering how opCast is the only problem where not having   
 return-type-overloads is really an issue, perhaps it could just get   
 special treatment and the problem'd be solved?
FTR: I still have no opinion for or against overloading on return type.
Fair enough.
 Can we find examples where not having it produces cumbersome code
 compared with having RTO?
Lets use opCast as an example. I think writing a class which you want to be convertable to several different basic types is a case where this would be useful, for example... class SomeNewType { int opCast() {} float opCast() {} } void main() { SomeNewType p = new SomeNewType(); int a; float b; a = p; //error cannot implicitly cast from SomeNewType to int b = p; //error cannot implicitly cast from SomeNewType to float a = cast(int)p; //ok b = cast(float)p; //ok } to achieve this, now, you need to use a seperate function for each i.e. class SomeNewType { int toInt() {} float toFloat() {} } Not bad per-se. But, it leaves one wondering what the point of opCast is. Maybe the rationale is that you should only need 1 opCast, as in you cast to x, and all the other types you need should be castable from that type? If so, then why does int cast to more than 1 type? surely these cases are comparable? Lets consider it when writing a template, i.e. generic programming. template foo(T) { void foo(T p) { int a = cast(int)p; //would be possible int a = p.toInt(); //ok } } but lets go further and try... template foo(T, NT) { void foo(T p, out NT q) { q = cast(typeof(NT))p; //would be possible q = p.to???(); //impossible? } } Granted, it seems reasonably rare for most programs to need to do this, there may be workarounds? Another possible use is optimisation, it may be possible for your class to return types x, y and z more efficiently than returning x and having something else convert from x to y or x to z. i.e. maybe you're caching those values. or simply producing them from scratch is faster than converting from an existing value of another type. Overall I think this is a minor feature, and maybe it's more work than it's worth. But then, maybe someone can come up with a really good use for it. I've done my best to lay it all out for everyone as I see it. Regan
Feb 22 2005
parent Derek Parnell <derek psych.ward> writes:
On Wed, 23 Feb 2005 11:36:27 +1300, Regan Heath wrote:

 On Tue, 22 Feb 2005 10:23:16 +0200, Georg Wrede <georg.wrede nospam.org>  
 wrote:
 Regan Heath wrote:
 On Tue, 22 Feb 2005 01:26:44 +0100, xs0 <xs0 xs0.com> wrote:

 This whole issue comes down to where you draw the line, where you   
 balance  convenience vs pedanticism. (if that aint a word it should   
 be). Walter has  drawn it in one place, almost everyone disagrees in   
 some way, large or  small.
True. But then we might not always be able to see the real reason. After all, his CV looks better than ours!
Well, in most cases I tend to agree with Walter, but, it's not due to his CV.
 Also, some of the reasons
 might take a lot of explaining, especially if they're subtle. All
 that is time away from actually writing the D compiler.   .-)
True, and we don't want that. :0)
 And sometimes it may be just as simple as a feature demanding more
 research and writing from Walter, than what it's worth.
True, which is why it's up to us to attempt to show how useful it can be, then he has some idea whether it's worth looking at.
 I certainly don't disagree with Walter; actually I think D is the  
 best  language I've ever seen (at least in the  
 compiled-to-native-code  category).
Me too! And an ever increasing number of my friends here!
I think most people here, are here for this very reason. Or they think D will become the best and want to follow it's evolution.
 Well, considering how opCast is the only problem where not having   
 return-type-overloads is really an issue, perhaps it could just get   
 special treatment and the problem'd be solved?
FTR: I still have no opinion for or against overloading on return type.
Fair enough.
 Can we find examples where not having it produces cumbersome code
 compared with having RTO?
Lets use opCast as an example. I think writing a class which you want to be convertable to several different basic types is a case where this would be useful, for example... class SomeNewType { int opCast() {} float opCast() {} } void main() { SomeNewType p = new SomeNewType(); int a; float b; a = p; //error cannot implicitly cast from SomeNewType to int b = p; //error cannot implicitly cast from SomeNewType to float a = cast(int)p; //ok b = cast(float)p; //ok } to achieve this, now, you need to use a seperate function for each i.e. class SomeNewType { int toInt() {} float toFloat() {} } Not bad per-se. But, it leaves one wondering what the point of opCast is. Maybe the rationale is that you should only need 1 opCast, as in you cast to x, and all the other types you need should be castable from that type? If so, then why does int cast to more than 1 type? surely these cases are comparable? Lets consider it when writing a template, i.e. generic programming. template foo(T) { void foo(T p) { int a = cast(int)p; //would be possible int a = p.toInt(); //ok } } but lets go further and try... template foo(T, NT) { void foo(T p, out NT q) { q = cast(typeof(NT))p; //would be possible q = p.to???(); //impossible? } } Granted, it seems reasonably rare for most programs to need to do this, there may be workarounds? Another possible use is optimisation, it may be possible for your class to return types x, y and z more efficiently than returning x and having something else convert from x to y or x to z. i.e. maybe you're caching those values. or simply producing them from scratch is faster than converting from an existing value of another type. Overall I think this is a minor feature, and maybe it's more work than it's worth. But then, maybe someone can come up with a really good use for it. I've done my best to lay it all out for everyone as I see it. Regan
I could use such a feature today (yesterday actually). -- Derek Melbourne, Australia 23/02/2005 9:39:53 AM
Feb 22 2005
prev sibling parent Georg Wrede <georg.wrede nospam.org> writes:
Regan Heath wrote:
 In other words, implicit conversion causes the problem, and it's a  
 seperate issue to solve, adding this new explicit function selection 
 will  not add new sources of bugs, they're already there.
This reveals a useful use for overloading on return type. One could explicitly disallow certain cases: real foo(sometype z) {whatever} int foo(sometype z) {assert(0)} real x; x = foo(bla); // ok int y; y = foo(bla); // Assertion failed. The overloading mechanism should of course first search the overloads extensively, before any implicit conversions. Actually, we might even decide that for any function that has return type overload, _no implicit conversions are allowed_.
Feb 22 2005
prev sibling parent reply Derek <derek psych.ward> writes:
On Mon, 21 Feb 2005 14:03:04 +0100, xs0 wrote:

 The main one seems to be, what would happen if someone coded a function
 call but did not assign the return to anything? How would the compiler know
 which signature to use if it had to use return-type as a determinate?
Well, that's not the only "main" issue, imho.. For example, consider: int mysqrt(double a) { return floor(sqrt(a)); } double eval_some_cost(...) { double cost=sqrt(..); if (something) cost*=1.3; return cost; } Now, this works fine and all. But, I (or my co-worker) can decide sometime in the future that there should also be a double mysqrt(double) returning the unrounded square root (or even something completely different, but that is bad style, I guess). The problem is that without any change in either the int version or in eval_some_cost(), the code will produce different results. This is most probably the reason why ambiguous function calls are not allowed and you need to be explicit which version you want to use.
I understand what you are saying. However, I imagine it would work in a similar manner to function signature matching does today. That is it would have to exactly match or an error is produced. Consider what it currently does... void foo(real x) {} void main() { foo(5); } This compiles okay as the compiler silently (implictly) converts the integer 5 to a real 5 before calling the foo routine. But, to paraphrase a collegue "But, I (or my co-worker) can decide sometime in the future that there should also be an foo(int)", such as ... void foo(real x) {} void foo(int x) {} void main() { foo(5); } Now the compiler complains because of the ambiguity. So if return type was included in the deal, your example would also have the compiler complaining when the new double mysqrt() was included into the mix.
 
 For example...
    cast(int)foo('x');  // Call the 'int' version and ignore the result.
    bar( cast(real)foo('y') ); // Call the 'real' version of foo and bar.
Hmm, but there's no casting, so why use the cast() operator? I'd like better something along the lines of
Because it was just an example! I'm not seriously saying *this must be the syntax*. But I did note that you understood what I was getting at ;-)
 foo:int('x');
 bar(foo:real('y'));
Maybe ... foo('x'):int; bar( foo('y'):real; ); not that it matters too much (because Walter will *never* implement it anyway - too much against his world-view, I suspect).
 And cast(type)obj can almost be directly translated to obj.opCast:type() 
 (almost because implicit casts should still be allowed when there's no 
 ambiguity). Ambiguity should be defined differently for return types, 
 though - the above problem is still not solved, even if there is an 
 exact match (actually, precisely because of the exact match).
Sorry, but I don't follow you here. What do you mean by "the above problem is still not solved, ..."? If the coder explicitly tells the compiler which version of the function to call, what is the problem?
 So, I'd say that if there are return types A and B, and either can be 
 implicitly casted to the other, the caller should be forced to specify 
 which is the right one when using either.
Yes, exactly.
 That's ok, though; even if you write A a=func(), it actually doesn't 
 mean that func() should return something of type A, it just means that 
 you'd like the compiler to implicitly cast it to A. 
If it is possible, but this is a different thing to getting the compiler to sort out which of the possible function signatures to select. Which is what I'm talking about.
 This logic is 
 actually the same as with double a=2/3 -- you'll still get 0, because 
 the casting gets done on assignment, not evaluation..
True, but what has this got to do with function selection? I'm missing the point I think, sorry. -- Derek Melbourne, Australia
Feb 21 2005
parent xs0 <xs0 xs0.com> writes:
 Now the compiler complains because of the ambiguity. 
It does? That's great, actually. My guess, however, is that it only does so because a constant was used. If it was a variable, it wouldn't (or maybe I'm forgetting something?)
 Maybe ...
    foo('x'):int;
    bar( foo('y'):real; );
 
 not that it matters too much (because Walter will *never* implement it
 anyway - too much against his world-view, I suspect).
Well, I agree it's best not to implement it (see my other post), but to explain my syntax - my first thought was foo.int('x'), which somehow makes sense to me, but there's two problems - int has to be looked up to determine whether it is a type (which is not a problem with basic types, but is with everything else), and furthermore, if you have a type within a type, you can't be sure what is what. is a.b.c "a returning b.c" or "a.b returning c"? so, I just replaced the dot with : to avoid both problems (and, it has a similar meaning as in template(T : Q), which is specialization)
 Sorry, but I don't follow you here. What do you mean by "the above problem
 is still not solved, ..."? If the coder explicitly tells the compiler which
 version of the function to call, what is the problem?
If the coder does that, there is no problem. The "above problem" is that perhaps he didn't at some point in time, because there was nothing to specify (i.e. there was only one method). Later, another method was added, and because the return type exactly matched the inferred return type (_double_ a = mysqrt(..)), another method was chosen and it was the wrong one.
 If it is possible, but this is a different thing to getting the compiler to
 sort out which of the possible function signatures to select. Which is what
 I'm talking about.
Well, you said "To counter this, one could make the rule that every call to a function must either assign the result or [snip]". I think that just assigning the result is not indicative enough..
 [snip: double a=2/3]
True, but what has this got to do with function selection? I'm missing the point I think, sorry.
The point was that the type you're assigning the result to doesn't have any influence on how that result is produced, which is good - you don't want 2/3 to mean 0 sometimes and 0.66666 other times. The same goes for methods (i.e. you don't want another method to be chosen, just because the result will be assigned to some other type). xs0
Feb 21 2005
prev sibling parent reply Chris Sauls <ibisbasenji gmail.com> writes:
I still think the signature for opCast should change to something like:
   bit opCast(inout T result);

Return 'true' on success, 'false' on failure (could not cast for some 
reason).  Then we could have as many opCasts as we need.

My apologies if this has come up already.

-- Chris S
Feb 22 2005
next sibling parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Tue, 22 Feb 2005 16:53:50 -0600, Chris Sauls <ibisbasenji gmail.com>  
wrote:
 I still think the signature for opCast should change to something like:
    bit opCast(inout T result);

 Return 'true' on success, 'false' on failure (could not cast for some  
 reason).  Then we could have as many opCasts as we need.
Interesting.. how would you use it? same as other types i.e. cast(Type)object; if so, where/how do you get the return value? I assume the inout gets assigned to the lhs?
 My apologies if this has come up already.
I don't remember it, but I've not been here as long as some, and my memory isn't perfect. Regan
Feb 22 2005
parent reply Chris Sauls <ibisbasenji gmail.com> writes:
Regan Heath wrote:
 if so, where/how do you get the return value?
 I assume the inout gets assigned to the lhs?
You assume right. My thought-train went something like: class Foo { private int value; bit opCast(inout int result) { result = value; return true; } bit opCast(inout float result) { return false; } } int i, j; float f; Foo foo = new Foo; i = cast(int) foo; // foo.opCast(i); j = (cast(int) foo) + 5; // int tmp; foo.opCast(foo); j = tmp + 5; f = cast(float) foo; // throws an exception (opCast returned false) I'm not sure how I feel about the tmp variable, but D already does similar things to implement some other features. Such as the auto-generated function "literal" that Walter recently mentioned foreach loops generate. (If I remember that right.) -- Chris S
Feb 22 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Tue, 22 Feb 2005 17:19:32 -0600, Chris Sauls <ibisbasenji gmail.com>  
wrote:
 Regan Heath wrote:
 if so, where/how do you get the return value?
 I assume the inout gets assigned to the lhs?
You assume right. My thought-train went something like: class Foo { private int value; bit opCast(inout int result) { result = value; return true; } bit opCast(inout float result) { return false; } } int i, j; float f; Foo foo = new Foo; i = cast(int) foo; // foo.opCast(i); j = (cast(int) foo) + 5; // int tmp; foo.opCast(foo); j = tmp + 5;
Typo? did you mean foo.opCast(tmp)?
 f = cast(float) foo;      // throws an exception (opCast returned false)
Ok, so this is different behaviour to when you cast a class to an incorrect base eg. class A {} class B {} B b = new B(); A a; a = cast(A)b; //a == null after this call, no exception thrown. Could it, should it be the same? eg. opCast returns false, set lhs to typeof(lhs).init?
 I'm not sure how I feel about the tmp variable, but D already does  
 similar things to implement some other features.  Such as the  
 auto-generated function "literal" that Walter recently mentioned foreach  
 loops generate.  (If I remember that right.)
Couldn't the tmp be optimised away? Regan
Feb 22 2005
parent reply Chris Sauls <ibisbasenji gmail.com> writes:
Regan Heath wrote:
 i = cast(int) foo;        // foo.opCast(i);
 j = (cast(int) foo) + 5;  // int tmp; foo.opCast(foo); j = tmp + 5;
Typo? did you mean foo.opCast(tmp)?
Woops. I did.
 f = cast(float) foo;      // throws an exception (opCast returned false)
Ok, so this is different behaviour to when you cast a class to an incorrect base eg. class A {} class B {} B b = new B(); A a; a = cast(A)b; //a == null after this call, no exception thrown.
Or you could set the result param to null and return true, to mimic the language-defined cast.
 
 Could it, should it be the same?
 eg. opCast returns false, set lhs to typeof(lhs).init?
I hadn't thought of that...
 Couldn't the tmp be optimised away?
Surely. -- Chris S
Feb 22 2005
parent "Regan Heath" <regan netwin.co.nz> writes:
On Tue, 22 Feb 2005 18:49:43 -0600, Chris Sauls <ibisbasenji gmail.com>  
wrote:
 Regan Heath wrote:
 i = cast(int) foo;        // foo.opCast(i);
 j = (cast(int) foo) + 5;  // int tmp; foo.opCast(foo); j = tmp + 5;
Typo? did you mean foo.opCast(tmp)?
Woops. I did.
I thought so :)
 f = cast(float) foo;      // throws an exception (opCast returned  
 false)
Ok, so this is different behaviour to when you cast a class to an incorrect base eg. class A {} class B {} B b = new B(); A a; a = cast(A)b; //a == null after this call, no exception thrown.
Or you could set the result param to null and return true, to mimic the language-defined cast.
Indeed. So the questions are: - Is it better to throw an exception or not? - If it's better, should the built in casts do it? - Can that be decided in general or should it be decided on a case by case basis?
  Could it, should it be the same?
 eg. opCast returns false, set lhs to typeof(lhs).init?
I hadn't thought of that...
It would mimic the built in cast behaviour.
 Couldn't the tmp be optimised away?
Surely.
In which case I think this idea has merit. Can anyone think of anything 'bad'(tm) about it? Regan
Feb 22 2005
prev sibling parent reply xs0 <xs0 xs0.com> writes:
 I still think the signature for opCast should change to something like:
   bit opCast(inout T result);
 
 Return 'true' on success, 'false' on failure (could not cast for some reason).
 Then we could have as many opCasts as we need. 
Two questions: 1) why inout not just out? 2) why not have void return type, and in case of failure you can just throw an exception yourself? presumably this would be a rare occasion, and you can avoid a bunch of checks this way.. Otherwise, not a bad idea at all, if you ask me. The only issue I can see is that the result is not returned (as in "return something;"), so it can't be used in expressions as easily (i.e. you need those temporary variables, which probably complicates implementation significantly). xs0
Feb 22 2005
next sibling parent reply Derek Parnell <derek psych.ward> writes:
On Wed, 23 Feb 2005 02:09:08 +0100, xs0 wrote:

 I still think the signature for opCast should change to something like:
   bit opCast(inout T result);
 
 Return 'true' on success, 'false' on failure (could not cast for some reason).
 Then we could have as many opCasts as we need. 
Two questions: 1) why inout not just out? 2) why not have void return type, and in case of failure you can just throw an exception yourself? presumably this would be a rare occasion, and you can avoid a bunch of checks this way.. Otherwise, not a bad idea at all, if you ask me. The only issue I can see is that the result is not returned (as in "return something;"), so it can't be used in expressions as easily (i.e. you need those temporary variables, which probably complicates implementation significantly).
Agree, this is not a bad compromise. void opCast(out T result); Eg. class Foo{ int val; void opCast(out uint result) { if (val < 0) throw new Exception( std.string.format("Foo: Cannot convert %d to a uint", val)); result = cast(uint)val * 100; } } . . . Foo A = new Foo; uint y = A; . . . Hmmm... this I could really use right now! -- Derek Melbourne, Australia 23/02/2005 12:38:22 PM
Feb 22 2005
parent reply Kris <Kris_member pathlink.com> writes:
In article <lx6y84tsdjq9.6hf2uroobfec$.dlg 40tude.net>, Derek Parnell says...
On Wed, 23 Feb 2005 02:09:08 +0100, xs0 wrote:

 I still think the signature for opCast should change to something like:
   bit opCast(inout T result);
 
 Return 'true' on success, 'false' on failure (could not cast for some reason).
 Then we could have as many opCasts as we need. 
Two questions: 1) why inout not just out? 2) why not have void return type, and in case of failure you can just throw an exception yourself? presumably this would be a rare occasion, and you can avoid a bunch of checks this way.. Otherwise, not a bad idea at all, if you ask me. The only issue I can see is that the result is not returned (as in "return something;"), so it can't be used in expressions as easily (i.e. you need those temporary variables, which probably complicates implementation significantly).
Agree, this is not a bad compromise. void opCast(out T result); Eg. class Foo{ int val; void opCast(out uint result) { if (val < 0) throw new Exception( std.string.format("Foo: Cannot convert %d to a uint", val)); result = cast(uint)val * 100; } } . . . Foo A = new Foo; uint y = A; . . . Hmmm... this I could really use right now! --
Isn't there another way (or alternate design) to resolve the issue you have, Derek? I ask for several broad reasons; each of which is not intended to reflect in any manner upon your particular coding style/approach: 1) casting should be limited to special cases only. It should absolutely not be used as a panacea for design issues. I'm amazed how much writing is being done about it! Much like goto, I suppose. <sigh> 2) the compiler should catch as many errors as possible at compile time. Using opCast() to throw *runtime* exceptions is, in my opinion, akin to casting one's code unto the winds. 3) in exceptional circumstances, isn't it better to leverage an explicitly provided method? Within the class/struct? Arguing opCast() vs method() is talking syntactics rather than semantics ~ yet I find it hard to imagine the (deliberately?) ugly cast(X) being preferable over a clean method invocation. Oh well. 4) the apparently popular use of casts to make the method-selection algorithm 'work', simply shows a serious flaw in the design of the latter. I've raged about that aspect in the past, and it's a shame the energy of this thread isn't served in that direction instead. Spent quite a bit of time messing with opCast last year. Came to the (potentially flawed) conclusion that it's a bit of an outcast ~ so to speak. D would likely be better off without it. IMO. - Kris
Feb 22 2005
next sibling parent reply xs0 <xs0 xs0.com> writes:
Hi :)


 1) casting should be limited to special cases only. It should absolutely not be
 used as a panacea for design issues. I'm amazed how much writing is being done
 about it! Much like goto, I suppose. <sigh>
I tend to agree, but there is this issue with interfaces that I have that casting solves really neatly. Consider SomeInterface with 25 methods (like Java's List). If you want to implement it, you need to write and maintain all those 25 methods. OTOH, you can just be castable to it and need to write only one, using some generic implementation to handle everything, you just provide the data (of course, the functionality most probably isn't the same (like modifying the list in List's case), but I find it that in most cases it is not a big issue). Now, without overloadable casts, you can only handle one interface in this manner, which isn't that many...
 2) the compiler should catch as many errors as possible at compile time. Using
 opCast() to throw *runtime* exceptions is, in my opinion, akin to casting one's
 code unto the winds.
Yup, I agree.. Returning null should be the norm, as it is with non-user-defined casts..
 3) in exceptional circumstances, isn't it better to leverage an explicitly
 provided method? Within the class/struct? Arguing opCast() vs method() is
 talking syntactics rather than semantics ~ yet I find it hard to imagine the
 (deliberately?) ugly cast(X) being preferable over a clean method invocation.
Oh
 well.
Well, consider the above case. Let's say you also have a method that takes SomeInterface, and you call it 150 times in your project. Isn't casting it in one place better than calling func(obj.asSomeInterface()) 150 times? And maybe you later decide to change the method to use AnotherInterface. If you used casts, you only have two places to change (the method and the opCast()), if you use .asSomeInterface(), there's 150 places to change...
 4) the apparently popular use of casts to make the method-selection algorithm
 'work', simply shows a serious flaw in the design of the latter. I've raged
 about that aspect in the past, and it's a shame the energy of this thread isn't
 served in that direction instead.
I'd rather say that it's bad design to overload methods with parameters that are implicitly castable between each other.. IMO, of course :) xs0
Feb 22 2005
next sibling parent "Regan Heath" <regan netwin.co.nz> writes:
On Wed, 23 Feb 2005 04:16:56 +0100, xs0 <xs0 xs0.com> wrote:
 1) casting should be limited to special cases only. It should  
 absolutely not be
 used as a panacea for design issues. I'm amazed how much writing is  
 being done
 about it! Much like goto, I suppose. <sigh>
I tend to agree, but there is this issue with interfaces that I have that casting solves really neatly. Consider SomeInterface with 25 methods (like Java's List). If you want to implement it, you need to write and maintain all those 25 methods. OTOH, you can just be castable to it and need to write only one, using some generic implementation to handle everything, you just provide the data (of course, the functionality most probably isn't the same (like modifying the list in List's case), but I find it that in most cases it is not a big issue). Now, without overloadable casts, you can only handle one interface in this manner, which isn't that many...
Good point.
 2) the compiler should catch as many errors as possible at compile  
 time. Using
 opCast() to throw *runtime* exceptions is, in my opinion, akin to  
 casting one's
 code unto the winds.
Yup, I agree.. Returning null should be the norm, as it is with non-user-defined casts..
I agree. I think consistency is a good thing here.
 3) in exceptional circumstances, isn't it better to leverage an  
 explicitly
 provided method? Within the class/struct? Arguing opCast() vs method()  
 is
 talking syntactics rather than semantics ~ yet I find it hard to  
 imagine the
 (deliberately?) ugly cast(X) being preferable over a clean method  
 invocation. Oh
 well.
Well, consider the above case. Let's say you also have a method that takes SomeInterface, and you call it 150 times in your project. Isn't casting it in one place better than calling func(obj.asSomeInterface()) 150 times? And maybe you later decide to change the method to use AnotherInterface. If you used casts, you only have two places to change (the method and the opCast()), if you use .asSomeInterface(), there's 150 places to change...
Good point.
 4) the apparently popular use of casts to make the method-selection  
 algorithm
 'work', simply shows a serious flaw in the design of the latter. I've  
 raged
 about that aspect in the past, and it's a shame the energy of this  
 thread isn't
 served in that direction instead.
I'd rather say that it's bad design to overload methods with parameters that are implicitly castable between each other.. IMO, of course :)
I tend to agree here also, however is it at all possible that the methods could provide more efficient, or a more exact 'something' for specific types than a cast and 1 method could? Regan
Feb 22 2005
prev sibling parent Kris <Kris_member pathlink.com> writes:
In article <cvgsj4$2dja$1 digitaldaemon.com>, xs0 says...
Hi :)


 1) casting should be limited to special cases only. It should absolutely not be
 used as a panacea for design issues. I'm amazed how much writing is being done
 about it! Much like goto, I suppose. <sigh>
I tend to agree, but there is this issue with interfaces that I have that casting solves really neatly. Consider SomeInterface with 25 methods (like Java's List). If you want to implement it, you need to write and maintain all those 25 methods. OTOH, you can just be castable to it and need to write only one, using some generic implementation to handle everything, you just provide the data (of course, the functionality most probably isn't the same (like modifying the list in List's case), but I find it that in most cases it is not a big issue). Now, without overloadable casts, you can only handle one interface in this manner, which isn't that many...
Ouch! :-)
 2) the compiler should catch as many errors as possible at compile time. Using
 opCast() to throw *runtime* exceptions is, in my opinion, akin to casting one's
 code unto the winds.
Yup, I agree.. Returning null should be the norm, as it is with non-user-defined casts..
opCast() is, by definition, a runtime call. Getting the compiler to invoke it at compile-time would certainly be interesting.
 3) in exceptional circumstances, isn't it better to leverage an explicitly
 provided method? Within the class/struct? Arguing opCast() vs method() is
 talking syntactics rather than semantics ~ yet I find it hard to imagine the
 (deliberately?) ugly cast(X) being preferable over a clean method invocation.
Oh
 well.
Well, consider the above case. Let's say you also have a method that takes SomeInterface, and you call it 150 times in your project. Isn't casting it in one place better than calling func(obj.asSomeInterface()) 150 times? And maybe you later decide to change the method to use AnotherInterface. If you used casts, you only have two places to change (the method and the opCast()), if you use .asSomeInterface(), there's 150 places to change...
Aye
 4) the apparently popular use of casts to make the method-selection algorithm
 'work', simply shows a serious flaw in the design of the latter. I've raged
 about that aspect in the past, and it's a shame the energy of this thread isn't
 served in that direction instead.
I'd rather say that it's bad design to overload methods with parameters that are implicitly castable between each other.. IMO, of course :)
There's the rub ~ a Haitian Divorce waiting to happen.
Feb 22 2005
prev sibling parent Derek Parnell <derek psych.ward> writes:
On Wed, 23 Feb 2005 02:25:40 +0000 (UTC), Kris wrote:

 In article <lx6y84tsdjq9.6hf2uroobfec$.dlg 40tude.net>, Derek Parnell says...
[snip]
Agreed, this is not a bad compromise.

  void opCast(out T result);
[snip]
Hmmm... this I could really use right now!
 
 Isn't there another way (or alternate design) to resolve the issue you have,
 Derek? I ask for several broad reasons; each of which is not intended to
reflect
 in any manner upon your particular coding style/approach:
Thanks Kris. Your questions and observations below a very good and valid ones.
 1) casting should be limited to special cases only. It should absolutely not be
 used as a panacea for design issues. I'm amazed how much writing is being done
 about it! Much like goto, I suppose. <sigh>
LOL. Yes it has become a little too emotional. I agree with your sentiments above. And we are talking about *explicit* casting, right? Currently, an explicit cast does one of two things, depending on what is getting cast to what... 1) To tell the compiler that some data, even though it has a data type of 'x', is to be treated as if it has a data type of 'y', and that no actual data is modified by this. 2) To tell the compiler that data of type 'x' is to be modified (converted) to a new storage format of data type 'y'. Now (1) is definitely a low-use, special circumstance, situation. It may even cause runtime errors, but that's okay because the coder has accepted responsibility of that due to the explicit cast. But (2) is a common activity of programs. To convert data from one format to another is done all the time. Because it is commonly required, it would be _nice_ to have some syntax sugar to help us (lazy?) coders. And maybe, an improved opCast is a way to achieve that sugary rush.
 2) the compiler should catch as many errors as possible at compile time. Using
 opCast() to throw *runtime* exceptions is, in my opinion, akin to casting one's
 code unto the winds.
Yes, in principle I agree. But consider the conversion process. Often conversions can only fail at run time. If we use an improved opCast in order to invoke a customized conversion function, then one would expect that runtime errors may occur. For example, if we have two classes, Foo and Bar, then aFoo = aBar; would trip up with the "cannot implicitly cast" error. But aFoo = cast(Foo)aBar; would first try to see if Bar has an opCast(Foo) defined, and if so invoke it, otherwise issue an error like it does today. If we knew that a runtime error is sometime possible we could write ... try { aFoo = cast(Foo) aBar;} catch ( ... whatever ... ) ...;
 3) in exceptional circumstances, isn't it better to leverage an explicitly
 provided method? Within the class/struct? Arguing opCast() vs method() is
 talking syntactics rather than semantics ~ yet I find it hard to imagine the
 (deliberately?) ugly cast(X) being preferable over a clean method invocation.
Oh
 well.
Yes, it is just syntax. aFoo = cast(Foo)aBar; verses aFoo = aBar.toFoo(); or aBar.toFoo( aFoo ); I suspect the 'cast' syntax is more amenable to templates though. template(T) { T a = new T; a = cast(T)parm; } (yeah, the above syntax is wrong but you get the idea )
 4) the apparently popular use of casts to make the method-selection algorithm
 'work', simply shows a serious flaw in the design of the latter. I've raged
 about that aspect in the past, and it's a shame the energy of this thread isn't
 served in that direction instead.
True, one could do ... class Bar{ void Convert(out Foo x) { .... }; void Convert(out uint x) { ... }; void Convert(out real x) { ... }; void Convert(out char[] x) { ... }; } Foo aFoo; real r; aBar.Convert( aFoo ); aBar.Convert( r ); Which is identical to the opCast() idea being discussed here, except that it is non-standard (everyone comes up with a new name for 'Convert'). This sort of method-selection is not, per se, indicative of a serious flaw in the design. The difference that has been suggested is ... class Bar{ void opCast(out Foo x) { .... }; void opCast(out uint x) { ... }; void opCast(out real x) { ... }; void opCast(out char[] x) { ... }; } Foo aFoo; real r; aFoo = cast(Foo)aBar; r = cast(real)aBar;
 Spent quite a bit of time messing with opCast last year. Came to the
 (potentially flawed) conclusion that it's a bit of an outcast ~ so to speak.  D
 would likely be better off without it. IMO.
I totally agree with you regards the current implementation of opCast. It is basically worthless. -- Derek Melbourne, Australia 23/02/2005 1:44:38 PM
Feb 22 2005
prev sibling parent reply Chris Sauls <ibisbasenji gmail.com> writes:
xs0 wrote:
 Two questions:
 
 1) why inout not just out?
I know I had a reason... but I forget it now.
 2) why not have void return type, and in case of failure you can just 
 throw an exception yourself? presumably this would be a rare occasion, 
 and you can avoid a bunch of checks this way..
That's possible too. I just assumed the D runtime might have some use for a success/failure flag, for throwing a default exception or something. I admit I haven't looked at how the D compiler/runtime handle casting right now.
 
 Otherwise, not a bad idea at all, if you ask me. The only issue I can 
 see is that the result is not returned (as in "return something;"), so 
 it can't be used in expressions as easily (i.e. you need those temporary 
 variables, which probably complicates implementation significantly).
Yes that's most of my own personal issue with it, but it does give us multiple overloads for casting, and without having to add overload-on-return-type to the language spec. Although that could still possibly have its uses, but I'm not even diving into that can of worms. Also I would assume an opCast with a stable return type in its signature would be easier for the compiler anyhow. Keyword is of course 'assume'. -- Chris S
Feb 22 2005
parent xs0 <xs0 xs0.com> writes:
 2) why not have void return type, and in case of failure you can just 
 throw an exception yourself? presumably this would be a rare occasion, 
 and you can avoid a bunch of checks this way..
That's possible too. I just assumed the D runtime might have some use for a success/failure flag, for throwing a default exception or something. I admit I haven't looked at how the D compiler/runtime handle casting right now.
afaik, non-class types either always can or always can't be cast, so that is checked at compile time. casting between classes returns a null if the cast is not possible (that's why there's no instanceof operator, like in Java, you can just do if (cast(SomeClass)obj) {}
 Yes that's most of my own personal issue with it, but it does give us 
 multiple overloads for casting, and without having to add 
 overload-on-return-type to the language spec.
I was actually referring to compiling such code; you can't just replace cast(SomeType)obj with SomeType tmp; obj.opCast(tmp) because it could be in the middle of an expression. Although, now that I think about it, there is no need for that tmp variable, the compiler can use the result directly, unlike the coder..
  Also I would assume an opCast with a stable return type in its 
 signature would be easier for the compiler anyhow.  Keyword is of course 
 'assume'.
Well, I assume that too, and I agree that overloaded return types are a bad thing (except possibly for opCast(), because its not a "normal" function and because imho it has legitimate uses) xs0
Feb 22 2005