Closed htmldoug closed 3 years ago
@htmldoug Thanks for this bug report! I'd be curious to minimize the actual eq situation.
scala> :paste
// Entering paste mode (ctrl-D to finish)
object AkkaAxis extends InnerClassAxis("akka")
object PlayAxis extends InnerClassAxis("play")
class InnerClassAxis(prefix: String) {
case class Value(version: String)
}
// Exiting paste mode, now interpreting.
defined object AkkaAxis
defined object PlayAxis
defined class InnerClassAxis
scala> AkkaAxis.Value("2").getClass == PlayAxis.Value("2").getClass
res0: Boolean = true
I guess the idiomatic workaround for this is using reflection API?
scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._
scala> def axisTag[A: TypeTag](a: A) = implicitly[TypeTag[A]]
axisTag: [A](a: A)(implicit evidence$1: reflect.runtime.universe.TypeTag[A])reflect.runtime.universe.TypeTag[A]
scala> axisTag(AkkaAxis.Value("2"))
res0: reflect.runtime.universe.TypeTag[AkkaAxis.Value] = TypeTag[AkkaAxis.Value]
scala> axisTag(AkkaAxis.Value("2")) == axisTag(PlayAxis.Value("2"))
res1: Boolean = false
Thanks for the response. That's about as far as I got, too. The next question: how do we make a TypeTag
available to isStronglyCompatible
?
If I recall correctly, there's an interesting use for the loathed Scala feature auto-tupling, and it's that we can use it to capture the individual types given to a method that looks like a parameter but in fact accepts tuples.
So we can make Seq(...)
wrapper called TypedSeq(...)
with:
def apply[A1: TypeTag, A2: TypeTag]((a1: A1, a2: A2)): Seq[(VirtualAxis, TypeTag[_])]
That sounds potentially too much work to support inherited inner classes, but it's a potential solution.
Wow, TIL. That does seem like overkill.
My workaround has improved into:
case class AkkaAxis(version: String) extends PrefixedWeakAxis("akka")
object AkkaAxis extends CurrentAxis[AkkaAxis] {
val Akka24 = AkkaAxis("2.4.20")
val Akka25 = AkkaAxis("2.5.23")
val Akka26 = AkkaAxis("2.6.3")
}
case class PlayAxis(version: String) extends PrefixedWeakAxis("play")
object PlayAxis extends CurrentAxis[PlayAxis] {
val Play25 = PlayAxis("2.5.19")
val Play27 = PlayAxis("2.7.4")
val Play28 = PlayAxis("2.8.1")
}
abstract class CurrentAxis[T: ClassTag] {
def current: Def.Initialize[T] = Def.setting {
virtualAxes.value.collectFirst {
case a: T => a
}.get
}
}
abstract class PrefixedWeakAxis(prefix: String) extends VirtualAxis.WeakAxis {
def version: String
val majorMinor: String = version.split('.').take(2).mkString
def nameComponent: String = s"$prefix$majorMinor"
override def directorySuffix: String = s"-$nameComponent"
override def idSuffix: String = directorySuffix
override def toString: String = s"$nameComponent($version)"
}
Stepping back, I care more that there's a convenient way to get the current value of an axis than that inner classes are supported.
I'm going to close this PR, since it seems like inner classes are a dead end, and the workaround is good enough. Thanks for reviewing.
Problem definition
This may not be a problem with sbt-projectmatrix at all. However, the scripted test demonstrates what I wanted to do, but failed in a surprising way. The project fails to load with:
Note that
play27(2.7.4)
is aWeakAxis
.The problem seems to be that at compile time, the inner classes have different types, but at runtime, they
eq
each other. That makes isStronglyCompatible conclude (undesirably) that the "play"WeakAxis
must have values of the same axis type defined in the "akka" project.Slightly related, the same problem seems to exist with enums.
Workaround
sbt-projectmatrix works as expected if I redefine the axis class in each of the inner class instances.
The duplication is unsatisfying.
Paths forward
I don't have a good suggestion for what could be used as an axis identifier instead of
weakAxis.getClass
, e.g. using aTypeTag
seems difficult. MaybeVirtualAxis
could expose an overridabledef axisIdentifier: Any = getClass
. This would not fix the problem of the shared class instance, but it would at least expose an escape hatch that could be used to eliminate the duplication.Or perhaps there's a better workaround I missed that would make something like that unnecessary? WDYT?