Создайте «обогащенный» тип из класса case с Shapeless в Scala - PullRequest
0 голосов
/ 28 сентября 2018

У меня есть этот пример кода:

import java.util.UUID

import shapeless.LabelledGeneric
import shapeless.record._
import shapeless.syntax.singleton._

object LabelTest extends App {

  case class IncomingThing(name: String, age: Int)
  case class DatabaseIncomingThing(name: String, age: Int, id: UUID)

  val genIncoming = LabelledGeneric[IncomingThing]
  val genDatabase = LabelledGeneric[DatabaseIncomingThing]

  val thing = IncomingThing("John", 42)

  val structuralVersionOfIncomingThing = genIncoming.to(thing)

  val updated = genDatabase.from(structuralVersionOfIncomingThing + ('id ->> UUID.randomUUID()))

  println(updated) // DatabaseIncomingThing(John,42,a45081f2-4ed5-4d2b-8fd9-4d8986875ed7)

}

Это хорошо, потому что мне не нужно писать шаблон, который копирует все поля от IncomingThing до DatabaseIncomingThing.Однако я бы не хотел поддерживать оба этих типа, так как между ними существует очень четкая связь (один имеет id, другой нет).

Есть ли способ создать типиз данного класса дела путем добавления или удаления одного поля?Я представляю что-то вроде

type IncomingThing = withoutField[DatabaseIncomingThing]('id)

или что-то в этом роде.

1 Ответ

0 голосов
/ 28 сентября 2018

Вместо DatabaseIncomingThing

val updated: DatabaseIncomingThing = 
  genDatabase.from(structuralVersionOfIncomingThing + ('id ->> UUID.randomUUID()))

вы можете работать с необработанными HList

val updated1: FieldType[Witness.`'name`.T, String] :: FieldType[Witness.`'age`.T, Int] :: FieldType[Witness.`'id`.T, UUID] :: HNil = 
  structuralVersionOfIncomingThing + ('id ->> UUID.randomUUID())
val updated2: FieldType[Witness.`'name`.T, String] :: FieldType[Witness.`'age`.T, Int] :: HNil = 
  updated1 - 'id

На уровне типа

implicitly[Remover.Aux[FieldType[Witness.`'name`.T, String] :: FieldType[Witness.`'age`.T, Int] :: FieldType[Witness.`'id`.T, UUID] :: HNil,
  Witness.`'id`.T,
  (UUID, FieldType[Witness.`'name`.T, String] :: FieldType[Witness.`'age`.T, Int] :: HNil)]]

implicitly[Updater.Aux[
  FieldType[Witness.`'name`.T, String] :: FieldType[Witness.`'age`.T, Int] :: HNil,
  FieldType[Witness.`'id`.T, UUID],
  FieldType[Witness.`'name`.T, String] :: FieldType[Witness.`'age`.T, Int] :: FieldType[Witness.`'id`.T, UUID] :: HNil]]

Выможете создать свой класс типов

trait WithoutField[A, K] {
  type Out <: HList
}

object WithoutField {
  type Aux[A, K, Out0 <: HList] = WithoutField[A, K] { type Out = Out0 }
  def instance[A, K, Out0 <: HList]: Aux[A, K, Out0] = new WithoutField[A, K] { type Out = Out0 }

  implicit def mkWithoutField[A, L <: HList, K, T, L1 <: HList](implicit
    labelledGeneric: LabelledGeneric.Aux[A, L],
    remover: Remover.Aux[L, K, (T, L1)]): Aux[A, K, L1] = instance
}

и использовать его

def foo[Out <: HList](implicit withoutField: WithoutField.Aux[DatabaseIncomingThing, Witness.`'id`.T, Out]) = {
  // now you can use type Out inside
  type IncomingThing = Out
  ???
}
...