Interface method with variable arguments

Community Forums/Monkey2 Talk/Interface method with variable arguments

Skn3(Posted 2015) [#1]
Is this something that could be achieved / added to MX2?

The ability to specify that the implementing class must have the method defined, but the arguments are not hard coded.

e.g.


You can achieve this in MX1 as shown below, but it would be a nice thing to add, removing the redundant Setup definition in each class.
Function Main:Int()
	Local ent:GameEntity = New GameFood
	ent.Setup()
End

Interface GameEntity
	Method Setup:Void()
End

Class GameFood Implements GameEntity
	Method Setup:Void()
		
	End

	Method Setup:Void(name:String)
		
	End
End

Class GameWeapon Implements GameEntity
	Method Setup:Void()
		
	End
	
	Method Setup:Void(attack:Int, level:Int)
		
	End
End



ziggy(Posted 2015) [#2]
I can't see how you could keep type safety here.
Example, given that item is of kind IGameEntity, which has a Setup:Void(*) declaration:
Function DoSomething(item:IGameEntity)
    item.Setup( ?????? ) '--> Which signature to use to call this method??
End



marksibly(Posted 2015) [#3]
> You can achieve this in MX1 as shown below,

I can't see what that achieves though - the GameEntity interface can't be used for anything as it only contains the void() method which doesn't actually do anything.

But in general, this wouldn't be easy to do - it'd have to be done dynamically at runtime via box objects and type checking etc - the end result would be something like:

Interface GameEntity
   Method Setup:Void( params:SetupParams )
End

Class GameThing Implements GameEntity
   Method Setup:Void( params:SetupParams )
      Local p:=GameThingParams( params )
      If Not p Error "Runtime typing sucks!"
      ...OK to use p...
  End
End



Samah(Posted 2015) [#4]
You might be better off using a generic for that:
Interface GameEntity<P>
	Method Setup:Void(params:P)
End

Class SetupParams
	' common params go here
End

Class FoodParams Extends SetupParams
	Field name:String
	Method New(name:String)
		Self.name = name
	End
End

Class WeaponParams Extends SetupParams
	Field attack:Int, level:Int
	Method New(attack:Int, level:Int)
		Self.attack = attack
		Self.level = level
	End
End

Class GameFood Implements GameEntity<FoodParams>
	Method Setup:Void(params:FoodParams)
		Local name := params.name
	End
End

Class GameWeapon Implements GameEntity<WeaponParams>
	Method Setup:Void(params:WeaponParams)
		Local attack := params.attack
		Local level := params.level
	End
End

Local food := New GameFood(New FoodParams("apple"))
Local weapon := New GameWeapon(New WeaponParams(1, 2))

Bounded generics would be nice here too, if you could define it as:
Interface GameEntity<P Extends SetupParams>

Which prevents you from doing:
Class Foo Extends GameEntity<String>


Something else I'd love to see is default/optional interface methods like in Objective-C and Java 8! :)


Skn3(Posted 2015) [#5]
I wasnt really requesting a catch-all for variable argument length, but instead a way to just specify that said class should implement a method of any variety. A way to enforce a pattern onto implementers. The compiler could work out if the implementing class has the method. If there is no matching method definition (but there IS at least 1) it could just ignore the call. Maybe even the nearest call is found and null values auto filled.

So basically as an engine developer I can require users to Implement Setup(). The user can implement any variety of Setup() and that will count. If the compiler cant find a matching Setup(), but there is at least 1 Setup(), it can still compile.

I can see this is a kind of "out there" request and not very strict. It came from writing some engine code and then implementing "Setup()" differently in each implementing class. It would be nice if there was some way to enforce the Setup() method and provide a safe way for it to be called regardless of arguments used.

A boxing/unboxing of variables would work as described, but it would be quite a lot of overhead.


Skn3(Posted 2015) [#6]
Bounded generics would be nice here too, if you could define it as:

Interface GameEntity<P Extends SetupParams>
Which prevents you from doing:

Class Foo Extends GameEntity<String>
Something else I'd love to see is default/optional interface methods like in Objective-C and Java 8! :)


+1 to both of these. Especially optional methods. I am sure I read that it was being added, but to argue why this is a benefit, we can create grouped delegate interfaces and then the implementer can just implement the methods appropriate for their use.


Samah(Posted 2015) [#7]
@Skn3: I wasnt really requesting a catch-all for variable argument length, but instead a way to just specify that said class should implement a method of any variety.

I really don't see the point of this though. The point of an interface is to define a solid contract such that anything that uses it shouldn't care about the internal implementation, just the method signatures.
If the concrete method signatures are not known at compile time (especially if we're using reflection), all kinds of gremlins could pop up. Knowing that a class implements a "Setup" method is useless if you don't know the arguments.

Local name:String = (read class name string from a file)
Local ci:ClassInfo = GetClass(name)
Local obj:GameEntity = GameEntity(ci.NewInstance())
obj.Setup(...) ' compiler doesn't know what concrete type it is



Skn3(Posted 2015) [#8]
Well in current monkey there is an issue that if you re extending a class and you try and use a method that exists in the base class, you then cant have a different set of arguments in the extended class. It complains that the method doesn't match any of the base implementations. My proposed idea (albeit a bit wacky) might have been a solution to that. I can see though that is probably a little dangerous.

Is there another solution to this problem then? or is this a quirk that MX2 doesn't have? I had started using SetupClassNameHere() to avoid name conflicts, but this just seems a bit rough to me.


marksibly(Posted 2015) [#9]
> It complains that the method doesn't match any of the base implementations.

Fixed in monkey2!


Skn3(Posted 2015) [#10]
Good news!