You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
With the introduction of type parameters for types and functions in Go 1.18, oak's event package could be changed significantly.
Currently, to bind and trigger a binding, one needs to use the empty interface object:
package event
funcexample() {
b:=NewBus(nil)
b.Bind(Enter, 0, func(cCID, iinterface{}) int {
e, ok:=i.(EnterPayload) // this needs to be known, or more realistically, looked up if!ok {
// ??? realistically, this should never happen, unless this event was triggered by bad code
}
// use e return0
})
// ok, compiles and runs correctlyb.Trigger(Enter, EnterPayload{
//...
})
// bad, compiles, but bindings will fail, and if bindings don't type check they'll panic b.Trigger(Enter, 42)
}
With 1.18 we can do this:
package event
typeTypedEventID[Tany] struct {
EventIDstring
}
typeSafeBindable[Tany] func(CID, T) intfuncTriggerSafe[Tany](evTypedEventID[T], dataT) {
DefaultBus.Trigger(ev.EventID, data)
}
funcTriggerSafeHandler[Tany](bHandler, evTypedEventID[T], dataT) {
b.Trigger(ev.EventID, data)
}
funcBindSafe[Tany](evTypedEventID[T], cCID, fnSafeBindable[T]) {
DefaultBus.Bind(ev.EventID, c, func(cCID, finterface{}) int {
tf:=f.(T)
returnfn(c, tf)
})
}
funcBindSafeHandler[Tany](bHandler, evTypedEventID[T], cCID, fnSafeBindable[T]) {
b.Bind(ev.EventID, c, func(cCID, finterface{}) int {
tf:=f.(T)
// could still check ok here but could a bad type get in? returnfn(c, tf)
})
}
var (
SafeEnter=TypedEventID[EnterPayload]{
EventID: Enter,
}
//... could predeclare all events with their payloads as top level variables, both in engine and in game
)
funcexample() {
h:=NewBus(nil)
BindSafeHandler[EnterPayload](h, SafeEnter, 0, func(cCID, eEnterPayload) int {
// use e return0
})
// ok, compiles and has runtime guaranteesTriggerSafeHandler[EnterPayload](h, SafeEnter, EnterPayload{})
// ok, does not compileTriggerSafeHandler[EnterPayload](h, SafeEnter, 42)
}
... and granted, all the Safe naming in there is verbose, but it would be backwards compatible, and in Oak 4 could be overhauled to consume the existing names.
A notable downside-- methods cannot receive type parameters, so working with custom handlers demands passing those handlers in as arguments. The use of non-event handler interfaces is rare, and we don't have a way of enabling this at all, so I'm more or less OK with this trade off.
This would enable us to move all of our event documentation to strongly typed structures, removing empty interfaces from the apparent event interface. Granted we are still using them internally as events pipe around their payloads, a. eventually that interface could be hidden or recommended against using directly and b. maybe there's some more generic functions we could write to remove that as well (although I have not found a way so far).
The text was updated successfully, but these errors were encountered:
Related to this, if the standard way of interacting with events would involve predeclaring them with compile time typing, we'd be more incentivized to move event names from strings to integers-- one can no longer concatenate events together, so the main argument for strings (which could have been replaced by bitflags anyway, maybe), goes away, and our event library would have improved efficiency.
As another thought, it may be appropriate to have two event libraries, if this new syntax becomes too difficult to use for prototyping / beginners, but I'm not sold either way on that yet.
With the introduction of type parameters for types and functions in Go 1.18, oak's event package could be changed significantly.
Currently, to bind and trigger a binding, one needs to use the empty interface object:
With 1.18 we can do this:
... and granted, all the
Safe
naming in there is verbose, but it would be backwards compatible, and in Oak 4 could be overhauled to consume the existing names.A notable downside-- methods cannot receive type parameters, so working with custom handlers demands passing those handlers in as arguments. The use of non-event handler interfaces is rare, and we don't have a way of enabling this at all, so I'm more or less OK with this trade off.
This would enable us to move all of our event documentation to strongly typed structures, removing empty interfaces from the apparent event interface. Granted we are still using them internally as events pipe around their payloads, a. eventually that interface could be hidden or recommended against using directly and b. maybe there's some more generic functions we could write to remove that as well (although I have not found a way so far).
The text was updated successfully, but these errors were encountered: