digitalmars.D - The purpose of opCast?
- Derek (8/8) Feb 18 2005 I know this is a "squeaky wheel", but has anyone found a good reason to ...
- John Demme (8/14) Feb 18 2005 Although I haven't found a good use for it yet, it's a matter of
- Norbert Nemec (17/22) Feb 19 2005 But at best, it is a half-hearted attempt to cover up the asymmetry
- Regan Heath (22/45) Feb 20 2005 Some can, to some types, this seems to have been decided on a case by ca...
- Norbert Nemec (11/21) Feb 20 2005 I think, it would make parsing a mess. Currently, the type of an
- Georg Wrede (6/11) Feb 21 2005 Interesting thought.
- Derek (21/36) Feb 21 2005 The main one seems to be, what would happen if someone coded a function
- xs0 (37/43) Feb 21 2005 Well, that's not the only "main" issue, imho.. For example, consider:
- xs0 (4/5) Feb 21 2005 should of course be
- Regan Heath (4/8) Feb 21 2005 I really should read all threads before answering.. regardless that wont...
- Regan Heath (35/78) Feb 21 2005 I assume the above line is supposed to be
- xs0 (30/62) Feb 21 2005 Yup, it is similar, but I think that in this case, Parent.foo will still...
- Regan Heath (12/68) Feb 21 2005 If it doesn't compile and you're trying to show something that 'could'
- xs0 (31/50) Feb 21 2005 My mistake then.. Although I think it should be the case, according to
- Regan Heath (58/104) Feb 21 2005 If you had specified them, would it have compiled?
- xs0 (69/119) Feb 22 2005 What kind of questions is that? Are you suggesting you're not sure
- Georg Wrede (6/8) Feb 22 2005 If we had overloading on return type, then in some
- Regan Heath (61/175) Feb 22 2005 No, your example didn't show one, you described one being added later. A...
- brad domain.invalid (12/18) Feb 22 2005 I haven't read the whole thread, but I think I get the general gist.
- Regan Heath (8/24) Feb 22 2005 No, you missunderstand. The behaviour isn't changing at all. Cast still ...
- brad domain.invalid (9/29) Feb 22 2005 cast currently takes one type an converts it to another type. What you
- Regan Heath (14/37) Feb 22 2005 Nope. It's comparable to casting a parameter. eg.
- brad domain.invalid (19/37) Feb 22 2005 Fair enough, though I can't say I've personally used cast as a method
- Regan Heath (17/47) Feb 22 2005 Neither, D would give an error, same as it does for.
- Charles Hixson (23/38) Feb 23 2005 ...> Regan
- Regan Heath (38/72) Feb 23 2005 Well, yes, currently that's exactly what is happening.
- Charles Hixson (36/140) Feb 25 2005 The problem is, it isn't REALLY a cast that is wanted, it's a
- Georg Wrede (15/40) Feb 26 2005 int:foo(x) looks indeed nicer than foo:int(x). My worry is that both
- Regan Heath (26/41) Feb 27 2005 This is a good point.
- Georg Wrede (20/22) Feb 23 2005 No, you missunderstand. The behaviour isn't changing at all. Cast
- xs0 (77/146) Feb 22 2005 Bah, my example was obviously both parts of it. Furthermore, I don't see...
- Regan Heath (37/131) Feb 22 2005 As I said, I want to fiddle with it.
- xs0 (51/118) Feb 22 2005 It starts with "For example, consider:". It ends with "returning the
- Regan Heath (51/141) Feb 22 2005 I tried, I got to a point where, without knowing what you were trying to...
- xs0 (36/106) Feb 22 2005 int a() { return 1; }
- Regan Heath (96/200) Feb 23 2005 Yes, thank you. Small modification:
- xs0 (83/155) Feb 23 2005 No, there could be a large amount of rules to make the process totally
- Regan Heath (4/9) Feb 23 2005 I don't think so, and nothing you have said has convinced me. Sounds lik...
- Georg Wrede (12/24) Feb 22 2005 True. But then we might not always be able to see the real reason.
- Regan Heath (61/86) Feb 22 2005 Well, in most cases I tend to agree with Walter, but, it's not due to hi...
- Derek Parnell (6/120) Feb 22 2005 I could use such a feature today (yesterday actually).
- Georg Wrede (14/17) Feb 22 2005 This reveals a useful use for overloading on return type.
- Derek (37/88) Feb 21 2005 I understand what you are saying. However, I imagine it would work in a
- xs0 (27/43) Feb 21 2005 It does? That's great, actually. My guess, however, is that it only does...
- Chris Sauls (6/6) Feb 22 2005 I still think the signature for opCast should change to something like:
- Regan Heath (9/14) Feb 22 2005 Interesting.. how would you use it? same as other types i.e.
- Chris Sauls (23/25) Feb 22 2005 You assume right. My thought-train went something like:
- Regan Heath (14/38) Feb 22 2005 Typo? did you mean foo.opCast(tmp)?
- Chris Sauls (7/26) Feb 22 2005 Or you could set the result param to null and return true, to mimic the
- Regan Heath (12/34) Feb 22 2005 I thought so :)
- xs0 (10/14) Feb 22 2005 Two questions:
- Derek Parnell (23/39) Feb 22 2005 Agree, this is not a bad compromise.
- Kris (23/59) Feb 22 2005 Isn't there another way (or alternate design) to resolve the issue you h...
- xs0 (23/38) Feb 22 2005 I tend to agree, but there is this issue with interfaces that I have
- Regan Heath (8/55) Feb 22 2005 I agree. I think consistency is a good thing here.
- Kris (6/43) Feb 22 2005 opCast() is, by definition, a runtime call. Getting the compiler to invo...
- Derek Parnell (77/105) Feb 22 2005 [snip]
- Chris Sauls (14/25) Feb 22 2005 That's possible too. I just assumed the D runtime might have some use
- xs0 (15/29) Feb 22 2005 afaik, non-class types either always can or always can't be cast, so
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
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
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
On Sat, 19 Feb 2005 11:53:02 +0100, Norbert Nemec <Norbert Nemec-online.de> wrote:John Demme schrieb: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?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) ...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
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)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...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.
Feb 20 2005
Norbert Nemec wrote:Regan Heath schrieb: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?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.
Feb 21 2005
On Mon, 21 Feb 2005 12:16:13 +0200, Georg Wrede wrote:Norbert Nemec 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? 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, AustraliaRegan Heath schrieb: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?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.
Feb 21 2005
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
double cost=sqrt(..);should of course be double cost=mysqrt(..); :) xs0
Feb 21 2005
On Mon, 21 Feb 2005 14:04:00 +0100, xs0 <xs0 xs0.com> wrote:I really should read all threads before answering.. regardless that wont compile. Regandouble cost=sqrt(..);should of course be double cost=mysqrt(..); :)
Feb 21 2005
On Mon, 21 Feb 2005 14:03:04 +0100, xs0 <xs0 xs0.com> wrote:I assume the above line is supposed to be double cost=mysqrt(..); ? if so, it doesn't compile, return type is wrong.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.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.Because it has basically the same meaning.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'));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
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..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..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); }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.Hmm, but there's no casting, so why use the cast() operator?Because it has basically the same meaning.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
On Tue, 22 Feb 2005 01:26:44 +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.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..Wrong.>> or in eval_some_cost(), the code will produce differentThe problem is that without any change in either the int versionYup, 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?).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); }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..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.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.Hmm, but there's no casting, so why use the cast() operator?Because it has basically the same meaning.That isn't what I meant.Agreed, but it's not the way D works currently.Yup, currently you can't overload just on return types :)Perhaps. A more generic soln would be more useful however.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)This is a hack. I think we can do better. Regan
Feb 21 2005
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? :)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).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.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)) ?So what did you mean?Yup, currently you can't overload just on return types :)That isn't what I meant.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..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.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
On Tue, 22 Feb 2005 02:12:27 +0100, xs0 <xs0 xs0.com> wrote:If you had specified them, would it have compiled? If so, post the example here, if not...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? :)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.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).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.True, however I'm also specifying the overload I want, because the compiler cannot read my mind and I doesn't guess (thank Bob).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...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.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.So what did you mean?Yup, currently you can't overload just on return types :)That isn't what I meant.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.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()?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.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); }cast(type)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.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
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...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...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.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..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.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)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..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.That's impossible (really, cast is not overload-selection-operator, it's casting-operator). Any other suggestions?Well, what is your better suggestion then?cast(type)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)?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.Yes, I agree completely, it's just not what you understand it to be... xs0
Feb 22 2005
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
On Tue, 22 Feb 2005 10:20:33 +0100, xs0 <xs0 xs0.com> wrote:No.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?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...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.See above.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).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.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.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).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..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.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.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.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.This is not my 'intent' it is simply the effect.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..This is the intent.It's not converting per-se, more extending and setting bits to 0.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..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.Ok.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..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.No, this is the best method/soln I've heard so far.That's impossible (really, cast is not overload-selection-operator, it's casting-operator). Any other suggestions?Well, what is your better suggestion then?cast(type)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).Why not?cast(T: int) { return Something!(T).convert(this); } cast(T: double) { return Something!(T).convert(this); }That syntax does not seem obvious to me.How would you suggest overloading opCast() if return-type overloading is not allowedNo 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, andAhh, 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.It appears Georg agrees with me. ReganWhich 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...
Feb 22 2005
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
On Wed, 23 Feb 2005 10:41:05 +1300, <brad domain.invalid> 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.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?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
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.It is engineered, yes - but that doesn't mean that it couldn't happen in the real world. BradAlso, 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
On Wed, 23 Feb 2005 11:51:22 +1300, <brad domain.invalid> wrote:Regan Heath wrote: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.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.It already is, that's part of my point, it's already used to resolve ambiguity, see the above example.True. I'd argue that if it did you have a bigger 'design' problem. ReganIt is engineered, yes - but that doesn't mean that it couldn't happen in the real world.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
Regan Heath wrote: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 :)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 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. BradIt 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.
Feb 22 2005
On Wed, 23 Feb 2005 14:06:05 +1300, <brad domain.invalid> wrote:Regan Heath wrote:Each to thier own.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.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.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 fooNeither, 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.I know. I believe this behaviour cause more problems than it solves. But that's my personal philosophy. ReganThe 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.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.
Feb 22 2005
Regan Heath wrote:On Wed, 23 Feb 2005 11:51:22 +1300, <brad domain.invalid> wrote:...> 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.Regan Heath wrote:Nope. It's comparable to casting a parameter. eg.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?
Feb 23 2005
On Wed, 23 Feb 2005 17:03:41 -0800, Charles Hixson <charleshixsn earthlink.net> wrote:Regan Heath wrote: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);On Wed, 23 Feb 2005 11:51:22 +1300, <brad domain.invalid> wrote:...> 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).Regan Heath wrote:Nope. It's comparable to casting a parameter. eg.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?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
Regan Heath wrote:On Wed, 23 Feb 2005 17:03:41 -0800, Charles Hixson <charleshixsn earthlink.net> wrote: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));Regan Heath wrote: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);On Wed, 23 Feb 2005 11:51:22 +1300, <brad domain.invalid> wrote:...> 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).Regan Heath wrote:Nope. It's comparable to casting a parameter. eg.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?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 25 2005
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
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
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
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? :)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.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.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..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.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.Whatever.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.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.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.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.Why not?cast(T: int) { return Something!(T).convert(this); } cast(T: double) { return Something!(T).convert(this); }That syntax does not seem obvious to me.So in short your example seems to be the c++ hack, with extra characters making it less clear (to me).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.How would you suggest overloading opCast() if return-type overloading is not allowedNo idea, I'll think about this, if, return type overloading is officially "not going to happen, ever".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?Again, it's not a parameter, it's a Type.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.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
On Wed, 23 Feb 2005 01:46:42 +0100, xs0 <xs0 xs0.com> wrote:Obviously? I think not.Bah, my example was obviously both parts of it.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.Furthermore, I don't see what you could possibly gain by compiling the first partAs 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.Well, it could pick one at random.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.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 give up, you're either ignoring my point, or you simply disagree, either way this is pointless.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.As above.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.I simply asked you to provide one that compiled, and told you why I wanted it.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..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.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...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 noticed, and am resisting the urge to be sarcastic in return.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)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.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.I never said "mainly".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).It depends on each situation, but generally speaking no new syntax means less to learn, less to parse etc.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?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.I see you're no longer interested, so I'll stop now. ReganWhatever.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.
Feb 22 2005
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.Obviously? I think not.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.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.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.I'm assuming you don't have access to the compiler from that universe? :)Now you're being silly.I agree completely :) Now reread what I said..The compiler doesn't error because it cannot pick the overloadWell, 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.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)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.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..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).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.yupNow, 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?That's just ridiculous. "Cast by definition" has a well-defined purpose and it is not selecting overloads.As above.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.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.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, 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?It depends on each situation, but generally speaking no new syntax means less to learn, less to parse etc.Further it requires no additional syntax and is easily parsable.Why would no additional syntax for new functionality be a plus?Not in this way.And why would it be more easily parsable than any other syntax?Because it's already being parsed.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?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.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.. xs0I see you're no longer interested, so I'll stop now.It's not converting per-se, more extending and setting bits to 0.Whatever.
Feb 22 2005
On Wed, 23 Feb 2005 03:27:44 +0100, xs0 <xs0 xs0.com> wrote: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.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 don't 'have' to help. I figured you'd want to, to clarify your example for me. It appears I was wrong.Done. I still don't agree that "there is a large difference there".I agree completely :) Now reread what I said..The compiler doesn't error because it cannot pick the overloadWell, 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.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?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)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.I'm trying to think of any and waiting for someone else to think of some.Georg also said "Of course, changing the semantics of cast may, for all I know, raise other issues." But you conveniently ignored that..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).Brad didn't agree, but you also conveniently ignored that..I am conversing with brad as we speak. How am I ignoring him?I am disagreeing with several of your points.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.Only purpose, disagree. It has several purposes, you've supplied some above. It's functionality, or effect.. agree. It does type conversion.As above.yupNow, 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?That's just ridiculous. "Cast by definition" has a well-defined purpose and it is not selecting overloads.As above.So, you have no desire to provide an example to back up your statement, fine.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).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.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.No, that was not my argument. I never said "often".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.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".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.No, it means more to learn and harder to parse.It depends on each situation, but generally speaking no new syntax means less to learn, less to parse etc.Further it requires no additional syntax and is easily parsable.Why would no additional syntax for new functionality be a plus?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.I think we have a different definition of "parse". To me, parse means take the text and break it into tokens.Not in this way.And why would it be more easily parsable than any other syntax?Because it's already being parsed.Yep, that's my definitionParse: 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.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.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.You could, that would be illogical. ReganWell, 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..I see you're no longer interested, so I'll stop now.It's not converting per-se, more extending and setting bits to 0.Whatever.
Feb 22 2005
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.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: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?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)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'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.Well, in your summary of the current score, you only mentioned Georg but not Brad. And I never said you ignored Brad.Brad didn't agree, but you also conveniently ignored that..I am conversing with brad as we speak. How am I ignoring him?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?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.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.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?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.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).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.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 definitionIt's also illogical to claim I was no longer interested (considering the amount of text after "Whatever"), but you still did.. xs0You could, that would be illogical.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..Whatever.I see you're no longer interested, so I'll stop now.
Feb 22 2005
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?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().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? :)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?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.No, I am listening to what you write, and replying, I just disagree, how am I ignroing you?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: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?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)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'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.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.Well, in your summary of the current score, you only mentioned Georg but not Brad. And I never said you ignored Brad.Brad didn't agree, but you also conveniently ignored that..I am conversing with brad as we speak. How am I ignoring him?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);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.Right, I just wanted it in code, so I could see exactly what you meant, you've given me that now, I'm happy. :)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: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 }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.See above. I have restated it for clarity.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?Sure, but it's the commonly accepted way for D.Resolving an overload collision doesn't require a cast, it's just one way of doing it.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.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.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.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?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.It seems the same as the definition below, to me.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).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.No, it isn't. It says "Parsing breaks down words into functional units", what you're saying is "Parsing breaks input into words".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 definitionAlso 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?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. ReganIt's also illogical to claim I was no longer interested (considering the amount of text after "Whatever"), but you still did..You could, that would be illogical.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..Whatever.I see you're no longer interested, so I'll stop now.
Feb 23 2005
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 actuallyWhere does it say no-one would agree with you?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.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...Why do I need to repeat everything? Derek wrote that.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 }This has nothing to do with anything. Go read what was said if you want to comment on it.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.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.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.Well, everyone else seems to think that there is a difference between lexical analysis and parsing..Nope, that's lexing (or lexical analysis, if you want to look it up).It seems the same as the definition below, to me.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.cNo, 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 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
On Thu, 24 Feb 2005 02:20:42 +0100, xs0 <xs0 xs0.com> wrote: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. ReganBut your reasoning is wrong.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.
Feb 23 2005
Regan Heath wrote:On Tue, 22 Feb 2005 01:26:44 +0100, xs0 <xs0 xs0.com> wrote: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.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.Me too! And an ever increasing number of my friends here!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).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?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?
Feb 22 2005
On Tue, 22 Feb 2005 10:23:16 +0200, Georg Wrede <georg.wrede nospam.org> wrote:Regan Heath wrote:Well, in most cases I tend to agree with Walter, but, it's not due to his CV.On Tue, 22 Feb 2005 01:26:44 +0100, xs0 <xs0 xs0.com> wrote:True. But then we might not always be able to see the real reason. After all, his CV looks better than ours!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.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 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.Me too! And an ever increasing number of my friends here!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).Fair enough.FTR: I still have no opinion for or against overloading on return type.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?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
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:I could use such a feature today (yesterday actually). -- Derek Melbourne, Australia 23/02/2005 9:39:53 AMRegan Heath wrote:Well, in most cases I tend to agree with Walter, but, it's not due to his CV.On Tue, 22 Feb 2005 01:26:44 +0100, xs0 <xs0 xs0.com> wrote:True. But then we might not always be able to see the real reason. After all, his CV looks better than ours!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.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 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.Me too! And an ever increasing number of my friends here!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).Fair enough.FTR: I still have no opinion for or against overloading on return type.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?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
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
On Mon, 21 Feb 2005 14:03:04 +0100, xs0 wrote: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.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.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 ;-)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 offoo: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
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..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[snip: double a=2/3]True, but what has this got to do with function selection? I'm missing the point I think, sorry.
Feb 21 2005
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
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
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
On Tue, 22 Feb 2005 17:19:32 -0600, Chris Sauls <ibisbasenji gmail.com> wrote:Regan Heath wrote:Typo? did you mean foo.opCast(tmp)?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)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
Regan Heath wrote:Woops. I did.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)?Or you could set the result param to null and return true, to mimic the language-defined cast.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 hadn't thought of that...Couldn't the tmp be optimised away?Surely. -- Chris S
Feb 22 2005
On Tue, 22 Feb 2005 18:49:43 -0600, Chris Sauls <ibisbasenji gmail.com> wrote:Regan Heath wrote:I thought so :)Woops. I did.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)?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?Or you could set the result param to null and return true, to mimic the language-defined cast.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.It would mimic the built in cast behaviour.Could it, should it be the same? eg. opCast returns false, set lhs to typeof(lhs).init?I hadn't thought of that...In which case I think this idea has merit. Can anyone think of anything 'bad'(tm) about it? ReganCouldn't the tmp be optimised away?Surely.
Feb 22 2005
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
On Wed, 23 Feb 2005 02:09:08 +0100, xs0 wrote: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 PMI 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).
Feb 22 2005
In article <lx6y84tsdjq9.6hf2uroobfec$.dlg 40tude.net>, Derek Parnell says...On Wed, 23 Feb 2005 02:09:08 +0100, xs0 wrote: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. - KrisAgree, 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! --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).
Feb 22 2005
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
On Wed, 23 Feb 2005 04:16:56 +0100, xs0 <xs0 xs0.com> wrote:Good point.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...I agree. I think consistency is a good thing here.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..Good point.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...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? Regan4) 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 :)
Feb 22 2005
In article <cvgsj4$2dja$1 digitaldaemon.com>, xs0 says...Hi :)Ouch! :-)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...opCast() is, by definition, a runtime call. Getting the compiler to invoke it at compile-time would certainly be interesting.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..Aye3) 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...There's the rub ~ a Haitian Divorce waiting to happen.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 :)
Feb 22 2005
On Wed, 23 Feb 2005 02:25:40 +0000 (UTC), Kris wrote:In article <lx6y84tsdjq9.6hf2uroobfec$.dlg 40tude.net>, Derek Parnell says...[snip][snip]Agreed, this is not a bad compromise. void opCast(out T result);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
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
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) {}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.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