I’m using Scala 3.3.0 and I want to capture that the type of one field depends on the value of another field, which seems to be possible using match types.
type B[X <: Boolean] = X match {
case true => Option[String]
case false => String
}
class C(val b: Boolean, val c: B[b.type])
As expected, when instantiating C
, if b
is true
, then c
must be an Option[String]
and otherwise just a String
.
val c1 = C(true, None)
val c2 = C(true, Some("Foo"))
val c3 = C(false, "Bar")
// val c4 = C(false, Some("Foo")) Does not compile as expected
// val c5 = C(true, "Foo") Does not compile as expected
On top of that I implemented a function that, given a Boolean
and an Option[String]
, returns a C
like follows:
def bWitness(b: Boolean, s: Option[String]): B[b.type] = b match {
case _: true => s
case _: false => s.getOrElse("")
}
def buildC(myBool: Boolean, maybeString: Option[String]): C = C(myBool, bWitness(myBool, maybeString))
Now, in case that b
is false
and s
is None
, I want to return an error and not just the empty string, so I would like to wrap everything in an Either
case class Error(msg: String)
def bWitnessEither(b: Boolean, s: Option[String]): Either[Error, B[b.type]] = b match {
case _: true => Right(s)
case _: false => s.map(Right(_)).getOrElse(Left(Error("Not value present.")))
}
Here, however, the compiler complains that it could not fully reduce the match type. Can anybody explain to me, what the reason for this is? What do I need to change in order for the code to compile? Is it possible at all?
Surprisingly (at least for me), if I just return Left
the code compiles, or for the following implementation, the compiler only complains about the first case. What is the explanation?
def bWitnessEither2(b: Boolean, s: Option[String]): Either[Error, B[b.type]] = b match {
case _: true => Right(s) // The compiler seems to only complain in this case
case _: false => Left(Error("foo"))
}
You can find the code here: Scastie - An interactive playground for Scala.