2015-03-31 6 views
2

Предположим, у меня есть следующий код Scala:Добавить пользовательские компиляции проверяет время в Scala

sealed trait Foo 
sealed trait Bar 
object Foo1 extends Foo 
object Foo2 extends Foo 
object Foo3 extends Foo 
object Bar1 extends Bar 
object Bar2 extends Bar 
object Bar3 extends Bar 
case class Hello(foo:Foo, bar:Bar) 

val a = Hello(Foo1, Bar2) // allowed 
val b = Hello(Foo2, Bar2) // suppose not allowed 

Мне нужно поймать во время компиляции, если какой-либо несовместимое сочетание применяется к Hello. Предположим, что допускаются только следующие комбинации: (Foo1, Bar1), (Foo1, Bar2), (Foo2, Bar3) и (Foo3, Bar3).

Можно ли проверить это во время компиляции? Я понимаю, что plugins и macros могут позволить мне это сделать. Некоторые намеки были бы оценены. Вышеприведенные учебники выглядят устаревшими для последних версий Scala (2.11.x), поэтому указатели на другие учебники также будут отличными.

В реальном примере имеется около 10 экземпляров Foo и Bar, что дает в общей сложности 100 комбинаций, около половины из них недействительны. Более того, допустимые комбинации могут в будущем измениться произвольно.

EDIT

Актуальной проблемой является немного более сложным. Метод Hello принимает Seq следующим образом:

case class Hello(foos:Seq[Foo], bars:Seq[Bar]) 

Примеры сложных критериев:

  1. Если foos содержат Foo1 то bars не может иметь Bar1.
  2. foos не может содержать Foo1 и Foo3 вместе.

примеры кода:

Hello(Seq(Foo1), Seq(Bar2, Bar3)) // valid 
Hello(Seq(Foo1, Foo3), Seq(Bar1)) // invalid due to rule 2 
+1

в дополнение к @ ответ DaunnC, есть это объяснение техники [здесь] (http://www.chuusai.com/2011/07/16/ fundeps-в-Скале /). –

+0

Существуют ли какие-либо правила, которые определяют, какие комбинации действительны, а какие нет? –

+0

@MilesSabin No. Действительные комбинации довольно произвольные .. но есть определенные правила. См. Мое редактирование выше. – Jus12

ответ

3

Там трюк с типами, не уверен, что это очень красиво (не красиво на всех), и вы должны написать много таких правил вручную, но мб вам не нужно использовать тяжелые плагины/LIBS:

trait Validator[F, S] { } 
object Validator { 
    implicit object firstPair extends Validator[Foo1.type, Bar1.type] 
    implicit object secondPair extends Validator[Foo1.type, Bar2.type] 
    implicit object thirdPair extends Validator[Foo2.type, Bar3.type] 
    implicit object fourthPair extends Validator[Foo3.type, Bar3.type] 
} 

case class Hello[F <: Foo, S <: Bar](foo: F, bar: S)(implicit v: Validator[F, S]) 

val a = Hello(Foo1, Bar2) // allowed 
val b = Hello(Foo2, Bar2) // break in compile time 
+3

Это абсолютно правильный путь ... было бы совершенно нецелесообразно использовать макросы или плагины для чего-то, что имеет прямое решение в первоклассной Scala. –

+0

Не так уж и уродливый. Кажется, это самый простой способ. – Jus12

+0

Однако для этого я бы хотел использовать плагины. Фактическая ситуация более сложная, потому что сами 'Foo' и' Bar' состоят из других типов, а класс 'Hello' принимает массивы в качестве параметров. – Jus12