Replies: 4 comments 13 replies
-
Soo, this PR still relies on using What I had in mind is more like this: package io.scalaland.chimney.internal.compiletime
import io.scalaland.chimney.internal.compiletime.{datatypes, ChimneyDefinitionsPlatform}
trait ChimneyAsLibrary
extends Derivation
with ChimneyDefinitionsPlatform
with datatypes.IterableOrArraysPlatform
with datatypes.ProductTypesPlatform
with datatypes.SealedHierarchiesPlatform
with datatypes.ValueClassesPlatform
with rules.TransformImplicitRuleModule
with rules.TransformSubtypesRuleModule
with rules.TransformToSingletonRuleModule
with rules.TransformOptionToOptionRuleModule
with rules.TransformPartialOptionToNonOptionRuleModule
with rules.TransformToOptionRuleModule
with rules.TransformValueClassToValueClassRuleModule
with rules.TransformValueClassToTypeRuleModule
with rules.TransformTypeToValueClassRuleModule
with rules.TransformEitherToEitherRuleModule
with rules.TransformMapToMapRuleModule
with rules.TransformIterableToIterableRuleModule
with rules.TransformProductToProductRuleModule
with rules.TransformSealedHierarchyToSealedHierarchyRuleModule package io.moia.protos.teleproto.internal
import io.scalaland.chimney.internal.compiletime.ChimneyAsLibrary
import scala.reflect.macros.blackbox
class EncoderMacros(val c: blackbox.Context)
extends io.scalaland.chimney.internal.compiletime.ChimneyAsLibrary
with ... /* teleprotos own rules e.g. EncodeImplicitRule */ {
def deriveEncoderWithDefaults[
From: WeakTypeTag,
To: WeakTypeTag
]: Expr[Encoder[From, To]] = {
// pseudocode:
val inputName = freshTermName(...)
val inputExpr: Expr[From] = c.Expr[From](q"$inputName")
val cfg = TransformerConfiguration() // customize, read config with DSL etc
val context = TransformationContext.ForTotal(inputExpr, cfg)
deriveFinalTransformationResultExpr(context).toEither.fold(
derivationErrors => /* report somehow */,
toExpr =>
c.Expr[Encoder[From, To](
q"""
new Encoder[${Type[From]}, ${Type[To]}] {
def encode($inputName: ${Type[From]}): ${Type[To]} = $toExpr
}
"""
)
)
}
// chain of responsibility of sort: attempt first rule, it it yields another, then another, etc
final override protected val rulesAvailableForPlatform: List[Rule] = List(
// TransformImplicitRule, // looks for Transformer/PartialTransformer, instead provide your own:
EncodeImplicitRule, // <- this should look for implicit Encoder
TransformSubtypesRule,
TransformToSingletonRule,
TransformOptionToOptionRule,
TransformPartialOptionToNonOptionRule,
TransformToOptionRule,
TransformValueClassToValueClassRule,
TransformValueClassToTypeRule,
TransformTypeToValueClassRule,
TransformEitherToEitherRule,
TransformMapToMapRule,
TransformIterableToIterableRule,
TransformProductToProductRule,
TransformSealedHierarchyToSealedHierarchyRule
)
} Something like this would be able to:
|
Beta Was this translation helpful? Give feedback.
-
I've updated the draft a bit:
package io.scalaland.chimney.internal.compiletime
trait ExportChimneyDerivationEngineForTeleproto
extends derivation.transformer.Derivation
with derivation.transformer.Configurations
with derivation.transformer.Contexts
with derivation.transformer.ImplicitSummoning
with derivation.transformer.ResultOps
with datatypes.IterableOrArrays
with datatypes.ProductTypes
with datatypes.SealedHierarchies
with datatypes.SingletonTypes
with datatypes.ValueClasses
with derivation.transformer.integrations.OptionalValues
with derivation.transformer.integrations.PartiallyBuildIterables
with derivation.transformer.integrations.TotallyBuildIterables
with derivation.transformer.integrations.TotallyOrPartiallyBuildIterables
with derivation.transformer.rules.TransformationRules
package io.scalaland.chimney.internal.compiletime
trait ExportChimneyDerivationEngineForTeleprotoPlatform
extends ExportChimneyDerivationEngineForTeleproto
with ChimneyDefinitionsPlatform
with datatypes.IterableOrArraysPlatform
with datatypes.ProductTypesPlatform
with datatypes.SealedHierarchiesPlatform
with datatypes.ValueClassesPlatform
with derivation.transformer.rules.TransformImplicitRuleModule
with derivation.transformer.rules.TransformSubtypesRuleModule
with derivation.transformer.rules.TransformToSingletonRuleModule
with derivation.transformer.rules.TransformOptionToOptionRuleModule
with derivation.transformer.rules.TransformPartialOptionToNonOptionRuleModule
with derivation.transformer.rules.TransformToOptionRuleModule
with derivation.transformer.rules.TransformValueClassToValueClassRuleModule
with derivation.transformer.rules.TransformValueClassToTypeRuleModule
with derivation.transformer.rules.TransformTypeToValueClassRuleModule
with derivation.transformer.rules.TransformEitherToEitherRuleModule
with derivation.transformer.rules.TransformMapToMapRuleModule
with derivation.transformer.rules.TransformIterableToIterableRuleModule
with derivation.transformer.rules.TransformProductToProductRuleModule
with derivation.transformer.rules.TransformSealedHierarchyToSealedHierarchyRuleModule
package io.scalaland.chimney.internal.compiletime
// using q: Quotes, require us to make it abstract class on Scala 3 to make it a stable identifier
abstract class ExportChimneyDerivationEngineForTeleprotoPlatform(q: scala.quoted.Quote)
extends ChimneyDefinitionsPlatform(q)
with ExportChimneyDerivationEngineForTeleproto
with datatypes.IterableOrArraysPlatform
with datatypes.ProductTypesPlatform
with datatypes.SealedHierarchiesPlatform
with datatypes.ValueClassesPlatform
with derivation.transformer.rules.TransformImplicitRuleModule
with derivation.transformer.rules.TransformSubtypesRuleModule
with derivation.transformer.rules.TransformToSingletonRuleModule
with derivation.transformer.rules.TransformOptionToOptionRuleModule
with derivation.transformer.rules.TransformPartialOptionToNonOptionRuleModule
with derivation.transformer.rules.TransformToOptionRuleModule
with derivation.transformer.rules.TransformValueClassToValueClassRuleModule
with derivation.transformer.rules.TransformValueClassToTypeRuleModule
with derivation.transformer.rules.TransformTypeToValueClassRuleModule
with derivation.transformer.rules.TransformEitherToEitherRuleModule
with derivation.transformer.rules.TransformMapToMapRuleModule
with derivation.transformer.rules.TransformIterableToIterableRuleModule
with derivation.transformer.rules.TransformProductToProductRuleModule
with derivation.transformer.rules.TransformSealedHierarchyToSealedHierarchyRuleModule Then in teleproto:
package io.moia.protos.teleproto.internal
trait TeleprotoDefinitions
extends io.scalaland.chimney.internal.compiletime.ExportChimneyDerivationEngineForTeleproto
// ...
{
// definitions of teleproto types e.g. Encoder, Decoder
// interfaces for e.g. summoning implicit Encoder, Decoder
// code which would parse teleproto configs and turn them into TransformationContext for Chimney
}
package io.moia.protos.teleproto.internal
trait TeleprotoDefinitionsPlatform
extends TeleprotoDefinitions
with io.scalaland.chimney.internal.compiletime.ExportChimneyDerivationEngineForTeleprotoPlatform {
// implementations of teleproto types e.g. Encoder, Decoder
// implementations for e.g. summoning implicit Encoder, Decoder
} package io.moia.protos.teleproto.internal
import scala.reflect.macros.blackbox
class TeleprotoMacros(val c: blackbox.Context)
extends TeleprotoDefinitionsPlatform
with ... /* teleprotos own rules e.g. EncodeImplicitRule */ {
def deriveEncoderWithDefaults[
From: WeakTypeTag,
To: WeakTypeTag
]: Expr[Encoder[From, To]] = {
// pseudocode:
val inputName = freshTermName(...)
val inputExpr: Expr[From] = c.Expr[From](q"$inputName")
val cfg = TransformerConfiguration() // customize, read config with DSL etc
val context = TransformationContext.ForTotal(inputExpr, cfg)
deriveFinalTransformationResultExpr(context).toEither.fold(
derivationErrors => /* report somehow */,
toExpr =>
c.Expr[Encoder[From, To](
q"""
new Encoder[${Type[From]}, ${Type[To]}] {
def encode($inputName: ${Type[From]}): ${Type[To]} = $toExpr
}
"""
)
)
}
def deriveEncoderWithDefaults[
From: WeakTypeTag,
To: WeakTypeTag
]: Expr[Encoder[From, To]] = {
// pseudocode:
val inputName = freshTermName(...)
val inputExpr: Expr[From] = c.Expr[From](q"$inputName")
val cfg = TransformerConfiguration() // customize, read config with DSL etc
val context = TransformationContext.ForPartial(inputExpr, cfg)
deriveFinalTransformationResultExpr(context).toEither.fold(
derivationErrors => /* report somehow */,
toExpr =>
c.Expr[Encoder[From, To](
q"""
new Encoder[${Type[From]}, ${Type[To]}] {
def encode($inputName: ${Type[From]}): ${Type[To]} = ${convertPartialToPbResult(toExpr)}
}
"""
)
)
}
// chain of responsibility of sort: attempt first rule, it it yields another, then another, etc
final override protected val rulesAvailableForPlatform: List[Rule] = List(
// TransformImplicitRule, // looks for Transformer/PartialTransformer, instead provide your own:
EncodeImplicitRule, // <- this should look for implicit Encoder
TransformSubtypesRule,
TransformToSingletonRule,
TransformOptionToOptionRule,
TransformPartialOptionToNonOptionRule,
TransformToOptionRule,
TransformValueClassToValueClassRule,
TransformValueClassToTypeRule,
TransformTypeToValueClassRule,
TransformEitherToEitherRule,
TransformMapToMapRule,
TransformIterableToIterableRule,
TransformProductToProductRule,
TransformSealedHierarchyToSealedHierarchyRule
)
} To understand this architecture:
|
Beta Was this translation helpful? Give feedback.
-
I drafted this PR as an example how Chimney can be use in another library with different type classes, and how to make it cross-compilable between Scala 2 and Scala 3. If this approach makes sense, we could release |
Beta Was this translation helpful? Give feedback.
-
FYI I dropped the attempts when it turned out there's no easy way to handle forward- and backward-compatibility annotations the way teleproto did it. Also it turned out that using chimney directly for our stuff is much easier than a year ago so migrating our apps to use chimney is eatier than migrating teleproto. Maybe I'll extract the prefixed proto enum migration but not sure if it's worth it. Thank you for the help. |
Beta Was this translation helpful? Give feedback.
-
This is a placeholder for discussions about migrating teleproto to use chimney macros inside.
WIP draft implementation for this can be found here: moia-oss/teleproto#338
Beta Was this translation helpful? Give feedback.
All reactions