jhc-systems / sqlest

Write SQL in Scala
https://jhc-systems.github.io/sqlest/latest/api/
Apache License 2.0
30 stars 17 forks source link

Fix runtime error when wrapping an extract[A] in extract #32

Closed DavidGregory084 closed 9 years ago

DavidGregory084 commented 9 years ago

Illustrated by 3f65f65711524a4f40b3f6394b79709fc68c4ea3:

val twoExtractor = extract[Two](
  a = stringExtractor,
  b = shapeExtractor
)

val badNestedExtractor = extract[AggregateOneTwo](
  one = extract[One](
    a = intExtractor,
    b = stringExtractor
  ),
  two = extract(twoExtractor)
)

This should really fail to compile - instead we get a runtime error when the extractor is used:

badNestedExtractor.extractHeadOption(tupleRows) should equal(Some(
  AggregateOneTwo(One(1, "a"), Two("a", Circle))
))

=>

[info]   java.lang.NoSuchMethodException: sqlest.extractor.CaseClassExtractorMacroSpec$$anonfun$17$$anon$57.applyDynamic(java.lang.String, scala.collection.Seq)
[info]   at java.lang.Class.getMethod(Class.java:1778)
[info]   at sqlest.extractor.CaseClassExtractorMacroSpec$$anonfun$17.reflMethod$Method1(CaseClassExtractorMacroSpec.scala:247)
[info]   at sqlest.extractor.CaseClassExtractorMacroSpec$$anonfun$17.apply$mcV$sp(CaseClassExtractorMacroSpec.scala:247)
[info]   at sqlest.extractor.CaseClassExtractorMacroSpec$$anonfun$17.apply(CaseClassExtractorMacroSpec.scala:234)
[info]   at sqlest.extractor.CaseClassExtractorMacroSpec$$anonfun$17.apply(CaseClassExtractorMacroSpec.scala:234)
[info]   at org.scalatest.Transformer$$anonfun$apply$1.apply$mcV$sp(Transformer.scala:22)
[info]   at org.scalatest.OutcomeOf$class.outcomeOf(OutcomeOf.scala:85)
[info]   at org.scalatest.OutcomeOf$.outcomeOf(OutcomeOf.scala:104)
[info]   at org.scalatest.Transformer.apply(Transformer.scala:22)
[info]   at org.scalatest.Transformer.apply(Transformer.scala:20)
[info]   ...
DavidGregory084 commented 9 years ago

This seems to be caused by the structural type in the return type of the macro:

  def extract[A]: Dynamic { def applyDynamic(method: String)(args: Any*): Extractor[Row, A] with SimpleExtractor[Row, A]; def applyDynamicNamed(method: String)(namedArgs: (String, Any)*): Extractor[Row, A] with SimpleExtractor[Row, A] } =
    macro CaseClassExtractorMacro.apply[Row, A]

once it's removed...

  def extract[A]: Dynamic = macro CaseClassExtractorMacro.apply[Row, A]

we get this slightly ugly compile-time error instead:

[error] /home/david/IdeaProjects/sqlest/extractors/src/test/scala/sqlest/extractor/CaseClassExtractorMacroSpec.scala:247: value applyDynamic is not a member of Dynamic
[error] error after rewriting to CaseClassExtractorMacroSpec.this.extract[A].<applyDynamic: error>("apply")
[error] possible cause: maybe a wrong Dynamic method signature?
[error]       two = extract(twoExtractor)
[error]             ^
DavidGregory084 commented 9 years ago

Nice!