Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Union types that include Newtypes don't satisfy pattern matching exhaustivity check in Scala 3 #1466

Open
spavikevik opened this issue Jan 30, 2025 · 0 comments

Comments

@spavikevik
Copy link

spavikevik commented Jan 30, 2025

Apologies in advance if the title is unclear.

Given the following example code:

object MyString extends Newtype[String]
type MyString = MyString.Type

class StringArray(val contents: Array[String])

def fn(s: MyString | StringArray) = s match
  case myS: MyString => println(myS)
  case arr: StringArray => println(arr.contents.mkString(","))

The compiler gives the following warning:

[warn]    |        match may not be exhaustive.
[warn]    |
[warn]    |        It would fail on pattern case: scala.reflect.ClassTag(_)

https://scastie.scala-lang.org/w509lJaCT3mtzUsnL4i2JA

I can see an implicit for ClassTag[Type] is defined in Newtype[A]. I am not sure where exactly this is used in practice, but I suppose it is there to help the compiler in situations like this one.

After playing around a bit, what I found to help is providing an instance for TypeTest. To my knowledge, TypeTest replaces ClassTag in Scala 3, and while ClassTag should still work, I guess there's something in this particular case that means it is not sufficient.
The example below compiles without warnings:

import zio.prelude.Newtype

import scala.reflect.TypeTest

trait MyNewtype[A] extends Newtype[A] {
given typeTest(using typeTest: TypeTest[Any, A]): TypeTest[Any, Type] = new TypeTest[Any, Type]:
    def unapply(x: Any): Option[x.type & Type] = x match
      case s: A => Some(s.asInstanceOf[x.type & Type])
      case _    => None
}

object MyString extends MyNewtype[String]
type MyString = MyString.Type

class StringArray(val contents: Array[String])

def fn(s: MyString | StringArray) = s match
  case myS: MyString => println(myS)
  case arr: StringArray => println(arr.contents.mkString(","))

https://scastie.scala-lang.org/B8Jhfu9eQ4S9wqyxtfkHbg

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant