You can't generate a de novo concrete type, except perhaps with a macro annotation. What you can do is use a typeclass to derive a generic type that has the transformed shape that you want.
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
You probably want to add some functionality to the RecordAsOption
typeclass (given the name, probably a lens-like method that applies the delta represented by the options to a value of type L
), since as it stands the type is kind of useless. The record representation isn't quite as nice as a case class in some respects, but it can interoperate nicely with other shapeless-based things (e.g. it would be easy to use similar techniques to spray-json-shapeless to deserialize JSON to a GenericUserUpdate
)