@saamyjoon Well, that’s a bit unfortunate. I’m stuck with design ideas for dynamic systems. Here are some use cases.
Use case 1:
I have a services
entity which currently has editable references to other strongly typed prefabs. This is not ideal, as I have to extend the services
entity every time I add a new service
prefab to the mix. The static dependency is tedious to maintain and could also lead to SG related caching issues after for example renaming a field. Therefore the goal is to convert it to a dynamic lookup. And here we start to hit a wall. Imagine a lookup process where some party calls Services.LookUp(some_service)
. The first operation will be lazy and perform a full search, but it should cache the result somewhere. Currently there seems to be no sane way to create a cache that would allow a O(1) lookup. This ends up that all look operation end up being O(n) in worst case. If such lookups are very frequent, this will waste a lot of time.
Use case 2:
Similar to use case 1 I want to stop using pre-defined enum
s in order to expose comparable values to the editor. Doing it using classes feels very much wrong to me, as we are creating some form of type pollution as well as potentially slowing things down for the required object instantiation compared to some simple value type like a struct or enum. However this seems to be the current status quo (see tag
class). Such value has to transfer a bit of associated information. In this scenario I would to send a pre-configured value (such as a type) over some intermediate component, which performs a lookup operation to find a component that can respond to the given type, and also caches it like in use case 1. Imagine a button that is constrained to a certain vehicle type, an intermediate vehicle spawner service component and a multiple vehicle spawner pool components where each pool has a vehicle type associated to them.
Verse is also lacking the ability to merge multiple constraints such as castable_subtype(a) & concrete_subtype(a)
. On top of that, if we start mixing in some generics (parametrics), things often fall apart quickly due to restrictions, features being not fully implemented or straight bugs that result to runtime issues as the compiler wasn’t able to catch them. However the most annoying issue one is Can't access a function from a preceding type
.
I figured out a tiny workaround, but I generally do not like it.
CanHandle(t: concrete_subtype(a))<decides>: void = {
r: concrete_subtype(a) = t # Workaround an object with `t`
R := r {} # Construct a value from `t`
my_target_a_subtype[R] # Now cast the object to a statically known type
}
While this somewhat works, it’s limited to the target type being locally fully known. I cannot delegate that decision to the editor.
@editable
TargetType: concrete_subtype(a)
CanHandle(t: concrete_subtype(a))<decides>: void = {
r: concrete_subtype(a) = t
R := r {}
TargetType[R] # Here we'll hit the next wall
}
TargetType[R]
will not compile as the compiler does not allow this, at least not right now. Additionally I just ignored the fact that TargetType
is strictly speaking not castable. On top of that @editable
still does not play well with things like concrete_subtype
(the integration doesn’t seem to have shipped yet).
As you can see, we’re jumping through many hoops, but there’s always some kind of a dead end that prevents us from creating dynamic logic.