2016-07-31 10 views
3

Ich habe ein Programm mit einigen Fallklassen. Einer von ihnen könnte eine Person sein, die auf einigen anderen Klassen beruhen, darunter ein namens StemmedString, die hier wichtig sein wird:Verwenden Sie formlos, um Daten aus Fallklassen zu extrahieren, zu ändern und die Fallklasse neu zu erstellen

case class Person(name: String, skills: List[Skill], hobbbies: List[StemmedString] 
case class Skill(score: Int, title: StemmedString) 
case class StemmedString(original: String, stemmed: String) 

Jetzt möchte ich diese Person in einer anderen Sprache übersetzen können. Um das zu tun, möchte ich von Person -> Liste [String] gehen. Das ist eine Liste von einzelnen Strings, die übersetzt werden müssen. Dann möchte ich von Liste [String] -> Person zurückgehen.

Ich möchte in der Lage sein, alle Arten von Dingen, die übersetzt werden sollen, zu definieren, ohne das Format von Person im Voraus zu wissen, so dass es über mehrere Fallklassen verallgemeinernd werden kann, die aus denselben TYPEN bestehen kann übersetzt werden.

Sagen wir, was wir übersetzen, sind alle StemmedStrings.

I LabelledGeneric und flatMap können zwei HLists, einer der Werte zu erstellen, die anderen Werte übersetzt werden, die nicht übersetzt werden:

trait LowPriorityUnTranslatable extends Poly1 { 
    implicit def default[T] = at[T](_ :: HNil) 
} 

object unTranslatable extends LowPriorityUnTranslatable { 
    implicit def caseStemmedString[K, T] = at[FieldType[K, StemmedString]](x => HNil) 
    implicit def caseSkill[K, T] = at[FieldType[K, Skill](x => HNil) 
} 


trait LowPriorityTranslatable extends Poly1 { 
    implicit def default[T] = at[T](HNil) 
} 

object Translatable extends LowPriorityTranslatable { 
    implicit def caseStemmedString[K, T] = at[FieldType[K, StemmedString]](_ :: HNil) 
    implicit def caseSkill[K, T] = at[FieldType[K, Skill](_ :: HNil) 
} 

Dies ist ein bisschen ausführlicher fühlt, aber funktioniert gut. Ich habe jetzt zwei HLists, und sie können leicht verkettet werden und kehrte zurück in die ursprüngliche Fallklasse align mit:

val person = Person("...") 
val gen = LabelledGeneric[Person] 
val personList = gen.to(person) 
val toTranslate = personList flatMap isTranslatable 
val notTranslated = personList flatMap unTranslatable  
gen.from((toTranslate ++ notTranslated) align personList) 

Das ist sehr cool, jetzt alles, was ich tun müssen, um einen Übersetzungsschritt in der Mitte hinzufügen. Es ist natürlich einfach, von isTranslatable -> List [String] zu gehen, aber ich kann mir nicht vorstellen, wie ich das machen soll, damit ich wieder zurück kann. Ich fing an, etwas über Shapeless zu lernen, weil es für diese Situation das richtige Werkzeug zu sein schien, aber ich verstehe nicht, wie ich es vollständig anwenden soll. In meinem Kopf, wenn ich can solve this question, dann wird es mir gut gehen, aber es könnte sein, dass es einen viel einfacheren Weg gibt, um dieses Problem formlos zu lösen. Jede Einsicht würde sehr geschätzt werden!

Antwort

1

Wenn Sie nicht mit änderbaren Datenstrukturen (ein Iterator in diesem Fall) ausmachen, können Sie everything/everywhere für eine einfache Lösung verwenden.

Zuerst extrahieren wir die zu übersetzenden Strings mit einer everything Abfrage; In diesem Beispiel extrahieren wir die ursprünglichen Werte der Objekte StemmedString. Die Singleton-Listen werden unter Verwendung der Append-Funktion verkettet.

trait TranslatableLP extends Poly1 { 
    implicit def default[T] = at[T](_ => Nil: List[String]) 
} 

object Translatable extends TranslatableLP { 
    implicit def caseStemmedString = at[StemmedString](s => List(s.original)) 
} 

object Append extends Poly2 { 
    implicit val caseString = at[List[String], List[String]](_ ++ _) 
} 

val strings = everything(Translatable)(Append)(person) 

Jetzt übersetzen wir die Saiten:

def translate(s: String): String = ??? 

val translatedStrings = strings.map(translate) 

Und schließlich können wir die StemmedString Objekte abbilden, die übersetzten Strings everywhere mit:

object Update extends Poly1 { 
    def iterator = translatedStrings.iterator 
    implicit def caseStemmedString = at[StemmedString](_.copy(original = iterator.next)) 
} 

val translated = everywhere(Update)(person) 

Wenn ich etwas Zeit finde ich werde Versuchen Sie, eine sauberere Lösung zu finden, die nur unveränderliche Datenstrukturen verwendet.

+0

Schön! Wäre sehr interessiert, eine Version mit nur unveränderlichen Datenstrukturen zu sehen, wenn Sie die Zeit finden können! –