COMMUNITY

How to define generic parameter in abstract enums

Hi,

I need to define some tricky data structure and got stuck with defining generic parameter in an abstract enum.
I think the best to show the concrete case, I hope you will get my point from the example:
https://try.haxe.org/#Ac1D1

I think the easy (but discouraged) answer is:

typedef MY_GENERIC_DATA=Dynamic

The other answer is, “don’t use generic structures.” In other words, specify all of the possible structures and use them accordingly.

I think you may need a macro to do this, since I don’t believe Haxe supports String->T->Void (etc) right now

Assuming that you’re aiming at proper type-checking of your extern class methods, maybe following will be a feasible solution (https://try.haxe.org/#c5798):

class Test {
    static function main() {
        var dispatcher = new EventDispatcher();
        
        // assuming `name: String` is an event name
        dispatcher.on(MyEvent.SomeEvent, function(name:String, data:MyData): Void {
            trace(name);
            trace(data.id);
            trace(data.name);
        });
        dispatcher.on(MyEvent.OtherEvent, function(name:String, data:Int): Void {
            trace(name);
            trace(data);
        });
//         dispatcher.on(MyEvent.SomeEvent, function(name:String, data:Int): Void { // compilation error - as expected
//             trace(name);
//         });
        
        // not sure if this is how it's meant to be used with your extern but that's what I figured out from your sample code
        dispatcher.dispatch(MyEvent.SomeEvent, {id: "someId", name: "someName"});
        dispatcher.dispatch(MyEvent.OtherEvent, 13);
//         dispatcher.dispatch(MyEvent.OtherEvent, {id: "someId", name: "someName"}); // compilation error - as expected
    }
}

// NOTE: can't use extern since 'Type required for extern classes and interfaces' - can't use type parameters
// NOTE: could be also an `abstract` (probably :))
// NOTE: could simply use static methods if JS implementation uses plain functions
class EventDispatcher {
    private var ext: Dynamic;
    
    public function new() {
	    // here you have to write native, i.e. JS, implementation creating actual "extern" class instance  
        ext = new NativeEventDispatcher();
    }
    
    public function on<Data, Callback: haxe.Constraints.Function>(event:MyEvent<Data, Callback>, listener:Callback) {
	    // here you have to write native, i.e. JS, implementation calling actual "extern" `on` method
        ext.on(event, listener);
    }
    
    public function dispatch<Data, Callback: haxe.Constraints.Function>(event:MyEvent<Data, Callback>, data:Data) {
	    // here you have to write native, i.e. JS, implementation calling actual "extern" `dispatch` method
        ext.dispatch(event, data);
    }
}

// NOTE: couldn't make `EventDispatcher` work with `String->Data->Void` but `haxe.Constraints.Function` is enough for proper type-checking of `EventDispatcher` methods
// NOTE: had to change `OtherEvent` to an uniform callback definition, i.e. `String->Int->Void`
@:enum abstract MyEvent<Data, Callback: haxe.Constraints.Function>(String) {
    var SomeEvent:MyEvent<MyData, String->MyData->Void> = "someEvent";
    var OtherEvent:MyEvent<Int, String->Int->Void> = "otherEvent";
}

typedef MyData = {
    var id:String;
    var name:String;
}

// NOTE: example "native" implementation
@:keep // without this DCE removes this class implementation - it's an example, so whatever
class NativeEventDispatcher {
    private var listeners = new Map<String, Dynamic>(); 
    
    public function new() {}
    
    public function on(event:Dynamic, listener:Dynamic) {
        // only single listener for brevity
        listeners.set(event, listener);
    }
    
    public function dispatch(event:Dynamic, data:Dynamic) {
        // no error-logic for brevity
        listeners.get(event)(event, data);
    }
}

Edit.

Actually you can have var OtherEvent:MyEvent<Int, Int->Void> = "otherEvent"; if your extern invokes callback functions with different arguments for specific event types. :slight_smile: See https://try.haxe.org/#BCfE2.

1 Like

Are you trying to implement an EventEmitter ? I did something here https://github.com/darmie/EventEmitter , you may want to take a look and improve upon it?