Pytanie Wpisz typy klas i typy zależne


Po pierwsze, nie wiem, jak właściwie oznaczyć mój problem. To może być również powód, dla którego nie znalazłem przydatnych zasobów. Wszelkie wskazówki są bardzo cenne.

trait Context[T]
{
    self =>

    trait Rule
    {
        def apply( value: T ): Boolean
    }

    implicit class RichRule[A <: Rule]( a: A )
    {
        def and[B <: Rule]( b: B ): and[A, B] = self.and( a, b )
        def or[B <: Rule]( b: B ): or[A, B] = self.or( a, b )
    }

    sealed trait Group[A <: Rule, B <: Rule] extends Rule
    {
        def a: A

        def b: B

        override def apply( value: T ) = ???
    }

    case class and[A <: Rule, B <: Rule]( a: A, b: B ) extends Group[A, B]
    case class or[A <: Rule, B <: Rule]( a: A, b: B ) extends Group[A, B]
}

Biorąc pod uwagę powyższy kod, mogę teraz definiować i łańcuchować Rulesw ten sposób:

new Context[String]
{
    class MyRule extends Rule
    {
        override def apply( value: String ) = true
    }

    case class A() extends MyRule
    case class B() extends MyRule

    val x1: A and B or A = A() and B() or A()
}

Działa to tak jak zamierzałem, ale teraz jest to trudna część. Chcę wprowadzić klasę typu Combination to wyjaśnia, jak połączyć dwie reguły.

trait Combination[-A <: Rule, -B <: Rule]
{
    type Result <: Rule

    def combine( a: A, b: B ): Result
}
trait AndCombination[-A <: Rule, -B <: Rule] extends Combination[A, B]
trait OrCombination[-A <: Rule, -B <: Rule] extends Combination[A, B]

Ta klasa typów powinna teraz zostać przekazana operatorom.

implicit class RichRule[A <: Rule]( a: A )
{
    def and[B <: Rule]( b: B )( implicit c: AndCombination[A, B] ): and[A, B] = ???
    def or[B <: Rule]( b: B )( implicit c: OrCombination[A, B] ): or[A, B] = self.or( a, b )
}

Który nadal działa po kilku poprawkach.

implicit val c1 = new Combination[MyRule, MyRule]
{
    type Result = MyRule

    def combine( a: A, b: B ): MyRule = a
}

val x: A and B = A() and B()

Ale jeśli to się komplikuje, sprawy się rozpadają.

A() and B() and A()

Podniesie domyślny brakujący błąd: Combination[and[A, B], A] brakuje. Ale chcę, aby użył wyniku niejawnej kombinacji and[A, B] (type Result = MyRule), który już wie, jak sobie poradzić (Combination[and[A, B]#Result, A]).

Ważne jest, aby zachować informacje o typach połączonych reguł val x: A and B or A, złożenie ich razem na końcowy typ wyniku jest łatwe, ale nie to, czego chcę.

Jest to tak blisko, jak tylko mogłem, ale kompilacja się nie udała.

trait Context[T]
{
    self =>

    trait Rule

    trait Value extends Rule

    trait Group[A <: Rule, B <: Rule] extends Rule
    {
        def a: A

        def b: B

        implicit val resolver: Resolver[_ <: Group[A, B]]
    }

    case class and[A <: Rule, B <: Rule]( a: A, b: B )( implicit val resolver: Resolver[and[A, B]] ) extends Group[A, B]

    implicit class RichRule[A <: Rule]( a: A )
    {
        def and[B <: Rule]( b: B )( implicit resolver: Resolver[and[A, B]] ) = self.and[A, B]( a, b )
    }

    trait Resolver[-A <: Rule]
    {
        type R <: Value

        def resolve( a: A ): R
    }
}

object O extends Context[String]
{
    implicit val c1 = new Resolver[A and A]
    {
        override type R = A

        override def resolve( a: O.and[A, A] ) = ???
    }

    implicit def c2[A <: Value, B <: Value, C <: Value]( implicit r1: Resolver[A and B] ) = new Resolver[A and B and C]
    {
        override type R = C

        override def resolve( a: A and B and C ): C =
        {
            val x: r1.R = r1.resolve( a.a )
            new c2( x )
            ???
        }
    }

    class c2[A <: Value, B <: Value]( val a: A )( implicit r2: Resolver[A and B] ) extends Resolver[A and B]
    {
        override type R = B

        override def resolve( a: O.and[A, B] ) = a.b
    }

    case class A() extends Value

    val x: A and A and A = A() and A() and A()
}

11
2018-06-17 14:59


pochodzenie


Jakiej wersji Scala używasz i jakie IDE? - eliasah
2.11.6 z IntelliJ, kompilacja przez konsolę sbt - Taig


Odpowiedzi:


Powodem, dla którego twój kod nie może się skompilować, jest instrukcja

 new c2( x )

Kompilator musi rozwiązać implicit r2: Resolver[A and B] od x a jedyną dostępną informacją o typie jest typ x, który jest r1.R.

Tego rodzaju problemy wymagają udostępnienia większej ilości informacji o typie kompilatorowi i dodaniu jakiegoś niejawnego parametru. Kiedy potrzebujesz Resolver[A and B], nie możesz jej użyć R wpisz, aby rozwiązać inny Resolver[r1.R and C].

type ResolverAux[-A<:Rule,B] = Resolver[A] { type R = B }

Za pomocą tego dostępnego możesz napisać podpis swojego c2

implicit def c2[A <: Value, B <: Value, C <: Value,D<:Value]( implicit r1: ResolverAux[A and B,D], r2:Resolver[D and C] ):Resolver[A and B and C] = new Resolver[A and B and C]
  {
    override type R = C

    override def resolve( a: A and B and C ): C =
    {
      val x: r1.R = r1.resolve( a.a )
      new c2[r1.R,C]( x )
      ???
    }
  }

Zauważ, że używając aliasu typu i wprowadzając dodatkowy parametr ogólny, mogę wyrazić relację r1.R1 = D który jest następnie używany do rozstrzygnięcia drugiego niejawnego r2


9
2018-06-23 19:26



Dziękuję bardzo za przebrnięcie przez moje niechlujne pytanie i kod. Naprawdę to doceniam. Twoja odpowiedź wygląda obiecująco, ale prawdopodobnie zajmie mi to trochę czasu. Zawsze zastanawiałem się, co to jest Aux, gdy natknąłem się na jakiś skalak lub bezkształtny. - Taig