2016-05-20 9 views
2

Lasst uns sagen, ich habeleite neue Art von Fall-Klasse Shapeless

case class User(id: Long, name: String, age: Long, email: Option[String]) 

Gibt es eine Weise, die ich schaffen kann sagen lassen

type UpdateUser = ???.???[User] // update user has same fields as User except all are optional 

, die ich dann als

UpdateUser(name = Some("foo"), email = Some("[email protected]")) 
verwenden können

also grundsätzlich Mapping-Typen

Long   :: String   :: Long   :: Option[String] 
-> 
Option[Long] :: Option[String] :: Option[Long] :: Option[String] 

Also noch einmal Frage ist, ist es so (d. H. formlos verwenden), um eine solche abgeleitete Fallklasse mit solchen Feldern zu erzeugen (ohne dazu ein Makro zu schreiben.)

Fallklassen sind nur HLists. Können wir einen neuen Typ basierend auf einer bestimmten Art von HList erstellen?

+0

Mögliche Duplikate von http://stackoverflow.com/questions/12705309/scala-case-class-inheritance –

+0

.... was ist die Frage hier? –

+1

Es ist kein Duplikat davon. Die Frage ist, ob und wie wir bei Verwendung von Shapeless eine neue Fallklasse ableiten können, ohne explizit ein Makro zu schreiben. Ich denke, dass das Abstimmen nicht gerechtfertigt ist. – fromscalatohaskell

Antwort

1

Sie können keinen Betontyp de novo generieren, außer vielleicht mit einer Makroannotation. Sie können eine Typklasse verwenden, um einen generischen Typ mit der gewünschten transformierten Form abzuleiten.

import shapeless._, labelled._ 

trait RecordAsOption[L] { 
    type Out <: HList 
} 
object RecordAsOption { 
    def apply[L](implicit recordAsOption: RecordAsOption[L]) 
     : Aux[L, recordAsOption.Out] = 
    recordAsOption 
    type Aux[L, Out0 <: HList] = RecordAsOption[L] { type Out = Out0 } 
    implicit def hnilRecordAsOption[L <: HNil]: Aux[L, HNil] = 
    new RecordAsOption[L] { 
     type Out = HNil 
     def apply(l: L) = HNil 
    } 

    implicit def hconsRecordAsOption[K, V, T <: HList](
     implicit tail: RecordAsOption[T]) 
     : Aux[FieldType[K, V] :: T, FieldType[K, Option[V]] :: tail.Out] = 
    new RecordAsOption[FieldType[K, V] :: T] { 
     type Out = FieldType[K, Option[V]] :: tail.Out 
    } 

    implicit def genericRecordAsOption[T, R](
    implicit lg: LabelledGeneric.Aux[T, R], roa: RecordAsOption[T]) 
     : Aux[T, roa.Out] = 
    new RecordAsOption[T] { 
     type Out = roa.Out 
    } 
} 

case class User(id: Long, name: String, age: Long, email: Option[String]) 
val genericUserUpdate = RecordAsOption[User] 
type GenericUserUpdate = genericUserUpdate.Out 

Sie wollen wahrscheinlich einige Funktionen zum RecordAsOption typeclass (angesichts des Namens, der wahrscheinlich eine linsenartige Methode, die das Delta durch die Optionen auf einen Wert von Typ L vertreten gilt) hinzuzufügen, da, wie es steht die Typ ist irgendwie nutzlos. Die Datensatzdarstellung ist in mancher Hinsicht nicht ganz so schön wie eine Fallklasse, aber sie kann gut mit anderen formlos-basierten Dingen zusammenarbeiten (z. B. wäre es einfach, ähnliche Techniken zum spray-json-formlosen JSON zu einem GenericUserUpdate zu deserialisieren)

+0

Woher kommt 'GenericUser'? – Marcin

+1

Ein Fehler; Ich habe ursprünglich den 'LabelledGeneric' Teil separat gemacht, aber dann habe ich ihn in die Typklasse verschoben. – lmm

Verwandte Themen