I have a use case where I need to talk about class...
# getting-started
r
I have a use case where I need to talk about classes themselves rather than instances. Having to do
Foo::class
repeatedly is kind of annoying compared to just doing
Foo
in languages like Typescript/Python
Copy code
val relevantTypes = List<Base>(SubA::class, SubB::class, SubC::class, SubD::class)
Is there a nicer way to do this?
s
variadically? nope. you could hack something together with
inline
and
reified
but you would need to write a function definition for each arity you need
what you're describing is not all that common in Kotlin or other JVM languages, so designing the syntax around this use case wasn't a priority
moreover, when you pass
Foo
in without qualification, it actually references the class's
companion object
if it exists
if this kind of usage is extremely important to you, this is how you might simulate it
r
I find that I often end up passing classes themselves as data in programs where the domain im working in is abstract and very concerned with types themselves. im not sure what a better approach there is in kotlin
all i can think of is mapping strings / enum names to the classes
and that is n*2 work
in an entity component system, the type of component matters
you need to be able to ask `if component instanceof Transform``
s
why not just
if (component is Transform)
?
r
new to koitlin, you are right
that is better
but that is fine when hardcoding
i need to pass it dynamically
I create Systems
systems need to say what components they care about
that is dynamic and not hardcoded
s
we all create systems
r
the S in ECS = systems
s
it's just a matter of finding the correct abstraction to represent them lol
r
specific term sorry lol
like class PhysicsSystem { track = listOf(Transform::class, RigidBody::class)
so it knows only to track entities who are instances of those classes
or maybe something like in chess val piece ... fn(piece: KClassPiece) { if piece is Piece
s
I think the chess example is actually probably a good use case for a sealed class or maybe an enum lol
r
maybe sure
but i represent these things with classes already
like if I already have this 50 line file for a King piece
im not going to do also make an Enum for the King name
i should be able to just use the class itself as the n ame
map { "King" -> King::class }
this looks terrible to do
s
I mean, it's hard to give suggestions without knowing exactly what you need, but in this case, why not just have a
val name
on
Piece
and have your classes implement it?
r
just redundancy. if the sub class name is King, having a val name = "King" is unnecessary
class RigidBody : Component { val name = "RigidBody" } like this sucks
s
My point is more that
KClass
may not be the correct abstraction for what you need, and that you may be better served more directly modeling the data required rather than relying on one that was written with entirely different goals in mind
r
im gonna have hundreds of components in my game
Sure but I am not aware of a better approach yet. The fact of the matter is this is a use case where I need to be able to say "Give me a list of classes, and I will filter all instances for only the ones who are instances of those classes"
that should be easy to express in a language I think. it comes up a lot at least in my projects.
k
This looks a bit like the "Switch Statement Code Smell". Are you sure you can't do this using proper OOP principles?
s
If you need instances of one class you could
instances.filterIsInstance<ClassImLookingFor>()
r
@Klitos Kyriacou I'm doing this to stay in line with proper OOP principles. I have an abstract base class
Component
which is used everywher else in my program as the interface of which you should pass in sub classes instances (so liskov + DI)
s
if you need instances of multiple classes, you can't use this approach unless you can have them inherit from a shared type
r
sorry to be clear its composition not multi inheritance class Entity { val components: ListComponent }
so filtering all entity instances for only the ones whos .components includes all given class types
s
so like, are you writing an implementation or are you trying to provide like a physics DSL? like I said before, this is a tough thing to do variadically, but you can always
.filter { it is Transform || it is RigidBody }
at the call site
r
how do I paste kotlin code
Untitled.kt
@Shawn so I am trying to do that filter you did but it conceptually needs to be variadic
since the sorts of systems that might exist in your game engine are variable
this works right now but the syntax is filter(entities, listOf(RigidBody::class, Transform::class, ...)
and i just find it a bit ugly and verbose
i am sure that stuff this abstract rarely comes up in mobile android dev where kotlin priorities are but this seems like a very reasonable use case
s
I don't do mobile android dev, and this kind of thing still rarely comes up in my line of work lol
r
lol yeah it also loads on a certain style of programming for sure
it has came up for me in like 6 times now in multiple languages
because all of my apps are using heavy OOP with tons of super abstract interfaces/classes
that i inject everywhere variadically
a rust/c programmer would find my code super overly complex and alien to look at
i am totally open to an alternative but i have not been able to find one satisfying
cause like, the problem at hand just actually is about types themselves instead of instances.
s
I actually don't think this is all that "OO" actually—in fact I think the way you're trying to encode your logic into the type system is much closer to what folks writing Haskell or F# like to do with their type systems
r
i do wish oop languages could get as semantic / type oriented as those fp languages
talking about types themselves during runtime
s
you mentioned TypeScript earlier and the folks at MS working on it definitely take a lot of inspiration from or at least have a good understanding of the goals F# is trying to achieve
yeah, I don't think Kotlin is getting Kinds anytime soon lol
not real, first-class ones anyhow
r
yeah TS is has the most sophisticated typing feature set ive seen so far
but its JS and i am tired of that
kotlin is really pretty
and looks robust
s
it's still a JVM language catering to JVM user needs, so the language designers will typically prioritize things like compiler performance/maintainability and legibility for folks used to writing Java and other OO languages over, say, making higher-order types easier to represent
moreover, most of us do get by without the exact syntactic constructions you're asking for by using other approaches, even if they seem a bit verbose in comparison
Scala did not prioritize those things and look where it is now
r
But the other approaches seem to compromise on these OOP principles about dependency injection and liskov substitution
i can't hardcode what components to filter for
the description of an ECS is "you have some systems who care about some components that they use to filter entities by"
translating that to a computer program, its clear we gotta be passing in types themselves
in the abstract.
"Dont use dependency injection, make your programming more concrete and have less abstraction layers" sounds like the only alternative
s
Well, your original example is still hard-coding the types you need, right?
r
you hardcode what types each System needs, sure
but you can make new systems at any point
Untitled.cpp
those systems then get integrated into the game engine
and all the game engine code talks about systems in terms of the base interface. DI+liskov substitution going on.
cause thats how the game engine needs to work to be extensible for new features. let people make their own systems that track whatever components they want, and then just plug-and-play it into the engine.
s
admittedly, this is a bit harder if we're talking about providing an engine for others to consumer and plug into, but I guess the best way to do it probably depends on the details how your pipeline actually works
to me it kinda seems like `Component`s are being passed to systems kinda like a message queue, and each individual system wants to subscribe to specific queues (i.e. types of messages/components)
r
@Shawn yeah kinda. but its not event based. every frame of the game, the system goes through and updates every entity who has the components it cares about
so a PhysicsSystem will go through every entity with a RigidBody component, and perform some operation like accelerating the velocity downwards for gravity etc.
every frame
p
yeah, there’s no way to do a
trackN
extension function or anything to (abuse) reified types to get arbitrary resolution. You could manually write out the first few overloads, but they have to have different names for overload resolution, even in Kotlin, so it’s still a pretty awkward UX