2016-09-30 1 views
1

je la méthode suivante:Shapeless: HList complexe Constraint

import shapeless._ 
import shapeless.UnaryTCConstraint._ 
def method[L <: HList : *->*[Seq]#λ](list: L) = println("checks") 

Il me permet de vérifier les points suivants se produit:

val multipleTypes = "abc" :: 1 :: 5.5 :: HNil 
val onlyLists = Seq("abc") :: Seq(1) :: Seq(5.5) :: HNil 

method(multipleTypes) // throws could not find implicit value ... 
method(onlyList) // prints checks 

Comment puis-je augmenter method avec une autre liste de paramètres, quelque chose comme:

def method2[L <: HList : *->*[Seq]#λ, M <: HList](list: L)(builder: M => String) = println("checks") 

Mais avec la restriction que le HList M doit être de la même taille que le HList l a nd contenir que des éléments des types internes du HList L. Permettez-moi de donner un exemple:

// This should work 
method2(Seq("abc") :: Seq(1) :: Seq(5.5) :: HNil){ 
    case a :: 1 :: d :: HNil => "works" 
} 
// This should throw some error at compile time, because the second element is Seq[Int] 
// therefore in the builder function I would like the second element to be of type Int. 
method2(Seq("abc") :: Seq(1) :: Seq(5.5) :: HNil){ 
    case a :: true :: d :: HNil => "fails" 
} 
// This should also fail because the HLists are not of the same length. 
method2(Seq("abc") :: Seq(1) :: Seq(5.5) :: HNil){ 
    case 1 :: d :: HNil => "fails" 
} 

Si je définis method2 comme ceci:

def method2[L <: HList : *->*[Seq]#λ](list: L)(builder: L => String) = println("checks") 

Il résout presque le problème, la seule chose qui est absent est ce constructeur aura des éléments de type Seq [T] au lieu d'éléments de type T.

+0

Quel devrait être le résultat de 'method2 (Seq ("abc", "def") :: Seq (1, 2,3) :: Seq (5.5) :: HNil) {cas a :: b :: c :: HNil => a.toString + b.toString + c.toString} '? – Kolmar

+0

Cela devrait fonctionner puisque HList est de la même taille, et vous n'avez fait aucune restriction sur les types de a, b et c, donc ils devraient être correctement déduits comme étant 'String',' Int' et 'Double'. –

Répondre

2

Ceci est un cas pour ops.hlist.Comapped.

Vous voudriez définir method2 comme

def method2[L <: HList : *->*[Seq]#λ, M <: HList] 
    (list: L) 
    (builder: M => String) 
    (implicit ev: Comapped.Aux[L, Seq, M]) 

Mais cela ne fonctionnera pas, car le type M doit être calculée avant l'argument builder la vérification du type.

Ainsi, la mise en œuvre effective devient quelque chose comme:

class Impl[L <: HList : *->*[Seq]#λ, M <: HList] 
    (list: L) 
    (implicit ev: Comapped.Aux[L, Seq, M]) 
{ 
    def apply(builder: M => String) = println("checks") 
} 

def method2[L <: HList : *->*[Seq]#λ, M <: HList] 
    (list: L) 
    (implicit ev: Comapped.Aux[L, Seq, M]) = new Impl[L, M](list) 

Et vous ne pouvez pas l'appeler directement. Vous pouvez utiliser un apply supplémentaire, ou une autre méthode pour fournir la liste des arguments implicites implicitement:

method2(Seq("abc") :: Seq(1) :: Seq(5.5) :: HNil) apply { 
    case a :: 1 :: d :: HNil => "works" 
}