Вопрос

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

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

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

Конкретным примером может быть:

У меня есть особенность для 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"

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

Редактировать:

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

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

Это было полезно?

Решение

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

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

Итак, теперь у вас есть

trait DFA[S]{
  ...

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

И с помощью runDFA неожиданно стало довольно легко определить параметры типа для:У него ничего нет.

Другие советы

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

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

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

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

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

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

Из бесформенного гид:

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

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 =
  ???

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

пожалуйста, взгляни на бесформенное гид для получения подробной информации.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top