February 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
February 23, 2005
On Tue, 22 Feb 2005 17:19:32 -0600, Chris Sauls <ibisbasenji@gmail.com> wrote:
> Regan Heath wrote:
>> if so, where/how do you get the return value?
>> I assume the inout gets assigned to the lhs?
>
> You assume right.  My thought-train went something like:
>
> class Foo {
>    private int value;
>
>    bit opCast(inout int result) {
>      result = value;
>      return true;
>    }
>
>    bit opCast(inout float result) {
>      return false;
>    }
> }
>
> int i, j;
> float f;
> Foo foo = new Foo;
>
> i = cast(int) foo;        // foo.opCast(i);
> j = (cast(int) foo) + 5;  // int tmp; foo.opCast(foo); j = tmp + 5;

Typo? did you mean foo.opCast(tmp)?

> f = cast(float) foo;      // throws an exception (opCast returned false)

Ok, so this is different behaviour to when you cast a class to an incorrect base eg.

class A {}
class B {}
B b = new B();
A a;

a = cast(A)b;  //a == null after this call, no exception thrown.

Could it, should it be the same?
eg. opCast returns false, set lhs to typeof(lhs).init?

> I'm not sure how I feel about the tmp variable, but D already does similar things to implement some other features.  Such as the auto-generated function "literal" that Walter recently mentioned foreach loops generate.  (If I remember that right.)

Couldn't the tmp be optimised away?

Regan
February 23, 2005
Regan Heath wrote:
>> i = cast(int) foo;        // foo.opCast(i);
>> j = (cast(int) foo) + 5;  // int tmp; foo.opCast(foo); j = tmp + 5;
> 
> Typo? did you mean foo.opCast(tmp)?

Woops. I did.

>> f = cast(float) foo;      // throws an exception (opCast returned false)
> 
> Ok, so this is different behaviour to when you cast a class to an  incorrect base eg.
> 
> class A {}
> class B {}
> B b = new B();
> A a;
> 
> a = cast(A)b;  //a == null after this call, no exception thrown.

Or you could set the result param to null and return true, to mimic the language-defined cast.

> 
> Could it, should it be the same?
> eg. opCast returns false, set lhs to typeof(lhs).init?
>

I hadn't thought of that...

> Couldn't the tmp be optimised away?

Surely.

-- Chris S
February 23, 2005
>> And I can't produce a compilable example, because the example included  an overload on return type, which is not possible...
> 
> No, your example didn't show one, you described one being added later. All  I want is an example that compiles. Which should be possible.

Bah, my example was obviously both parts of it. Furthermore, I don't see what you could possibly gain by compiling the first part, as the code was trivial. I also already wrote what will make it compile (cast(int) instead of floor()). Finally, it's not possible, because both parts of the example were from the parallel universe in which return overloads in D exist. I'm assuming you don't have access to the compiler from that universe? :)


> No.
> 
> If the compiler errors because it cannot pick the overload, then, by  adding a cast I am selecting an overload, the fact that it casts/converts  is the side effect. In other words the point/purpose of the cast changes  depending on what it's used for.

The compiler doesn't error because it cannot pick the overload, it errors, because Walter said it should, and for good reasons. There's a large difference there.

As for the purpose, if you say the purpose of casting can be overload selection, then you'll also have to admit that there are also a large number of other possible purposes to casting. But then, you can't argue that it is the best syntax for doing so, because all those other purposes are just as valid, and casting is not somehow the natural choice. And it's certainly not the only choice..



> I think that, because the cast comes first, you're assuming it's the  purpose, because the overload selection comes second it's a side effect. A  side effect can occur first, just as the purpose can occur second.

No, I think that because cast by itself has the functionality it has, its purpose is to change/convert types. The fact that that functionality can help you resolve overloads is definitely the side-effect; if there were no overloads, cast's purpose in the language would still remain the same.


> Yes, you can cast a return type now, now the purpose is to cast. Were it  possible to cast to select an overload, then doing so would be the purpose.

cast is not selecting an overload. The coder is the one selecting the overload by changing the type of arguments. If the coder uses the cast operator, it is just his choice, he could use other techniques (for example, instead of casting int to double you can also add 0.0 to it with the same result).


> The purpose of cast now, is not always to convert, but sometimes to select  an overload, the purpose changes depending on where it's used and why (by  definition).

That's just ridiculous. "Cast by definition" has a well-defined purpose and it is not selecting overloads.


>> Since you were so smart about compilable examples, I'm surprised you  wrote an example that can't be compiled :P (Sorry, couldn't resist)
> 
> I find your comment petty.

Well, I find it petty that you proclaimed my example invalid just because you failed to see what its point was and wanted to compile it. At least I apologized..


> The example above cannot be compiled because it is an error, I was giving  an example of an error case which your syntax would solve.
> The same is not true of your example, which was an example of 'before'
> the  collision was added.

Ahh, so you wanted to show something without it actually being compilable code. So did I, yet you keep being a smart-ass about it..


> I am not the only one, see Georg's reply.

I'm also not the only one, see Brad's reply. Now it's 2 vs 2. Let's see who wins. Man, this is fun :) (if you failed to notice, I'm trying to be sarcastic)


> I understand everything you've said above, and I agree it works as you've  said.
> 
> But, the point you're missing is that the 'intent' of a statement is not  necessarily the same as the effect generated by the compiler.

The 'intent' of statements has nothing to do with the language (even more so outside computers). And even so, if I see

int result=cast(int)func(...);

I interpret the so-called intent as "call func(); because it doesn't return int, cast the return value to int". I'll also bet that that would be the interpretation of the vast majority of programmers. Start a poll or something, if you don't believe me.


> This behaviour/method is generally accepted and understood by programmers,  and extending it to include return values will be the same, generally  accepted and understood.

Well, I'd say that if someone understands cast as mainly being the thing to select overloads, he's a newbie (no offense meant to newbies; I was once a newbie too and also believed many wrong things).


> Further it requires no additional syntax and is easily parsable.

Why would no additional syntax for new functionality be a plus? And why would it be more easily parsable than any other syntax? If anything, it's harder - you need to figure out whether there is a function call involved, and if so, if it has overloaded return types and choose some completely different functionality based on the answers to those questions.


>> no, it isn't secondary, just the opposite (and casting int to long is  converting, the first is 32 bits, the other is 64 bits).
> 
> It's not converting per-se, more extending and setting bits to 0.

Whatever.


> int -> uint makes no changes to the memory.

And the point is? That only the memory contents matter? What a weak argument. It's just electric charges in some silicon wafer without interpretation, so think about it a little (to start you of, the same memory bit pattern can be an int, a float, a single UTF32 character, 1-4 UTF8 characters, a processor op, a Java bytecode, and a gazillion other things). To make my own point clear - changing interpretation is far more important than changing memory contents.


>>>> cast(T: int)    { return Something!(T).convert(this); }
>>>> cast(T: double) { return Something!(T).convert(this); }
>>>
>>>  That syntax does not seem obvious to me.
>>
>> Why not?
> 
> Well.. it looks like a function, returning nothing, taking something  called T which must be an int, if that's the case why not just go:
> 
> cast(int) { .. }
> 
> in which case it looks just like the c++ hack to me.

well, to me it looks very similar to "template cast(T: int)" (which would mean "a specialization of cast for the type int"). It doesn't take T which must be an int, T is a type. I did consider cast(int) {..}, but that looks like you're trying to cast the code block, so I decided to propose something closer to templates. It doesn't have a return type, because it's already obvious what it is and you'd only have to type it twice.


> So in short your example seems to be the c++ hack, with extra characters  making it less clear (to me).
>
>> How would you suggest overloading opCast() if return-type overloading is  not allowed
> 
> No idea, I'll think about this, if, return type overloading is officially  "not going to happen, ever".

Well, my guess is that there's like 30% chance of overloading opcast in some completely new way (i.e. with some new syntax) and like 0.0001% chance of overloading return types, so you might as well start thinking about it.


> Ahh, then we're talking at cross puposes.

I clearly stated that I don't think overloading return types is a good idea, with the exception of opCast(). So why the ahh?


>> I was just trying to show how T may not always be dummy)?
> 
> I dont think it's any different to using a dummy, and using typeof(param)  on it.

Again, it's not a parameter, it's a Type.


> It appears Georg agrees with me.

Well, both my wife and my cousin agree with me, so what? For all I know it's just you with different newsreader settings.. Like I said, start a poll and we'll see how cast is understood..


xs0
February 23, 2005
Regan Heath wrote:

>> on the  return type.  Surely that changes the role of cast?
> 
> 
> Nope. It's comparable to casting a parameter. eg.
> 
> void foo(int a)   {}
> void foo(float a) {}
> 
> foo(cast(int)5);
> 
> cast still converts the type, which is it's role/function, but, the intent  (of the programmer) here is to select the overload and resolve the  ambiguity.
Fair enough, though I can't say I've personally used cast as a method for selecting a particular overload.  I usually use it when assigning a variable that is not quite of the right type.

I think that overloading on return type just smells wrong.  I get the feeling that it would lead to murky areas, and much of what D is all about getting rid of the murk.  For example

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

char c = foo(3); // which foo, and how are the implicit cast rules defined for different architectures?

foo(3); // which foo?

Is there a blindingly obvious example of why we might like to have return type overloading?  I can't think of a language that has it - but I am willing to learn :)

>> It is engineered, yes - but that doesn't mean that it couldn't happen in  the real world.
> 
> 
> True. I'd argue that if it did you have a bigger 'design' problem.

The problem is, in the real world with maintenance on a deadline you don't always get to redesign the project, and often what can be done quick and dirty gets done.

Brad
February 23, 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
February 23, 2005
On Wed, 23 Feb 2005 14:06:05 +1300, <brad@domain.invalid> wrote:
> Regan Heath wrote:
>
>>> on the  return type.  Surely that changes the role of cast?
>>   Nope. It's comparable to casting a parameter. eg.
>>  void foo(int a)   {}
>> void foo(float a) {}
>>  foo(cast(int)5);
>>  cast still converts the type, which is it's role/function, but, the intent  (of the programmer) here is to select the overload and resolve the  ambiguity.
> Fair enough, though I can't say I've personally used cast as a method for selecting a particular overload.  I usually use it when assigning a variable that is not quite of the right type.

Each to thier own.

> I think that overloading on return type just smells wrong.  I get the feeling that it would lead to murky areas, and much of what D is all about getting rid of the murk.  For example
>
> int foo (int i) {}
> uint foo (int i) {}
>
> char c = foo(3); // which foo

Neither, D would give an error, same as it does for.

void foo(int i);
void foo(uint i);

char c = 3;

foo(c);

> , and how are the implicit cast rules defined for different architectures?

No implicit cast rules. All explicit, as required.

> foo(3); // which foo?
>
> Is there a blindingly obvious example of why we might like to have return type overloading?  I can't think of a language that has it - but I am willing to learn :)

C++ has it, with a hack, with it's cast operator.

The problem with the C++ one, according to Walter is it's implicit nature, meaning it can cause hidden bugs. I agree, which is why I think it should be explicit.

For a few examples see my other post to "Georg Wrede" in another branch of this thread.

>>> It is engineered, yes - but that doesn't mean that it couldn't happen in  the real world.
>>   True. I'd argue that if it did you have a bigger 'design' problem.
>
> The problem is, in the real world with maintenance on a deadline you don't always get to redesign the project, and often what can be done quick and dirty gets done.

I know. I believe this behaviour cause more problems than it solves.
But that's my personal philosophy.

Regan
February 23, 2005
On Wed, 23 Feb 2005 01:46:42 +0100, xs0 <xs0@xs0.com> wrote:
>>> And I can't produce a compilable example, because the example included  an overload on return type, which is not possible...
>>  No, your example didn't show one, you described one being added later. All  I want is an example that compiles. Which should be possible.
>
> Bah, my example was obviously both parts of it.

Obviously? I think not.

> Furthermore, I don't see what you could possibly gain by compiling the first part

As I said, I want to fiddle with it.

> , as the code was trivial.

> I also already wrote what will make it compile (cast(int)

Can you post it then, so I can fiddle with it.

> instead of floor()). Finally, it's not possible, because both parts of the example were from the parallel universe in which return overloads in D exist.

I'm interested in the code, before these overloads are used.

> I'm assuming you don't have access to the compiler from that universe? :)

Now you're being silly.

>> No.
>>  If the compiler errors because it cannot pick the overload, then, by  adding a cast I am selecting an overload, the fact that it casts/converts  is the side effect. In other words the point/purpose of the cast changes  depending on what it's used for.
>
> The compiler doesn't error because it cannot pick the overload

Well, it could pick one at random.

> , it errors, because Walter said it should, and for good reasons. There's a large difference there.

I don't think so, given that the compiler will never and should never pick one at random.

> As for the purpose, if you say the purpose of casting can be overload selection, then you'll also have to admit that there are also a large number of other possible purposes to casting.

Name one.

> But then, you can't argue that it is the best syntax for doing so, because all those other purposes are just as valid, and casting is not somehow the natural choice.

I believe it is. Georg would agree (it seems).

> And it's certainly not the only choice..

No, but as I said, I believe it's the best one.

>> I think that, because the cast comes first, you're assuming it's the  purpose, because the overload selection comes second it's a side effect. A  side effect can occur first, just as the purpose can occur second.
>
> No, I think that because cast by itself has the functionality it has, its purpose is to change/convert types. The fact that that functionality can help you resolve overloads is definitely the side-effect; if there were no overloads, cast's purpose in the language would still remain the same.

I give up, you're either ignoring my point, or you simply disagree, either way this is pointless.

>> Yes, you can cast a return type now, now the purpose is to cast. Were it  possible to cast to select an overload, then doing so would be the purpose.
>
> cast is not selecting an overload. The coder is the one selecting the overload by changing the type of arguments. If the coder uses the cast operator, it is just his choice, he could use other techniques (for example, instead of casting int to double you can also add 0.0 to it with the same result).

As above.

>> The purpose of cast now, is not always to convert, but sometimes to select  an overload, the purpose changes depending on where it's used and why (by  definition).
>
> That's just ridiculous. "Cast by definition" has a well-defined purpose and it is not selecting overloads.

As above.

>>> Since you were so smart about compilable examples, I'm surprised you  wrote an example that can't be compiled :P (Sorry, couldn't resist)
>>  I find your comment petty.
>
> Well, I find it petty that you proclaimed my example invalid just because you failed to see what its point was and wanted to compile it. At least I apologized..

I simply asked you to provide one that compiled, and told you why I wanted it.

>> The example above cannot be compiled because it is an error, I was giving  an example of an error case which your syntax would solve.
>  > The same is not true of your example, which was an example of 'before'
>  > the  collision was added.
>
> Ahh, so you wanted to show something without it actually being compilable code. So did I, yet you keep being a smart-ass about it..

Your code was not representing something that couldn't or shouldn't compile, as I've said it showed the code 'before' adding the fictional feature.

My code shouldn't have compiled.

If you can't see the difference...

>> I am not the only one, see Georg's reply.
>
> I'm also not the only one, see Brad's reply. Now it's 2 vs 2. Let's see who wins. Man, this is fun :) (if you failed to notice, I'm trying to be sarcastic)

I noticed, and am resisting the urge to be sarcastic in return.

>> I understand everything you've said above, and I agree it works as you've  said.
>>  But, the point you're missing is that the 'intent' of a statement is not  necessarily the same as the effect generated by the compiler.
>
> The 'intent' of statements has nothing to do with the language (even more so outside computers). And even so, if I see
>
> int result=cast(int)func(...);
>
> I interpret the so-called intent as "call func(); because it doesn't return int, cast the return value to int". I'll also bet that that would be the interpretation of the vast majority of programmers. Start a poll or something, if you don't believe me.

Sure, that's the literal meaning, and most people will agree, I do. But, the overall goal is to call a function and for it to result in an int.

If cast(int)func causes the function that returns an int to be called then the overall goal has been achieved, just not in the exact manner you have described.

As I said, it's the difference between effect and intent.

>> This behaviour/method is generally accepted and understood by programmers,  and extending it to include return values will be the same, generally  accepted and understood.
>
> Well, I'd say that if someone understands cast as mainly being the thing to select overloads, he's a newbie (no offense meant to newbies; I was once a newbie too and also believed many wrong things).

I never said "mainly".

>> Further it requires no additional syntax and is easily parsable.
>
> Why would no additional syntax for new functionality be a plus?

It depends on each situation, but generally speaking no new syntax means less to learn, less to parse etc.

> And why would it be more easily parsable than any other syntax?

Because it's already being parsed.

> If anything, it's harder - you need to figure out whether there is a function call involved, and if so, if it has overloaded return types and choose some completely different functionality based on the answers to those questions.

That isn't parsing, it's process the results of parsing.

>>> no, it isn't secondary, just the opposite (and casting int to long is  converting, the first is 32 bits, the other is 64 bits).
>>  It's not converting per-se, more extending and setting bits to 0.
>
> Whatever.

I see you're no longer interested, so I'll stop now.

Regan
February 23, 2005
On Tue, 22 Feb 2005 18:49:43 -0600, Chris Sauls <ibisbasenji@gmail.com> wrote:
> Regan Heath wrote:
>>> i = cast(int) foo;        // foo.opCast(i);
>>> j = (cast(int) foo) + 5;  // int tmp; foo.opCast(foo); j = tmp + 5;
>>  Typo? did you mean foo.opCast(tmp)?
>
> Woops. I did.

I thought so :)

>>> f = cast(float) foo;      // throws an exception (opCast returned false)
>>  Ok, so this is different behaviour to when you cast a class to an  incorrect base eg.
>>  class A {}
>> class B {}
>> B b = new B();
>> A a;
>>  a = cast(A)b;  //a == null after this call, no exception thrown.
>
> Or you could set the result param to null and return true, to mimic the language-defined cast.

Indeed. So the questions are:

- Is it better to throw an exception or not?
- If it's better, should the built in casts do it?
- Can that be decided in general or should it be decided on a case by case basis?

>>  Could it, should it be the same?
>> eg. opCast returns false, set lhs to typeof(lhs).init?
>>
>
> I hadn't thought of that...

It would mimic the built in cast behaviour.

>> Couldn't the tmp be optimised away?
>
> Surely.

In which case I think this idea has merit.
Can anyone think of anything 'bad'(tm) about it?

Regan
February 23, 2005
On Wed, 23 Feb 2005 02:09:08 +0100, xs0 wrote:

>> I still think the signature for opCast should change to something like:
>>   bit opCast(inout T result);
>> 
>> Return 'true' on success, 'false' on failure (could not cast for some reason).  Then we could have as many opCasts as we need.
> 
> Two questions:
> 
> 1) why inout not just out?
> 2) why not have void return type, and in case of failure you can just
> throw an exception yourself? presumably this would be a rare occasion,
> and you can avoid a bunch of checks this way..
> 
> Otherwise, not a bad idea at all, if you ask me. The only issue I can see is that the result is not returned (as in "return something;"), so it can't be used in expressions as easily (i.e. you need those temporary variables, which probably complicates implementation significantly).

Agree, this is not a bad compromise.

  void opCast(out T result);

Eg.

  class Foo{
    int val;

   void opCast(out uint result)
   {
     if (val < 0)
       throw new Exception(
              std.string.format("Foo: Cannot convert %d to a uint", val));
     result = cast(uint)val * 100;
   }
  }
  . . .
  Foo A = new Foo;
  uint y = A;

  . . .

Hmmm... this I could really use right now!
-- 
Derek
Melbourne, Australia
23/02/2005 12:38:22 PM