在我的项目中,我使用 Scala 3,我认为我发现了一个奇怪的情况,即多态扩展方法不能很好地处理自定义文字类型。我减少了代码,以便我可以用最少的代码来显示问题:
object Demo2:
extension [T](either: Either[String, T])
def toOptionCustom: Option[T] = either.fold(_ => None, Some(_))
type MyValue = "value1" | "value2" | "value3"
val myValues: List[MyValue] = List("value1", "value2", "value3")
def toMyValue(value: String): Either[String, MyValue] = myValues.find(_.toString == value).toRight("Invalid value")
def hello(myValue: MyValue): String = s"Hello $myValue"
def demo1: Option[String] =
toMyValue("value1").toOption.map(hello) // this line compiles
def demo2: Option[String] =
toMyValue("value1").fold(_ => None, Some(_)).map(hello) // this line also compiles
def demo3: Option[String] =
toMyValue("value1").toOptionCustom.map(hello) // but this line doesn't compile
编译器无法编译最后一行,指出函数
hello
应该是 String => String
类型,在我看来这是错误的。它应该接受 MyValue => String
类型函数并且 hello
符合它。 toOptionCustom
方法基本上是我对 toOption
的自定义实现,我认为它应该有效。 demo2
和demo3
基本相同,只是demo3
经过了扩展方法。我是不是错过了什么?
我的Scala版本是3.4.2,这是目前最新的。
问题与您的
Singleton
类型的 MyValue
性质有关。如果您将 <: Singleton
添加到扩展类型参数中,它就可以正常工作:
extension [T <: Singleton](either: Either[String, T])
Scala 中的类型推断将单例类型扩展到底层的非单例类型。在您的示例中,
MyValue
的底层非单例类型确实是String
。
以下示例展示了这种特殊性:
def foo[T](opt: Option[T]) = opt
def bar[T <: Singleton](opt: Option[T]) = opt
val one: Option[1] = Some(1)
val a = foo(one) // (a: Option[Int])
val b = bar(one) // (b: Option[1])