Общая структура данных с объединенным типом в Scala - PullRequest
2 голосов
/ 16 ноября 2011

Я пытаюсь достичь чего-то примерно следующим образом:

/* 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 ]

2 голосов
/ 16 ноября 2011

Я думаю, что следующее может работать.Я не знаю, каковы ваши T и S, поэтому я пытаюсь использовать эти

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
0 голосов
/ 19 ноября 2011

Я думаю, что вы должны отделить определение контейнера от определения типов, приемлемых для самого контейнера.

Прежде всего, скажем (например), что S - это String, а T - фиктивный класс.

class A
class B extends A

type T = A
type S = String

Теперь давайте воспользуемся шаблоном класса типов, чтобы определить S и подклассы T как приемлемый контент для нашего контейнера:

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 говорит, что вы можете принять R только в том случае, если в области видимости есть неявный AcceptableContent [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]

Итак, invoke теперь становится:

def invoke[N : AcceptableContent](f: String => N) : Container[N] = new Container(f("test"))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...