Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split subtypes array initialization to multiple functions. #485

Merged
merged 5 commits into from
Aug 17, 2023

Conversation

nox213
Copy link

@nox213 nox213 commented Aug 14, 2023

We are using Magnolia in production and mainly use it to derive the parent type's type class instances from the type class instances of subtypes. One of our types has more than 700 subtypes, and when derives typeclass instances of this type, the process of initializing the huge subtypes array is performed in a single method, resulting in a "Method too large" compilation error. This PR splits the initialization of the subtypes array into multiple functions and calls each function at the end to combine the array. This method also increases the size of the method, but to a much slower degree, so I think it's still worthwhile. I would appreciate it if you could review it.

core/src/main/scala/magnolia1/magnolia.scala Outdated Show resolved Hide resolved
Comment on lines 660 to 661
(t: $genericType) => t.isInstanceOf[$subType],
(t: $genericType) => t.asInstanceOf[$subType]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can extract those as helper methods in the beginning to reduce the method size

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it's not so easy, because if we make helper methods they need to deal with ClassTag 🤔

Copy link
Author

@nox213 nox213 Aug 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I understand what you mean. Yes, it seems we need to generate sort of runtime reflection codes by macro if we want extract that codes as helper method. So I would say keeping the current way is better even if the runtime reflection code would reduce the method size dramatically. What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, let's keep it like this

@nox213 nox213 requested a review from joroKr21 August 14, 2023 10:36
@nox213
Copy link
Author

nox213 commented Aug 16, 2023

@joroKr21 I fixed based on what you commented. Could you review this PR again?

val assignments = typeclasses.zipWithIndex.map { case ((subType, typeclass), idx) =>
val symbol = subType.typeSymbol
val (annotationTrees, inheritedAnnotationTrees) = annotationsOf(symbol)
q"""arr(start + $idx) = $SubtypeObj[$typeConstructor, $genericType, $subType](
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

arr and start need to be fresh names for hygiene.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

be8bd49
I fixed it. Could you explain me the reason why we need fresh names even if they are local variables of that method?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the user code might contain the same names. It's just insurance.

Comment on lines 404 to 406
$ArrayObj(..${annotationTrees}): Array[_root_.scala.Any],
$ArrayObj(..${inheritedAnnotationTrees}): Array[_root_.scala.Any],
$ArrayObj(..${typeAnnotationsOf(symbol, fromParents = true)}): Array[_root_.scala.Any],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again for hygiene:

Suggested change
$ArrayObj(..${annotationTrees}): Array[_root_.scala.Any],
$ArrayObj(..${inheritedAnnotationTrees}): Array[_root_.scala.Any],
$ArrayObj(..${typeAnnotationsOf(symbol, fromParents = true)}): Array[_root_.scala.Any],
$ArrayObj[$AnyTpe](..${annotationTrees}),
$ArrayObj[$AnyTpe](..${inheritedAnnotationTrees}),
$ArrayObj[$AnyTpe](..${typeAnnotationsOf(symbol, fromParents = true)}),

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

)"""
}
val tree =
q"""def $name(arr: Array[$arrayType], start: Int) = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extract this to the top level:

val ArrayTpe = typeOf[Array[Any]].typeConstructor

And use it like this:

Suggested change
q"""def $name(arr: Array[$arrayType], start: Int) = {
q"""def $name(arr: $ArrayTpe[$arrayType], start: $IntTpe) = {

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nox213 nox213 requested a review from joroKr21 August 16, 2023 11:31
Copy link
Contributor

@joroKr21 joroKr21 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

@adamw adamw merged commit 69a1259 into softwaremill:scala2 Aug 17, 2023
@adamw
Copy link
Member

adamw commented Aug 17, 2023

Thanks! Release is in progress

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants