scala的联合类型集合

scala的联合类型集合,scala,functional-programming,scala-collections,discriminated-union,Scala,Functional Programming,Scala Collections,Discriminated Union,在scala中是否可能有一个联合类型的集合。讨论的工会类型有几种方法,最高评级的答案让人觉得最自然,我有这样的想法: sealed trait StringOrNumber[T] object StringOrNumber { implicit object IntWitness extends StringOrNumber[Int] implicit object StringWitness extends StringOrNumber[String] } 但是当我试图制作一

在scala中是否可能有一个联合类型的集合。讨论的工会类型有几种方法,最高评级的答案让人觉得最自然,我有这样的想法:

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] 

但多蒂将在明年上市。 现在,您可以使用您的方法,但这有点棘手,需要隐含。
您还可以尝试任何一种类型(仅当您有两种泛型类型时),并且还可以关注scalaz库。这都是关于类型级编程的。

您复制的部分答案不完整。还有另一部分是匹配的。它表明这种类型的联合在运行时是有效的。 所以一般来说,您混合了两种不同的东西:编译时类型联合(您提到的问题中也讨论了它,最初由Miles Sabin编写),它影响编译器检查,以及运行时类型检查


因此,只要您使用运行时方法,scala编译器就不理解这种联合,建议您使用任何

运行时联合类型的最接近的模拟,您可以在当前版本的scala中进行,就是在类扩展某些特性的情况下包装联合的类型。它是样板文件,在
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)
}
现在您可以定义您的
地图了

scala> val m: Map[String, StringOrNumber] = Map("str" -> "hellp", "int" -> 32)
m: Map[String,StringOrNumber] = Map(str -> IsString(hellp), int -> IsNumber(32))

Scala已经有了内置的
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)
  }
}

让我向您介绍我的解决方案,使用逆变和类型约束:

//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
  }
}

“StringOrNumber”只是编译器隐式提供的标记。它们就像进入方法foo[T:StringOrNumber](…)的入场券,它们本身不是字符串或数字。是的,编译器不会接受它。经过反思,这种方法实际上只适用于单个方法,它依赖于方法的类型参数来实现联合。我需要一些可以存在于泛型方法之外的东西,我试过了,但也没有编译错误:(27,62)类型不匹配;发现:String(“hellp”)required:StringOrNumber[]val m:Map[String,StringOrNumber[]]=Map(“str”->“hellp”,“int”->32)我会研究scalaz,但作为初学者可能很难理解,我希望有一种方法可以实现字符串的抽象类型,或者在我想使用它时,我可以进行模式匹配。这似乎是在一个案例中包装的唯一方法classOk是的,结合隐式这似乎可以job@LukeDeFeo如果你真的喜欢隐式转换的解决方案,你应该接受Kolmar的答案。然而,我想引用Welsh/Gurnell的“带猫的Scala”:“带有非隐式参数的隐式方法形成了一种称为隐式转换的[…]Scala模式。这是一种在现代Scala代码中不受欢迎的较旧编程模式”。我只是说:我没有建议您使用隐式转换。这是Scala令人失望的一面。例如,假设我有5个枚举类型,它们是由代码生成器(如Thrift)自动生成的。我想要一个具有类型参数的函数,该参数必须是那些特定的5个枚举类之一(所有这些类都具有相同的方法)。我可以省略type参数并添加
match。。case
boilerplate无处不在,或者添加该boilerplate以创建一个完全无用的“fromnum1”、“fromnum2”…值构造函数case类包装器的密封特征。无论哪种方式,Scala都是为了增加样板。。。
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
  }
}
sizeOf[String](someExtendOfString)