Overriding parent methods with contravariant arguments - haxe

Basically, I want to override a parent class with different arguments. For example:
class Hold<T> {
public var value:T;
public function new(value:T) {
set(value);
}
public function set(value:T) {
this.value = value;
}
}
Then override that class, something like:
class HoldMore extends Hold<T> {
public var value2:T;
public function new(value:T, value2:T) {
super(value);
set(value, value2);
}
override public function set(value:T, value2:T) {
this.value = value;
this.value2 = value2;
}
}
Obviously this will return an error, Field set overloads parent class with different or incomplete type. Is there a way around this? I tried using a public dynamic function, and then setting set in the new() function, but that gave a very similar error. Any thoughts?

This is just a complement to #stroncium's answer, which is totally correct.
Here is an example how it could look like:
class Hold<T> {
public var value:T;
public function new(value:T) {
set(value);
}
public function set(value:T) {
this.value = value;
}
}
class HoldMore<T> extends Hold<T> {
public var value2:T;
public function new(value:T, value2:T) {
super(value);
setBoth(value, value2);
}
// you cannot override "set" with a different signature
public function setBoth(value:T, value2:T) {
this.value = value;
this.value2 = value2;
}
}
alternatively, you could use an array as parameter or a dynamic object holding multiple values in order to "set" them using the same method, but you loose some of the compiler's type checking.

If you wrote the base class you could add an optional argument to it, this would be a workaround though, not directly what you want to do.

In the current state it totally won't work. There is not only 1 problem, but few of them:
Type T is meaningless in context of this new class, you should either use some concrete type or template this class over T.
You can not change the number of arguments of function when overriding it. However you can add another function(with a different name) to accept 2 arguments and do what you want (which is the way you would use in most languages, by the way).
I don't really understand how you see a contravariance problem there. The actual problem is that haxe doesn't support function overload. (It actually does, the function signature is name + full type, but that's not what you would want to write nor support, and is mostly used for js/java externs.)

Unfortunately the language doesn't allow it.

Related

Implementing Set<T>

I'm trying to wrap my head around abstract by implementing a Set data-type, like so:
abstract Set<T>(Map<T, Bool>) {
public inline function new() {
this = new Map<T, Bool>();
}
public inline function has(item:T):Bool {
return this.exists(item);
}
public inline function add(item:T):Set<T> {
this.set(item, true);
return null;
}
public inline function remove(item:T):Set<T> {
this.remove(item);
return null;
}
public inline function iterator():Iterator<T> {
return this.keys();
}
}
The compiler doesn't like this, though. It tells me Set.hx:8: characters 11-29 : Abstract Map has no #:to function that accepts IMap<util.Set.T, Bool>
I don't really understand this at all, since if I change the constructor to
public inline function new(val:Map<T, Bool>) {
this = val;
}
and then instantiate with var set = new Set(new Map());, it works.
That's pretty gross, though. I'd like the ability to instantiate Sets without exposing the underlying implementation. Ultimately, I'd prefer a constructor with the signature new(?initial:Iterable<T>). Is this possible? Am I misunderstanding something?
The problem is that currently it's impossible to instantiate Map without they key type being known (and since Set.T is a free type parameter, this doesn't work). However since the constructor is inline, T may well be known at the call site. The problem is that the compiler still tries to generate Set.new. You can avoid this by prefixing it with #:extern. Working example: https://try.haxe.org/#1D06C

In Haxe, how do you pass Enum values in functions, and then convert them to Strings within the function?

I can't seem to get this working, but I'd be surprised if it wasn't possible in Haxe.
I'm trying to pass a couple of Enum values defined in my game to a function, so that it can then concatenate them as String types and pass that to other functions.
Example:
// In a general Entity class:
public override function kill():Void {
messages.dispatchCombined(entityType, ListMessages.KILLED);
super.kill();
}
And in my Messages.hx class:
package common;
import msignal.Signal.Signal1;
/**
* A Message / Event class using Signals bound to String names.
* #author Pierre Chamberlain
*/
class Messages{
var _messages:MessagesDef;
public function new() {
_messages = new MessagesDef();
}
public function add(pType:String, pCallback:FuncDef) {
if (_messages[pType] == null) {
_messages[pType] = new Signal1<Dynamic>();
}
var signals = _messages[pType];
signals.add( pCallback );
}
public function dispatch(pType:String, pArg:Dynamic):Bool {
var signals = _messages[pType];
if (signals == null) return false;
signals.dispatch(pArg);
return true;
}
//Compiler doesn't like passing enums :(
public inline function addCombined(pSource:Enum, pEvent:Enum, pCallback:FuncDef) {
add( combine(pSource, pEvent), pCallback );
}
public inline function dispatchCombined(pSource:Enum, pEvent:Enum, pArg:Dynamic):Bool {
return dispatch( combine(pSource, pEvent), pArg);
}
//How can I just pass the enum "names" as strings?
static inline function combine(a:Enum, b:Enum):String {
return String(a) + ":" + String(b);
}
}
typedef MessagesDef = Map<String, Signal1<Dynamic>>;
typedef FuncDef = Dynamic->Void;
Note how addCombined, dispatchCombined and combine expect an "Enum" type, but in this case I'm not sure if Haxe actually expects the entire Enum "class" to be passed (ie: ListMessages instead of ListMessages.KILLED) or if a value should work. Anyways, compiler doesn't like it - so I'm assuming another special Type has to be used.
Is there another way to go about passing enums and resolving them to strings?
I think you need EnumValue as parameter type (if it is only for enum values), and use Std.String to convert to String values.
static inline function combine(a:EnumValue, b:EnumValue):String {
return Std.string(a) + ":" + Std.string(b);
}
Of course that can be written smaller using String interpolation:
static inline function combine(a:EnumValue, b:EnumValue):String {
return '$a:$b';
}
Of course that can be 'more dynamic' using type parameters:
static inline function combine<A, B>(a:A, b:B):String {
return '$a:$b';
}
There is totally no need to use Dynamic as suggested. If you use Dynamic, you basically turn off the type system.
live example:
http://try.haxe.org/#a8844
Use Dynamic instead of Enum or pass them as Strings right away since you can always convert to enum from String if you need it later.
Anyway pass the enum as enum:Dynamic and then call Std.string(enum);
EDIT: Using EnumValue is definitely better approach than Dynamic, I use Dynamic in these functions because I send more than just Enums there and I am not worried about type safety in that case.

Unification and implicit cast of the type parameter

class Base, and class Ext extends Base.
class B<T> with typed method foo<T>(value:T)
Why B<Base>.foo doest not accept instance of B<Ext> (implicit downcast of the type parameter?) by default?
Here is an example
http://try.haxe.org/#d443f
class Test {
static function main() {
var bExt = new B(new Ext());
var bBase = new B(new Base());
bBase.foo(bExt);
//ofc
//bBase.foo(cast bExt);
}
}
class B<T>
{
public function new(v:T)
{
}
public function foo(v:B<T>)
{
//
}
}
class Base {
public function new(){}
}
class Ext extends Base {
public function new(){
super();
}
}
Is there any way to trigger implicit cast of the type parameter for B.foo?
There are three ways to interpret and answer your question:
1. foo(v:B<T>):
This is your example and it doesn't compile because T isn't allowed to be be variant. It happens because of the very existence of foo and because allowing bBase.foo(bExt), that is, unifying bExt with bBase, will then allow bBaseOfbExt.foo(bBase).
It is the fact that foo exists and that it can potentially modify the type that makes the bExt unification with bBase unsafe; you can see a similar (but maybe clearer) explanation in the manual, using arrays: type system – variance.
2. foo(v:T):
This is closer to what's on the body of your question (but not in the example) and it works fine.
3. foo<A>(v:B<A>):
Finally, if you have a type parameterized method, it also works, but you'd probably face other variance issues elsewhere.

Return derived type when return type is base type c#

I have a Base abstract Class and a Derived Class
Base abstract class
public abstract class BaseData: IListingData
{
private int? _photos;
public string DataKey { get; set; }
public abstract List<Images> Photos { get; set; }
}
Derived Class
public class DerivedData1 : BaseData
{
public override List<Images> Photos
{
get
{ return new List<Images>(); } set {}
}
}
public class DerivedData2 : BaseData
{
public override List<Images> Photos
{
get
{ return new List<Images>(); } set {}
}
}
I have a Service function:
public List<ListingData> FilterListings(PredicateHandler predicates)
{
//Retrieved from database and based on certain predicates, it will create List of DerivedData1 or DerivedData2
Return new List<DerivedData1>(); //This is where the ERROR is.
}
I am unable to return Derived Type. I tried casting and I get the following same compile error. Cannot convert expression type 'System.Collections.Generic.List< DerivedData1>' to return type 'System.Collections.Generic.List< ListingData>'
I also tried changing the return type of the service function FilterListings() to the Interface IListingData, but I still encounter the a casting error.
I searched on other Stackoverflow posts. Which answers the questions of returning a derived Type from within a Base class. But I think this is a different scenario.
Bottom line, My service-class function has a return type Animal() and from inside the function I want to return Dog()
What am I missing?
In your example code, you cannot return new List of DerivedData1 where the function return type is List of ListingData.
The reason is there is no hierarchical relations between the two list types.
What you can do is:
public List<ListingData> FilterListings(PredicateHandler predicates)
{
var list = new List<BaseData>();
var list.Add(new DerivedData1());
var list.Add(new DerivedData2());
return list;
}
I'd use List<object> if I were in your place, and cast object to whatever is needed when iterating (for example). Your issue is that List<Base> and List<DerivedFromBase> are treated as unrelated (which is the intended behaviour, even if uncomfortable).

Type parameters - get concrete type from type T : IMyInterface

Suppose I have a List<IMyInterface>...
I have three classes which implement IMyInterface: MyClass1, MyClass2, and MyClass3
I have a readonly Dictionary:
private static readonly Dictionary<Type, Type> DeclarationTypes = new Dictionary<Type, Type>
{
{ typeof(MyClass1), typeof(FunnyClass1) },
{ typeof(MyClass2), typeof(FunnyClass2) },
{ typeof(MyClass3), typeof(FunnyClass3) },
};
I have another interface, IFunnyInteface<T> where T : IMyInterface
I have a method:
public static IFunnyInterface<T> ConvertToFunnyClass<T>(this T node) where T : IMyInterface
{
if (DeclarationTypes.ContainsKey(node.GetType())) {
IFunnyInterface<T> otherClassInstance = (FunnyInterface<T>) Activator.CreateInstance(DeclarationTypes[node.GetType()], node);
return otherClassInstance;
}
return null;
}
I'm trying to call the constructor of FunnyClasses and insert as parameter my MyClass object. I don't want to know which object it is: I just want to instantiate some FunnyClass with MyClass as a parameter.
What happens when I call ConvertToFunnyClass, T is of type IMyInterface, and when I try to cast it to FunnyInterface<T>, it says I can't convert FunnyClass1, for instance, to FunnyInterface<IMyInterface>
My current workaround (not a beautiful one), is this:
public static dynamic ConvertToFunnyClass<T>(this T node) where T : IMyInterface
{
if (DeclarationTypes.ContainsKey(node.GetType())) {
var otherClassInstance = (FunnyInterface<T>) Activator.CreateInstance(DeclarationTypes[node.GetType()], node);
return otherClassInstance;
}
return null;
}
And I don't like it because the return type is dynamic, so when I access it from somewhere else, I have no idea what type it is, and I lose intellisense, and stuff. I don't know about any performance implications either.
Any clues?
Thanks in Advance!
Resolution
As I'm using C# 4.0, I could stop casting errors using covariance (output positions only), and so I changed my IFunnyInterface to
IFunnyInteface<out T> where T : IMyInterface
Thank you all for the replies.
Essentially, your problem is that you are trying to convert FunnyInterface<T> to FunnyInterface<IMyInterface>. As has been mentioned several times (one example is here, more information here), this is not valid in most circumstances. Only in .NET 4, when the generic type is an interface or delegate, and the type parameter has been explicitly declared as variant with in or out, can you perform this conversion.
Is FunnyInterface actually an interface?
thecoop answer points you exactly to why you can't do it.
A cleaner solution to the problem (besides using dynamic) would be a base non-Generics Interface:
public interface IFunnyInterfaceBase
{
}
public interface IFunnyInteface<T> : IFunnyInterfaceBase
where T : IMyInterface
{
}
And you need to move methods signature you use in that code from IFunnyInteface to IFunnyInterfaceBase.
This way you would be able to write something like this:
MyClass2 c2 = new MyClass2();
IFunnyInterfaceBase funnyInstance = c2.ConvertToFunnyClass();
The Exception you said you got in your code is not due to the extension method signature itself (the method is fine)..it is originated by the type of your lvalue (the type of the variable you use to store its return value)!
Obviously this solution applies only if you can modify IFunnyInterface source code!

Resources