[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-21 Thread asfgit
Github user asfgit closed the pull request at:

https://github.com/apache/spark/pull/23062


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-20 Thread srowen
Github user srowen commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r235033743
  
--- Diff: 
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/ScalaReflection.scala 
---
@@ -788,12 +788,37 @@ object ScalaReflection extends ScalaReflection {
   }
 
   /**
-   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search
-   * than the exact matching algorithm in `Class.getConstructor`. The 
first assignment-compatible
-   * matching constructor is returned. Otherwise, it returns `None`.
+   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search than
+   * the exact matching algorithm in `Class.getConstructor`. The first 
assignment-compatible
+   * matching constructor is returned if it exists. Otherwise, we check 
for additional compatible
+   * constructors defined in the companion object as `apply` methods. 
Otherwise, it returns `None`.
*/
-  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Constructor[_]] = {
-Option(ConstructorUtils.getMatchingAccessibleConstructor(cls, 
paramTypes: _*))
+  def findConstructor[T](cls: Class[T], paramTypes: Seq[Class[_]]): 
Option[Seq[AnyRef] => T] = {
+Option(ConstructorUtils.getMatchingAccessibleConstructor(cls, 
paramTypes: _*)) match {
+  case Some(c) => Some(x => c.newInstance(x: _*).asInstanceOf[T])
--- End diff --

I think this cast isn't needed because Class[T].newInstance returns T 
already, but it's fine to leave. 


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-19 Thread srowen
Github user srowen commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234861969
  
--- Diff: 
sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/ScalaReflectionSuite.scala
 ---
@@ -109,6 +109,35 @@ object TestingUDT {
   }
 }
 
+/** An example derived from Twitter/Scrooge codegen for thrift  */
+object ScroogeLikeExample {
+  def apply(x: Int): ScroogeLikeExample = new Immutable(x)
+
+  def unapply(_item: ScroogeLikeExample): Option[Int] = Some(_item.x)
+
+  class Immutable(val x: Int) extends ScroogeLikeExample
+}
+
+trait ScroogeLikeExample extends Product1[Int] with Serializable {
+  import ScroogeLikeExample._
+
+  def x: Int
+
+  override def _1: Int = x
+
+  def copy(x: Int = this.x): ScroogeLikeExample = new Immutable(x)
+
+  override def canEqual(other: Any): Boolean = 
other.isInstanceOf[ScroogeLikeExample]
+
+  private def _equals(x: ScroogeLikeExample, y: ScroogeLikeExample): 
Boolean =
+  x.productArity == y.productArity &&
--- End diff --

That's OK, leave in Product, if it's actually testing the case you have in 
mind. Yes I know equals() is needed. The new implementation looks good.


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-19 Thread drewrobb
Github user drewrobb commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234861629
  
--- Diff: 
sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/ScalaReflectionSuite.scala
 ---
@@ -109,6 +109,35 @@ object TestingUDT {
   }
 }
 
+/** An example derived from Twitter/Scrooge codegen for thrift  */
+object ScroogeLikeExample {
+  def apply(x: Int): ScroogeLikeExample = new Immutable(x)
+
+  def unapply(_item: ScroogeLikeExample): Option[Int] = Some(_item.x)
+
+  class Immutable(val x: Int) extends ScroogeLikeExample
+}
+
+trait ScroogeLikeExample extends Product1[Int] with Serializable {
+  import ScroogeLikeExample._
+
+  def x: Int
+
+  override def _1: Int = x
+
+  def copy(x: Int = this.x): ScroogeLikeExample = new Immutable(x)
+
+  override def canEqual(other: Any): Boolean = 
other.isInstanceOf[ScroogeLikeExample]
+
+  private def _equals(x: ScroogeLikeExample, y: ScroogeLikeExample): 
Boolean =
+  x.productArity == y.productArity &&
--- End diff --

I'm worried about changing the tests to use a concrete subtype, because the 
reflection calls might behave differently in that case either now or later on. 
I simplified a little more. `canEqual` is necessary to implement product. 
`equals` is necessary or tests will not pass (it will check object pointer 
equality), and `hashCode` is needed for scalastyle to pass since `equals` is 
necessary.


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-19 Thread srowen
Github user srowen commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234859506
  
--- Diff: 
sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/ScalaReflectionSuite.scala
 ---
@@ -109,6 +109,35 @@ object TestingUDT {
   }
 }
 
+/** An example derived from Twitter/Scrooge codegen for thrift  */
+object ScroogeLikeExample {
+  def apply(x: Int): ScroogeLikeExample = new Immutable(x)
+
+  def unapply(_item: ScroogeLikeExample): Option[Int] = Some(_item.x)
+
+  class Immutable(val x: Int) extends ScroogeLikeExample
+}
+
+trait ScroogeLikeExample extends Product1[Int] with Serializable {
+  import ScroogeLikeExample._
+
+  def x: Int
+
+  override def _1: Int = x
+
+  def copy(x: Int = this.x): ScroogeLikeExample = new Immutable(x)
+
+  override def canEqual(other: Any): Boolean = 
other.isInstanceOf[ScroogeLikeExample]
+
+  private def _equals(x: ScroogeLikeExample, y: ScroogeLikeExample): 
Boolean =
+  x.productArity == y.productArity &&
--- End diff --

Hm, actually that probably won't work any more or less. OK, it's because 
there is an Encoder for Product. You can still simplify the equals() and so on 
I think, but looks like that's easier than a new Encoder. Or is it sufficient 
to test a Seq of a concrete subtype of ScroogeLikeExample?


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-19 Thread srowen
Github user srowen commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234858573
  
--- Diff: 
sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/ScalaReflectionSuite.scala
 ---
@@ -109,6 +109,35 @@ object TestingUDT {
   }
 }
 
+/** An example derived from Twitter/Scrooge codegen for thrift  */
+object ScroogeLikeExample {
+  def apply(x: Int): ScroogeLikeExample = new Immutable(x)
+
+  def unapply(_item: ScroogeLikeExample): Option[Int] = Some(_item.x)
+
+  class Immutable(val x: Int) extends ScroogeLikeExample
+}
+
+trait ScroogeLikeExample extends Product1[Int] with Serializable {
+  import ScroogeLikeExample._
+
+  def x: Int
+
+  override def _1: Int = x
+
+  def copy(x: Int = this.x): ScroogeLikeExample = new Immutable(x)
+
+  override def canEqual(other: Any): Boolean = 
other.isInstanceOf[ScroogeLikeExample]
+
+  private def _equals(x: ScroogeLikeExample, y: ScroogeLikeExample): 
Boolean =
+  x.productArity == y.productArity &&
--- End diff --

Just use SparkSession.createDataset?


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-19 Thread drewrobb
Github user drewrobb commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234854202
  
--- Diff: 
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/ScalaReflection.scala 
---
@@ -788,12 +788,37 @@ object ScalaReflection extends ScalaReflection {
   }
 
   /**
-   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search
-   * than the exact matching algorithm in `Class.getConstructor`. The 
first assignment-compatible
-   * matching constructor is returned. Otherwise, it returns `None`.
+   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search than
+   * the exact matching algorithm in `Class.getConstructor`. The first 
assignment-compatible
+   * matching constructor is returned if it exists. Otherwise, we check 
for additional compatible
+   * constructors defined in the companion object as `apply` methods. 
Otherwise, it returns `None`.
*/
-  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Constructor[_]] = {
-Option(ConstructorUtils.getMatchingAccessibleConstructor(cls, 
paramTypes: _*))
+  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Seq[AnyRef] => Any] = {
--- End diff --

Ok, that makes total sense. I've made this change also


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-19 Thread drewrobb
Github user drewrobb commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234853788
  
--- Diff: 
sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/ScalaReflectionSuite.scala
 ---
@@ -109,6 +109,35 @@ object TestingUDT {
   }
 }
 
+/** An example derived from Twitter/Scrooge codegen for thrift  */
+object ScroogeLikeExample {
+  def apply(x: Int): ScroogeLikeExample = new Immutable(x)
+
+  def unapply(_item: ScroogeLikeExample): Option[Int] = Some(_item.x)
+
+  class Immutable(val x: Int) extends ScroogeLikeExample
+}
+
+trait ScroogeLikeExample extends Product1[Int] with Serializable {
+  import ScroogeLikeExample._
+
+  def x: Int
+
+  override def _1: Int = x
+
+  def copy(x: Int = this.x): ScroogeLikeExample = new Immutable(x)
+
+  override def canEqual(other: Any): Boolean = 
other.isInstanceOf[ScroogeLikeExample]
+
+  private def _equals(x: ScroogeLikeExample, y: ScroogeLikeExample): 
Boolean =
+  x.productArity == y.productArity &&
--- End diff --

My previous answer was not complete. `Product1` is also necessary so that 
the implicit `Encoders.product[T <: Product : TypeTag]` will work with this 
class, if omitted the DatasetSuite test will not compile: 
```
[error] 
/home/drew/spark/sql/core/src/test/scala/org/apache/spark/sql/DatasetSuite.scala:1577:
 value toDS is not a member of 
Seq[org.apache.spark.sql.catalyst.ScroogeLikeExample]
[error] val ds = data.toDS
```
I could add some new encoder, but I think that might be worse as the goal 
of this PR is for Scrooge classes to work with the provided implicit encoders.


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-19 Thread srowen
Github user srowen commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234846910
  
--- Diff: 
sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/ScalaReflectionSuite.scala
 ---
@@ -109,6 +109,35 @@ object TestingUDT {
   }
 }
 
+/** An example derived from Twitter/Scrooge codegen for thrift  */
+object ScroogeLikeExample {
+  def apply(x: Int): ScroogeLikeExample = new Immutable(x)
+
+  def unapply(_item: ScroogeLikeExample): Option[Int] = Some(_item.x)
+
+  class Immutable(val x: Int) extends ScroogeLikeExample
+}
+
+trait ScroogeLikeExample extends Product1[Int] with Serializable {
+  import ScroogeLikeExample._
+
+  def x: Int
+
+  override def _1: Int = x
+
+  def copy(x: Int = this.x): ScroogeLikeExample = new Immutable(x)
+
+  override def canEqual(other: Any): Boolean = 
other.isInstanceOf[ScroogeLikeExample]
+
+  private def _equals(x: ScroogeLikeExample, y: ScroogeLikeExample): 
Boolean =
+  x.productArity == y.productArity &&
--- End diff --

Sure, but you've already established it's a `ScroogeLikeExample` here. Why 
must it be Product1 just to check whether it's also Product1? seems like it's 
not adding anything. In fact, why not just compare the one element that this 
trait knows about? to the extent it can implement equals() meaningfully, that's 
all it is doing already.


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-19 Thread srowen
Github user srowen commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234846234
  
--- Diff: 
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/ScalaReflection.scala 
---
@@ -788,12 +788,37 @@ object ScalaReflection extends ScalaReflection {
   }
 
   /**
-   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search
-   * than the exact matching algorithm in `Class.getConstructor`. The 
first assignment-compatible
-   * matching constructor is returned. Otherwise, it returns `None`.
+   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search than
+   * the exact matching algorithm in `Class.getConstructor`. The first 
assignment-compatible
+   * matching constructor is returned if it exists. Otherwise, we check 
for additional compatible
+   * constructors defined in the companion object as `apply` methods. 
Otherwise, it returns `None`.
*/
-  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Constructor[_]] = {
-Option(ConstructorUtils.getMatchingAccessibleConstructor(cls, 
paramTypes: _*))
+  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Seq[AnyRef] => Any] = {
--- End diff --

Just cast with: `method.apply(args: _*).asInstanceOf[T]`. That's valid 
because that is actually the assumption this code is making. I get no compile 
errors with no further changes beyond that. `cls: Class[T]` knows it produces a 
T from `newInstance`; did you make that change?


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-19 Thread srowen
Github user srowen commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234846491
  
--- Diff: 
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/ScalaReflection.scala 
---
@@ -788,12 +788,37 @@ object ScalaReflection extends ScalaReflection {
   }
 
   /**
-   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search
-   * than the exact matching algorithm in `Class.getConstructor`. The 
first assignment-compatible
-   * matching constructor is returned. Otherwise, it returns `None`.
+   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search than
+   * the exact matching algorithm in `Class.getConstructor`. The first 
assignment-compatible
+   * matching constructor is returned if it exists. Otherwise, we check 
for additional compatible
+   * constructors defined in the companion object as `apply` methods. 
Otherwise, it returns `None`.
*/
-  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Constructor[_]] = {
-Option(ConstructorUtils.getMatchingAccessibleConstructor(cls, 
paramTypes: _*))
+  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Seq[AnyRef] => Any] = {
+Option(ConstructorUtils.getMatchingAccessibleConstructor(cls, 
paramTypes: _*)) match {
+  case Some(c) => Some((x: Seq[AnyRef]) => c.newInstance(x: _*))
+  case None =>
+val companion = mirror.staticClass(cls.getName).companion
+val moduleMirror = mirror.reflectModule(companion.asModule)
+val applyMethods = companion.asTerm.typeSignature
+  .member(universe.TermName("apply")).asTerm.alternatives
+applyMethods.find { method =>
+  val params = method.typeSignature.paramLists.head
+  // Check that the needed params are the same length and of 
matching types
+  params.size == paramTypes.tail.size &&
+  params.zip(paramTypes.tail).forall { case(ps, pc) =>
+ps.typeSignature.typeSymbol == mirror.classSymbol(pc)
+  }
+}.map { applyMethodSymbol =>
+  val expectedArgsCount = 
applyMethodSymbol.typeSignature.paramLists.head.size
+  val instanceMirror = mirror.reflect(moduleMirror.instance)
+  val method = 
instanceMirror.reflectMethod(applyMethodSymbol.asMethod)
+  (_args: Seq[AnyRef]) => {
+// Drop the "outer" argument if it is provided
+val args = if (_args.size == expectedArgsCount) _args else 
_args.tail
--- End diff --

OK, I guess it already works this way, so leave it. But I was suggesting a 
check that they're the same Class, not just whether it's a Class. But that's 
wrong; 'outer' may be another class. I was somehow thinking of 'this'.


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-19 Thread drewrobb
Github user drewrobb commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234844435
  
--- Diff: 
sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/ScalaReflectionSuite.scala
 ---
@@ -109,6 +109,64 @@ object TestingUDT {
   }
 }
 
+/** An example derived from Twitter/Scrooge codegen for thrift  */
+object ScroogeLikeExample {
+  def apply(
+x: Int
+  ): ScroogeLikeExample =
+new Immutable(
+  x
+)
+
+  def unapply(_item: ScroogeLikeExample): _root_.scala.Option[Int] = 
_root_.scala.Some(_item.x)
+
+  class Immutable(val x: Int) extends ScroogeLikeExample
+
+  trait Proxy extends ScroogeLikeExample {
+protected def _underlying_ScroogeLikeExample: ScroogeLikeExample
+override def x: Int = _underlying_ScroogeLikeExample.x
+  }
+}
+
+trait ScroogeLikeExample
+  extends _root_.scala.Product1[Int]
--- End diff --

It provides `productArity` used in `equals` used below. 


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-19 Thread drewrobb
Github user drewrobb commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234844425
  
--- Diff: 
sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/ScalaReflectionSuite.scala
 ---
@@ -109,6 +109,64 @@ object TestingUDT {
   }
 }
 
+/** An example derived from Twitter/Scrooge codegen for thrift  */
+object ScroogeLikeExample {
+  def apply(
+x: Int
+  ): ScroogeLikeExample =
+new Immutable(
+  x
+)
+
+  def unapply(_item: ScroogeLikeExample): _root_.scala.Option[Int] = 
_root_.scala.Some(_item.x)
+
+  class Immutable(val x: Int) extends ScroogeLikeExample
+
+  trait Proxy extends ScroogeLikeExample {
+protected def _underlying_ScroogeLikeExample: ScroogeLikeExample
+override def x: Int = _underlying_ScroogeLikeExample.x
+  }
+}
+
+trait ScroogeLikeExample
+  extends _root_.scala.Product1[Int]
+  with java.io.Serializable
+{
+  import ScroogeLikeExample._
+
+  def x: Int
+
+  def _1: Int = x
--- End diff --

sure, i can't figure out if it is strictly necessary though


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-19 Thread drewrobb
Github user drewrobb commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234844322
  
--- Diff: 
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala
 ---
@@ -462,12 +462,12 @@ case class NewInstance(
   val d = outerObj.getClass +: paramTypes
   val c = getConstructor(outerObj.getClass +: paramTypes)
   (args: Seq[AnyRef]) => {
-c.newInstance(outerObj +: args: _*)
+c(Seq(outerObj +: args: _*))
--- End diff --

Ah, good catch. I meant to change this before.


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-19 Thread drewrobb
Github user drewrobb commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234844315
  
--- Diff: 
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/ScalaReflection.scala 
---
@@ -973,8 +998,19 @@ trait ScalaReflection extends Logging {
 }
   }
 
+  /**
+   * If our type is a Scala trait it may have a companion object that
+   * only defines a constructor via `apply` method.
+   */
+  def getCompanionConstructor(tpe: Type): Symbol = {
--- End diff --

sure


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-19 Thread drewrobb
Github user drewrobb commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234844306
  
--- Diff: 
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/ScalaReflection.scala 
---
@@ -788,12 +788,37 @@ object ScalaReflection extends ScalaReflection {
   }
 
   /**
-   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search
-   * than the exact matching algorithm in `Class.getConstructor`. The 
first assignment-compatible
-   * matching constructor is returned. Otherwise, it returns `None`.
+   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search than
+   * the exact matching algorithm in `Class.getConstructor`. The first 
assignment-compatible
+   * matching constructor is returned if it exists. Otherwise, we check 
for additional compatible
+   * constructors defined in the companion object as `apply` methods. 
Otherwise, it returns `None`.
*/
-  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Constructor[_]] = {
-Option(ConstructorUtils.getMatchingAccessibleConstructor(cls, 
paramTypes: _*))
+  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Seq[AnyRef] => Any] = {
+Option(ConstructorUtils.getMatchingAccessibleConstructor(cls, 
paramTypes: _*)) match {
+  case Some(c) => Some((x: Seq[AnyRef]) => c.newInstance(x: _*))
+  case None =>
+val companion = mirror.staticClass(cls.getName).companion
+val moduleMirror = mirror.reflectModule(companion.asModule)
+val applyMethods = companion.asTerm.typeSignature
+  .member(universe.TermName("apply")).asTerm.alternatives
+applyMethods.find { method =>
+  val params = method.typeSignature.paramLists.head
+  // Check that the needed params are the same length and of 
matching types
+  params.size == paramTypes.tail.size &&
+  params.zip(paramTypes.tail).forall { case(ps, pc) =>
+ps.typeSignature.typeSymbol == mirror.classSymbol(pc)
+  }
+}.map { applyMethodSymbol =>
+  val expectedArgsCount = 
applyMethodSymbol.typeSignature.paramLists.head.size
+  val instanceMirror = mirror.reflect(moduleMirror.instance)
+  val method = 
instanceMirror.reflectMethod(applyMethodSymbol.asMethod)
+  (_args: Seq[AnyRef]) => {
+// Drop the "outer" argument if it is provided
+val args = if (_args.size == expectedArgsCount) _args else 
_args.tail
--- End diff --

You would still have to check the length, in case the first actual argument 
was for some reason also a class.


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-19 Thread srowen
Github user srowen commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234833024
  
--- Diff: 
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/ScalaReflection.scala 
---
@@ -788,12 +788,37 @@ object ScalaReflection extends ScalaReflection {
   }
 
   /**
-   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search
-   * than the exact matching algorithm in `Class.getConstructor`. The 
first assignment-compatible
-   * matching constructor is returned. Otherwise, it returns `None`.
+   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search than
+   * the exact matching algorithm in `Class.getConstructor`. The first 
assignment-compatible
+   * matching constructor is returned if it exists. Otherwise, we check 
for additional compatible
+   * constructors defined in the companion object as `apply` methods. 
Otherwise, it returns `None`.
*/
-  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Constructor[_]] = {
-Option(ConstructorUtils.getMatchingAccessibleConstructor(cls, 
paramTypes: _*))
+  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Seq[AnyRef] => Any] = {
+Option(ConstructorUtils.getMatchingAccessibleConstructor(cls, 
paramTypes: _*)) match {
+  case Some(c) => Some((x: Seq[AnyRef]) => c.newInstance(x: _*))
+  case None =>
+val companion = mirror.staticClass(cls.getName).companion
+val moduleMirror = mirror.reflectModule(companion.asModule)
+val applyMethods = companion.asTerm.typeSignature
+  .member(universe.TermName("apply")).asTerm.alternatives
+applyMethods.find { method =>
+  val params = method.typeSignature.paramLists.head
+  // Check that the needed params are the same length and of 
matching types
+  params.size == paramTypes.tail.size &&
+  params.zip(paramTypes.tail).forall { case(ps, pc) =>
+ps.typeSignature.typeSymbol == mirror.classSymbol(pc)
+  }
+}.map { applyMethodSymbol =>
+  val expectedArgsCount = 
applyMethodSymbol.typeSignature.paramLists.head.size
+  val instanceMirror = mirror.reflect(moduleMirror.instance)
+  val method = 
instanceMirror.reflectMethod(applyMethodSymbol.asMethod)
+  (_args: Seq[AnyRef]) => {
+// Drop the "outer" argument if it is provided
+val args = if (_args.size == expectedArgsCount) _args else 
_args.tail
--- End diff --

I guess we could check whether the dropped arg is of the type of the Class 
argument to be safer, but this probably works


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-19 Thread srowen
Github user srowen commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234834497
  
--- Diff: 
sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/ScalaReflectionSuite.scala
 ---
@@ -109,6 +109,64 @@ object TestingUDT {
   }
 }
 
+/** An example derived from Twitter/Scrooge codegen for thrift  */
+object ScroogeLikeExample {
+  def apply(
+x: Int
+  ): ScroogeLikeExample =
+new Immutable(
+  x
+)
+
+  def unapply(_item: ScroogeLikeExample): _root_.scala.Option[Int] = 
_root_.scala.Some(_item.x)
+
+  class Immutable(val x: Int) extends ScroogeLikeExample
+
+  trait Proxy extends ScroogeLikeExample {
+protected def _underlying_ScroogeLikeExample: ScroogeLikeExample
+override def x: Int = _underlying_ScroogeLikeExample.x
+  }
+}
+
+trait ScroogeLikeExample
+  extends _root_.scala.Product1[Int]
+  with java.io.Serializable
+{
+  import ScroogeLikeExample._
+
+  def x: Int
+
+  def _1: Int = x
--- End diff --

override?


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-19 Thread srowen
Github user srowen commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234834269
  
--- Diff: 
sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/ScalaReflectionSuite.scala
 ---
@@ -109,6 +109,64 @@ object TestingUDT {
   }
 }
 
+/** An example derived from Twitter/Scrooge codegen for thrift  */
+object ScroogeLikeExample {
+  def apply(
+x: Int
+  ): ScroogeLikeExample =
+new Immutable(
+  x
+)
+
+  def unapply(_item: ScroogeLikeExample): _root_.scala.Option[Int] = 
_root_.scala.Some(_item.x)
--- End diff --

why qualify as `_root_.scala.` here?


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-19 Thread srowen
Github user srowen commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234834453
  
--- Diff: 
sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/ScalaReflectionSuite.scala
 ---
@@ -109,6 +109,64 @@ object TestingUDT {
   }
 }
 
+/** An example derived from Twitter/Scrooge codegen for thrift  */
+object ScroogeLikeExample {
+  def apply(
+x: Int
+  ): ScroogeLikeExample =
+new Immutable(
+  x
+)
+
+  def unapply(_item: ScroogeLikeExample): _root_.scala.Option[Int] = 
_root_.scala.Some(_item.x)
+
+  class Immutable(val x: Int) extends ScroogeLikeExample
+
+  trait Proxy extends ScroogeLikeExample {
+protected def _underlying_ScroogeLikeExample: ScroogeLikeExample
+override def x: Int = _underlying_ScroogeLikeExample.x
+  }
+}
+
+trait ScroogeLikeExample
+  extends _root_.scala.Product1[Int]
+  with java.io.Serializable
--- End diff --

Import this, unless this is somehow to avoid scala's Serializable


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-19 Thread srowen
Github user srowen commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234833180
  
--- Diff: 
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala
 ---
@@ -462,12 +462,12 @@ case class NewInstance(
   val d = outerObj.getClass +: paramTypes
   val c = getConstructor(outerObj.getClass +: paramTypes)
   (args: Seq[AnyRef]) => {
-c.newInstance(outerObj +: args: _*)
+c(Seq(outerObj +: args: _*))
--- End diff --

Do these need to be called as varargs? it's no longer a Constructor but a 
function accepting a Seq.


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-19 Thread srowen
Github user srowen commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234834318
  
--- Diff: 
sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/ScalaReflectionSuite.scala
 ---
@@ -109,6 +109,64 @@ object TestingUDT {
   }
 }
 
+/** An example derived from Twitter/Scrooge codegen for thrift  */
+object ScroogeLikeExample {
+  def apply(
--- End diff --

I think this is more readable as one liner


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-19 Thread srowen
Github user srowen commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234834550
  
--- Diff: 
sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/ScalaReflectionSuite.scala
 ---
@@ -109,6 +109,64 @@ object TestingUDT {
   }
 }
 
+/** An example derived from Twitter/Scrooge codegen for thrift  */
+object ScroogeLikeExample {
+  def apply(
+x: Int
+  ): ScroogeLikeExample =
+new Immutable(
+  x
+)
+
+  def unapply(_item: ScroogeLikeExample): _root_.scala.Option[Int] = 
_root_.scala.Some(_item.x)
+
+  class Immutable(val x: Int) extends ScroogeLikeExample
+
+  trait Proxy extends ScroogeLikeExample {
+protected def _underlying_ScroogeLikeExample: ScroogeLikeExample
+override def x: Int = _underlying_ScroogeLikeExample.x
+  }
+}
+
+trait ScroogeLikeExample
+  extends _root_.scala.Product1[Int]
--- End diff --

Does it really need to extend Product1 for the test? quite possibly, just 
didn't see it.


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-19 Thread srowen
Github user srowen commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234833053
  
--- Diff: 
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/ScalaReflection.scala 
---
@@ -973,8 +998,19 @@ trait ScalaReflection extends Logging {
 }
   }
 
+  /**
+   * If our type is a Scala trait it may have a companion object that
+   * only defines a constructor via `apply` method.
+   */
+  def getCompanionConstructor(tpe: Type): Symbol = {
--- End diff --

Can this be private?


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-19 Thread drewrobb
Github user drewrobb commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234834427
  
--- Diff: 
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/ScalaReflection.scala 
---
@@ -788,12 +788,37 @@ object ScalaReflection extends ScalaReflection {
   }
 
   /**
-   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search
-   * than the exact matching algorithm in `Class.getConstructor`. The 
first assignment-compatible
-   * matching constructor is returned. Otherwise, it returns `None`.
+   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search than
+   * the exact matching algorithm in `Class.getConstructor`. The first 
assignment-compatible
+   * matching constructor is returned if it exists. Otherwise, we check 
for additional compatible
+   * constructors defined in the companion object as `apply` methods. 
Otherwise, it returns `None`.
*/
-  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Constructor[_]] = {
-Option(ConstructorUtils.getMatchingAccessibleConstructor(cls, 
paramTypes: _*))
+  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Seq[AnyRef] => Any] = {
--- End diff --

I guess externally `findConstructor` would then be cleaner though? It's too 
bad it is only called in one place.


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-19 Thread drewrobb
Github user drewrobb commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234834125
  
--- Diff: 
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/ScalaReflection.scala 
---
@@ -788,12 +788,37 @@ object ScalaReflection extends ScalaReflection {
   }
 
   /**
-   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search
-   * than the exact matching algorithm in `Class.getConstructor`. The 
first assignment-compatible
-   * matching constructor is returned. Otherwise, it returns `None`.
+   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search than
+   * the exact matching algorithm in `Class.getConstructor`. The first 
assignment-compatible
+   * matching constructor is returned if it exists. Otherwise, we check 
for additional compatible
+   * constructors defined in the companion object as `apply` methods. 
Otherwise, it returns `None`.
*/
-  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Constructor[_]] = {
-Option(ConstructorUtils.getMatchingAccessibleConstructor(cls, 
paramTypes: _*))
+  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Seq[AnyRef] => Any] = {
--- End diff --

Real fast I get:
```
[error] 
/home/drew/spark/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/ScalaReflection.scala:798:
 type mismatch;
[error]  found   : _$31 where type _$31
[error]  required: T
[error]   case Some(c) => Some((x: Seq[AnyRef]) => c.newInstance(x: _*))
[error] ^
[error] 
/home/drew/spark/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/ScalaReflection.scala:818:
 type mismatch;
[error]  found   : Any
[error]  required: T
[error] method.apply(args: _*)
[error]  
```

`Constructor` has a type parameter, but  the signature of `newInstance` 
doesn't use it, and in the second case `method.apply` method returned by 
reflection has a return type of `Any`.  I think that can only be resolved with 
adding `asInstanceOf[Seq[AnyRef] => T]`, which I don't think would be any 
better (unless I'm missing something)?


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-19 Thread srowen
Github user srowen commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234832389
  
--- Diff: 
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/ScalaReflection.scala 
---
@@ -788,12 +788,37 @@ object ScalaReflection extends ScalaReflection {
   }
 
   /**
-   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search
-   * than the exact matching algorithm in `Class.getConstructor`. The 
first assignment-compatible
-   * matching constructor is returned. Otherwise, it returns `None`.
+   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search than
+   * the exact matching algorithm in `Class.getConstructor`. The first 
assignment-compatible
+   * matching constructor is returned if it exists. Otherwise, we check 
for additional compatible
+   * constructors defined in the companion object as `apply` methods. 
Otherwise, it returns `None`.
*/
-  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Constructor[_]] = {
-Option(ConstructorUtils.getMatchingAccessibleConstructor(cls, 
paramTypes: _*))
+  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Seq[AnyRef] => Any] = {
--- End diff --

A Constructor can only be for an Object, so it was already only returning 
something that can make any AnyRef. I think that's better.


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-19 Thread drewrobb
Github user drewrobb commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234828814
  
--- Diff: 
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/ScalaReflection.scala 
---
@@ -788,12 +788,37 @@ object ScalaReflection extends ScalaReflection {
   }
 
   /**
-   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search
-   * than the exact matching algorithm in `Class.getConstructor`. The 
first assignment-compatible
-   * matching constructor is returned. Otherwise, it returns `None`.
+   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search than
+   * the exact matching algorithm in `Class.getConstructor`. The first 
assignment-compatible
+   * matching constructor is returned if it exists. Otherwise, we check 
for additional compatible
+   * constructors defined in the companion object as `apply` methods. 
Otherwise, it returns `None`.
*/
-  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Constructor[_]] = {
-Option(ConstructorUtils.getMatchingAccessibleConstructor(cls, 
paramTypes: _*))
+  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Seq[AnyRef] => Any] = {
--- End diff --

I think you are correct, but I was keeping with the existing return type 
used in where this is called in `eval` and elsewhere and don't know if I should 
make changes not related to my issue. Same thing with changing `_` to `T`. 
Would require adding a type parameter to `NewInstance` which would be a very 
large change.


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-19 Thread drewrobb
Github user drewrobb commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234828642
  
--- Diff: 
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/ScalaReflection.scala 
---
@@ -788,12 +788,37 @@ object ScalaReflection extends ScalaReflection {
   }
 
   /**
-   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search
-   * than the exact matching algorithm in `Class.getConstructor`. The 
first assignment-compatible
-   * matching constructor is returned. Otherwise, it returns `None`.
+   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search than
+   * the exact matching algorithm in `Class.getConstructor`. The first 
assignment-compatible
+   * matching constructor is returned if it exists. Otherwise, we check 
for additional compatible
+   * constructors defined in the companion object as `apply` methods. 
Otherwise, it returns `None`.
*/
-  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Constructor[_]] = {
-Option(ConstructorUtils.getMatchingAccessibleConstructor(cls, 
paramTypes: _*))
+  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Seq[AnyRef] => Any] = {
+Option(ConstructorUtils.getMatchingAccessibleConstructor(cls, 
paramTypes: _*)) match {
+  case Some(c) => Some((x: Seq[AnyRef]) => c.newInstance(x: _*))
+  case None =>
+val companion = mirror.staticClass(cls.getName).companion
+val moduleMirror = mirror.reflectModule(companion.asModule)
+val applyMethods = companion.asTerm.typeSignature
+  .member(universe.TermName("apply")).asTerm.alternatives
+applyMethods.filter{ method =>
--- End diff --

thanks, good suggestion


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-19 Thread drewrobb
Github user drewrobb commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234828661
  
--- Diff: 
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/ScalaReflection.scala 
---
@@ -788,12 +788,37 @@ object ScalaReflection extends ScalaReflection {
   }
 
   /**
-   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search
-   * than the exact matching algorithm in `Class.getConstructor`. The 
first assignment-compatible
-   * matching constructor is returned. Otherwise, it returns `None`.
+   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search than
+   * the exact matching algorithm in `Class.getConstructor`. The first 
assignment-compatible
+   * matching constructor is returned if it exists. Otherwise, we check 
for additional compatible
+   * constructors defined in the companion object as `apply` methods. 
Otherwise, it returns `None`.
*/
-  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Constructor[_]] = {
-Option(ConstructorUtils.getMatchingAccessibleConstructor(cls, 
paramTypes: _*))
+  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Seq[AnyRef] => Any] = {
+Option(ConstructorUtils.getMatchingAccessibleConstructor(cls, 
paramTypes: _*)) match {
+  case Some(c) => Some((x: Seq[AnyRef]) => c.newInstance(x: _*))
+  case None =>
+val companion = mirror.staticClass(cls.getName).companion
+val moduleMirror = mirror.reflectModule(companion.asModule)
+val applyMethods = companion.asTerm.typeSignature
+  .member(universe.TermName("apply")).asTerm.alternatives
+applyMethods.filter{ method =>
+  val params = method.typeSignature.paramLists.head
+  // Check that the needed params are the same length and of 
matching types
+  params.size == paramTypes.tail.size &&
+  params.zip(paramTypes.tail).map{case(ps, pc) =>
--- End diff --

yes!


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-19 Thread drewrobb
Github user drewrobb commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234828613
  
--- Diff: 
sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ObjectExpressionsSuite.scala
 ---
@@ -410,6 +410,16 @@ class ObjectExpressionsSuite extends SparkFunSuite 
with ExpressionEvalHelper {
   dataType = ObjectType(classOf[outerObj.Inner]),
   outerPointer = Some(() => outerObj))
 checkObjectExprEvaluation(newInst2, new outerObj.Inner(1))
+
+// SPARK-8288 Test
+import org.apache.spark.sql.catalyst.ScroogeLikeExample
+val newInst3 = NewInstance(
+  cls = classOf[ScroogeLikeExample],
+  arguments = Literal(1) :: Nil,
+  propagateNull = false,
+  dataType = ObjectType(classOf[ScroogeLikeExample]),
+  outerPointer = Some(() => outerObj))
+checkObjectExprEvaluation(newInst3, ScroogeLikeExample(1))
--- End diff --

Thanks, that is a great tip-- I was looking for the place to place tests 
like this. Upon starting to add them I uncovered a few more code paths that my 
ScroogeExample didn't work with (because the trait was missing methods that 
actual scrooge code had to access member elements), so I fixed that also.


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-18 Thread viirya
Github user viirya commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234449241
  
--- Diff: 
sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/ObjectExpressionsSuite.scala
 ---
@@ -410,6 +410,16 @@ class ObjectExpressionsSuite extends SparkFunSuite 
with ExpressionEvalHelper {
   dataType = ObjectType(classOf[outerObj.Inner]),
   outerPointer = Some(() => outerObj))
 checkObjectExprEvaluation(newInst2, new outerObj.Inner(1))
+
+// SPARK-8288 Test
+import org.apache.spark.sql.catalyst.ScroogeLikeExample
+val newInst3 = NewInstance(
+  cls = classOf[ScroogeLikeExample],
+  arguments = Literal(1) :: Nil,
+  propagateNull = false,
+  dataType = ObjectType(classOf[ScroogeLikeExample]),
+  outerPointer = Some(() => outerObj))
+checkObjectExprEvaluation(newInst3, ScroogeLikeExample(1))
--- End diff --

Besides unit tests, can you also add the tests on encoding 
`ScroogeLikeExample` in Dataset?


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-18 Thread viirya
Github user viirya commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234449115
  
--- Diff: 
sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/ScalaReflectionSuite.scala
 ---
@@ -362,4 +392,11 @@ class ScalaReflectionSuite extends SparkFunSuite {
 assert(numberOfCheckedArguments(deserializerFor[(java.lang.Double, 
Int)]) == 1)
 assert(numberOfCheckedArguments(deserializerFor[(java.lang.Integer, 
java.lang.Integer)]) == 0)
   }
+
+  test("SPARK-8288") {
--- End diff --

Can you add a descriptive test name?


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-17 Thread srowen
Github user srowen commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234404871
  
--- Diff: 
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/ScalaReflection.scala 
---
@@ -788,12 +788,37 @@ object ScalaReflection extends ScalaReflection {
   }
 
   /**
-   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search
-   * than the exact matching algorithm in `Class.getConstructor`. The 
first assignment-compatible
-   * matching constructor is returned. Otherwise, it returns `None`.
+   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search than
+   * the exact matching algorithm in `Class.getConstructor`. The first 
assignment-compatible
+   * matching constructor is returned if it exists. Otherwise, we check 
for additional compatible
+   * constructors defined in the companion object as `apply` methods. 
Otherwise, it returns `None`.
*/
-  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Constructor[_]] = {
-Option(ConstructorUtils.getMatchingAccessibleConstructor(cls, 
paramTypes: _*))
+  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Seq[AnyRef] => Any] = {
+Option(ConstructorUtils.getMatchingAccessibleConstructor(cls, 
paramTypes: _*)) match {
+  case Some(c) => Some((x: Seq[AnyRef]) => c.newInstance(x: _*))
+  case None =>
+val companion = mirror.staticClass(cls.getName).companion
+val moduleMirror = mirror.reflectModule(companion.asModule)
+val applyMethods = companion.asTerm.typeSignature
+  .member(universe.TermName("apply")).asTerm.alternatives
+applyMethods.filter{ method =>
+  val params = method.typeSignature.paramLists.head
+  // Check that the needed params are the same length and of 
matching types
+  params.size == paramTypes.tail.size &&
+  params.zip(paramTypes.tail).map{case(ps, pc) =>
--- End diff --

Spacing here. Is this simpler with .forall?


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-17 Thread srowen
Github user srowen commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234404858
  
--- Diff: 
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/ScalaReflection.scala 
---
@@ -788,12 +788,37 @@ object ScalaReflection extends ScalaReflection {
   }
 
   /**
-   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search
-   * than the exact matching algorithm in `Class.getConstructor`. The 
first assignment-compatible
-   * matching constructor is returned. Otherwise, it returns `None`.
+   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search than
+   * the exact matching algorithm in `Class.getConstructor`. The first 
assignment-compatible
+   * matching constructor is returned if it exists. Otherwise, we check 
for additional compatible
+   * constructors defined in the companion object as `apply` methods. 
Otherwise, it returns `None`.
*/
-  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Constructor[_]] = {
-Option(ConstructorUtils.getMatchingAccessibleConstructor(cls, 
paramTypes: _*))
+  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Seq[AnyRef] => Any] = {
+Option(ConstructorUtils.getMatchingAccessibleConstructor(cls, 
paramTypes: _*)) match {
+  case Some(c) => Some((x: Seq[AnyRef]) => c.newInstance(x: _*))
+  case None =>
+val companion = mirror.staticClass(cls.getName).companion
+val moduleMirror = mirror.reflectModule(companion.asModule)
+val applyMethods = companion.asTerm.typeSignature
+  .member(universe.TermName("apply")).asTerm.alternatives
+applyMethods.filter{ method =>
--- End diff --

Nit: whitespace around braces, operators, etc


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-17 Thread srowen
Github user srowen commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234404797
  
--- Diff: 
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala
 ---
@@ -453,7 +453,7 @@ case class NewInstance(
   @transient private lazy val constructor: (Seq[AnyRef]) => Any = {
 val paramTypes = ScalaReflection.expressionJavaClasses(arguments)
 val getConstructor = (paramClazz: Seq[Class[_]]) => {
-  ScalaReflection.findConstructor(cls, paramClazz).getOrElse {
+  ScalaReflection.findConstructor(cls, paramClazz).getOrElse{
--- End diff --

Nit: undo this one


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-17 Thread srowen
Github user srowen commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234404845
  
--- Diff: 
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/ScalaReflection.scala 
---
@@ -788,12 +788,37 @@ object ScalaReflection extends ScalaReflection {
   }
 
   /**
-   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search
-   * than the exact matching algorithm in `Class.getConstructor`. The 
first assignment-compatible
-   * matching constructor is returned. Otherwise, it returns `None`.
+   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search than
+   * the exact matching algorithm in `Class.getConstructor`. The first 
assignment-compatible
+   * matching constructor is returned if it exists. Otherwise, we check 
for additional compatible
+   * constructors defined in the companion object as `apply` methods. 
Otherwise, it returns `None`.
*/
-  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Constructor[_]] = {
-Option(ConstructorUtils.getMatchingAccessibleConstructor(cls, 
paramTypes: _*))
+  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Seq[AnyRef] => Any] = {
--- End diff --

Wouldn't the result also be AnyRef, if this produces an Object?
Is it not possible to write this with a type T for the class and its 
result? maybe there's a reason.


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-17 Thread srowen
Github user srowen commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234404786
  
--- Diff: 
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/objects/objects.scala
 ---
@@ -498,6 +504,7 @@ case class NewInstance(
   final $javaType ${ev.value} = ${ev.isNull} ?
 ${CodeGenerator.defaultValue(dataType)} : $constructorCall;
 """
+
--- End diff --

Nit: revert changes like this if possible


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-17 Thread srowen
Github user srowen commented on a diff in the pull request:

https://github.com/apache/spark/pull/23062#discussion_r234404888
  
--- Diff: 
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/ScalaReflection.scala 
---
@@ -788,12 +788,37 @@ object ScalaReflection extends ScalaReflection {
   }
 
   /**
-   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search
-   * than the exact matching algorithm in `Class.getConstructor`. The 
first assignment-compatible
-   * matching constructor is returned. Otherwise, it returns `None`.
+   * Finds an accessible constructor with compatible parameters. This is a 
more flexible search than
+   * the exact matching algorithm in `Class.getConstructor`. The first 
assignment-compatible
+   * matching constructor is returned if it exists. Otherwise, we check 
for additional compatible
+   * constructors defined in the companion object as `apply` methods. 
Otherwise, it returns `None`.
*/
-  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Constructor[_]] = {
-Option(ConstructorUtils.getMatchingAccessibleConstructor(cls, 
paramTypes: _*))
+  def findConstructor(cls: Class[_], paramTypes: Seq[Class[_]]): 
Option[Seq[AnyRef] => Any] = {
+Option(ConstructorUtils.getMatchingAccessibleConstructor(cls, 
paramTypes: _*)) match {
+  case Some(c) => Some((x: Seq[AnyRef]) => c.newInstance(x: _*))
+  case None =>
+val companion = mirror.staticClass(cls.getName).companion
+val moduleMirror = mirror.reflectModule(companion.asModule)
+val applyMethods = companion.asTerm.typeSignature
+  .member(universe.TermName("apply")).asTerm.alternatives
+applyMethods.filter{ method =>
--- End diff --

.find rather than filter.headOption maybe?


---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org



[GitHub] spark pull request #23062: [SPARK-8288][SQL] ScalaReflection can use compani...

2018-11-16 Thread drewrobb
GitHub user drewrobb opened a pull request:

https://github.com/apache/spark/pull/23062

[SPARK-8288][SQL] ScalaReflection can use companion object constructor

## What changes were proposed in this pull request?

This change fixes a particular scenario where default spark SQL can't 
encode (thrift) types that are generated by twitter scrooge. These types are a 
trait that extends `scala.ProductX` with a constructor defined only in a 
companion object, rather than a actual case class. The actual case class used 
is child class, but that type is almost never referred to in code. The type has 
no corresponding constructor symbol and causes an exception. For all other 
purposes, these classes act just like case classes, so it is unfortunate that 
spark SQL can't serialize them nicely as it can actual case classes. For an 
full example of a scrooge codegen class, see 
https://gist.github.com/anonymous/ba13d4b612396ca72725eaa989900314.

This change catches the case where the type has no constructor but does 
have an `apply` method on the type's companion object. This allows for thrift 
types to be serialized/deserialized with implicit encoders the same way as 
normal case classes. This fix had to be done in three places where the 
constructor is assumed to be an actual constructor:

1) In serializing, determining the schema for the dataframe relies on 
inspecting its constructor (`ScalaReflection.constructParams`). Here we fall 
back to using the companion constructor arguments.
2) In deserializing or evaluating, in the java codegen ( 
`NewInstance.doGenCode`), the type couldn't be constructed with the new 
keyword. If there is no constructor, we change the constructor call to try the 
companion constructor.
3)  In deserializing or evaluating, without codegen, the constructor is 
directly invoked (`NewInstance.constructor`). This was fixed with scala 
reflection to get the actual companion apply method.

The return type of `findConstructor` was changed because the companion 
apply method constructor can't be represented as a 
`java.lang.reflect.Constructor`.

There might be situations in which this approach would also fail in a new 
way, but it does at a minimum work for the specific scrooge example and will 
not impact cases that were already succeeding prior to this change

Note: this fix does not enable using scrooge thrift enums, additional work 
for this is necessary. With this patch, it seems like you could patch 
`com.twitter.scrooge.ThriftEnum` to extend `_root_.scala.Product1[Int]` with 
`def _1 = value` to get spark's implicit encoders to handle enums, but I've yet 
to use this method myself.

Note: I previously opened a PR for this issue, but only was able to fix 
case 1) there: https://github.com/apache/spark/pull/18766

## How was this patch tested?

I've fixed all 3 cases and added two tests that use a case class that is 
similar to scrooge generated one. The test in ScalaReflectionSuite checks 1), 
and the additional asserting in ObjectExpressionsSuite checks 2) and 3).


You can merge this pull request into a Git repository by running:

$ git pull https://github.com/drewrobb/spark SPARK-8288

Alternatively you can review and apply these changes as the patch at:

https://github.com/apache/spark/pull/23062.patch

To close this pull request, make a commit to your master/trunk branch
with (at least) the following in the commit message:

This closes #23062


commit e27f933710c289d5bc1ddcad38eecde0e555ac60
Author: Drew Robb 
Date:   2017-07-29T01:45:00Z

ScalaReflection can use companion object constructor




---

-
To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org
For additional commands, e-mail: reviews-h...@spark.apache.org