About Monkey 2 › Forums › Monkey 2 Programming Help › Generics puzzle
This topic contains 14 replies, has 5 voices, and was last updated by 
 wiebow
 2 years, 8 months ago.
- 
		AuthorPosts
 - 
		
			
				
August 20, 2016 at 11:43 pm #3267
I’m usually intimidated by more “advanced” OO concepts like Generics and Interfaces, but have been studying it lately. I ran into this following issue, which may be trivial to anyone more familiar with Generics, but is puzzling me.
Imagine that I have a generic Entity class that can have components. Normally the components are of the “Component” class, but in this case I want to create an “AdvancedEntity” class that only uses “AdvancedComponent” components (both still share most of their inner workings with their respective base classes, so it’s justifiable to have this structure).
This simple code example works:
Monkey1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859#Import "<std>"Using std..Function Main()Local test := New AdvancedEntity( "AdvEntity" )test.components.Add( "testComp", New AdvancedComponent( "AdvComponent" ) )''components' will correctly return an AdvancedComponent heretest.components[ "testComp" ].AdvancedUpdate()EndClass Entity<T>Field name:= "Default Name"Field components :StringMap<T>Method New( name:String )Self.name = namecomponents = New StringMap<T>EndEndClass ComponentField name := "Component Name"Field entity: Entity<Component>Method New( name:String )Self.name = nameEndMethod Update()EndEndClass AdvancedComponent Extends ComponentMethod New( name:String )Super.New( name )EndMethod AdvancedUpdate()Print( " ~n" + "***** " + name + " *****" )EndEndClass AdvancedEntity Extends Entity<AdvancedComponent>Method New( name:String )Super.New( name )EndEndHowever, notice how I’m accessing the “components” map directly, and I wanted to make that private.
I want to wrap that into an “AddComponent( name:String, comp:T ) method that is supposed to add whatever component subclass that the AdvancedEntity uses, in this case an Advanced component. I tried this, but it doesn’t work.
Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869#Import "<std>"Using std..Function Main()Local test := New AdvancedEntity( "AdvEntity" )'I've replaced this line with the method that does the same thing without giving direct access to the stringmap 'components'test.AddComponent( "testComp", New AdvancedComponent( "AdvComponent" ) )test.components[ "testComp" ].AdvancedUpdate()EndClass Entity<T>ProtectedField components :StringMap<T>PublicField name:= "Default Name"Method New( name:String )Self.name = namecomponents = New StringMap<T>EndMethod AddComponent( name:String, comp:T ) Virtualcomponents.Add( name, comp )'This line doesn't work! (but it's necessary for components to work well)comp.entity = SelfEndEndClass ComponentField name := "Component Name"Field entity: Entity<Component>Method New( name:String )Self.name = nameEndMethod Update()EndEndClass AdvancedComponent Extends ComponentMethod New( name:String )Super.New( name )EndMethod AdvancedUpdate()Print( " ~n" + "***** " + name + " *****" )EndEndClass AdvancedEntity Extends Entity<AdvancedComponent>Method New( name:String )Super.New( name )EndEndIt actually works as intended if I remove the “comp.entity = Self” line inside the AddComponent method, but that’s not good, since I need to provide the component with that information.
Maybe the ‘entity’ field could be a Generic type as well? I tried that, but ran into “cyclic declaration” errors.
Help!
August 21, 2016 at 12:10 am #3271I think you need to make Component generic too – I tried this but the compiler complained about cyclic declarations, which I don’t think is quite right. Will look into it a bit later.
August 21, 2016 at 12:27 am #3272To simplify things a bit, is this more or less what you’re after?
It doesn’t compile due to cyclic declaration error, but I *think* it should be possible:
(Just added some hypothetial ‘Wheres’ that wont work yet, but I think they make it clearer!)
Monkey1234567891011121314151617181920212223242526272829303132333435#Import "<std>"Using std..Class Entity<T> Where T Extends ComponentField components:=New Stack<T>EndClass Component<T> Where T Extends EntityField entity:TEndClass AdvancedEntity Extends Entity<AdvancedComponent>EndClass AdvancedComponent Extends Component<AdvancedEntity>EndFunction Main()Local entity:=New AdvancedEntityLocal component:=New AdvancedComponententity.components.Push( component )component.entity=entityEndAugust 21, 2016 at 1:55 am #3273Yes! That’s exactly what I’m looking for.
Thanks Mark, I also ran into the cyclic error when I tried something similar and thought it was actually invalid.
August 21, 2016 at 2:26 am #3274why would anybody want to torture themselves with that? I think it’s overcomplicating something that can easily be solved with a different approach.
August 21, 2016 at 2:42 am #3275Such as?
August 21, 2016 at 2:58 am #3278At the moment I’m just trying to learn, and this seemed like a good “stress test” for the technique. I had never used Generics before, and I like objected oriented style a lot, so this felt like a gap in what I could do.
The particular case that prompted me to use this (not my test example code) can be solved with casting, but it’s a method that would be called a lot and I’d rather avoid casting if possible.
My next step is to learn interfaces properly. I understand how they work, but I’ve never thought of a practical situation where I’d rather use interfaces than classes. Also keep in mind that I’m an artist, not a programmer, and was never formally taught any of that, so I really appreciate that M2 is simple enough for me to learn it but still offers those possibilities.
Thanks!
August 21, 2016 at 3:21 am #3279Such as?
I don’t know. but a first object that needs another object to be created and the other object needing the first object for that one to be created is kind of Pointless to me. I know it’s just my thinking and I am not an authority on this but It doesn’t make any sense to me. I don’t want to start a big argument about this but I would like to see a good implementation of it to change my mind.
and Yes my point can be ignored.
you won’t need casting if you apply good use of Polymorphy and may be useful for generics as well.
August 21, 2016 at 3:28 am #3280you won’t need casting if you apply good use of Polymorphy. and may be useful for generics as well.
What does this even mean? If you are going to post in a negative manner at least try and do it constructively.
Generics are like macro assembler and can generate very optimal code. They are not pointless.
August 21, 2016 at 3:33 am #3281I never said generics are pointless, just that particular use of them.
don’t get so defensive, as I said, Ignore. it’s just an opinion by this humble programmer that learned to program mostly by self.
August 21, 2016 at 3:52 am #3282Your opinion is that the use case is pointless? The fact is inlining of method AdvancedUpdate by high volume iterators of AdvancedEntities is a worthy request.
August 21, 2016 at 3:56 am #3283@Simon,
I am not going to question your opinion you are quite a bit smarter and talented than I am. but until I see a good example of it (the reason it’s getting cyclic redundancy) i won’t change my mind. When you show me a good example I might change my mind, apologize and start using it that way. I am a bit of a hard head that way. Sorry.
August 21, 2016 at 4:04 am #3309That’s cool, I wasn’t getting at you or anything, but it *is* an interesting problem IMO.
The traditional way to solve this would almost inevitably involve a dynamic downcast somewhere, possibly eventually in quite a few places (that’s why I wanted to see your solution, perhaps there’s a way to do it without..?) and downcasts are both dangerous and slow.
I think what fascinates me about generics and problems like this is that they can provide ‘almost dynamic’ like typing behaviour, but with 0 runtime overhead since everything’s done at compile time.
Are they confusing? Perhaps, although I think I presented the fundamental problem pretty clearly above. Aand perhaps by messing around with problems like this we can come up ways to make generics easier to use? Maybe not though…
And I’d still be interested to see what people could come up with in terms of a non-generic solution – perhaps there’s a non-generic way to solve this too?
August 21, 2016 at 4:24 am #3310Yes, you presented the problem pretty clearly. I just don’t like it. My response is that I wrote a thought that came out way to loud, An opinion that maybe I should have kept to myself.
It was interesting to see Simon get on the defensive as well.
August 21, 2016 at 7:56 am #3316There are quite a few advanced properties of the language I don’t use, and I still solve my problems so yes, there is probably a better solution to this problem. I also worked with component systems and extending components more than one time is asking for problems. But this IS an interesting discussion, even if I , like Jesse, don’t see myself jumping into this rabbit hole
 - 
		AuthorPosts
 
You must be logged in to reply to this topic.