Cast problem

classic Classic list List threaded Threaded
12 messages Options
Reply | Threaded
Open this post in threaded view
|

Cast problem

Martijn Loots
Hi.

I'm trying to get this working:

   class A {
     public var v: Int;
     public function new() v = 0
     public function copy() { var dst = new A(); dst.v = v; return dst; }
   }

   class B extends A {
     public function foo() trace("value = " + v)
   }

   class Tester {
     public static function main() {
       var alist: List<A> = new List();
       var blist: List<B> = new List();

       // filling alist ...
       var a : A;
       a = new A(); a.v = 1; alist.add(a);
       a = new A(); a.v = 2; alist.add(a);

       // now copy to blist
       for (a in alist) blist.add(a.copy()); // <= ERROR HERE

       // now use blist
       for (b in blist) b.foo();
     }
   }

This throws an error (as it should) like:

   Tester.hx:22: characters 21-40 : A should be B
   Tester.hx:22: characters 21-40 : For function argument 'item'

because it needs a cast from A to B. I know it's unsafe, but
how do I get such a thing to compile..?

Thanks in advance.
--
-Martijn    @..@    ( Martijn Loots       -  Hengelo  [NL] )
-          (`--')   ( martijn<@>cosix.com -  www.cosix.com )
-         ( >__< )  ----------------------------------------
-         ^^^  ^^^  (   Netwerken, Security, Open Source   )

--
haXe - an open source web programming language
http://haxe.org
Reply | Threaded
Open this post in threaded view
|

Re: Cast problem

Ian Martins
Martijn Loots wrote:

> Hi.
>
> I'm trying to get this working:
>
>   class A {
>     public var v: Int;
>     public function new() v = 0
>     public function copy() { var dst = new A(); dst.v = v; return dst; }
>   }
>
>   class B extends A {
>     public function foo() trace("value = " + v)
>   }
>
>   class Tester {
>     public static function main() {
>       var alist: List<A> = new List();
>       var blist: List<B> = new List();
>
>       // filling alist ...
>       var a : A;
>       a = new A(); a.v = 1; alist.add(a);
>       a = new A(); a.v = 2; alist.add(a);
>
>       // now copy to blist
>       for (a in alist) blist.add(a.copy()); // <= ERROR HERE
>
>       // now use blist
>       for (b in blist) b.foo();
>     }
>   }
>
> This throws an error (as it should) like:
>
>   Tester.hx:22: characters 21-40 : A should be B
>   Tester.hx:22: characters 21-40 : For function argument 'item'
>
> because it needs a cast from A to B. I know it's unsafe, but
> how do I get such a thing to compile..?
>
> Thanks in advance.
you can get it to compile by changing it to this:
    for (a in alist) blist.add(cast a.copy());

but it'll crash at runtime since you're calling "foo" on objects of type
A, which don't define it.



--
haXe - an open source web programming language
http://haxe.org
Reply | Threaded
Open this post in threaded view
|

Re: Cast problem

Martijn Loots
On Fri, 1 May 2009, Ian Martins wrote:

> Martijn Loots wrote:
>> Hi.
>>
>> I'm trying to get this working:
>>
>>   class A {
>>     public var v: Int;
>>     public function new() v = 0
>>     public function copy() { var dst = new A(); dst.v = v; return dst; }
>>   }
>>
>>   class B extends A {
>>     public function foo() trace("value = " + v)
>>   }
>>
>>   class Tester {
>>     public static function main() {
>>       var alist: List<A> = new List();
>>       var blist: List<B> = new List();
>>
>>       // filling alist ...
>>       var a : A;
>>       a = new A(); a.v = 1; alist.add(a);
>>       a = new A(); a.v = 2; alist.add(a);
>>
>>       // now copy to blist
>>       for (a in alist) blist.add(a.copy()); // <= ERROR HERE
>>
>>       // now use blist
>>       for (b in blist) b.foo();
>>     }
>>   }
>>
>> This throws an error (as it should) like:
>>
>>   Tester.hx:22: characters 21-40 : A should be B
>>   Tester.hx:22: characters 21-40 : For function argument 'item'
>>
>> because it needs a cast from A to B. I know it's unsafe, but
>> how do I get such a thing to compile..?
>>
>> Thanks in advance.
> you can get it to compile by changing it to this:
>   for (a in alist) blist.add(cast a.copy());
>
> but it'll crash at runtime since you're calling "foo" on objects of type A,
> which don't define it.
>
Exactly. I thought that by referring to a B class object, it
would automatically recognize B's methods... :(

I have a number of classes subclassed like this and they
all bug at runtime. Is there any solution to use B's methods ?

Thanks in advance,
--
-Martijn    @..@    ( Martijn Loots       -  Hengelo  [NL] )
-          (`--')   ( martijn<@>cosix.com -  www.cosix.com )
-         ( >__< )  ----------------------------------------
-         ^^^  ^^^  (   Netwerken, Security, Open Source   )

--
haXe - an open source web programming language
http://haxe.org
Reply | Threaded
Open this post in threaded view
|

Re: Cast problem

Ian Martins
Martijn Loots wrote:

> On Fri, 1 May 2009, Ian Martins wrote:
>
>> Martijn Loots wrote:
>>> Hi.
>>>
>>> I'm trying to get this working:
>>>
>>>   class A {
>>>     public var v: Int;
>>>     public function new() v = 0
>>>     public function copy() { var dst = new A(); dst.v = v; return
>>> dst; }
>>>   }
>>>
>>>   class B extends A {
>>>     public function foo() trace("value = " + v)
>>>   }
>>>
>>>   class Tester {
>>>     public static function main() {
>>>       var alist: List<A> = new List();
>>>       var blist: List<B> = new List();
>>>
>>>       // filling alist ...
>>>       var a : A;
>>>       a = new A(); a.v = 1; alist.add(a);
>>>       a = new A(); a.v = 2; alist.add(a);
>>>
>>>       // now copy to blist
>>>       for (a in alist) blist.add(a.copy()); // <= ERROR HERE
>>>
>>>       // now use blist
>>>       for (b in blist) b.foo();
>>>     }
>>>   }
>>>
>>> This throws an error (as it should) like:
>>>
>>>   Tester.hx:22: characters 21-40 : A should be B
>>>   Tester.hx:22: characters 21-40 : For function argument 'item'
>>>
>>> because it needs a cast from A to B. I know it's unsafe, but
>>> how do I get such a thing to compile..?
>>>
>>> Thanks in advance.
>> you can get it to compile by changing it to this:
>>   for (a in alist) blist.add(cast a.copy());
>>
>> but it'll crash at runtime since you're calling "foo" on objects of
>> type A, which don't define it.
>>
> Exactly. I thought that by referring to a B class object, it
> would automatically recognize B's methods... :(
>
> I have a number of classes subclassed like this and they
> all bug at runtime. Is there any solution to use B's methods ?
>
> Thanks in advance,
it won't work because the objects are still A's. you need a way of
creating B's from A's.

you could replace
    public function copy() { var dst = new A(); dst.v = v; return dst; }
with:
    public function copy() { var dst = new B(); dst.v = v; return dst; }
(copy is no longer a good name for it)




--
haXe - an open source web programming language
http://haxe.org
Reply | Threaded
Open this post in threaded view
|

Re: Cast problem

David Bergman
In reply to this post by Martijn Loots
On May 1, 2009, at 1:33 PM, Martijn Loots wrote:

On Fri, 1 May 2009, Ian Martins wrote:

Martijn Loots wrote:
Hi.
I'm trying to get this working:

 class A {
   public var v: Int;
   public function new() v = 0
   public function copy() { var dst = new A(); dst.v = v; return dst; }
 }

 class B extends A {
   public function foo() trace("value = " + v)
 }

 class Tester {
   public static function main() {
     var alist: List<A> = new List();
     var blist: List<B> = new List();

     // filling alist ...
     var a : A;
     a = new A(); a.v = 1; alist.add(a);
     a = new A(); a.v = 2; alist.add(a);

     // now copy to blist
     for (a in alist) blist.add(a.copy()); // <= ERROR HERE

     // now use blist
     for (b in blist) b.foo();
   }
 }
This throws an error (as it should) like:

 Tester.hx:22: characters 21-40 : A should be B
 Tester.hx:22: characters 21-40 : For function argument 'item'
because it needs a cast from A to B. I know it's unsafe, but
how do I get such a thing to compile..?
Thanks in advance.
you can get it to compile by changing it to this:
 for (a in alist) blist.add(cast a.copy());

but it'll crash at runtime since you're calling "foo" on objects of type A, which don't define it.

Exactly. I thought that by referring to a B class object, it
would automatically recognize B's methods... :(

What are you trying to do? Yes, by using the 'cast' that Martiin suggested, you tell the compiler not to worry, that the object indeed is of type B. The problem is that is *not* an object of type B, i.e., you are putting the compiler in a false sense of security ;-)

What did you want to happen here during execution (now that you have overcome your initial problem, of not compiling, by that evil 'cast')? There was a reason that the compiler complained. It suspected that the runtime type will not be compatible with the blist, since its static type is not, and in fact, the compiler was totally right!

TECH NOTE: remember, we have operational covariance (...) w.r.t. collections in haXe, although the types are invariant, and we do not have Scala's ability to steer that decision...


I have a number of classes subclassed like this and they
all bug at runtime. Is there any solution to use B's methods ?

What is the solution you *would like* to see for objects not being of type B, as in your example above, when issued methods that only a B understands? NOTE: you understand that the 'copy' method of A always generates A objects, right?

TECH NOTE: I ask because it is a common concern in OO languages of how to make specific copies, shallow or deep, of subclasses objects, and avoid the slicing; especially true in C++

There are two variants regarding methods in haXe:

1. If you do have a hierarchical relation between the types (as you do...): downcast to a more specific type that indeed has the method in the signature. For it not to crash in runtime, the object should be of that more specific type...

2. If you do not have a hierarchical relation between the types: skip typing and use dynamic invocation. For it not to crash (or at least throw an exception...), the object should be of a type that has that method in its signature. One way to achieve that is

untyped b.foo()

    but I would recommend at least using an anonymous "OCamlish" type signature, such as

typedef HandleFoo = { foo: Void -> Void };

    and assigning to a variable of that type.

/David

--
haXe - an open source web programming language
http://haxe.org
Reply | Threaded
Open this post in threaded view
|

Re: Cast problem

David Bergman
On May 1, 2009, at 2:20 PM, David Bergman wrote:

> What are you trying to do? Yes, by using the 'cast' that Martiin  
> suggested,

Two apologies are in order:

1. Martijn Loots' name is (as you see not with 'ii' but 'ij')

2. It was not Martijn that suggested the case, but Ian (Martins) that  
suggested it *to* Martijn.

Oy.

/David

--
haXe - an open source web programming language
http://haxe.org
Reply | Threaded
Open this post in threaded view
|

Re: Cast problem

Martijn Loots
In reply to this post by David Bergman
On Fri, 1 May 2009, David Bergman wrote:

> On May 1, 2009, at 1:33 PM, Martijn Loots wrote:
>
>> On Fri, 1 May 2009, Ian Martins wrote:
>>
>>> Martijn Loots wrote:
>>>> Hi.
>>>> I'm trying to get this working:
>>>>
>>>> class A {
>>>>   public var v: Int;
>>>>   public function new() v = 0
>>>>   public function copy() { var dst = new A(); dst.v = v; return dst; }
>>>> }
>>>>
>>>> class B extends A {
>>>>   public function foo() trace("value = " + v)
>>>> }
>>>>
>>>> class Tester {
>>>>   public static function main() {
>>>>     var alist: List<A> = new List();
>>>>     var blist: List<B> = new List();
>>>>
>>>>     // filling alist ...
>>>>     var a : A;
>>>>     a = new A(); a.v = 1; alist.add(a);
>>>>     a = new A(); a.v = 2; alist.add(a);
>>>>
>>>>     // now copy to blist
>>>>     for (a in alist) blist.add(a.copy()); // <= ERROR HERE
>>>>
>>>>     // now use blist
>>>>     for (b in blist) b.foo();
>>>>   }
>>>> }
>>>> This throws an error (as it should) like:
>>>>
>>>> Tester.hx:22: characters 21-40 : A should be B
>>>> Tester.hx:22: characters 21-40 : For function argument 'item'
>>>> because it needs a cast from A to B. I know it's unsafe, but
>>>> how do I get such a thing to compile..?
>>>> Thanks in advance.
>>> you can get it to compile by changing it to this:
>>> for (a in alist) blist.add(cast a.copy());
>>>
>>> but it'll crash at runtime since you're calling "foo" on objects of type
>>> A, which don't define it.
>>>
>> Exactly. I thought that by referring to a B class object, it
>> would automatically recognize B's methods... :(
>
Before I say anything at all, *thank* you very much David for
your long and detailed reply. I hope I'm now allowed to utter
some more little abusive haXe-attacks... ;-)

> What are you trying to do? Yes, by using the 'cast' that Martiin suggested,
> you tell the compiler not to worry, that the object indeed is of type B. The
> problem is that is *not* an object of type B, i.e., you are putting the
> compiler in a false sense of security ;-)
>
Yep...

> What did you want to happen here during execution (now that you have overcome
> your initial problem, of not compiling, by that evil 'cast')? There was a
> reason that the compiler complained. It suspected that the runtime type will
> not be compatible with the blist, since its static type is not, and in fact,
> the compiler was totally right!
>
ROTFL... I know... I know... but, as I still see it now, I *need* to
f*ck it up a bit (remember the good old days of C... pointing to some
other struct did not harm anyone, as long as you did use the struct
correctly)

> TECH NOTE: remember, we have operational covariance (...) w.r.t. collections
> in haXe, although the types are invariant, and we do not have Scala's ability
> to steer that decision...
>
>>
>> I have a number of classes subclassed like this and they
>> all bug at runtime. Is there any solution to use B's methods ?
>
> What is the solution you *would like* to see for objects not being of type B,
> as in your example above, when issued methods that only a B understands?
> NOTE: you understand that the 'copy' method of A always generates A objects,
> right?
>
Correct. As Ian Martins mentioned earlier, I may need a local copy
method in class B. But because B only promotes additional methods
and no additional variables, I hoped that just using class A's
copy would be sufficient...

> TECH NOTE: I ask because it is a common concern in OO languages of how to
> make specific copies, shallow or deep, of subclasses objects, and avoid the
> slicing; especially true in C++
>
> There are two variants regarding methods in haXe:
>
> 1. If you do have a hierarchical relation between the types (as you do...):
> downcast to a more specific type that indeed has the method in the signature.
> For it not to crash in runtime, the object should be of that more specific
> type...
>
Yes, 1. is my case. Classes containing classes containing classes etc.
I have to split my code, because it will run in 2 distinct phases on
different machines, but also because it will become too complex and
error prone. As I thought, I need to split it into 3 parts:

  a) data structure classes with elementary data handling methods
     (refer class A).

  b) a phase 1 program using subclasses of A with additional methods
     (ref. class B) that parses XML and external data.

  c) a phase 2 program using subclasses of A with additional methods
     (ref. class C, not mentioned in my example) that uses the
     data structure created during phase 1 to generate a Flash
     movie.

It works right now, using big classes that contain all data handling,
phase 1 and phase 2 code, the used phase code allowed to be compiled
using "#if phaseX ... #end" compile flags. No good programming practice
either.. :(

If I can solve this specific issue, the remaining splitting job will
be very straightforward. If I can't solve this, I'll have to duplicate
code and that's never a good solution...

> 2. If you do not have a hierarchical relation between the types: skip typing
> and use dynamic invocation. For it not to crash (or at least throw an
> exception...), the object should be of a type that has that method in its
> signature. One way to achieve that is
> untyped b.foo()
>
>   but I would recommend at least using an anonymous "OCamlish" type
> signature, such as
>
> typedef HandleFoo = { foo: Void -> Void };
>
>   and assigning to a variable of that type.
>
> /David
>
Hmm, maybe I can mix it a bit... Thanks again. Some more issues
to consider... :-)

Grtz,
--
-Martijn    @..@    ( Martijn Loots       -  Hengelo  [NL] )
-          (`--')   ( martijn<@>cosix.com -  www.cosix.com )
-         ( >__< )  ----------------------------------------
-         ^^^  ^^^  (   Netwerken, Security, Open Source   )

--
haXe - an open source web programming language
http://haxe.org
Reply | Threaded
Open this post in threaded view
|

Re: Cast problem

Martijn Loots
In reply to this post by David Bergman
On Fri, 1 May 2009, David Bergman wrote:

> On May 1, 2009, at 2:20 PM, David Bergman wrote:
>
>> What are you trying to do? Yes, by using the 'cast' that Martiin suggested,
>
> Two apologies are in order:
>
> 1. Martijn Loots' name is (as you see not with 'ii' but 'ij')
>
> 2. It was not Martijn that suggested the case, but Ian (Martins) that
> suggested it *to* Martijn.
>
> Oy.
>
LOL

Apologies (not necessary IMO though) accepted.. ;-)

My name is difficult enough for none-Dutch speakers...

--
-Martijn    @..@    ( Martijn Loots       -  Hengelo  [NL] )
-          (`--')   ( martijn<@>cosix.com -  www.cosix.com )
-         ( >__< )  ----------------------------------------
-         ^^^  ^^^  (   Netwerken, Security, Open Source   )

--
haXe - an open source web programming language
http://haxe.org
Reply | Threaded
Open this post in threaded view
|

Re: Cast problem

David Bergman
In reply to this post by Martijn Loots
On May 1, 2009, at 3:26 PM, Martijn Loots wrote:

Before I say anything at all, *thank* you very much David for
your long and detailed reply. I hope I'm now allowed to utter
some more little abusive haXe-attacks... ;-)

My response was probably overly cryptic, which is often the case with me :-|

What are you trying to do? Yes, by using the 'cast' that Martiin suggested, you tell the compiler not to worry, that the object indeed is of type B. The problem is that is *not* an object of type B, i.e., you are putting the compiler in a false sense of security ;-)

Yep...

You are a bad boy ;-)

What did you want to happen here during execution (now that you have overcome your initial problem, of not compiling, by that evil 'cast')? There was a reason that the compiler complained. It suspected that the runtime type will not be compatible with the blist, since its static type is not, and in fact, the compiler was totally right!

ROTFL... I know... I know... but, as I still see it now, I *need* to
f*ck it up a bit (remember the good old days of C... pointing to some
other struct did not harm anyone, as long as you did use the struct
correctly)

In haXe, one expresses that laissez-faire attitude via the Dynamic type, not by fooling the type system via 'cast'. Furthermore, the cases where one can truly ignore type often revolve around storing - as you say - "pointers" to anonymous stuff in a container. Not operating on those poor - then misinterpreted - objects.

TECH NOTE: remember, we have operational covariance (...) w.r.t. collections in haXe, although the types are invariant, and we do not have Scala's ability to steer that decision...

I have a number of classes subclassed like this and they
all bug at runtime. Is there any solution to use B's methods ?

What is the solution you *would like* to see for objects not being of type B, as in your example above, when issued methods that only a B understands? NOTE: you understand that the 'copy' method of A always generates A objects, right?

Correct. As Ian Martins mentioned earlier, I may need a local copy
method in class B. But because B only promotes additional methods
and no additional variables, I hoped that just using class A's
copy would be sufficient...

You hope that their layout happen to coincide? Well, yes and no. The pure structural layout may very well coincide, but at least the virtual tables - pointing to the implementations of methods - do not. This is a bit dependent on the intended target (for instance, ActionScript handles methods quite differently from C++...) but in most cases, you do end up with a (conceptual or actual) virtual table that is one entry too short when forcing a(n actual) A to become a B, i.e., the 'cast' does not convert the virtual table.

One should not rely on memory layout tricks, since haXe - as a language proper - says very little to reassure one of anything there and the current target platforms vary wildly.

TECH NOTE: I ask because it is a common concern in OO languages of how to make specific copies, shallow or deep, of subclasses objects, and avoid the slicing; especially true in C++

REFLEXIVE AND TANGENTIAL NOTE: if you really want to create an object of the called type when "copying" (only what the super class A considers vital would be copied) you could do:

    var dst: A = cast Type.createEmptyInstance(Type.getClass(this));

which would create a B if invoking it for a B.

If you want to keep your copy function, you could always sneak in a parameter identifying the type of the copy to create. BUT, in such a case, it is better to leave the runtime reflection scene and enter compile-time generic programming. We unfortunately do not have parametrized functions in haXe, which is related to the omission of overloaded forms of one method name (the right decision, by the way, since the initial intended target languages do not really allow for overloaded forms, and we do not want to do too much name mangling since that would make integration with "native" code on those platforms trickier). This means that we *cannot* write:

public function copy<T : A>: T { var c: T = new T(); c.v = this.v; return c; } 

There are two variants regarding methods in haXe:

1. If you do have a hierarchical relation between the types (as you do...): downcast to a more specific type that indeed has the method in the signature. For it not to crash in runtime, the object should be of that more specific type...

Yes, 1. is my case. Classes containing classes containing classes etc.
I have to split my code, because it will run in 2 distinct phases on
different machines, but also because it will become too complex and
error prone. As I thought, I need to split it into 3 parts:

a) data structure classes with elementary data handling methods
   (refer class A).

b) a phase 1 program using subclasses of A with additional methods
   (ref. class B) that parses XML and external data.

c) a phase 2 program using subclasses of A with additional methods
   (ref. class C, not mentioned in my example) that uses the
   data structure created during phase 1 to generate a Flash
   movie.

It works right now, using big classes that contain all data handling,
phase 1 and phase 2 code, the used phase code allowed to be compiled
using "#if phaseX ... #end" compile flags. No good programming practice
either.. :(

I think you have to employ reflective or dynamic techniques, as the ones mentioned above. Look at the types Type and Reflect in the standard library. What you are doing, conceptually, is definitely that of enriching a pretty dry (A) entity along those dynamic phases, so dynamism is the way to go...

If I can solve this specific issue, the remaining splitting job will
be very straightforward. If I can't solve this, I'll have to duplicate
code and that's never a good solution...

No, that is an evil solution. Again, consider using either (i) reflection or (ii) dynamic constructs here; the 'or' is not necessarily exclusive, by the way.

There are basically three way to go about this, whereof the first is the one you have tried:

1. Dance around with class hierarchies, forcing types up and down, casting your way forward.

2. Reflection via Type and Reflect.

3. Dynamism, using Dynamic as the foundation, and adding methods and properties as needed.

This reflects (pun intended...) the trichotomy which is haXe, in my view: Static, Reflective and Dynamic.

Come to think about it, the third route is the one you should take.

2. If you do not have a hierarchical relation between the types: skip typing and use dynamic invocation. For it not to crash (or at least throw an exception...), the object should be of a type that has that method in its signature. One way to achieve that is
untyped b.foo()

 but I would recommend at least using an anonymous "OCamlish" type signature, such as

typedef HandleFoo = { foo: Void -> Void };

 and assigning to a variable of that type.

/David

Hmm, maybe I can mix it a bit... Thanks again. Some more issues
to consider... :-)

Again, go with Dynamic and anonymous types in cases you are certain about a method or property existing, which will make the subsequent operations type-safe (i.e., it will at least fail before operating ;-) )

/David

--
haXe - an open source web programming language
http://haxe.org
Reply | Threaded
Open this post in threaded view
|

Re: Cast problem

Martijn Loots
On Fri, 1 May 2009, David Bergman wrote:

> On May 1, 2009, at 3:26 PM, Martijn Loots wrote:
>
>> Before I say anything at all, *thank* you very much David for
>> your long and detailed reply. I hope I'm now allowed to utter
>> some more little abusive haXe-attacks... ;-)
>
> My response was probably overly cryptic, which is often the case with me :-|
>
Not at all, it *did* make me think about one of my old teachers
though... ;-)

>>> What are you trying to do? Yes, by using the 'cast' that Martiin
>>> suggested, you tell the compiler not to worry, that the object indeed is
>>> of type B. The problem is that is *not* an object of type B, i.e., you are
>>> putting the compiler in a false sense of security ;-)
>>>
>> Yep...
>
> You are a bad boy ;-)
>
Just trying hard to spare some lines of code that will add up
in the course of the total development... It's early times now;
better to choose right the first time (what I did not..) but
4000+ lines of code now will probably grow in tens of thousends
later on. I appreciate any help right now and if you want to
yell or throw things at me, go ahead. If it helps... okay.

>>> What did you want to happen here during execution (now that you have
>>> overcome your initial problem, of not compiling, by that evil 'cast')?
>>> There was a reason that the compiler complained. It suspected that the
>>> runtime type will not be compatible with the blist, since its static type
>>> is not, and in fact, the compiler was totally right!
>>>
>> ROTFL... I know... I know... but, as I still see it now, I *need* to
>> f*ck it up a bit (remember the good old days of C... pointing to some
>> other struct did not harm anyone, as long as you did use the struct
>> correctly)
>
> In haXe, one expresses that laissez-faire attitude via the Dynamic type, not
> by fooling the type system via 'cast'. Furthermore, the cases where one can
> truly ignore type often revolve around storing - as you say - "pointers" to
> anonymous stuff in a container. Not operating on those poor - then
> misinterpreted - objects.
>>
>>> TECH NOTE: remember, we have operational covariance (...) w.r.t.
>>> collections in haXe, although the types are invariant, and we do not have
>>> Scala's ability to steer that decision...
>>>
>>>> I have a number of classes subclassed like this and they
>>>> all bug at runtime. Is there any solution to use B's methods ?
>>>
>>> What is the solution you *would like* to see for objects not being of type
>>> B, as in your example above, when issued methods that only a B
>>> understands? NOTE: you understand that the 'copy' method of A always
>>> generates A objects, right?
>>>
>> Correct. As Ian Martins mentioned earlier, I may need a local copy
>> method in class B. But because B only promotes additional methods
>> and no additional variables, I hoped that just using class A's
>> copy would be sufficient...
>
> You hope that their layout happen to coincide? Well, yes and no. The pure
> structural layout may very well coincide, but at least the virtual tables -
> pointing to the implementations of methods - do not. This is a bit dependent
> on the intended target (for instance, ActionScript handles methods quite
> differently from C++...) but in most cases, you do end up with a (conceptual
> or actual) virtual table that is one entry too short when forcing a(n actual)
> A to become a B, i.e., the 'cast' does not convert the virtual table.
>
> One should not rely on memory layout tricks, since haXe - as a language
> proper - says very little to reassure one of anything there and the current
> target platforms vary wildly.
>
Whoops. That's a real eyeopener for me. With all those years programming
and development under my belt, I just did not realise that defining a
struct is quite something else memory-map-wise than inheritance... :-(

The problem is clear now. I'll take my Professional haXe and Neko book
up again tonight and go study "Dynamics"...

>>> TECH NOTE: I ask because it is a common concern in OO languages of how to
>>> make specific copies, shallow or deep, of subclasses objects, and avoid
>>> the slicing; especially true in C++
>
> REFLEXIVE AND TANGENTIAL NOTE: if you really want to create an object of the
> called type when "copying" (only what the super class A considers vital would
> be copied) you could do:
>
>   var dst: A = cast Type.createEmptyInstance(Type.getClass(this));
>
> which would create a B if invoking it for a B.
>
> If you want to keep your copy function, you could always sneak in a parameter
> identifying the type of the copy to create. BUT, in such a case, it is better
> to leave the runtime reflection scene and enter compile-time generic
> programming. We unfortunately do not have parametrized functions in haXe,
> which is related to the omission of overloaded forms of one method name (the
> right decision, by the way, since the initial intended target languages do
> not really allow for overloaded forms, and we do not want to do too much name
> mangling since that would make integration with "native" code on those
> platforms trickier). This means that we *cannot* write:
>
> public function copy<T : A>: T { var c: T = new T(); c.v = this.v;
> return c; }
>
Okay, this is not obvious to me right now. I'll try to grasp what you
wrote here after some coffee tomorrow morning.

>>> There are two variants regarding methods in haXe:
>>>
>>> 1. If you do have a hierarchical relation between the types (as you
>>> do...): downcast to a more specific type that indeed has the method in the
>>> signature. For it not to crash in runtime, the object should be of that
>>> more specific type...
>>>
>> Yes, 1. is my case. Classes containing classes containing classes etc.
>> I have to split my code, because it will run in 2 distinct phases on
>> different machines, but also because it will become too complex and
>> error prone. As I thought, I need to split it into 3 parts:
>>
>> a) data structure classes with elementary data handling methods
>>   (refer class A).
>>
>> b) a phase 1 program using subclasses of A with additional methods
>>   (ref. class B) that parses XML and external data.
>>
>> c) a phase 2 program using subclasses of A with additional methods
>>   (ref. class C, not mentioned in my example) that uses the
>>   data structure created during phase 1 to generate a Flash
>>   movie.
>>
>> It works right now, using big classes that contain all data handling,
>> phase 1 and phase 2 code, the used phase code allowed to be compiled
>> using "#if phaseX ... #end" compile flags. No good programming practice
>> either.. :(
>
> I think you have to employ reflective or dynamic techniques, as the ones
> mentioned above. Look at the types Type and Reflect in the standard library.
> What you are doing, conceptually, is definitely that of enriching a pretty
> dry (A) entity along those dynamic phases, so dynamism is the way to go...
>
I skipped Dynamic upto now, thinking it probably was an escape for
poor programming skills. As I understand now, it is much more than
that (sorry Nicolas...) and I'll catch up on my reading first.

>> If I can solve this specific issue, the remaining splitting job will
>> be very straightforward. If I can't solve this, I'll have to duplicate
>> code and that's never a good solution...
>
> No, that is an evil solution. Again, consider using either (i) reflection or
> (ii) dynamic constructs here; the 'or' is not necessarily exclusive, by the
> way.
>
> There are basically three way to go about this, whereof the first is the one
> you have tried:
>
> 1. Dance around with class hierarchies, forcing types up and down, casting
> your way forward.
>
> 2. Reflection via Type and Reflect.
>
> 3. Dynamism, using Dynamic as the foundation, and adding methods and
> properties as needed.
>
> This reflects (pun intended...) the trichotomy which is haXe, in my view:
> Static, Reflective and Dynamic.
>
> Come to think about it, the third route is the one you should take.
>
>>> 2. If you do not have a hierarchical relation between the types: skip
>>> typing and use dynamic invocation. For it not to crash (or at least throw
>>> an exception...), the object should be of a type that has that method in
>>> its signature. One way to achieve that is
>>> untyped b.foo()
>>>
>>> but I would recommend at least using an anonymous "OCamlish" type
>>> signature, such as
>>>
>>> typedef HandleFoo = { foo: Void -> Void };
>>>
>>> and assigning to a variable of that type.
>>>
>>> /David
>>>
>> Hmm, maybe I can mix it a bit... Thanks again. Some more issues
>> to consider... :-)
>
> Again, go with Dynamic and anonymous types in cases you are certain about a
> method or property existing, which will make the subsequent operations
> type-safe (i.e., it will at least fail before operating ;-) )
>
David, you have been a great help so far _O_

As I need to boost my knowledge first now, I'll abstain from
more comments for the time being, but will post my findings
as I progress (hopefully with problems solved..).

Thanks again and have a fine weekend.
--
-Martijn    @..@    ( Martijn Loots       -  Hengelo  [NL] )
-          (`--')   ( martijn<@>cosix.com -  www.cosix.com )
-         ( >__< )  ----------------------------------------
-         ^^^  ^^^  (   Netwerken, Security, Open Source   )

--
haXe - an open source web programming language
http://haxe.org
Reply | Threaded
Open this post in threaded view
|

Re: Cast problem

Kelvin Luck
In reply to this post by David Bergman
>
> There are basically three way to go about this, whereof the first is
> the one you have tried:
>
> 1. Dance around with class hierarchies, forcing types up and down,
> casting your way forward.
>
> 2. Reflection via Type and Reflect.
>
> 3. Dynamism, using Dynamic as the foundation, and adding methods and
> properties as needed.

... or 4... Composition.

I didn't read the whole thread in detail but I didn't quite get the reason  
inheritance is being used... Couldn't B and C be completely different  
classes which both contain a reference to an A (the data object). Then the  
A can be easily shared and B and C are completely independant of each  
other...

Like I said, I could quite easily have missed something as I didn't read  
the thread in too much detail but if something seems as complicated as  
this sounds sometimes it's best to just try a completely different  
approach...

Hope that helps,

Kelvin :)

--
haXe - an open source web programming language
http://haxe.org
Reply | Threaded
Open this post in threaded view
|

Re: Cast problem

Martijn Loots
On Fri, 1 May 2009, Kelvin Luck wrote:

>>
>> There are basically three way to go about this, whereof the first is
>> the one you have tried:
>>
>> 1. Dance around with class hierarchies, forcing types up and down,
>> casting your way forward.
>>
>> 2. Reflection via Type and Reflect.
>>
>> 3. Dynamism, using Dynamic as the foundation, and adding methods and
>> properties as needed.
>
> ... or 4... Composition.
>
> I didn't read the whole thread in detail but I didn't quite get the reason
> inheritance is being used... Couldn't B and C be completely different classes
> which both contain a reference to an A (the data object). Then the A can be
> easily shared and B and C are completely independant of each other...
>
I thought of that a number of weeks ago too and asked for an #include
compiler directive, so I could include the A part in both B and C
classes.

Someone than pointed me out to use inheritance as an obvious solution
in an OO context... but it bites me now I try to implement this.

In my ideal world, the A classes all live in a library to be
used by both B and C classes.

> Like I said, I could quite easily have missed something as I didn't read the
> thread in too much detail but if something seems as complicated as this
> sounds sometimes it's best to just try a completely different approach...
>
I'll think about the reference solution though. It is rather
complex, because most A type classes refer to a number of
other A type classes. I don't see the solution yet, but need
some sleep right now.

I'll come back on the subject.

> Hope that helps,
>
> Kelvin :)
>
Thanks as allways. Appreciate the effort.

Grtz,
--
-Martijn    @..@    ( Martijn Loots       -  Hengelo  [NL] )
-          (`--')   ( martijn<@>cosix.com -  www.cosix.com )
-         ( >__< )  ----------------------------------------
-         ^^^  ^^^  (   Netwerken, Security, Open Source   )

--
haXe - an open source web programming language
http://haxe.org