I’m having a bit of trouble with the type system. ...
# server
e
I’m having a bit of trouble with the type system. Anyone able to help me resolve this? The idea is that the type defined by the variable
e
in the
main()
function determines the return type of the
dispatch()
function. The error I’m getting is:
Copy code
Type mismatch.
Required:
  Result<E>
Found:
  Result<ExampleEvent>
snippet.kt.cpp
b
how is Result.success() implemented?
also: what happens if you change line 5 to
Copy code
val event: E = ExampleEvent("tada!")
?
d
you can't "upcast" ExampleEvent to E because there is no guarantee that E and ExampleEvent are related
I think the problem may be because Result is invariant in it's type paramter, so Result<E> is not related to Result<ExampleEvent>? (need IDE)
e
I tried to resolve the relationship to E by saying that E is bound to type
Event
I think the problem may be because Result is invariant
This is probably the root issue
could I force it to work with a cast?
wait,
Result
looks like it’s setup for covariance:
Result<out T>
d
aha - it is - but the success method is not
TBH, I'm not sure why you would want to force it - E and ExampleEvent are not related - but even with
<E: ExampleEvent>
it is still broken because ExampleEvent cannot be an E.
e
my goal was to try and get `dispatch()`to coerce the response type to the appropriate event type. I thought E was bound to ExampleEvent because ExampleEvent “is a” Event
e.g.
fun <E: Event> dispatch(...): Result<E>
such that the type contained by Result was a subclass of
Event
and specified by the generic parameter
E
d
I thought E was bound to ExampleEvent because ExampleEvent “is a” Event
The compiler doesn't know about ExampleEvent in the function signature - there is only E and Event at that point.
such that the type contained by Result was a subclass of
Event
and specified by the generic parameter
E
yes - it is - but nowhere in that is ExampleEvent 🙂
As far as the compiler is concerned, both E and ExampleEvent extend Event, but that doesn't mean they are the same
e
ok I get you. I was thinking by setting an upper bound of
Event
that would be enough, given that ExampleEvent is a subtype
d
covariance and contravariance are a bit of a nightmare all around really. 😂
e
It seems when you have two “things”, both having the same covariant bounds
<E : Event>
, they don’t play nicely together. You’d think they would.
looking at the original error…
Copy code
Required:
  Result<E>
Found:
  Result<ExampleEvent>
I thought it would know that ExampleEvent “is a” Event, thus satisfying that bound
same error here:
I can instantiate a
ResultEvent
because
ExampleEvent
satisfies the upper bound of the generic. Unless I specify the type…
this works though:
val re = ResultEvent(ExampleEvent("tada!"))
if you take the type off
It works if I force cast it
val re = ResultEvent(ExampleEvent("tada!")) as ResultEvent<E>
Then this
println(testingCovariance<ExampleEvent>())
prints this
ResultEvent(e=ExampleEvent(msg=tada!))
so I guess I just have to cast it.
a
I think you need to at least use reified type, so you can add a condition in your method to return success only after checking that ExampleEvent is assignable from E. And even then, it is not compile-safe, it is only runtime safe. Well, kind of. At least, you can provide a meaningful error at runtime, if the user used the wrong event type. But still, , I think it is dangerous, because you require user to know what event implementation will be created inside method call depending on method argument. It cannot work without compromising with type safety. Dispatch problems are difficult to solve. Generally, I find myself dealing with them either by using a sealed interface as return type, to force user to use a when expression to discriminate the result, or I juste create a method for each type of return value. Example:
Copy code
fun dispatchCreate() : CreateEvent
fun dispatchUpdate() : UpdateEvent
fun dispatchDelete() : DeleteEvent
The first solution is heavier for users, the second solution is more work for the author of the API. Or you can just use reified types, if runtime safety is enough.
e
Thanks Alexis. Great explanation