https://kotlinlang.org logo
#getting-started
Title
# getting-started
r

Ray Rahke

02/21/2024, 9:15 PM
Is there any way to simplify
Copy code
val x = 7
fn(object { val x = x })
to
Copy code
val x = 7
fn(object { x })
h

hfhbd

02/22/2024, 6:31 AM
What’s the point of the object at all? The parameter type will be Any (in almost all cases).
👆 1
r

Ray Rahke

02/22/2024, 7:16 AM
for an event system i am building
in this case, it would be like publish("some label", { x, y })
so you can later see an event with event.x, event.y
s

Szymon Jeziorski

02/22/2024, 8:08 AM
Instead of creating anonymous objects I would suggest creating data classes specific for domain context (for example data class per event type) - it would make the code and its context more readable and type safe
h

hfhbd

02/22/2024, 8:24 AM
Please take a look at the documentation: Kotlin is a static typed language, so you need to use an interface/class to access a property (without using reflection/precompilers etc.). If you pass an (non-local) object without any super class/interface, the function receives Any.
r

Ray Rahke

02/23/2024, 3:55 PM
@Szymon Jeziorski I don't think that is feasible for my use case. I am building out a PubSub event system architecture, oriented as a library for people to extend and use easily for any purposes. Different types of data are attached to events based on different types of labels.
Copy code
somewhere:...
publisher.publish("update", { fps: 60, entities: List<Entity> } )
publisher.publish("input", { mousex: 0, mousey: 0 })

then later...

if (event.label == "update) {
  fps = event.content.fps
} else if (event.label == "input") {
  mousex = event.content.mousex
}
i dont think I can use sealed classes here because the Event interface needs to be super general
using liskov substitution, programming to interface rather than implementation
h

hfhbd

02/23/2024, 4:00 PM
You can use generics
r

Ray Rahke

02/23/2024, 4:02 PM
I'm not clear on how that would work
subscribers should be able to subscribe or stop subscribing to any channels throughout the runtime
it can't be staticly determined at runtime
h

hfhbd

02/23/2024, 4:02 PM
Copy code
sealed interface Event<Context> {
    data class Update<Context>(val context: Context): Event<Context>
    data class Input<Context>(val context: Context): Event<Context>
}
data class FPS(val fps: Int, val entities: List<Entity>)
data class Mouse(val x: Int, val y: Int)
publisher.publish(Event.Update(context = FPS(fps = 60, entities = listOf<Entity>()))
publisher.publish(Event.Input(Mouse(x = 0, y = 0))

then later...

when (val event: Event<*>) {
    is Event.Update -> {
        val context = event.context
        if (context is FPS) {
            val fps = context.fps
        }
    }

    is Event.Input -> {
        val context = event.context
        if (context is Mouse) {
            val mouseX = context.x
        }
    }
}
r

Ray Rahke

02/23/2024, 4:03 PM
so the generic is being implicitly determined?
by the function argument pass?
Also, will this allow for consumers of my library to add their own event types?
extending it
users of my library need to be able to create their own publishers that have their own event formats
h

hfhbd

02/23/2024, 4:05 PM
Yes, but you need to cast it/check the type of the context during runtime
r

Ray Rahke

02/23/2024, 4:05 PM
with an if statement should work right
h

hfhbd

02/23/2024, 4:06 PM
yes, I updated the code
r

Ray Rahke

02/23/2024, 4:06 PM
could you show an example of how to extend it
the sealed class
if some consumer later added a new event type
is it just Event.newwhatever =
h

hfhbd

02/23/2024, 4:07 PM
open class Custom<Context>(val context: Context): Event<Context>
r

Ray Rahke

02/23/2024, 4:07 PM
but then people who expect that will have to annotate the type as Custom rigth?
like
Copy code
class SomeSubscriber : Subscriber {
  fun foo() {
     for (event in this.events) {
         if (event.update)
}
will they have to make the type of Event no longer Event
but be Custom
h

hfhbd

02/23/2024, 4:08 PM
Custom inherits from Event
r

Ray Rahke

02/23/2024, 4:09 PM
so if they have the type as
Event
, then
if Event.Custom ->
will be red error
because the ven diagram is the other direction
if I have two publishers - PhysicsEngine and RendererEngine, I assume they will both need to implement their own
open class PhysicsEvent
open class RenderEvent
now imagine i have a subscriber somewhere who needs to listen to both of those
and whatever other events he might care about.
am I going to have to do some super gross class inheritance merging?
h

hfhbd

02/23/2024, 4:12 PM
It depends on your actual code, but if you only need one event publisher, then yes you can either create a super class/interface or just use Any and you need to cast it during runtime. This is how static typed languages work.
r

Ray Rahke

02/23/2024, 4:13 PM
I don't think this is a static typed language thing. I implemented this same system in typescript with 100% coverage (no use of Any)
consumers could implement their own publishers/subscribers with a simple mixin
and then everywhere else in the codebase could infer the type perfectly
since this is a library for an ECS game engine, it needs to allow for anyone creating an arbitrary number of subscribers and publishers
not relying on one big monolithic enum
h

hfhbd

02/23/2024, 4:15 PM
TypeScript converts to JS at runtime, so there are no types at all during runtime. Kotlin type system based mostly on Java.
r

Ray Rahke

02/23/2024, 4:15 PM
TS will not convert if there are any violations of the static type systsem
it shouldn't be compared to javascript. It has a more sophisticated type system than most langauges
my point was that in the ts codebase, the language server was able to perfectly infer the shape of events everywhere from any publisher
never any unknowns or
any
maybe i misunderstand
h

hfhbd

02/23/2024, 4:24 PM
Okay, so what's the signature of
publisher.publish
? And did you enable
noPropertyAccessFromIndexSignature
?
r

Ray Rahke

02/23/2024, 4:25 PM
i dont know what that second thing is but the signature of publish is
publish(label: string, data: any)
internally it creates an event `Event(from=this, label=label, data=dat)``
h

hfhbd

02/23/2024, 4:26 PM
So it's any, untyped and checks the properties during runtime.
r

Ray Rahke

02/23/2024, 4:26 PM
currently yeah, I started Kotlin this month so I haven't learned if theres a better way
h

hfhbd

02/23/2024, 4:27 PM
Wait, I mean your (existing?) TS code.
r

Ray Rahke

02/23/2024, 4:28 PM
Oh yeah no, it's not using any
I am actually doing some super arcane ts magic to get it to infer
forgot its been a few months and it took me like 82 hours to get working so I forget
but something i remeber is like
if you do
Copy code
class Whatever extends Publisher {
  events = { "update": () => ({fps: this.getFPS()}) }
}
you could then do anywhere else in the code
Copy code
typeof Whatever['events']
and instantly know the event API of that publisher
since TS is able to infer literal anonymous objects
and then Event was like
Copy code
class Event {
  from: Publisher
  label: keyof typeof Publisher['events']
  data: ReturnType<typeof Publisher[this['label']]>
}
I am guessing TS is the only language existing currently that can pull this off