Scala中具有联合类型的通用数据结构

时间:2011-11-16 14:44:48

标签: generics scala types

我正在尝试大致如下:

/* I assume that we have some types S and T */

sealed trait Container[U]
class SContainer(s: S) extends Container[S]
class TContainer[U <: T](u: U) extends Container[U]

/* this requires something more */
def invoke[U](f: String => U): Container[U]

简单地说,我想要一个Container类,它可以包含S类型的内容,也可以包含T类型的内容。 Container类明显满足这些属性,因为只有两种可能的方法来构造容器。

但是,现在我想创建一个函数invoke来包装容器中的值。我的例子中给出的定义显然不起作用,因为我对U没有限制。

不幸的是,由于类型擦除,我们不能定义两个函数:

def invoke(f: String => S): Container[S]
def invoke[U <: T](f: String => U): Container[U]

因为它们在运行时会有相同的类型。

如何在invoke上表达这种类型约束?

2 个答案:

答案 0 :(得分:2)

我认为以下内容可行。我不知道你的TS是什么,所以我正在尝试使用这些

type T = AnyVal
type S = String

您可以使用视图绑定:

object Container {
   implicit def stuffS(s: S): Container[S] = new SContainer(s)
   implicit def stuffT[U <: T](u: U): Container[T] = new TContainer(u)
}
sealed trait Container[U]
case class SContainer(s: S) extends Container[S]
case class TContainer[U <: T](u: U) extends Container[T]

然后您的invoke变为:

def invoke[B, A <% Container[B]](f: String => A): Container[B] = f("hallo")

这些电话有效:

invoke(_.size)    // -> Container[T] = TContainer(5)
invoke(_.reverse) // -> Container[S] = SContainer(ollah)

这是不允许的:

invoke(Symbol(_))

修改

如果你想要Container[U]而不是Container[T],那就更简单了:

object Container {
  implicit def stuffS(s: S): Container[S] = new SContainer(s)
  implicit def stuffT[U <: T](u: U): Container[U] = new TContainer(u)
}
sealed trait Container[U]
case class SContainer(s: S) extends Container[S]
case class TContainer[U <: T](u: U) extends Container[U]

def invoke[A <% Container[A]](f: String => A): Container[A] = f("hallo")

invoke(_.size)      // Container[Int]
invoke(_.reverse)   // Container[String]
invoke(Symbol(_))   // forbidden

答案 1 :(得分:0)

我认为你应该将容器的定义与容器本身可接受的类型的定义分开。

首先让我们说(例如)S是String而T是虚拟类。

class A
class B extends A

type T = A
type S = String

现在,让我们使用类型类模式将T和S的子类定义为容器的可接受内容:

sealed class AcceptableContent[X] private() 
object AcceptableContent {
  implicit object SAcceptable extends AcceptableContent [S]
  implicit def TAcceptable[U <: T] = new AcceptableContent[U]
}

现在可以像这样定义Container类:

class Container[R : AcceptableContent](content: R)

上下文绑定: AcceptableContent表示只有在范围内存在隐式AcceptableContent [R]时才能接受R,这就是你想要的。 事实上,现在:

scala> new Container("test")
res0: Container[java.lang.String] = Container@1d1fceed

scala> new Container(new A)
res1: Container[A] = Container@631803fb

scala> new Container(new B)
res2: Container[B] = Container@67446579

scala> new Container(true) // Ok, Container accepts String, A, B but not Boolean
<console>:12: error: could not find implicit value for evidence parameter of type AcceptableContent[Boolean]

所以,现在调用变为:

def invoke[N : AcceptableContent](f: String => N) : Container[N] = new Container(f("test"))