Simplify type signatures


#1

Hi, Is there a way to simplify the following code


sealed class Async_load_state<WaitT, ErrorT, DoneT> () {
    class Wait<WaitT, ErrorT, DoneT>(val wait: WaitT): Async_load_state<WaitT, ErrorT, DoneT>() {}
    class Error<WaitT, ErrorT, DoneT>(val error: ErrorT): Async_load_state<WaitT, ErrorT, DoneT>() {}
    class Done<WaitT, ErrorT, DoneT>(val done: DoneT): Async_load_state<WaitT, ErrorT, DoneT>() {}

    fun is_done(): Boolean  {
        return when(this) {
            is Async_load_state.Error<*, *, *> -> true
            is Async_load_state.Done<*, *, *> -> true
            else -> false
        }
    }
}


class Async_load<WaitT, ErrorT, DoneT> (
        val w: WaitT,
        f: Async_load<WaitT, ErrorT, DoneT>.(WaitT) -> Unit) {

    var state: Async_load_state<WaitT, ErrorT, DoneT> = Async_load_state.Wait<WaitT, ErrorT, DoneT>(w)
    init { f(w) }

    fun error(error: ErrorT) {
        state = Async_load_state.Error<WaitT, ErrorT, DoneT>(error)
    }

    fun done(done: DoneT) {
        state = Async_load_state.Done<WaitT, ErrorT, DoneT>(error)
    }

}

Intuitively, <WaitT, ErrorT, DoneT> appears way too many times.

Thanks!


#2

No, there isn’t


#3

Use W, E and D instead? Generic arguments are a single letter for a reason :slight_smile:

Joking aside, generic parameters can easily propagate and explode - generics should be used judiciously


#4

Maybe you could use type aliases.


#5

the problem with it is that you need to pass the generics from the base classes to the superclasses.
a type alias is not going to help with passing generics through


#6

IIRC, in other languages, one can write this as:

data AsyncLoad WaitT ErrorT DoneT =
Wait WaitT | Error ErrorT | Done DoneT

where *T are type variables and Wait/Error/Done are constructors.

In this particular case I was not sure if:

  1. There was an easier way to do this in Kotlin but I was unaware of it OR
  2. Kotlin is slightly more verbose

It looks like we on agreeing on 2.


#7

I do not really understand what your code should do. What are WaitT ErrorT and DoneT should represent? It seems that they are used only internally, why make class generic at all?


#8

Can you replace

class Done<WaitT, ErrorT, DoneT>(val done: DoneT): Async_load_state<WaitT, ErrorT, DoneT>() {}

with

class Done<DoneT>(val done: DoneT): Async_load_state<Nothing, Nothing, DoneT>()

#9

That would require all the type parameters to be declared out. This would also result in any type Done<T> to be a subtype of all Async_load_state<W,E,D>, where T is a subtype of D, whereas in the current solution it would only be a subtype of a single instance type of Async_load_state.