I am on an heavily typed system with some generic methods declared as def execute]C <: A#C](cmd: C):EitherT[Future, Fail, Seq[A#E]]
(where A
is a generic type on the class.
This works well. However in my tests, when I mock those calls, I have to explictiely type the super types of Fail
and A#E
or my code cannot compile.
// Event is the base type of Receiver#E
val event:Event = SubOfEvent()
handler.execute(any[SubOfCommand]) returns Seq(event).asRightT[Future, Fail]
val fail:Fail = SubOfFail()
handler.execute(any[SubOfCommand]) returns fail.asLeftT[Future, Seq[Receiver#E]]
If I inline the declarations of event
or fail
I have a Type mismatch:
found : cats.data.EitherT[scala.concurrent.Future,SubOfFail,scala.collection.immutable.Seq[SubOfEvent]]
required: cats.data.EitherT[scala.concurrent.Future,Fail,scala.collection.immutable.Seq[Receiver#E]]
(which expands to) cats.data.EitherT[scala.concurrent.Future,Fail,scala.collection.immutable.Seq[Event]]
Note: SubOfFail <: Fail, but class EitherT is invariant in type A.
You may wish to define A as +A instead. (SLS 4.5)
handler.execute(any[SubOfCommand]) returns SubOfFail().asLeftT[Future,
^
I understand the message about EitherT
being invariant in type A
. But I was expecting it to be able to translate EitherT[F, SubOfA, B]
as EitherT[F, SubOfA.asInstanceOf[A], B]
.
Can someone help me to reveal the flaw in my reasoning ?
Thanks
Being invariant means exactly that it isn't possible to "translate" this way: an
EitherT[F, SubOfA, B]
simply isn't anEitherT[F, A, B]
.Now, a separate question is why the type for
fail
isn't inferred to beFail
despite an expected type for the wholefail.asLeftT[Future, Seq[Receiver#E]]
. And the answer there is that Scala type inference just doesn't work that way; when typingexpression.method(...)
, it will typeexpression
first and can't make use of the expected return type ofmethod
.You can still write them inline, but you need type ascription:
In case of
Seq
, explicit type parameter will work as well: