Inheritance bug or strange behavior?

BlitzMax Forums/BlitzMax Programming/Inheritance bug or strange behavior?

Dreamora(Posted 2006) [#1]
The following source produces the output
0
B: 2
C: 3

This is an extremely broken output.

z is defined on "a" level, so it should be the same field for all extended as well even if default initialized?
But it is able to have 3 different values, what the heck is the compiler doing there? creating dublicates of the same field for every extended class???


Type a
	Field z%
End Type
Type b Extends a
	Field z% = 2
End Type
Type c Extends b
	Field z% = 3
End Type
Local me:a = New c
handleit(me)
End

Function handleit(it:a)
 Print it.z
 If b(it)	Print "B: " + b(it).z
 If c(it)	Print "C: " + c(it).z
End Function



The following code indeed returns the expected output of
3
B: 3
C: 3

Type a
	Field z%
End Type
Type b Extends a
	Method New()
		z = 2
	End Method
End Type
Type c Extends b
	Method New()
		z = 3
	End Method
End Type
Local me:a = New c
handleit(me)
End

Function handleit(it:a)
 Print it.z
 If b(it)	Print "B: " + b(it).z
 If c(it)	Print "C: " + c(it).z
End Function



Augen(Posted 2006) [#2]
This won't help, but i find it freaky that a Method New can be made in BMax even thought there is inbuilt NEW! Cool.

Edit: See next post. Thanks for that explanation Dreamora; I didn't realize they were the exact same thing. I have looked at the documentation, but I find the tutorials posted on this site much more helpful.


Dreamora(Posted 2006) [#3]
method new is exactly what is called when you use new - its the default constructor. See the documentation for further information.


marksibly(Posted 2006) [#4]
This is correct, and is the same in most OO languages. Fields declared in extending types will 'hide' those in base types.


Jesse(Posted 2006) [#5]
so I was partly correct. although I didn't know, that when you upcast to a base you access the base field and that the field can be declared in the base and in extended types.
I thing it would be be better to access the base with a super or a self rather than a cast. But That is just my opinion "not an experts' ".

Don't bother, I'll just stick to learning. :-)


H&K(Posted 2006) [#6]
Well you can access the base field with a super, as long as you have cast your object to the extended type. In your original example the Me needed to be cast back to the extended type, then the original field would have been available via super

I only disagreed with Dream in that I thought it wasnt an error. I however agree with him that its a stupid thing to do. You are using 8 btyes to store one int. (in a single extention)

I agree with Dream totaly, that you should have just the one field, that you allocate either inside "New", or in your create function


Jesse(Posted 2006) [#7]
so what is the difference global int, constant , field just 8 bytes. it was just a general example. I could have other uses for it. Maybe not a great deal of options but usefull none the less.


H&K(Posted 2006) [#8]
Oh I agree that it could be useful, but in the code you first published it in, it was the wrong tool.
In that code, Dreams first post, were the value was allocated to the same field each time in the "New" function was a better way to do it.

(And an Int Field is normaly just 4 bytes, all this double name does, is hide an int variable from you. Now be Honest, did you think that you were createing a second variable or just changing the value of the first?)


tonyg(Posted 2006) [#9]
and, from Mark's post
Fields declared in extending types will 'hide' those in base types.

What would 'hiding' achieve? What could it be used for?
They *can* be different variables types (e.g. string, float etc) but I'm not sure how that would help either.


Dreamora(Posted 2006) [#10]
It would allow you some great things.

But to me its an extremely critical thing. It removes the consistency from inheritance which is something that I thought a "modern language" (which the GC and typesafeness indicates) wouldn't break. At least not unless the language has true support for selection and redefition on extension.

Critical because of this little thingy here:

Type a
	Field z% = 3
End Type
Type b Extends a
	Field z% = 2
End Type
Type c Extends b

End Type
Local me:a = New c
me.z		= 17
handleit(me)
End

Function handleit(it:a)
 Print it.z
 If b(it)	Print "B: " + b(it).z
 If c(it)	Print "C: " + c(it).z
End Function


Now asume how much you can actually break due to that "feature".

There is actually no way to tell Class C to use z from Class A. It will always use z from Class B. (yeah I know I can always do A(self).z but thats after all inacceptable if I want to assign c.z statically to a.z as I know that b.z will only be needed if casted to b for some reason! Pointers are no solution as c.z points to b.z so changing c.z pointer will change b.z as well)

Result is that you can use Class A anymore for general type declaration for list iteration and the like because the field on the class is totally useless for any extended type because Class B block the whole following inheritance.

So until BM learns the needed selection and redifinition mechanics, this "feature" to me is more of a bug than actually a feature as it isn't as controllable and fully implemented as it would be needed to be.
But it is a start for something that could turn out to be something really usefull.

PS: Mark, might I suggest having a look at Eiffel if you are interested in expanding BM more towards modern OO . It offers an extremely usefull way of how inheritance and selection/undefine/redefine should work in any modern language.
As well as how export / access should work as you can define for each field / method / function, which classes are allowed to use it.


Jesse(Posted 2006) [#11]
@H&K, yes I thought I was craeting a second variable, that is what my whole confusion came about. where I thought I knew something I had no real understanding.


Koriolis(Posted 2006) [#12]
But to me its an extremely critical thing. It removes the consistency from inheritance which is something that I thought a "modern language
Critical? Consistency?
Most modern and pragmatic OO languages work just like that. First point : you just can't "override" a field in an inherited type, as a field is just a piece of data in BlitzMax, just like in C# or Java. If member access was unified as in a 100% OO language, then there would actually not even be any concept of simple field, and "field" access would actually imply calling a hidden getter/setter, with the cost of a virtual function dispatch. A truely, purely and consistent OO language is certainly nice, but for some time the speed cost is *not* desirable for some of the things most programmers are using BlitzMax for.
Second point, in fact more important: if what you expected here was some kind of overriding, does it actually make sense here?
Also I have a simpler question: what *exactly* did you expect BlitzMax to do in your example? Remember that the declaration
	Field z% = 2
Is strictly a shortcut for
	Field z%
Method New()
    z = 2
End Method

If what you expected was to *not* redeclare anyy field but just have another initialization put in the constructor behind the scenes, as it does right now) then OK, that could actually be not that bad. But to me that's another special case, exception to the rule or simply put, inconsistency.


Brucey(Posted 2006) [#13]
It seems to work exactly I would expect it to.

Since the field is private for a given type/instance, casting to a specific type would make visible the field at that level.


tonyg(Posted 2006) [#14]
It would allow you some great things.

Fantastic! Now, what could it be used for?


Dreamora(Posted 2006) [#15]
To have the "core type" hold a given value, but to save a modified version of this value on the extended types for example.
So if you use it as core type it shows "untransformed" values but the extended type has transformed values (that are kept updated all the time)


Koriolis: What I actually assume is that field z is field z throughout the whole inheritance. It makes actually quite little sense to have a field z, that shows X when you ask for its value on core type, but that shows y if you cast it to an extended type.

I fully understand that this allows some interesting things.
And am aware that this sadly is needed to work like this if we want to get multi implementation of abstract types at some point, as it would badly break otherwise unless redefine / undefine / select mechanics come in.


PS: Check out eiffel and you will see that you don't lose anything with a clear and fully consistent OO language with re / un-define and select mechanisms. You actually get quite a lot when it comes to usefully design your OO system. That is actually a reason why Eiffel is used in critical systems like medical stuff and aircrafts, where Java and C# never will be used and C/C++ only after tousands of hour of testing normally.
It isn't one of those funny languages that never grow up due to too complicated ideas. Its quite the opposite, as C# already has taken a few of Eiffels mechanics (delegates for example, which are called agent in Eiffel) as other languages as well.


H&K(Posted 2006) [#16]
I dont care if it is supposed to be in a modern OO languge or not. My only difference of opinon with Dream is that I didnt thing it was an error to be able to do it.

I cannot honestly thou dissagree with Dream that it a stupid thing to do. Cos it is. Its not a useful tool at all. I did admit earlier that possibly it might be useful in some weird code model, but I just think it is a way to confuse you when you are useing the type.

If I want a different field in the exteneded type, I would give it a new name, if I wanted the same variable to have a different value in the extended type, I would simply give it a new value.

@Jesse,
Im really supprised that you did know you where createing a new value. But given that you did know that, can you now see that you need to cast, even if you had given different names?
Type a
	Field z%
End Type
Type b Extends a
	Field y% = 2
End Type
Type c Extends a
	Field x% = 3
End Type
Local me:a = New c
Print C(me).x
'Print me.x



ziggy(Posted 2006) [#17]
Thats correct, you're overriding fields, that's a BMax feature.


Koriolis(Posted 2006) [#18]
Well in fact ziggy that's precisely the point: you're NOT overriding anything, you create a new field that hides the one in the parent type. But anyway I would indeed call that a by design feature, surely not a bug.


H&K(Posted 2006) [#19]
you create a new field that hides the one in the parent type

Err, thats what overriding is. (Its just that noramally we'd only do it for functions/Methods)


Jesse(Posted 2006) [#20]
@H&K, Just note that I admitted to have misunderstood the whole concept. I had originally created a field name index for each of the types to identify the type of operation I was doing. 1 was for the addition type, 2 for subtraction type..... it was kind of like a constant. Considering what I just learned from the language, under normal circumstances its not possible but now I know.
I am not the kind of person that denies something just to look good. I am more humble than that. I am not afraid to make mistakes just scared that when I point them out somebody crucifies me. its OK I do it anyway.


H&K(Posted 2006) [#21]
@Jessie.

I wasnt having a go, I was just pointing out that if you had used different names in the first place, it would have been more obvious that you had to cast, because the field in "C" wouldnt exist, as apposed to the program using the field in the base type.

When you first posted it, I had thought that your understanding was that you were only createing one field. As you have said other, then I believe you. Was just shocked that you understood it to that level, yet did realize that you needed to cast.

I do however think its a silly thing to do (duplicate names), as it gains you no memory, just makes it harder to see iif any future problem is one of allocation or casting.


Dreamora(Posted 2006) [#22]
Koriolis: Yes it is a feature. but I'm not sure if a language should really have a "non-name uniqueness" features within inheritance of non-abstract types.

(I agree that it is needed when you actually implement an Abstract Type, at least *and only then* if Mark is planning to add the possibility to implement multiple abstract types within a single type as C# / Java allow it. But I think this is the only circumstance where a new instance of the name should be created after all, not if you define a field with the same value on an extended type of a non-abstract type)


Jesse(Posted 2006) [#23]
I didn't know about the cast. I learned it from the explanations given by others.


H&K(Posted 2006) [#24]
If you are going to use fields of the same name, then you have to make sure that the object is cast to the right type.

In your example you have cast your type C to an A, so it trys to use the field in A. If you recast it back to a C, or make Me a C in the first place, you should get the value you expect


As it was I, who first posted that you needed to cast, Then you could at least imply that I had something to do with it rather than saying "the explanations given by others"

(This is not important, but you made me cry)

In that thread you had a specific problem that was anwsered (by MEEEEE), this thread tho is a disscustion of if its a valid thing to do. ;) And I dont think it is. I didnt say so in your thread, because it was not my place to say so. However in this thread, you are the only person I know who is acctualy using it, so thats why I keep questioning you.


Jesse(Posted 2006) [#25]
sorry that I wasn't too concerned with who posted what. in the future I'll try to be more in touch with who I read from. In my blured out memories I can kind of recall that post. And yes it was your post.

edit:
don't miss understand me. your opinion is important to me, It's just that it didn't sink in until a couple of posts down the thread with further explanation.


ziggy(Posted 2006) [#26]
@Dreamora: I understand your point, but I think this is a very important feature, if not, an update of a base class (an update of a module) could cause inherited classes to become incompatible.


H&K(Posted 2006) [#27]
@Jesse,

If you want a laugh at how long it took me to get it then read this
http://www.blitzbasic.com/Community/posts.php?topic=60015#669484


Dreamora(Posted 2006) [#28]
ziggy: and how are they not incompatible with dublicate fields?

What is the use if the module updates the field but your extended class does not get updated? Thats about pointless as the main use of extending is to have the full functionality of the base class and extend it, not replace it.


Jesse(Posted 2006) [#29]
Good one H&K. thanks. Now I don't feel alone in this.


Koriolis(Posted 2006) [#30]
Koriolis: Yes it is a feature. but I'm not sure if a language should really have a "non-name uniqueness" features within inheritance of non-abstract types.
Actually I agree here. BlitzMax would probably better generate an error. But IMHO *not* do any kind of overridng here, for speed concerns.

Err, thats what overriding is. (Its just that noramally we'd only do it for functions/Methods
No, that's not overriding at all. Not in the sense commonly admitted in OO languages (that would be overloading, not overriding). Overriding is a dynamic (runtime) feature ( in general implemented via virtual tables) allowing a method to have multiple implementations (or if you prefer to have several methods to be transparently selected at runtime based on the object's runtime type). Here there is absolutely nothing that imply runtime support. You have two completly different pieces of data, they just happen to have the same name in the parent and the inherited type. The parent and inherited type just form different namespaces here, that's about all. And because they have the same name, from the inherited type perspective the homonym field in the base type is hidden. Everything works at compile time in this area, and the object's runtime type never enter the equation.