简体   繁体   English

Scala 类型类,用于从派生或现有隐式值提供实例

[英]Scala typeclass for providing an instance either from derivation or from an existing implicit value

I'm getting started with generic programming in Scala and I'm trying to design a flexible buildable schema type (a generic description of ADTs) that can be translated into third party serialization (eg, circe, upickle) or schema (eg, tapir) type class instances.我开始使用 Scala 中的通用编程,我正在尝试设计一个灵活的可构建模式类型(ADT 的通用描述),可以转换为第三方序列化(例如,circe、upickle)或模式(例如,tap ) 类型 class 实例。 When building schemas for products and coproducts, it has to be able to resolve schemas for subcomponents.在为产品和副产品构建模式时,它必须能够解析子组件的模式。

The way I would like to achieve this is with a Provider type class, that will "provide" an instance of a given type either by resolving a Deriver or by resolving an existing implicit instance.我想实现这一点的方法是使用Provider类型 class,它将通过解析Deriver或解析现有的隐式实例来“提供”给定类型的实例。 By making the derivation provider a lower priority method, it should prefer instances to derivations.通过使派生提供程序成为优先级较低的方法,它应该更喜欢实例而不是派生。

Below is a version of my solution applied to a simpler case: generating a NamesFor[T] instance that contains the extracted field names of type T.下面是我的解决方案的一个版本,它应用于一个更简单的案例:生成一个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
    }
}

I can derive instances in the following two ways:我可以通过以下两种方式派生实例:

1: implicit provider of existing NamesFor[T] instance 1:现有NamesFor[T]实例的隐式提供者

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

2: implicit deriver of a NamesFor[T] from T 2: NamesFor[T]T的隐式派生

case class Test( int : Int )

val deriver = implicitly[ Deriver[ Test, NamesFor[ Test ] ] ]

println( deriver.derive.names ) // output: int :: HNil

but it fails when I try to resolve a provider of NamesFor[T] without an implicit NamesFor[T] instance in scope.但是当我尝试在NamesFor[T]中解析没有隐式NamesFor[T]实例的 NamesFor[T] 提供者时,它失败了。 That is, it fails to resolve an instance by way of provideDerivation :也就是说,它无法通过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 )

Any thoughts on how I could make this all work?关于如何使这一切正常工作的任何想法?

You seem to just forget to extend Provider from LowPriorityProvider您似乎只是忘记从LowPriorityProvider扩展Provider

object Provider extends LowPriorityProvider {...}

Then然后

case class Test( int : Int )

val provider = implicitly[ Provider[ NamesFor[ Test ] ] ]

println( provider.provide.names )

compiles编译

https://scastie.scala-lang.org/DmytroMitin/S7LrOQocSSeMZHetCEifDA/1 https://scastie.scala-lang.org/DmytroMitin/S7LrOQocSSeMZHetCEifDA/1

Scala 2.13.8, Shapeless 2.3.9 Scala 2.13.8,无形 2.3.9

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM