Navigation Menu

Skip to content
This repository has been archived by the owner on Apr 8, 2020. It is now read-only.

Compilation fail using a case class with no parameters #124

Open
alvarogimenez opened this issue Jan 15, 2019 · 7 comments
Open

Compilation fail using a case class with no parameters #124

alvarogimenez opened this issue Jan 15, 2019 · 7 comments

Comments

@alvarogimenez
Copy link

I'm getting an ambiguous implicit values compilation error when using the @reader annotation in a case class without parameters in combination with the @readers annotation over a case class with more than one parameter.

I've got this error reproduced by modifying the test ReadersNestedMacroTest, included in the last release, with the following changes:

  1. Add a new service annotated with @reader represented by a case class with no parameters
@reader
case class SomeServiceWithoutParameters()

@reader
case class WebApplication(someService: SomeService, emptyService: SomeServiceWithoutParameters)
  1. Add an additional parameter to the ApplicationConfiguration case class, which uses the @readers annotation:
case class AdditionalConfig(config3: String)

@readers
case class ApplicationConfiguration(httpConfig: HttpConfig, additionalConfig: AdditionalConfig)

Step (2.) is important since adding a new parameter to this case class seems to reproduce the error while leaving it with only one parameter results in a successful compilation.

The full test code causing the compilation error would be:

package org.zalando.grafter.macros


@readers
case class ApplicationConfiguration(httpConfig: HttpConfig, additionalConfig: AdditionalConfig)

/**
 * For a nested configuration you need to annotate the nested elements with the @readers annotation
 * as well
 */
@readers
case class HttpConfig(cfg1: HttpSubConfig1, cfg2: HttpSubConfig2 )

case class HttpSubConfig1(port: Int)
case class HttpSubConfig2(host: String)
case class AdditionalConfig(config3: String)

@reader
case class HttpClient(config: HttpSubConfig2)


@reader
case class SomeService(client: HttpClient)

@reader
case class SomeServiceWithoutParameters()

@reader
case class WebApplication(someService: SomeService, emptyService: SomeServiceWithoutParameters)


object MyApp extends App {

  val config = ApplicationConfiguration(HttpConfig(HttpSubConfig1(80), HttpSubConfig2("bla")), AdditionalConfig("3"))

  val app = WebApplication.reader[ApplicationConfiguration].apply(config)

}

And the compilation error trace when executing sbt clean test is:

[error] .../grafter/macros/src/test/scala/org/zalando/grafter/macros/ReadersNestedMacroTest.scala:36:34: ambiguous implicit values:
[error]  both method reader in object SomeServiceWithoutParameters of type [A3]=> cats.data.Reader[A3,org.zalando.grafter.macros.SomeServiceWithoutParameters]
[error]  and method transitiveadditionalConfigReader in object ApplicationConfiguration of type [A](implicit r1: cats.data.Reader[org.zalando.grafter.macros.AdditionalConfig,A], implicit r2: cats.data.Reader[org.zalando.grafter.macros.ApplicationConfiguration,org.zalando.grafter.macros.AdditionalConfig])cats.data.Reader[org.zalando.grafter.macros.ApplicationConfiguration,A]
[error]  match expected type cats.data.Reader[org.zalando.grafter.macros.ApplicationConfiguration,org.zalando.grafter.macros.SomeServiceWithoutParameters]
[error]   val app = WebApplication.reader[ApplicationConfiguration].apply(config)
@etorreborre
Copy link
Contributor

Interesting one, I'm not sure how well we can solve this case. I'll have a look at it.

@etorreborre
Copy link
Contributor

etorreborre commented Jan 17, 2019

I don't know if that's a satisfactory answer for you but I think that if you have a service having no parameters you can consider it like a "static" piece of configuration and put it in your ApplicationConfig

@readers
case class ApplicationConfiguration(
  httpConfig: HttpConfig, 
  additionalConfig: AdditionalConfig, 
  emptyService: SomeServiceWithoutParameters)

val config = ApplicationConfiguration(
    HttpConfig(HttpSubConfig1(80), HttpSubConfig2("bla")),
    AdditionalConfig("3"),
    SomeServiceWithoutParameters())

Then it all compiles.

Does that work for you?

@alvarogimenez
Copy link
Author

Thanks for the workaround. We decided to downgrade Grafter to version 2.6.0 as a temporary solution since we don't have any nested configuration classes that need to be supported by the last version of @readers annotation.
I still think that all our services should be injected using the @reader annotation even if they have no parameters. It would be great if you can clarify us if supporting parameterless Services annotated with @reader in combination with large configuration objects (which seems to cause the compilation error) would be supported in the future or we should move all our parameterless services to the configuration modules as a final solution.
Thanks in advance.

@etorreborre
Copy link
Contributor

It is indeed silly to have to repeat in a Configuration object that you need a ParameterlessService since it is obvious how to build one. I gave this issue another go and it seems that I have a working solution now. However I have some issues to publish a new version now. Could you please try to grab one from master?

@alvarogimenez
Copy link
Author

alvarogimenez commented Jan 18, 2019

Thanks @etorreborre for the quick response. I have tried your last changes in my project and I'm still getting the same error. I have made a test with a simplified graph (since I have more than 50 components in the real scenario) which is not compiling using grafter#master

object ParameterlessComponentSpec {

  @readers
  case class ApplicationConfiguration(c1: HttpConfig1, c2: HttpConfig2)

  case class HttpConfig1(c1: String)
  case class HttpConfig2(c2: String)

  @reader
  case class ParameterlessComponent()

  @reader
  case class Service(parameterlessComponent: ParameterlessComponent)

  @reader
  case class Application(service: Service)


  object MyApp extends App {

    val config = ApplicationConfiguration(
      HttpConfig1("1"),
      HttpConfig2("2")
    )

    val app = Application.reader[ApplicationConfiguration].apply(config)
  }
}

And the error is:

[error] .../grafter/tests/src/test/scala/org/zalando/grafter/ParameterlessComponentSpec.scala:33:33: ambiguous implicit values:
[error]  both method transitivec2Reader in object ApplicationConfiguration of type [A](implicit r1: cats.data.Reader[org.zalando.grafter.macros.ParameterlessComponentSpec.HttpConfig2,A], implicit r2: cats.data.Reader[org.zalando.grafter.macros.ParameterlessComponentSpec.ApplicationConfiguration,org.zalando.grafter.macros.ParameterlessComponentSpec.HttpConfig2])cats.data.Reader[org.zalando.grafter.macros.ParameterlessComponentSpec.ApplicationConfiguration,A]
[error]  and method transitivec1Reader in object ApplicationConfiguration of type [A](implicit r1: cats.data.Reader[org.zalando.grafter.macros.ParameterlessComponentSpec.HttpConfig1,A], implicit r2: cats.data.Reader[org.zalando.grafter.macros.ParameterlessComponentSpec.ApplicationConfiguration,org.zalando.grafter.macros.ParameterlessComponentSpec.HttpConfig1])cats.data.Reader[org.zalando.grafter.macros.ParameterlessComponentSpec.ApplicationConfiguration,A]
[error]  match expected type cats.data.Reader[org.zalando.grafter.macros.ParameterlessComponentSpec.ApplicationConfiguration,org.zalando.grafter.macros.ParameterlessComponentSpec.Service]
[error]     val app = Application.reader[ApplicationConfiguration].apply(config)

,which I think is related with the error in this issue. I've tried to change the number of parameters in ApplicationConfiguration case class and this time it seems to have no effect on the results.

@etorreborre etorreborre reopened this Jan 18, 2019
@etorreborre
Copy link
Contributor

Unfortunately I don't see what trick to apply here. I'll try to have another look during the week-end.

@etorreborre
Copy link
Contributor

I am sorry I could not find a way to make this work, you might have to either introduce a dummy parameter for your service or put an instance in the AppConfig. There might be a solution if macros allowed me to introduce a trait in addition to put new methods in a companion object but I can't do that. I am not even sure what will be the state of the macro annotation support in Scala 3 which is even more problematic here :-).

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

No branches or pull requests

2 participants