codingwell / scala-guice

Scala extensions for Google Guice
Apache License 2.0
341 stars 44 forks source link

Binding of generic types #65

Closed jendakol closed 4 years ago

jendakol commented 7 years ago

I have issue with binding some generic types, namely bind[Unit => String] and bind[(=> Unit) => String]. Guice itself is able to both bind and inject types like this, but current implementation of this library stops me from doing that, because:

I would like to ask if there is some reason why the Unit is converted to Void.
I would be very happy to fix it (it's easily solvable by a short macro which I already wrote for myself as a workaround), I just want to be sure there's not some good reason for having it like these.

Thx.

tsuckow commented 7 years ago

I can't think of a good reason. Every so often I try to fix some of these more nuanced issues but it inevitably triumphs over me.

PR's welcome.

jendakol commented 7 years ago

OK, I'll do my best.
But are you aware it will not be 100% compatible with the current version? I doubt there's a lot of people using it, but injecting the java.lang.Void => T will stop work, will be replaced by Unit => T (which was original binding). Thus, it will be necessary to raise version of this library but that will break you same as Guice versioning scheme... Just wanted to warn you :-)

tsuckow commented 7 years ago

I don't know that I ever claimed to be semver. There is also the argument about whether fixing an unexpected behaviour is "breaking".

tsuckow commented 6 years ago

I think with changes in 4.2.1 this is now "fixable." But I am having a hell of a time making a test case. I tried

 "allow binding by name to Unit" in {
      val foo:(=> Unit) => String = (a) => "dog"
      val module = new AbstractModule with ScalaModule {
        override def configure() = {
          bind[(=> Unit) => String].toInstance(foo)
          bindInterceptor[AOPI](methodMatcher = annotatedWith[AOP])
        }
      }
      import net.codingwell.scalaguice.InjectorExtensions._
      val injector = Guice.createInjector(module)
      val func = injector.instance[(=> Unit) => String]
      func shouldEqual foo
    }

But this results in [info] - should allow binding by name to Unit FAILED [info] java.lang.ClassNotFoundException: scala. [info] at java.net.URLClassLoader.findClass(URLClassLoader.java:381) [info] at java.lang.ClassLoader.loadClass(ClassLoader.java:424) [info] at java.lang.ClassLoader.loadClass(ClassLoader.java:357) [info] at java.lang.Class.forName0(Native Method) [info] at java.lang.Class.forName(Class.java:348) [info] at scala.reflect.runtime.JavaMirrors$JavaMirror.javaClass(JavaMirrors.scala:555) [info] at scala.reflect.runtime.JavaMirrors$JavaMirror$$anonfun$classToJava$1.apply(JavaMirrors.scala:1211) [info] at scala.reflect.runtime.JavaMirrors$JavaMirror$$anonfun$classToJava$1.apply(JavaMirrors.scala:1203) [info] at scala.reflect.runtime.TwoWayCaches$TwoWayCache$$anonfun$toJava$1.apply(TwoWayCaches.scala:49) [info] at scala.reflect.runtime.Gil$class.gilSynchronized(Gil.scala:19)

Which I assume is meaning foo isn't valid. It may also be due to me attempting to use TypeTag to determine the type.

tsuckow commented 4 years ago

I fiddled with this again and made 1 tweak in 4.2.8 that Unit didn't seem to be getting typed correctly.

However it seems the the reflection to find a Java type for (=> Unit) results in a class not found error of <byname>. I can see that the Symbol is for => T0 but not sure how to coerce that to classOf[Function0]