2016-08-30 10 views
6

próbuję dodać ograniczenia na HList (od bezkształtnej):ograniczeniem HList: sprawdzić pojedynczego wystąpienia typu

  • powinna ona zawierać dowolną liczbę elementów typu TA (od 0 do N);
  • powinien zawierać jeden i tylko jeden element typu TB.

Mój przykład jest tego rodzaju hierarchię:

trait T 
case class TA extends T 
case class TB extends T 

podać przykłady:

  • tb :: HNil jest ważny
  • ta :: tb ::HNil jest ważny
  • ta :: tb :: ta :: HNil jest ważny
  • ta :: HNil jest nieprawidłowy
  • HNil jest nieprawidłowy

nie mogę dowiedzieć się, jak wyrazić to jako ograniczenie.

+0

Czy inne typy są dozwolone w hlist? –

+0

Nie, tylko rzeczy, które rozszerzają T (i prawdopodobnie nigdy nie będą inne typy niż TA i TB). –

Odpowiedz

7

Możesz to zrobić w niestandardowej klasie typów, która świadczy, że jest dokładnie jeden TB, a wszystkie inne elementy to TA. Jeśli wyobrażasz sobie, że budujesz tę listę indukcyjnie, zobaczysz, że są dwa przypadki, z którymi musisz sobie poradzić - albo wszystko, co widziałeś do tej pory, to TA (które możemy zobaczyć z ToList[T, TA]), a bieżącym elementem jest TB, albo już widzieliście jeden TB i obecny element jest TA:

import shapeless._, ops.hlist.{ ToList } 

trait T 
case class TA() extends T 
case class TB() extends T 

trait UniqueTB[L <: HList] extends DepFn1[L] { 
    type Out = TB 
    def apply(l: L): TB 
} 

object UniqueTB { 
    def apply[L <: HList](implicit utb: UniqueTB[L]): UniqueTB[L] = utb 
    def getTB[L <: HList](l: L)(implicit utb: UniqueTB[L]): TB = utb(l) 

    implicit def firstTB[T <: HList](
    implicit tl: ToList[T, TA] 
): UniqueTB[TB :: T] = new UniqueTB[TB :: T] { 
    def apply(l: TB :: T): TB = l.head 
    } 

    implicit def afterTB[T <: HList](
    implicit utb: UniqueTB[T] 
): UniqueTB[TA :: T] = new UniqueTB[TA :: T] { 
    def apply(l: TA :: T): TB = utb(l.tail) 
    } 
} 

a potem:

scala> UniqueTB[TB :: HNil] 
res0: UniqueTB[shapeless.::[TB,shapeless.HNil]] = [email protected] 

scala> UniqueTB[TA :: TB :: HNil] 
res1: UniqueTB[shapeless.::[TA,shapeless.::[TB,shapeless.HNil]]] = [email protected] 

scala> UniqueTB[TA :: TB :: TA :: HNil] 
res2: UniqueTB[shapeless.::[TA,shapeless.::[TB,shapeless.::[TA,shapeless.HNil]]]] = [email protected] 

scala> UniqueTB[TB :: HNil] 
res3: UniqueTB[shapeless.::[TB,shapeless.HNil]] = [email protected] 

scala> UniqueTB[TA :: HNil] 
<console>:25: error: could not find implicit value for parameter utb: UniqueTB[shapeless.::[TA,shapeless.HNil]] 
     UniqueTB[TA :: HNil] 
      ^

scala> UniqueTB[HNil] 
<console>:25: error: could not find implicit value for parameter utb: UniqueTB[shapeless.HNil] 
     UniqueTB[HNil] 
      ^

scala> UniqueTB[TB :: TB :: HNil] 
<console>:25: error: could not find implicit value for parameter utb: UniqueTB[shapeless.::[TB,shapeless.::[TB,shapeless.HNil]]] 
     UniqueTB[TB :: TB :: HNil] 
      ^

Dałem klasę typu operacja, która zwraca TB, ale jeśli nie potrzebujesz, abyś mógł opuścić metodę-le ss.

+0

Myślę, że możesz użyć klasy typu IsHCons już dostarczonej przez shapeless, zamiast tworzyć nową. Jedyną pozostałością byłoby zdefiniowanie 'afterTB' w odniesieniu do istniejących instancji. Co myślisz? –

+0

Właściwie to nieważne, nie myśl, że możesz to zrobić. : | –

+0

@DenisRosca Tak, nie rozumiem, jak to by działało, ale mógłbym czegoś nie zauważyć. –

Powiązane problemy