私は Scala で汎用プログラミングを始めており、サードパーティのシリアル化 (circe、upickle など) またはスキーマ (tapir など) に変換できる柔軟なビルド可能なスキーマ タイプ (ADT の一般的な記述) を設計しようとしています。 ) クラスのインスタンスを入力します。製品と副産物のスキーマを構築する場合、サブコンポーネントのスキーマを解決できる必要があります。
これを実現したい方法は、型クラスを使用することです。型クラスは、を解決するか、既存の暗黙的なインスタンスを解決することProvider
によって、特定の型のインスタンスを「提供」します。Deriver
派生プロバイダーの優先順位を下げることで、派生よりもインスタンスを優先する必要があります。
以下は、より単純なケースに適用された私のソリューションのバージョンです:NamesFor[T]
タイプ T の抽出されたフィールド名を含むインスタンスを生成します。
import shapeless._
import shapeless.labelled.FieldType
trait NamesFor[ T ] {
type Names
def names : Names
}
object NamesFor {
type Aux[ T, Names0 ] = NamesFor[ T ] { type Names = Names0 }
}
trait Deriver[ From, To ] {
def derive : To
}
object Deriver {
implicit def deriveNameFromSymbol[ S <: Symbol ](
implicit wit : Witness.Aux[ S ],
) : Deriver[ S, String ] =
new Deriver[ S, String ] {
override def derive : String = wit.value.name
}
implicit def deriveNameFromFieldType[ K <: Symbol, T ](
implicit deriver : Deriver[ K, String ],
) : Deriver[ FieldType[ K, T ], String ] = new Deriver[ FieldType[ K, T ], String ] {
override def derive : String = deriver.derive
}
// Deriving HList
implicit val deriveFromHnil : Deriver[ HNil, HNil ] =
new Deriver[ HNil, HNil ] {
override def derive : HNil = HNil
}
implicit def deriveNameHListFromHList[ Head, Tail <: HList, Res <: HList ](
implicit
headDeriver : Lazy[ Deriver[ Head, String ] ],
tailDeriver : Deriver[ Tail, Res ],
) : Deriver[ Head :: Tail, String :: Res ] = new Deriver[ Head :: Tail, String :: Res ] {
override def derive : String :: Res = headDeriver.value.derive :: tailDeriver.derive
}
// Here's the deriver we're interested in
implicit def deriveNamesFromLabelledGeneric[ T, R <: HList, Names0 <: HList ](
implicit
lGenEv : LabelledGeneric.Aux[ T, R ],
rDer : Deriver[ R, Names0 ],
) : Deriver[ T, NamesFor[ T ] ] = new Deriver[ T, NamesFor[ T ] ] {
override def derive : NamesFor.Aux[ T, Names0 ] = new NamesFor[ T ] {
type Names = Names0
override def names : Names = rDer.derive
}
}
}
trait Provider[ T ] {
def provide : T
}
object Provider {
implicit def provideInstance[ T ](
implicit inst : T,
) : Provider[ T ] = new Provider[T] {
override def provide : T = inst
}
}
trait LowPriorityProvider {
implicit def provideDerivation[ From, To ](
implicit
deriver : Deriver[ From, To ],
) : Provider[ To ] = new Provider[ To ] {
override def provide : To = deriver.derive
}
}
次の 2 つの方法でインスタンスを派生させることができます。
NamesFor[T]
1: 既存のインスタンスの暗黙のプロバイダー
case class Test( int : Int )
implicit val namesInst : NamesFor[ Test ] = new NamesFor[Test] {
override type Names = String :: HNil
override def names : String :: HNil = "INT_FIELD" :: HNil
}
val provider = implicitly[ Provider[ NamesFor[ Test ] ] ]
println( provider.provide.names ) // output: INT_FIELD :: HNil
NamesFor[T]
2: fromの暗黙の派生元T
case class Test( int : Int )
val deriver = implicitly[ Deriver[ Test, NamesFor[ Test ] ] ]
println( deriver.derive.names ) // output: int :: HNil
しかし、スコープ内にNamesFor[T]
暗黙的なインスタンスを持たないプロバイダーを解決しようとすると失敗します。NamesFor[T]
つまり、provideDerivation
次の方法でインスタンスを解決できません。
case class Test( int : Int )
val provider = implicitly[ Provider[ NamesFor[ Test ] ] ]
// Err: could not find implicit value for parameter e: Provider[NamesFor[Test]]
println( provider.provide.names )
これをすべて機能させる方法について何か考えはありますか?