Параметры типа по сравнению с типами элементов в Scala
Я хотел бы знать, как типы членов работают в 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-to-DFA, факторизация языка и т.д. и имеющие все это внутри одного класса просто против практически любого принципа проектирования OO.
Ответы
Ответ 1
Во-первых, вам не нужна параметризация 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 внезапно довольно легко вывести параметры типа для: у него его нет.
Ответ 2
Scala вывод типа иногда оставляет желать лучшего.
Есть ли причина, по которой у вас не может быть метод внутри вашей DFA-функции?
def run[SQ <% Seq[S]](seq: SQ)
Если вам не нужен параметр D позже, вы также можете попробовать определить свой метод без него:
def runDFA[S, SQ <% Seq[S]](d: DFA[S])(seq: SQ) = ...
Ответ 3
Некоторая полезная информация о том, как эти два отличается:
Из бесформенного guide:
Без параметров типа вы не можете создавать зависимые типы, например
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 как тип параметра, effec vely делает getRepr бесполезным. Интуиция отвлечение от этого - это параметры типа полезны как "входы" и члены типа полезны как "выходы".
Подробнее см. в бесформенном guide.