Класс типов Scala для предоставления экземпляра на основе производного или существующего неявного значения

Я начинаю с общего программирования на Scala и пытаюсь разработать гибкий строимый тип схемы (общее описание ADT), который можно преобразовать в стороннюю сериализацию (например, circe, upickle) или схему (например, tapir ) экземпляры класса типа. При построении схем для продуктов и сопутствующих продуктов он должен иметь возможность разрешать схемы для субкомпонентов.

Я бы хотел добиться этого с помощью Provider класс типа, который "предоставит" экземпляр данного типа либо путем разрешения Deriverили разрешив существующий неявный экземпляр. Сделав поставщик деривации методом с более низким приоритетом, он должен предпочесть экземпляры деривациям.

Ниже представлена ​​версия моего решения, примененная к более простому случаю: создание экземпляра, содержащего извлеченные имена полей типа 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
    }
}

Я могу получить экземпляры двумя способами:

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

2: неявный производный от T

      case class Test( int : Int )

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

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

но это не удается, когда я пытаюсь разрешить поставщик без неявного 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 )

Есть мысли о том, как я могу заставить все это работать?

1 ответ

Вы, кажется, просто забыли сделатьProviderпродлеватьLowPriorityProvider

      object Provider extends LowPriorityProvider {...}

Затем

      case class Test( int : Int )

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

println( provider.provide.names )

компилирует

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

Скала 2.13.8, Бесформенный 2.3.9

Другие вопросы по тегам