Параметры типа и типы элементов в Scala - PullRequest
10 голосов
/ 26 февраля 2009

Я хотел бы знать, как типы элементов работают в Scala и как я должен связывать типы.

Один из подходов - сделать связанный тип параметром типа. Преимущества этого подхода в том, что я могу прописать дисперсию типа, и я могу быть уверен, что подтип не меняет тип. Недостатки в том, что я не могу вывести параметр типа из типа в функции.

Второй подход заключается в том, чтобы сделать связанный тип членом второго типа, из-за чего я не могу прописать границы для связанных типов подтипов и, следовательно, не могу использовать тип в параметрах функции ( когда x: X, X # T может не иметь никакого отношения к xT)

Конкретный пример будет:

У меня есть черта для DFA (может быть без параметра типа)

trait DFA[S] { /* S is the type of the symbols in the alphabet */
  trait State { def next(x : S); }
  /* final type Sigma = S */
}

и я хочу создать функцию для запуска этого DFA над последовательностью ввода, и я хочу

  • функция должна принимать что угодно <% Seq[alphabet-type-of-the-dfa] в качестве типа входной последовательности
  • вызывающей функции не нужно указывать параметры типа, все должны быть выведены
  • Я бы хотел, чтобы функция вызывалась с конкретным типом DFA (но если есть решение, в котором функция не имеет параметра типа для DFA, все в порядке)
  • типы алфавита должны быть неограниченными (т. Е. Должен быть DFA для Char, а также для еще неизвестного пользовательского класса)
  • DFA с различными типами алфавита не являются подтипами

Я пробовал это:

def runDFA[S, D <: DFA[S], SQ <% Seq[S]](d : D)(seq : SQ) = ....

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

def runDFA[D <: DFA[S] forSome { type S }, SQ <% Seq[D#Sigma]]( ... same as above

это не сработало (недопустимая циклическая ссылка на тип D ??? (что это?))

Я также удалил параметр типа, создал абстрактный тип Sigma и попытался связать этот тип с конкретными классами. runDFA будет выглядеть как

def runDFA[D <: DFA, SQ <% Seq[D#Sigma]]( ... same as above

, но это неизбежно приводит к таким проблемам, как «несоответствие типов: ожидаемое dfa.Sigma, полученное D#Sigma»

Есть идеи? Указатели

Edit:

Как показывают ответы, простого способа сделать это не может, может кто-нибудь подробнее рассказать, почему это невозможно и что нужно изменить, чтобы это работало?

Причины, по которым я хочу, чтобы runDFA ro была бесплатной функцией (а не методом), заключаются в том, что я хочу, чтобы другие подобные функции, такие как минимизация автоматов, регулярные языковые операции, преобразования NFA в DFA, языковая факторизация и т. Д., Имели все это внутри одного класса просто против практически любого принципа ОО дизайна.

Ответы [ 3 ]

3 голосов
/ 27 февраля 2009

Во-первых, вам не нужна параметризация SQ <% Seq [S]. Запишите параметр метода как Seq [S]. Если SQ <% Seq [S], то любой его экземпляр неявно преобразуется в Seq [S] (это означает, что <% означает), поэтому при передаче в качестве Seq [S] компилятор автоматически вставит преобразование. </p>

Кроме того, то, что сказал Хорхе о параметрах типа в D и о том, чтобы сделать его методом в DFA, сохраняется. Из-за того, как внутренние классы работают в Scala, я бы настоятельно рекомендовал поставить runDFA на DFA. Пока не работает зависимая от пути вещь, работа с внутренними классами некоторого внешнего класса может быть немного болезненной.

Так что теперь у вас есть

trait DFA[S]{
  ...

  def runDFA(seq : Seq[S]) = ...
}

И runDFA внезапно довольно легко определить для параметров типа: у него нет никаких параметров.

3 голосов
/ 27 февраля 2009

Вывод типа Scala иногда оставляет желать лучшего.

Есть ли причина, по которой у вас не может быть метода внутри вашей черты DFA?

def run[SQ <% Seq[S]](seq: SQ)

Если вам не нужен параметр D позже, вы также можете попробовать определить свой метод без него:

def runDFA[S, SQ <% Seq[S]](d: DFA[S])(seq: SQ) = ...
0 голосов
/ 27 апреля 2017

Некоторая полезная информация о том, как они различаются:

Из бесформенной направляющей :

Без параметров типа невозможно создать зависимые типы, например

trait Generic[A] {
  type Repr
  def to(value: A): Repr
  def from(value: Repr): A
}

import shapeless.Generic
def getRepr[A](value: A)(implicit gen: Generic[A]) =
  gen.to(value)

Здесь тип, возвращаемый to, зависит от типа ввода A (поскольку предоставленное неявное зависит от A):

case class Vec(x: Int, y: Int)
case class Rect(origin: Vec, size: Vec)
getRepr(Vec(1, 2))
// res1: shapeless.::[Int,shapeless.::[Int,shapeless.HNil]] = 1 :: 2 ::
     HNil
getRepr(Rect(Vec(0, 0), Vec(5, 5)))
// res2: shapeless.::[Vec,shapeless.::[Vec,shapeless.HNil]] = Vec(0,0)
     :: Vec(5,5) :: HNil

без членов типа это было бы невозможно:

trait Generic2[A, Repr]
def getRepr2[A, R](value: A)(implicit generic: Generic2[A, R]): R =
  ???

Нам бы пришлось передать желаемое значение Repr в getRepr как параметр типа, что делает getRepr бесполезным. Интуи ве От этого следует, что параметры типа полезны в качестве «входов» и члены типа полезны как «выходные данные».

Подробнее см. Бесформенную направляющую .

...