Releases: arainko/ducktape
ducktape 0.1.4
This is a fifth release of ducktape in the 0.1.x line and it is fully backwards binary-compatible with previous releases.
This release includes a small bugfix for the optimization edge case discussed in #41 and some nicer error messages when an instance of Transformer
is missing.
What's Changed
- [Issue #8] Document recursive transformers and add nicer error messages when a
Transformer
is missing by @arainko in #36 - fix #41 by @arainko in #42
Full Changelog: v0.1.3...v0.1.4
ducktape 0.1.3
This is a fourth release of ducktape in the 0.1.x line and it is fully backwards binary-compatible with previous releases.
This release contains a new case class to case class configuration options that allows us to get matching fields from a different case class, an example:
import io.github.arainko.ducktape.*
final case class Person(firstName: String, lastName: String, age: Int)
final case class PersonButMoreFields(firstName: String, lastName: String, age: Int, socialSecurityNo: String)
val person = Person("Jerry", "Smith", 20)
final case class FieldSource(lastName: String, socialSecurityNo: String)
// 4. Grab and use all matching fields from a different case class (a compiletime error will be issued if none of the fields match)
val withAllMatchingFields =
person
.into[PersonButMoreFields]
.transform(Field.allMatching(FieldSource("SourcedLastName", "SOURCED-SSN")))
// withAllMatchingFields: PersonButMoreFields = PersonButMoreFields(
// firstName = "Jerry",
// lastName = "SourcedLastName",
// age = 20,
// socialSecurityNo = "SOURCED-SSN"
// )
It'll also issue a compiletime failure in case none of the fields of the case class you pass in match the any of the fields from the source case class
What's Changed
- update version in docs to 0.1.2 by @arainko in #28
- Backport changes from
series/0.2.x
by @arainko in #33 - [Issue #32] Merge case classes by @arainko in #35
Full Changelog: v0.1.2...v0.1.3
ducktape 0.1.2
This a third release of ducktape
in the 0.1.x line and it is fully backwards binary-compatible with previous releases.
This release contains a bugfix for a very weird interaction between the derivation of Transformer.Identity
and type path references in case classes. More info here.
What's Changed
- Try a new module-less approach for macros by @arainko in #24
- remove 'Playground' (OOPS!) by @arainko in #25
- Resolve Issue #26 by @arainko in #27
Full Changelog: v0.1.1...v0.1.2
ducktape 0.1.1
This a second release of ducktape
in the 0.1.x line and it is fully backwards binary-compatible with previous releases.
The theme of this release is more refinement in code generation. A lot of Transformer
instances do not make it to runtime now (eg. when transforming Option[A] => Optioin[B]
, A => Option[B]
, CollectionA[A] => Collection[B]
etc.).
For the curious there's now Transformer.Debug.showCode
to see the code that's being generated at compile time.
--- nitty gritty stuff --
- resolve an issue where implicit resolution in a macro (eg.
Expr.summon
andImplicits.search
) was failing and a fallback toscala.compiletime.summonInline
was needed
A solution to this issue was to create a macro that creates the typeclass instance itself not a macro that only does the underlying transformation.
Eg.
// OG version
inline def transform[A, B](value: A): B = ${ transformMacro[A, B]('value) }
def transformMacro[A: Type, B: Type](value: Expr[A])(using Quotes): Expr[B] = ??? // do the transformation here
inline given Transformer[A, B] = (a: A) => transform[A, B](a) // <-- this seems to trip implicit search with Expr.summon/Implicits.search but not summonInline
// fixed version
inline def transformer[A, B]: Transformer[A, B] = ${ transformerMacro[A, B]('value) }
def transformerMacro[A: Type, B: Type](using Quotes): Expr[Transformer[A, B] = ??? // do the transformation but also build an instance of the typeclass at the same time
inline given Transformer[A, B] = transformer[A, B] // <-- this doesn't trip Expr.summon/Implicits.search - weird weird weird
This fix allowed me to greatly simplify logic of Transformer
normalization, which can now be done straight after getting our hands on an instance of a transformer instead of going through the whole tree to transform some specific nodes after (summonInline
expands some time after the initial inlining?)
Examples:
case class Person(int: Int, str: String, inside: Inside)
case class Person2(int: Int, str: String, inside: Inside2)
case class Inside(str: String, int: Int, inside: EvenMoreInside)
case class Inside2(int: Int, str: String, inside: Option[EvenMoreInside2])
case class EvenMoreInside(str: String, int: Int)
case class EvenMoreInside2(str: String, int: Int)
val person = Person(1, "2", Inside("2", 1, EvenMoreInside("asd", 3)))
person.to[Person2]
person.to[Person2]
now expands to:
to[Person](person)[Person2]((inline$make$i1[Person, Person2](ForProduct)((((source: Person) =>
new Person2(
int = source.int,
str = source.str,
inside = new Inside2(
int = source.inside.int,
str = source.inside.str,
inside = Some.apply[EvenMoreInside2](
new EvenMoreInside2(
str = source.inside.inside.str,
int = source.inside.inside.int
)
)
))): Transformer[Person, Person2])): ForProduct[Person, Person2]))
Note how inside
expands to a Some(...)
, in the previous version this call would allocate two more instances of Transformer
and then do the transformation, now it does the transformation without any extra allocations.
It also rewrites collection-to-collection transformation to
val transformedCollection = sourceCollectio.map(src => ... /* transform from src to dest */).to(destCollectionFactory)
Just as in the Some(...)
example, no intermediate transformers are needed anymore!
I also plan to special case invocations of to
do rewrite the AST to contain the transformation lifted from the supplied transformer so in some cases the library will be able to get rid of an additional transformer instance.
- A look at the code docs for those that want to know what gets spliced into their code
What's Changed
- Add MiMa by @arainko in #20
- Resolve a longstanding issue with resolving Transformers in macros & more
Transformer
AST rewrites to get rid of unnecessary runtime instances by @arainko in #21 - 0.1.1 docs (
A look at the code
section),LiftTransformationModule
now actually encapsulates all the logic of lifting transformations by @arainko in #23
Full Changelog: 0.1.0...v0.1.1
Ducktape 0.1.0
What's Changed
- release version 0.0.14, add
via
andintoVia
docs by @arainko in #11 - Alternative encoding by @arainko in #12
- update
mdoc
to 2.3.3, cleanup docs by @arainko in #13 - Selectable arg selectors by @arainko in #15
- Resolved #17 and #16
- Bug fix for parameter ordering in intoVia (it relied on the ordering of a map, which is so stupid I don't even know how it got there in the first place)
- Transformer.Identity now respects type relationships and is not fixed on a single type
- Dropped the covariance in collection transformers to make Sets work
- Bumped to Scala 3.2.1
Full Changelog: v0.0.14...v0.1.0