CLOVIS
08/02/2020, 2:20 PMUnimplemented
Singleton, but that still doesn't look so good.
2. Use inheritance
Create one interface per optional field, and have each provider have its own event implementation that extends the correct interfaces. This could lead to a bit of repetition, though I guess combined with delegation it could look okay.
3. Use WeakHashMap in a typeclass to store the additional information
That doesn't seem very nice as well...
What do you think?Jannis
08/02/2020, 2:24 PMHowever, I do not understand how typeclasses can be used to add data to an objectThey don't typeclasses are behavior, not data (and data is ideally dumb). These two are strictly separated.
CLOVIS
08/02/2020, 2:30 PMJannis
08/02/2020, 2:30 PMJannis
08/02/2020, 2:31 PMJannis
08/02/2020, 2:36 PMdata Event = MkEvent { ...; evData: EventData a }
and EventData a
resolves my data through a type level function. But this is not possible in kotlin...
Normally this screams sum-types and thus sealed classes
but this becomes unwieldy with too many fields and too many repeated fieldsstojan
08/02/2020, 2:36 PMsealed class EventData
object Nothing : EventData()
data class Name(val name: String) : EventData()
data class Value(val value: Int): EventData()
data class Event<A : EventData>(val id: Long, val data: A)
fun main() {
val eventWithoutData = Event(0L, Nothing)
val eventWithName = Event(1L, Name("My event!"))
val eventWithValue = Event(2L, Value(15))
}
you could drop the sealed class (and use String
and Int
directly) if you don't care about exhaustive whenJannis
08/02/2020, 2:40 PMEventData
recursive using EventData<A>
and for example Name<A: EventData>(val name: String, val data: A)
you can build sort of linked lists of propertiesJannis
08/02/2020, 2:41 PMJannis
08/02/2020, 2:41 PMJannis
08/02/2020, 2:43 PMNameAndValueEvent = Event<Value<Name<Nothing>>>
CLOVIS
08/02/2020, 3:01 PMCLOVIS
08/02/2020, 3:07 PMJannis
08/02/2020, 3:13 PMCLOVIS
08/04/2020, 1:18 PMJannis
08/04/2020, 1:28 PMJannis
08/04/2020, 1:29 PMJannis
08/04/2020, 1:45 PMinterface HasName<A> { fun name(): Lens<A, Name> }
This provides a nice api that does not care about the underlying representation (you could even keep it in binary format, but that'd be overdoing it 😅). Lenses are amazing for this but require some learning effort...
You can also do this without lenses by defining the interface with a getter and setter method or probably some other method of accessing.
This is also very close to the inheritance method you described and defining an interface which each event implements. In haskell these instances are trivially derived through use of generics, with arrow-meta we could provide similar means of derivation, but right now this leads to boilerplate on each implementation of an event.
Not sure if I have a better idea tbh, you can always do the same with an unsafe representation e.g. define a type safe api based on lenses and lens typeclasses on top of an unsafe impl using hashmaps or similarCLOVIS
08/05/2020, 12:03 AM