簡體   English   中英

如果案例 class 具有類型參數,如何派生 Generic.Aux - Shapeless

[英]How to derive a Generic.Aux if the case class has a type parameter - Shapeless

給出:

sealed trait Data
final case class Foo() extends Data
final case class Bar() extends Data

final case class TimestampedData[A <: Data](data: A, timestamp: Long)

有沒有一種簡潔的方法來生成,例如,一個Generic.Aux需要

(A, Long)其中A <: Data

並輸出這個Coproduct

TimestampedData[Foo] :+: TimestampedData[Bar] :+: CNil

( Generic.Aux[(A, Long), TimestampedData[Foo]:+: TimestampedData[Bar]:+: CNil] )

不幸的是,由於我不太了解泛型編程,而且由於缺乏資源,我沒有嘗試太多。 我什至不確定如何解決這個問題。

謝謝

您可以嘗試使用PartiallyApplied 模式的方法

import shapeless.{Coproduct, DepFn2, Generic, HList}
import shapeless.ops.coproduct.{Inject, ToHList}
import shapeless.ops.hlist.{Mapped, ToCoproduct}

def toTimestamped[A <: Data] = new PartiallyApplied[A]

class PartiallyApplied[A <: Data] {
  def apply[C  <: Coproduct, 
            L  <: HList, 
            L1 <: HList, 
            C1 <: Coproduct](data: A, timestamp: Long)(implicit
    generic: Generic.Aux[Data, C],
    toHList: ToHList.Aux[C, L],
    mapped: Mapped.Aux[L, λ[A => TimestampedData[A with Data]], L1],
    toCoproduct: ToCoproduct.Aux[L1, C1],
    inject: Inject[C1, TimestampedData[A]],
  ): C1 = inject(TimestampedData[A](data, timestamp))
}
val x = toTimestamped(Foo(), 1L) // Inr(Inl(TimestampedData(Foo(),1)))
val y = toTimestamped(Bar(), 1L) // Inl(TimestampedData(Bar(),1))
type Coprod = TimestampedData[Bar] :+: TimestampedData[Foo] :+: CNil
x: Coprod // compiles
y: Coprod // compiles

或類型類1 2 3 4 5 (通常,這是比方法更靈活的解決方案,盡管現在似乎沒有方法優於方法,因為類型類的唯一實例)

trait ToTimestamped[A <: Data] extends DepFn2[A, Long] {
  type Out <: Coproduct
}
object ToTimestamped {
  type Aux[A <: Data, Out0 <: Coproduct] = ToTimestamped[A] { type Out = Out0 }
  def instance[A <: Data, Out0 <: Coproduct](f: (A, Long) => Out0): Aux[A, Out0] =
    new ToTimestamped[A] {
      override type Out = Out0
      override def apply(data: A, timestamp: Long): Out0 = f(data, timestamp)
    }

  implicit def mkToTimestamped[A  <: Data, 
                               C  <: Coproduct, 
                               L  <: HList, 
                               L1 <: HList, 
                               C1 <: Coproduct](implicit
    generic: Generic.Aux[Data, C],
    toHList: ToHList.Aux[C, L],
    mapped: Mapped.Aux[L, λ[A => TimestampedData[A with Data]], L1],
    toCoproduct: ToCoproduct.Aux[L1, C1],
    inject: Inject[C1, TimestampedData[A]],
  ): Aux[A, C1] =
    instance((data, timestamp) => inject(TimestampedData[A](data, timestamp)))
}

def toTimestamped[A <: Data](data: A, timestamp: Long)(implicit
  toTimestampedInst: ToTimestamped[A]
): toTimestampedInst.Out = toTimestampedInst(data, timestamp)

測試:

val x = toTimestamped(Foo(), 1L) // Inr(Inl(TimestampedData(Foo(),1)))
val y = toTimestamped(Bar(), 1L) // Inl(TimestampedData(Bar(),1))
type Coprod = TimestampedData[Bar] :+: TimestampedData[Foo] :+: CNil
implicitly[ToTimestamped.Aux[Foo, Coprod]] // compiles
x: Coprod // compiles
y: Coprod // compiles

在 Shapeless 中,有Mapped用於HList但沒有Coproduct ,因此我必須在類型級別Coproduct轉換為HList並返回。

λ[A =>...]kind-projector語法。 Mapped接受類型構造函數F[_]TimestampedData是上限F[_ <: Data] ,因此我不得不使用具有交集類型 ( with ) 的類型 lambda。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM