-
-
Notifications
You must be signed in to change notification settings - Fork 94
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Write an example implementation of a library with Chimney derivarion …
…engine but based on a different type-class
- Loading branch information
1 parent
732799b
commit ae9ac04
Showing
9 changed files
with
247 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
8 changes: 8 additions & 0 deletions
8
...y-engine/src/test/scala-2/io/scalaland/chimney/example/MyTypeClassCompanionPlatform.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package io.scalaland.chimney.example | ||
|
||
import scala.language.experimental.macros | ||
|
||
trait MyTypeClassCompanionPlatform { this: MyTypeClass.type => | ||
|
||
def deriving[From, To]: MyTypeClass[From, To] = macro internal.MyTypeClassMacros.derivingImpl[From, To] | ||
} |
57 changes: 57 additions & 0 deletions
57
.../src/test/scala-2/io/scalaland/chimney/example/macros/MyTypeClassDerivationPlatform.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package io.scalaland.chimney.example.internal | ||
|
||
import io.scalaland.chimney.example.MyTypeClass | ||
import io.scalaland.chimney.example.internal.MyTypeClassDerivation | ||
import io.scalaland.chimney.internal.compiletime.{DerivationEnginePlatform, StandardRules} | ||
|
||
trait MyTypeClassDerivationPlatform extends DerivationEnginePlatform with MyTypeClassDerivation with StandardRules { | ||
|
||
// in Scala-2-specific code, remember to import content of the universe | ||
import c.universe.* | ||
|
||
protected object MyTypes extends MyTypesModule { | ||
|
||
import Type.platformSpecific.* | ||
|
||
object MyTypeClass extends MyTypeClassModule { | ||
def apply[From: Type, To: Type]: Type[MyTypeClass[From, To]] = weakTypeTag[MyTypeClass[From, To]] | ||
def unapply[A](A: Type[A]): Option[(??, ??)] = | ||
A.asCtor[MyTypeClass[?, ?]].map(A0 => A0.param(0) -> A0.param(1)) // utility from Type.platformSpecific.* | ||
} | ||
} | ||
|
||
protected object MyExprs extends MyExprsModule { | ||
|
||
def callMyTypeClass[From: Type, To: Type](tc: Expr[MyTypeClass[From, To]], from: Expr[From]): Expr[To] = | ||
c.Expr[To](q"""$tc.convert($from)""") | ||
|
||
def createTypeClass[From: Type, To: Type](body: Expr[From] => Expr[To]): Expr[MyTypeClass[From, To]] = { | ||
val name = ExprPromise.platformSpecific.freshTermName("from") | ||
// remember to use full qualified names in Scala 2 macros!!! | ||
c.Expr[MyTypeClass[From, To]]( | ||
q""" | ||
new _root_.io.scalaland.chimney.example.MyTypeClass[${Type[From]}, ${Type[To]}] { | ||
def convert($name: ${Type[From]}): ${Type[To]} = ${body(c.Expr[From](q"$name"))} | ||
} | ||
""" | ||
) | ||
} | ||
} | ||
|
||
final override protected val rulesAvailableForPlatform: List[Rule] = List( | ||
MyTypeClassImplicitRule, // replacing TransformImplicitRule | ||
TransformSubtypesRule, | ||
TransformToSingletonRule, | ||
TransformOptionToOptionRule, | ||
TransformPartialOptionToNonOptionRule, | ||
TransformToOptionRule, | ||
TransformValueClassToValueClassRule, | ||
TransformValueClassToTypeRule, | ||
TransformTypeToValueClassRule, | ||
TransformEitherToEitherRule, | ||
TransformMapToMapRule, | ||
TransformIterableToIterableRule, | ||
TransformProductToProductRule, | ||
TransformSealedHierarchyToSealedHierarchyRule | ||
) | ||
} |
14 changes: 14 additions & 0 deletions
14
chimney-engine/src/test/scala-2/io/scalaland/chimney/example/macros/MyTypeClassMacros.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package io.scalaland.chimney.example.internal | ||
|
||
import io.scalaland.chimney.example.MyTypeClass | ||
|
||
import scala.reflect.macros.blackbox | ||
|
||
// Scala 2 macro bundle | ||
class MyTypeClassMacros(val c: blackbox.Context) extends MyTypeClassDerivationPlatform { | ||
|
||
// Scala 2 is kinda unaware during macro expansion that myTypeClassDerivation takes c.WeakTypeTag, and we need to | ||
// point it out for it, explicitly | ||
def derivingImpl[From: c.WeakTypeTag, To: c.WeakTypeTag]: c.Expr[MyTypeClass[From, To]] = | ||
myTypeClassDerivation[From, To] | ||
} |
6 changes: 6 additions & 0 deletions
6
...y-engine/src/test/scala-3/io/scalaland/chimney/example/MyTypeClassCompanionPlatform.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package io.scalaland.chimney.example | ||
|
||
trait MyTypeClassCompanionPlatform { this: MyTypeClass.type => | ||
|
||
inline def deriving[From, To] = ${ internal.MyTypeClassMacros.derivingImpl[From, To] } | ||
} |
50 changes: 50 additions & 0 deletions
50
...rc/test/scala-3/io/scalaland/chimney/example/internal/MyTypeClassDerivationPlatform.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package io.scalaland.chimney.example.internal | ||
|
||
import io.scalaland.chimney.example.MyTypeClass | ||
import io.scalaland.chimney.example.internal.MyTypeClassDerivation | ||
import io.scalaland.chimney.internal.compiletime.{DerivationEnginePlatform, StandardRules} | ||
|
||
abstract class MyTypeClassDerivationPlatform(q: scala.quoted.Quotes) | ||
extends DerivationEnginePlatform(q) | ||
with MyTypeClassDerivation | ||
with StandardRules { | ||
|
||
// in Scala-3-specific code, remember to import content of the quotes and quotes.reflect | ||
import q.*, q.reflect.* | ||
|
||
protected object MyTypes extends MyTypesModule { | ||
|
||
object MyTypeClass extends MyTypeClassModule { | ||
def apply[From: Type, To: Type]: Type[MyTypeClass[From, To]] = scala.quoted.Type.of[MyTypeClass[From, To]] | ||
def unapply[A](A: Type[A]): Option[(??, ??)] = A match | ||
case '[MyTypeClass[from, to]] => Some((Type[from].as_??, Type[to].as_??)) | ||
case _ => None | ||
} | ||
} | ||
|
||
protected object MyExprs extends MyExprsModule { | ||
|
||
def callMyTypeClass[From: Type, To: Type](tc: Expr[MyTypeClass[From, To]], from: Expr[From]): Expr[To] = | ||
'{ ${ tc }.convert(${ from }) } | ||
|
||
def createTypeClass[From: Type, To: Type](body: Expr[From] => Expr[To]): Expr[MyTypeClass[From, To]] = | ||
'{ new MyTypeClass[From, To] { def convert(from: From): To = ${ body('{ from }) } } } | ||
} | ||
|
||
final override protected val rulesAvailableForPlatform: List[Rule] = List( | ||
MyTypeClassImplicitRule, // replacing TransformImplicitRule | ||
TransformSubtypesRule, | ||
TransformToSingletonRule, | ||
TransformOptionToOptionRule, | ||
TransformPartialOptionToNonOptionRule, | ||
TransformToOptionRule, | ||
TransformValueClassToValueClassRule, | ||
TransformValueClassToTypeRule, | ||
TransformTypeToValueClassRule, | ||
TransformEitherToEitherRule, | ||
TransformMapToMapRule, | ||
TransformIterableToIterableRule, | ||
TransformProductToProductRule, | ||
TransformSealedHierarchyToSealedHierarchyRule | ||
) | ||
} |
14 changes: 14 additions & 0 deletions
14
...ney-engine/src/test/scala-3/io/scalaland/chimney/example/internal/MyTypeClassMacros.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package io.scalaland.chimney.example.internal | ||
|
||
import io.scalaland.chimney.example.MyTypeClass | ||
|
||
import scala.quoted.* | ||
|
||
class MyTypeClassMacros(q: Quotes) extends MyTypeClassDerivationPlatform(q) | ||
object MyTypeClassMacros { | ||
|
||
// Scala 3 expect all macros to be methods in objects, no Scala 2's macro-bundle-like equivalents, so we have to | ||
// instantiate a class and call it ourselves | ||
def derivingImpl[From: Type, To: Type](using q: Quotes): Expr[MyTypeClass[From, To]] = | ||
new MyTypeClassMacros(q).myTypeClassDerivation[From, To] | ||
} |
7 changes: 7 additions & 0 deletions
7
chimney-engine/src/test/scala/io/scalaland/chimney/example/MyTypeClass.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package io.scalaland.chimney.example | ||
|
||
trait MyTypeClass[From, To] { | ||
|
||
def convert(from: From): To | ||
} | ||
object MyTypeClass extends MyTypeClassCompanionPlatform |
72 changes: 72 additions & 0 deletions
72
...y-engine/src/test/scala/io/scalaland/chimney/example/internal/MyTypeClassDerivation.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package io.scalaland.chimney.example.internal | ||
|
||
import io.scalaland.chimney.example.MyTypeClass | ||
import io.scalaland.chimney.internal.compiletime.DerivationEngine | ||
|
||
trait MyTypeClassDerivation extends DerivationEngine { | ||
|
||
// example of platform-independent type definition | ||
protected val MyTypes: MyTypesModule | ||
protected trait MyTypesModule { this: MyTypes.type => | ||
|
||
// Provides | ||
// - MyTypeClass.apply[From, To]: Type[MyTypeClass[From, To]] | ||
// - MyTypeClass.unapply(tpe: Type[Any]): Option[(??, ??)] // existential types | ||
val MyTypeClass: MyTypeClassModule | ||
trait MyTypeClassModule extends Type.Ctor2[MyTypeClass] { this: MyTypeClass.type => } | ||
|
||
// use in platform-independent code (it cannot generate Type instances, as opposed to Scala 2/Scala 3 macros) | ||
object Implicits { | ||
|
||
implicit def MyTypeClassType[From: Type, To: Type]: Type[MyTypeClass[From, To]] = MyTypeClass[From, To] | ||
} | ||
} | ||
|
||
// example of platform-independent expr utility | ||
protected val MyExprs: MyExprsModule | ||
protected trait MyExprsModule { this: MyExprs.type => | ||
|
||
import MyTypes.Implicits.* | ||
|
||
def callMyTypeClass[From: Type, To: Type](tc: Expr[MyTypeClass[From, To]], from: Expr[From]): Expr[To] | ||
|
||
def createTypeClass[From: Type, To: Type](body: Expr[From] => Expr[To]): Expr[MyTypeClass[From, To]] | ||
|
||
def summonMyTypeClass[From: Type, To: Type]: Option[Expr[MyTypeClass[From, To]]] = | ||
Expr.summonImplicit[MyTypeClass[From, To]] | ||
|
||
// use in platform-independent code (since it does not have quotes nor quasiquotes) | ||
object Implicits { | ||
|
||
implicit class MyTypeClassOps[From: Type, To: Type](private val tc: Expr[MyTypeClass[From, To]]) { | ||
|
||
def convert(from: Expr[From]): Expr[To] = callMyTypeClass(tc, from) | ||
} | ||
} | ||
} | ||
|
||
import MyExprs.Implicits.* | ||
|
||
// example of a platform-independent Rule | ||
object MyTypeClassImplicitRule extends Rule("MyTypeClassImplicit") { | ||
|
||
override def expand[From, To](implicit | ||
ctx: TransformationContext[From, To] | ||
): DerivationResult[Rule.ExpansionResult[To]] = | ||
MyExprs.summonMyTypeClass[From, To] match { | ||
case Some(myTypeClass) => DerivationResult.expandedTotal(myTypeClass.convert(ctx.src)) | ||
case None => DerivationResult.attemptNextRule | ||
} | ||
} | ||
|
||
def myTypeClassDerivation[From: Type, To: Type]: Expr[MyTypeClass[From, To]] = | ||
MyExprs.createTypeClass[From, To] { (from: Expr[From]) => | ||
val cfg = TransformerConfiguration() // customize, read config with DSL etc | ||
val context = TransformationContext.ForTotal.create[From, To](from, cfg) | ||
|
||
deriveFinalTransformationResultExpr(context).toEither.fold( | ||
derivationErrors => reportError(derivationErrors.toString), // customize | ||
identity | ||
) | ||
} | ||
} |