Combine Match-Types and Either - "A match type could not be fully reduced"

57 Views Asked by At

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: https://scastie.scala-lang.org/rXQ0UOjKSzmOvNwr82It6g

0

There are 0 best solutions below