April 20, 2005
"Ben Hinkle" <ben.hinkle@gmail.com> wrote in message news:d3q2tm$1pag$1@digitaldaemon.com...
> > Who said anything about heteregeneous types? Object[] means whatever, including Foo[], because you can implicitly cast Foo[] to Object[]. I.e. everything is an Object.
>
> hmm. It is pretty scary if we are allowed to do
> class Foo{int foo;}
> void main() {
>   Foo[] x;
>   x.length = 2;
>   Object[] y = x; // implicitly cast
>   y[0] = new Object;
>   x[0].foo = 10; // Object is not a Foo - yikes!
> }
>
>
>

Don't be scared so easily.  :)

Seriously though, the only problem with that is the potential that the programmer may not have meant to do an identity assignment when they typed "y=x" and therefore may not have planned for the side effects of x and y having the same identity.  A situation that could be easily avoided if separate value and identity assignment operators were introduces.

I don't see anything being done there that lacks the "potential to be useful" in the right situation.  Sure, it could be misused, but that's true of anything that can be used at all.  The only hazard I see is the potential for it to be done accidentally, and that's not likely.

TZ


April 20, 2005
"TechnoZeus" <TechnoZeus@PeoplePC.com> wrote in message news:d457b2$2oai$1@digitaldaemon.com...
> "Ben Hinkle" <ben.hinkle@gmail.com> wrote in message news:d3q2tm$1pag$1@digitaldaemon.com...
>> > Who said anything about heteregeneous types? Object[] means whatever,
>> > including Foo[], because you can implicitly cast Foo[] to Object[].
>> > I.e.
>> > everything is an Object.
>>
>> hmm. It is pretty scary if we are allowed to do
>> class Foo{int foo;}
>> void main() {
>>   Foo[] x;
>>   x.length = 2;
>>   Object[] y = x; // implicitly cast
>>   y[0] = new Object;
>>   x[0].foo = 10; // Object is not a Foo - yikes!
>> }
>>
>>
>>
>
> Don't be scared so easily.  :)
>
> Seriously though, the only problem with that is the potential that the programmer may not have meant to do an identity assignment when they typed "y=x" and therefore may not have planned for the side effects of x and y having the same identity.  A situation that could be easily avoided if separate value and identity assignment operators were introduces.
>
> I don't see anything being done there that lacks the "potential to be useful" in the right situation.  Sure, it could be misused, but that's true of anything that can be used at all.  The only hazard I see is the potential for it to be done accidentally, and that's not likely.

Here's another example that might be more common:
class Foo{}
class Bar:Foo{}
void clamp(Foo[] x) { // cheesy function that changes x
  foreach(inout Foo y; x) {
    if (y < 0)  y = new Foo(0);
  }
}
...
Bar[] y;
...
fill(y); // y potentially has a Foo in it - a type violation.

Implicitly casting shouldn't open up such a large hole in the type system. At least in C violating the type system requires a cast.


April 20, 2005
> Here's another example that might be more common:
> class Foo{}
> class Bar:Foo{}
> void clamp(Foo[] x) { // cheesy function that changes x
>   foreach(inout Foo y; x) {
>     if (y < 0)  y = new Foo(0);
>   }
> }
> ....
> Bar[] y;
> ....
> fill(y); // y potentially has a Foo in it - a type violation.

But here, you're calling basically fillSomeElementsWithFoos(Bar[]). You obviously can't expect the result to be Bars only.. I mean, the problem is not the implicit cast, but the type of y, when used with fill(). Even if you call fill(cast(Foo[])y), it can still contain a Foo, while its type is Bar[]..

My point is that you need to know what your functions do, otherwise you'll run into problems, with implicit casting of arrays or not.

If implicit casting of arrays were to be removed, it would mean that each function that works on an array works exactly on that kind of array and nothing else. The result would be just requiring a ton of casts, which solves nothing, yet could cause more problems than it solves - in the case above, you would type "fill(cast(Foo[])y)". But then, when you write fill(Bar[]) to handle Bar[] filling, it won't get called, because you cast the real type away..


xs0
April 20, 2005
"xs0" <xs0@xs0.com> wrote in message news:d45psv$l74$1@digitaldaemon.com...
>
>> Here's another example that might be more common:
>> class Foo{}
>> class Bar:Foo{}
>> void clamp(Foo[] x) { // cheesy function that changes x
>>   foreach(inout Foo y; x) {
>>     if (y < 0)  y = new Foo(0);
>>   }
>> }
>> ....
>> Bar[] y;
>> ....
>> fill(y); // y potentially has a Foo in it - a type violation.
>
> But here, you're calling basically fillSomeElementsWithFoos(Bar[]). You obviously can't expect the result to be Bars only.. I mean, the problem is not the implicit cast, but the type of y, when used with fill(). Even if you call fill(cast(Foo[])y), it can still contain a Foo, while its type is Bar[]..

A core principle of upcasting is substitutability. If Bar is derived from Foo then everything that takes a Foo can take a Bar. With arrays, though, an array of Foo cannot be substituted with an array of Bar since an array of Foo can hold an aribitrary Foo while an array of Bar cannot. Implicitly upcasting containers is a big type safety hole if the result is allowed to change the contents of the container.

> My point is that you need to know what your functions do, otherwise you'll run into problems, with implicit casting of arrays or not.

Sure, but with enough care we wouldn't have any bugs at all and we'd have no need for type checking or type-safety. Imagine how hard it would be to track down something as subtle as this.

> If implicit casting of arrays were to be removed, it would mean that each function that works on an array works exactly on that kind of array and nothing else.

True, but that's life. I believe sacrificing type-safety is too high a price to pay for the convenience of implicit array coversions. In my D coding, for instance, I haven't ever needed implicit array conversions.

> The result would be just requiring a ton of casts, which solves nothing, yet could cause more problems than it solves - in the case above, you would type "fill(cast(Foo[])y)". But then, when you write fill(Bar[]) to handle Bar[] filling, it won't get called, because you cast the real type away..

Casting is the accepted way to blow away type safety. Let's keep it that way.


April 20, 2005
Ben Hinkle wrote:
> Implicitly upcasting containers is a big type safety hole if the result is allowed to change the contents of the container.

> Casting is the accepted way to blow away type safety. Let's keep it that way. 

See, it's not that simple. To have more type safety, you'd require more casts, but they result in less type safety, so it may actually be conterproductive..

Java and C++ both allow implicit array upcasts, too, so I wouldn't say that people can't live with it?

OTOH, If you look at Java's generics spec, they addressed this issue somewhat by having parameters be one of (with List as an example)
List<T> - accepts exactly T
List<? super T> - accepts T and T's supertypes
List<? extends T> - accepts T and T's subtypes

The first is read/write, the second is write-only, the third is read-only, for obvious reasons. Considering how write-only is significantly rarer than the other two, perhaps the best option with D would be to get support and a new syntax for demanding an exact type in function parameters, which is where most of the problem is. Perhaps

void func(Type! a)
void func(Type![] a)
void func(Type!(int)! a)
void func(Type!(int)![] a)

Read-only functions would work as they do now, which is fine because they don't suffer from type unsafety. Read/write functions would be able to demand an exact type (which may or may not be useful outside the context of arrays, but I don't see why it shouldn't be allowed). Finally, for write-only functions, I guess the way to go would be again requiring an exact type, which'd require an explicit cast. However,

this case of casting is not as problematic, because if you have like a FooSuperclass[] and cast it to Foo[] when calling a func, you're still keeping it locally as a FooSuperclass[], which it still is even after it gets filled with Foos (unlike casting a Subclass[] to Foo[], which is then no longer a Subclass[]).

Finally, to solve the local issue of

Foo[] a=...;
Object[] b=a;
b[0]=new Object;
a[0].do();

I'd say that implicit casting of arrays can only be done when calling functions, where it's only really needed anyway. Locally, you can always use the other type in the first place, I guess..

Thoughts?


xs0
April 20, 2005
"xs0" <xs0@xs0.com> wrote in message news:d4632l$uke$1@digitaldaemon.com...
>
> Ben Hinkle wrote:
>> Implicitly upcasting containers is a big type safety hole if the result is allowed to change the contents of the container.
>
>> Casting is the accepted way to blow away type safety. Let's keep it that way.
>
> See, it's not that simple. To have more type safety, you'd require more casts, but they result in less type safety, so it may actually be conterproductive..

I'm suggesting the user be more explicit about when they are risking type safety. The implicit cast is hiding some potentially very dangerous code.

> Java and C++ both allow implicit array upcasts, too, so I wouldn't say that people can't live with it?

Note Java allows the implicit cast but doesn't allow the assignment that would break the type-safety. http://java.sun.com/docs/books/jls/second_edition/html/arrays.doc.html#11430 I'm not suggesting D go the Java route and store all the run-time array type information. I do suggest D make the coder more explicitly acknowledge when they are taking a risk with type safety. I agree C++ allows implicit pointer upcasting but everyone grumbles about C++ type-safety holes like that. It's one of C++'s weaknesses in following C so closely.


April 20, 2005
Ben Hinkle wrote:
>>>Implicitly upcasting containers is a big type safety hole if the result is allowed to change the contents of the container.
>>
>>>Casting is the accepted way to blow away type safety. Let's keep it that way.
>>
>>See, it's not that simple. To have more type safety, you'd require more casts, but they result in less type safety, so it may actually be conterproductive..
> 
> I'm suggesting the user be more explicit about when they are risking type safety. The implicit cast is hiding some potentially very dangerous code.

I agree, but I don't think forcing them to explicitly cast is the best solution. See my previous post for what I'd suggest. Another option is simply using inout, although in that case you can't cast even if you want to (at least not very easily - you need a new var and a cast back).. btw, what I suggested is exactly the same regarding type safety as inout, the only inout's downside is that the caller's reference can be overwritten..


>>Java and C++ both allow implicit array upcasts, too, so I wouldn't say that people can't live with it?
> 
> Note Java allows the implicit cast but doesn't allow the assignment that would break the type-safety.

True, yet it still only happens at run-time, and it may even be unneeded:

Object[] blah=someFunc(); // returns a new String[];
blah[0]=new Whatever(blah[0]);


Although it's true - in D such an error will most probably not even get detected right away, which can probably cause quite a havoc, if you're lucky.. Again, I don't think forcing explicit casts will help; if a change is going to happen, it should be where the function is, not where the caller is (in other words, type safety should be enforced already when defining functions, not when calling them).


> I'm not suggesting D go the Java route and store all the run-time array type information. I do suggest D make the coder more explicitly acknowledge when they are taking a risk with type safety. 

> everyone grumbles about C++ type-safety holes. It's
> one of C++'s weaknesses in following C so closely. 

Agreed on both counts.


xs0
April 20, 2005
xs0 wrote:
> Ben Hinkle wrote:
> 
>>>> Implicitly upcasting containers is a big type safety hole if
>>>> the result is allowed to change the contents of the container.
>>> 
>>>> Casting is the accepted way to blow away type safety. Let's
>>>> keep it that way.
>>> 
>>> See, it's not that simple. To have more type safety, you'd
>>> require more casts, but they result in less type safety, so it
>>> may actually be conterproductive..
>> 
>> I'm suggesting the user be more explicit about when they are
>> risking type safety. The implicit cast is hiding some potentially
>> very dangerous code.
> 
> I agree, but I don't think forcing them to explicitly cast is the
> best solution. See my previous post for what I'd suggest. Another
> option is simply using inout, although in that case you can't cast
> even if you want to (at least not very easily - you need a new var
> and a cast back).. btw, what I suggested is exactly the same
> regarding type safety as inout, the only inout's downside is that the
> caller's reference can be overwritten..
> 
>>> Java and C++ both allow implicit array upcasts, too, so I
>>> wouldn't say that people can't live with it?
>> 
>> Note Java allows the implicit cast but doesn't allow the assignment
>>  that would break the type-safety.
> 
> True, yet it still only happens at run-time, and it may even be
> unneeded:
> 
> Object[] blah=someFunc(); // returns a new String[];
> blah[0]=new Whatever(blah[0]);

Anybody writing that piece of code is asking for First Blood. Out of his own nose.

There are limits to things. We don't want D to be jerk-proof. Probably there ought to be a law that says this kind of people should not have cutlery in the kitchen. (Why? Well, they might carve the dog or the postman, just out of ignorance: Oh, I just wanted to check if this knife is as sharp as they say on TV, Officer.)

> Although it's true - in D such an error will most probably not even
> get detected right away, which can probably cause quite a havoc, if
> you're lucky.. Again, I don't think forcing explicit casts will help;

April 20, 2005
>> True, yet it still only happens at run-time, and it may even be
>> unneeded:
>>
>> Object[] blah=someFunc(); // returns a new String[];
>> blah[0]=new Whatever(blah[0]);
> 
> Anybody writing that piece of code is asking for First Blood. Out of his own nose.

Why exactly? The point was that

a) the code handles any Object
b) Java would needlessly throw an exception here


> There are limits to things. We don't want D to be jerk-proof. Probably there ought to be a law that says this kind of people should not have cutlery in the kitchen. (Why? Well, they might carve the dog or the postman, just out of ignorance: Oh, I just wanted to check if this knife is as sharp as they say on TV, Officer.)

Bah, I can think of some other laws as well :P


xs0
April 20, 2005
xs0 wrote:
> 
>>> True, yet it still only happens at run-time, and it may even be
>>> unneeded:
>>>
>>> Object[] blah=someFunc(); // returns a new String[];
>>> blah[0]=new Whatever(blah[0]);
>>
>>
>> Anybody writing that piece of code is asking for First Blood. Out of his own nose.
> 
> 
> Why exactly? The point was that
> 
> a) the code handles any Object
> b) Java would needlessly throw an exception here

If the code handles "any Object", then why be surprised if the compiler doesn't catch the situation where you suddenly don't want it to?

>> There are limits to things. We don't want D to be jerk-proof. Probably there ought to be a law that says this kind of people should not have cutlery in the kitchen. (Why? Well, they might carve the dog or the postman, just out of ignorance: Oh, I just wanted to check if this knife is as sharp as they say on TV, Officer.)
> 
> 
> Bah, I can think of some other laws as well :P
> 
> 
> xs0