Let's say I have the following action which does at first a read operation and then updates the fields.
action1: DBIOAction[Any, NoStream, Read with Write]
Let action2
do another read operation on the same table. To do the three operation sequentially I do the following:
val action3 = action1 andThen action2
So I presume the output should be of the format.
action3: DBIOAction[Any, NoStream, Read with Write with Read]
But when I see that the output is of the form again :
DBIOAction[Any, NoStream, Read with Write]
Indeed in the method's signature I wrote DBIOAction[Any, NoStream, Read with Write with Read]
but the IntelliJ does not complain. This does not seem correct. Am I doing a mistake ?
Read with Write with Read
and Read with Write
are mostly the same type:
implicitly[Read with Write with Read =:= Read with Write] // compiles
implicitly[Read with Write =:= Read with Write with Read] // compiles
implicitly[Write with Read =:= Read with Write] // compiles
implicitly[Read with Write =:= Write with Read] // compiles
although according to the spec they seem to be not equivalent (≡):
Two compound types are equivalent if the sequences of their component are pairwise equivalent, and occur in the same order, and their refinements are equivalent.
Looking into compiler:
https://github.com/scala/scala/pull/3981
RefinedType#normalize
is responsible for flattening nested compound types to a flat representation.Types are normalized during
=:=
in search of a successful result.This means that
((A with B) with C) =:= (A with B with C)
.
private def normalizeImpl = {
// TODO see comments around def intersectionType and def merge
// SI-8575 The dealias is needed here to keep subtyping transitive, example in run/t8575b.scala
def flatten(tps: List[Type]): List[Type] = {
def dealiasRefinement(tp: Type) = if (tp.dealias.isInstanceOf[RefinedType]) tp.dealias else tp
tps map dealiasRefinement flatMap {
case RefinedType(parents, ds) if ds.isEmpty => flatten(parents)
case tp => List(tp)
}
}
val flattened = flatten(parents).distinct
if (decls.isEmpty && hasLength(flattened, 1)) {
flattened.head
} else if (flattened != parents) {
refinedType(flattened, if (typeSymbol eq NoSymbol) NoSymbol else typeSymbol.owner, decls, NoPosition)
} else if (isHigherKinded) {
etaExpand
} else super.normalize
}
Notice the usage of flatten(...)
and .distinct
.