2017-02-07 2 views
1

У меня есть следующий код;Параметр параметра типа должен быть признаком

object main 
{ 
    def main(args: Array[String]): Unit = 
    { 
     trait E 
     { 
      def test(): Unit = println("test :)") 
     } 

     class B[T](val x: Int) 
     { 
      def inc(): B[T] with T = new B[T](x + 1) with T 
     } 

     class A[T](f : B[T] with T => Unit) 
     { 
      def apply(b: B[T] with T) = f(b) 
     } 

     val b = new B[E](0) with E 
     val a = new A[E](b => b.test())(b) 
    } 
} 

Однако линия def inc(): B[T] with T = new B[T](x + 1) with T не компилируется, что дает ошибку, что «необходимый тип класса, но T найден» и «T должна быть черта смешивать в». Я понимаю, почему это так, но я не могу найти способ обойти это! Я не нашел способ ограничить T, чтобы быть чертой, из-за которой я боюсь, что такой подход не сработает ...

Чтобы дать дополнительную информацию о том, почему я пытаюсь достичь этого (просто выложите любой может предложить лучшее решение) У меня есть класс Parsec[S, U, E, A], который состоит из функций, которые принимают объект State[S, U, E] with E. Идея состоит в том, что U является пользователем данного состояния, A является результатом анализатора, S поток токенов и E является некоторым расширением состояния (например, можно было бы создать Parsec[Stream[String, Char], Int, IndentationSensitive, List[Expr]] и т. Д. И т. Д. U = Int будет то, что пользователь хотел сосчитать, например, и что не нужно вмешиваться в состояние, требуемое для чувствительности с отступом (которое равно двум Ints), которое было бы обеспечено путем смешивания в значении IndentationSensitive. Тогда, если пользователю нужна другая функциональность они могут держать смешивание в более признаков для парсеров.

Итак, есть в любом случае я могу тяготы параметра типа T в коде, так что я могу смешать его в B, или, если нет, то есть лучший способ выполнить то, что мне нужно?

Если это на самом деле не ясно, что я пытаюсь выполнить то this вопрос о иллюстрирует Просмотр Кода ситуации (в много более подробно). Но Parsec[S <: Stream[_, _], U, A] заменяется на Parsec[S <: Stream[_, _], U, E, A] и то же самое для государства и всех остальных частей.

+0

Я не знаю точно, но я ожидаю, что это невозможно. Микшины обрабатываются во время компиляции, но для этого требуется динамическое микширование. – puhlen

+0

но, конечно, этот тип может быть известен во время компиляции? В какой-то момент вы достигнете конкретного типа, если вы замените типы достаточно? –

+0

Это невозможно. –

ответ

0

Чтобы дать немного больше фона на то, почему я пытаюсь добиться этого (только закончит каждому может предложить лучшее решение) У меня есть Parsec[S, U, E, A] класса, который построен из функций, которые принимают State[S, U, E] with E объект. Идея заключается в том, что U является данным пользователем состояния, A является результатом синтаксического анализа, S потока лексем и Е некоторое расширение состояния (например, один, возможно, пожелают создать Parsec[Stream[String, Char], Int, IndentationSensitive, List[Expr]]

В в этом случае я бы просто добавил поле val extension: E в State и изменил функции для принятия State[S, U, E]. Если вы действительно хотите, вы можете добавить неявное преобразование от State[S, U, E] в E, чтобы функции могли напрямую обращаться к членам E, но я, вероятно, не делал этого сам.

0

Мне удалось найти решение проблемы, это не идеально, но он имеет дело с несколькими другими проблемами с Системой. А именно, когда мы создаем new State[S, U, E](input, pos, state) with E, что должно произойти с переменными, добавленными с E. Они заблудились, и это убийца.

Давайте определим новый тип type StateBuilder[S <: Stream[_, _], U, E] = (Option[State[S, U, E] with E], S, SourcePos, U) => State[S, U, E] with E. Это функция, которая может построить новое состояние типа, которое мы хотим, учитывая возможное предыдущее состояние и некоторые новые значения для «нормальных» параметров состояния.

Теперь мы можем переопределить состояние как;

case class State[S <: Stream[_, _], U, E](stateInput: S, statePos: SourcePos, stateUser: U, build: StateBuilder[S, U, E]) 

А теперь мы просто нужны некоторые из тех StateBuilder[S, U, E], которые получат прошли между государствами, но мы должны кормить его, когда мы создаем начальное состояние. Это прекрасно, но это означает, что пользователь должен понять, что они (что немного невыгодно). Пример построителя без расширений;

trait Default 
object Default 
{ 
    def build[S <: Stream[_, _], U](s: Option[State[S, U, Default] with Default], ts: S, pos: SourcePos, u: U): State[S, U, Default] with Default = 
    { 
     new State[S, U, Default](ts, pos, u, build) with Default 
    } 
} 

и более сложный может быть;

trait IndentationSensitive 
{ 
    var stateLevel: Int = 0 
    var stateRequiredIndent: Int = 0 
} 
object IndentationSensitive 
{  
    def build[S <: Stream[_, _], U](s: Option[State[S, U, IndentationSensitive] with IndentationSensitive], ts: S, pos: SourcePos, u: U): State[S, U, IndentationSensitive] with IndentationSensitive = 
    { 
     val s_ = new State[S, U, IndentationSensitive](ts, pos, u, build) with IndentationSensitive 
     s match 
     { 
      case Some(s) => 
       s_.stateLevel = s.stateLevel 
       s_.stateRequiredIndent = s.stateRequiredIndent 
      case None => 
       s_.stateLevel = 0 
       s_.stateRequiredIndent = 0 
     } 
     s_ 
    } 
} 

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

Смежные вопросы