2016-09-21 2 views
3

Ich habe eine polymorphe Funktion, die Listen in Sätze verwandeln kann:Ein zusätzliches Argument in eine polymorphe Funktion übergeben?

import shapeless.PolyDefns.~> 
import shapeless._ 

val lists = List(1,2) :: List("A", "B") :: List(1.1, 2.2) :: HNil 

object sss extends (List ~> Set) { 
    def apply[T](l:List[T]):Set[T] = { 
    l.toSet 
    } 
} 

lists.map(sss) // I want: Set(1,2) :: Set("A", "B") :: Set(1.1, 2.2) :: HNil 

Aber was, wenn ich das Verhalten dieser Funktion ändern wollen - ich will jetzt ein zusätzliches Argument hinzuzufügen, die das Element in der Eingabe angeben werden Liste sollte in das Set gelegt werden. Hier ist eine falsche Syntax - kannst du mir den richtigen Weg zeigen?

object sss extends (List ~> Set) { // Compiler says no! 
    def apply[T](i:Int)(l:List[T]):Set[T] = { 
    l.slice(i,i+1).toSet 
    } 
} 

Ich denke, dies nicht möglich ist, weil das zusätzliche Argument nicht mehr passen die Unterschrift Liste macht ~> Set, so wie kann ich dies zu überwinden?

+0

Etwas falsch: '// Ich will: 1 ::" A ":: 3.5 :: HNil' – pedrofurla

+0

korrigiert, danke! –

Antwort

1

Wie Sie bereits festgestellt haben, können Sie die Signatur der polymorphen Funktion nicht ändern. Aber könnte Ihre Funktion dynamisch erstellen:

class sss(i: Int) extends (List ~> Set) { 
    def apply[T](l:List[T]): Set[T] = { 
    l.slice(i, i+1).toSet 
    } 
} 

val sss1 = new sss(1) 
lists.map(sss1) // Set(2) :: Set(B) :: Set(2.2) :: HNil 
+0

Dies funktioniert nicht, wenn Sie 'sss1' nicht zu einem Objekt machen (aus ziemlich obskuren Gründen). –

+0

Seltsam, es funktionierte in meinem Testfall (wenn ich ein "val" erstelle, das die Instanz hält). – devkat

3

Es gibt ein paar Abhilfen für Parametrisierung eine Poly, von denen eine in der anderen Antwort erwähnt wird, obwohl die genaue Umsetzung wird es nicht funktionieren. Stattdessen müssen Sie dies tun:

import shapeless._, shapeless.poly.~> 

val lists = List(1, 2) :: List("A", "B") :: List(1.1, 2.2) :: HNil 

class sss(i: Int) extends (List ~> Set) { 
    def apply[T](l: List[T]): Set[T] = l.slice(i, i+1).toSet 
} 

object sss1 extends sss(1) 

lists.map(sss1) 

... wo die Tatsache, dass sss1 als ein Objekt definiert ist (keine val) ist erforderlich, dass die letzte Zeile zu kompilieren.

Dieser Ansatz kompiliert, aber es ist nicht möglich, es in vielen Kontexten zu verwenden - z. Sie können Ihr Objekt sss1 (oder was auch immer) nicht in einer Methode definieren, in der der Typ der Hlist generisch ist.

Hier ist eine leicht chaotische aber flexiblere Abhilfe, die ich verwendet habe vor:

import shapeless._ 

val lists = List(1, 2) :: List("A", "B") :: List(1.1, 2.2) :: HNil 

object sss extends Poly2 { 
    implicit def withI[T]: Case.Aux[List[T], Int, Set[T]] = 
    at((l, i) => l.slice(i, i + 1).toSet) 
} 

lists.zipWith(lists.mapConst(1))(sss) 
// Set(2) :: Set(B) :: Set(2.2) :: HNil 

Nun könnte man tatsächlich eine Methode schreiben, die eine generisches L <: HList und die Scheibe Parameter nahmen i -you'd nur einig braucht implizite Argumente zur Unterstützung der mapConst und zipWith Anwendungen.

Kein Ansatz ist jedoch sehr elegant, und ich persönlich tendiere dazu, Poly die meiste Zeit zu vermeiden - eine benutzerdefinierte Typklasse zu definieren ist fast sauberer und in vielen Fällen erforderlich.

+0

"Keine der beiden Methoden ist jedoch sehr elegant, und ich persönlich tendiere dazu, Poly zu meiden - die Definition einer benutzerdefinierten Typklasse ist fast sauberer und in vielen Fällen erforderlich." - Können Sie mich auf diese alternative Implementierung hinweisen? –

Verwandte Themen