联合类型集合 scala
Collection of Union types scala
在 scala 中是否可以有联合类型的集合。讨论了几种联合类型的方法 here 评分最高的答案感觉最原生,我有这样的东西:
sealed trait StringOrNumber[T]
object StringOrNumber {
implicit object IntWitness extends StringOrNumber[Int]
implicit object StringWitness extends StringOrNumber[String]
}
但是当我尝试制作包含两者的地图时
val m: Map[String, Any] = Map("str" -> "hellp", "int" -> 32)
scala 编译器将其视为 [String,Any] 的映射 有没有办法告诉 scala 编译器这是一个映射 [String, StringOrNumber]
编辑:
我不认为使用上述方法可以创建字符串或联合的集合。我认为它需要成为联合类型的另一种方法,因为上面的方法类似于重载方法,而不是类型系统中的真正联合类型
你试过了吗:
val m: Map[String, StringOrNumber] = Map("str" -> "hellp", "int" -> 32)
在这种情况下,您可能还需要显式构造 StringOrNumber
实例才能使其正常工作。
你应该写
val m: Map[String, StringOrNumber[_]] = ...
此功能目前正在 Dotty 中开发。据我所知,它会像
Class[T1 | T2]
和
Class[T1 & T2]
但 dotty 将在明年上市。
现在,您可以使用您的方法,但它有点棘手,需要隐含。
您也可以尝试 Either 类型(只有当您有 2 个泛型类型时),您还可以关注 scalaz 库。这都是关于 type-level 编程的。
您复制的部分答案不完整。还有另一部分与匹配。它表明这种类型的联合在运行时有效。
所以一般来说,你会混合两种不同的东西:编译时类型联合(你提到的问题中也讨论过,最初由 Miles Sabin here 编写)影响编译器检查,以及运行时类型检查。
所以,一旦你使用运行时方法,scala 编译器就不会理解这个联合,建议使用 Any
您可以在当前版本的 Scala 中执行的最接近运行时联合类型的模拟是包装联合类型,以防 类 扩展某些密封特征。它是 boilerplate-y 并在 AnyRef
类型上添加了一个额外的包装层,但它有效,它比仅使用 Any
更好,您还可以从联合类型添加隐式转换:
sealed trait StringOrNumber
object StringOrNumber {
case class IsNumber(i: Int) extends StringOrNumber
case class IsString(s: String) extends StringOrNumber
implicit def isNumber(i: Int): StringOrNumber = IsNumber(i)
implicit def isString(s: String): StringOrNumber = IsString(s)
}
现在您可以定义您的 Map
:
scala> val m: Map[String, StringOrNumber] = Map("str" -> "hellp", "int" -> 32)
m: Map[String,StringOrNumber] = Map(str -> IsString(hellp), int -> IsNumber(32))
Scala 已经有 built-in case
-类,它们能够表示其他类型的任意标记的不相交联合。
在您的情况下,定义 StringOrNumber
的最简单方法是:
sealed trait StringOrNumber
case class Num(n: Int) extends StringOrNumber
case class Str(s: String) extends StringOrNumber
val m: Map[String, StringOrNumber] = Map(
"str" -> Str("hellp"),
"int" -> Num(42)
)
for ((k, v) <- m) {
v match {
case Num(n) => println("It's an int: " + n)
case Str(s) => println("A string: " + s)
}
}
如果你不想为此创建额外的特征,并且如果你只有两种类型,只需使用 Either
:
type StringOrNum = Either[String, Int]
让我向您介绍我的解决方案,使用逆变和类型约束:
//Add this to your util library
trait Contra[-A]
type Union[A,B] = Contra[A] <:< Contra[B]
//And see a usage example below
@implicitNotFound("Only Int or String can be sized")
type Sizeable[T] = Union[T, Int with String]
def sizeOf[T: Sizeable](sizeable: T): Int = {
sizeable match {
case i: Int => i
case s: String => s.length
}
}
此解决方案的问题是此处不接受 Int 或 String 的扩展。此处输入的值被检查为 "Contravariant" 到 Int with String
.
有办法解决这个问题,你必须绕过类型推断,并在类型参数中提供基数 class,如下所示:
sizeOf[String](someExtendOfString)
在 scala 中是否可以有联合类型的集合。讨论了几种联合类型的方法 here 评分最高的答案感觉最原生,我有这样的东西:
sealed trait StringOrNumber[T]
object StringOrNumber {
implicit object IntWitness extends StringOrNumber[Int]
implicit object StringWitness extends StringOrNumber[String]
}
但是当我尝试制作包含两者的地图时
val m: Map[String, Any] = Map("str" -> "hellp", "int" -> 32)
scala 编译器将其视为 [String,Any] 的映射 有没有办法告诉 scala 编译器这是一个映射 [String, StringOrNumber]
编辑:
我不认为使用上述方法可以创建字符串或联合的集合。我认为它需要成为联合类型的另一种方法,因为上面的方法类似于重载方法,而不是类型系统中的真正联合类型
你试过了吗:
val m: Map[String, StringOrNumber] = Map("str" -> "hellp", "int" -> 32)
在这种情况下,您可能还需要显式构造 StringOrNumber
实例才能使其正常工作。
你应该写
val m: Map[String, StringOrNumber[_]] = ...
此功能目前正在 Dotty 中开发。据我所知,它会像
Class[T1 | T2]
和
Class[T1 & T2]
但 dotty 将在明年上市。 现在,您可以使用您的方法,但它有点棘手,需要隐含。 您也可以尝试 Either 类型(只有当您有 2 个泛型类型时),您还可以关注 scalaz 库。这都是关于 type-level 编程的。
您复制的部分答案不完整。还有另一部分与匹配。它表明这种类型的联合在运行时有效。 所以一般来说,你会混合两种不同的东西:编译时类型联合(你提到的问题中也讨论过,最初由 Miles Sabin here 编写)影响编译器检查,以及运行时类型检查。
所以,一旦你使用运行时方法,scala 编译器就不会理解这个联合,建议使用 Any
您可以在当前版本的 Scala 中执行的最接近运行时联合类型的模拟是包装联合类型,以防 类 扩展某些密封特征。它是 boilerplate-y 并在 AnyRef
类型上添加了一个额外的包装层,但它有效,它比仅使用 Any
更好,您还可以从联合类型添加隐式转换:
sealed trait StringOrNumber
object StringOrNumber {
case class IsNumber(i: Int) extends StringOrNumber
case class IsString(s: String) extends StringOrNumber
implicit def isNumber(i: Int): StringOrNumber = IsNumber(i)
implicit def isString(s: String): StringOrNumber = IsString(s)
}
现在您可以定义您的 Map
:
scala> val m: Map[String, StringOrNumber] = Map("str" -> "hellp", "int" -> 32)
m: Map[String,StringOrNumber] = Map(str -> IsString(hellp), int -> IsNumber(32))
Scala 已经有 built-in case
-类,它们能够表示其他类型的任意标记的不相交联合。
在您的情况下,定义 StringOrNumber
的最简单方法是:
sealed trait StringOrNumber
case class Num(n: Int) extends StringOrNumber
case class Str(s: String) extends StringOrNumber
val m: Map[String, StringOrNumber] = Map(
"str" -> Str("hellp"),
"int" -> Num(42)
)
for ((k, v) <- m) {
v match {
case Num(n) => println("It's an int: " + n)
case Str(s) => println("A string: " + s)
}
}
如果你不想为此创建额外的特征,并且如果你只有两种类型,只需使用 Either
:
type StringOrNum = Either[String, Int]
让我向您介绍我的解决方案,使用逆变和类型约束:
//Add this to your util library
trait Contra[-A]
type Union[A,B] = Contra[A] <:< Contra[B]
//And see a usage example below
@implicitNotFound("Only Int or String can be sized")
type Sizeable[T] = Union[T, Int with String]
def sizeOf[T: Sizeable](sizeable: T): Int = {
sizeable match {
case i: Int => i
case s: String => s.length
}
}
此解决方案的问题是此处不接受 Int 或 String 的扩展。此处输入的值被检查为 "Contravariant" 到 Int with String
.
有办法解决这个问题,你必须绕过类型推断,并在类型参数中提供基数 class,如下所示:
sizeOf[String](someExtendOfString)