【问题标题】:Implicit Encoder for TypedDataset and Type Bounds in ScalaScala 中 TypedDataset 和类型边界的隐式编码器
【发布时间】:2019-04-12 02:35:56
【问题描述】:

我的目标是创建一个MyDataFrame 类,它知道如何在给定路径上获取数据,但我想提供类型安全。我在使用带有远程数据类型限制的frameless.TypedDataset 时遇到了一些麻烦。例如

sealed trait Schema
final case class TableA(id: String) extends Schema
final case class TableB(id: String) extends Schema

class MyDataFrame[T <: Schema](path: String, implicit val spark: SparkSession) {
  def read = TypedDataset.create(spark.read.parquet(path)).as[T]
} 

但我不断收到could not find implicit value for evidence parameter of type frameless.TypedEncoder[org.apache.spark.sql.Row]。我知道TypedDataset.create 需要Injection 才能工作。但我不确定如何为通用T 编写此代码。我认为编译器能够推断出,因为Schema 的所有子类型都是case classes,所以它可以工作。

有人遇到过这种情况吗?

【问题讨论】:

    标签: scala apache-spark frameless


    【解决方案1】:

    所有隐式参数都应该在最后一个参数列表中,并且这个参数列表应该与非隐式参数分开。

    如果你尝试编译

    class MyDataFrame[T <: Schema](path: String)(implicit spark: SparkSession) {
      def read = TypedDataset.create(spark.read.parquet(path)).as[T]
    }
    

    你会看到错误

    Error:(11, 35) could not find implicit value for evidence parameter of type frameless.TypedEncoder[org.apache.spark.sql.Row]
        def read = TypedDataset.create(spark.read.parquet(path)).as[T]
    

    所以我们只需添加相应的隐式参数

    class MyDataFrame[T <: Schema](path: String)(implicit spark: SparkSession, te: TypedEncoder[Row]) {
      def read = TypedDataset.create(spark.read.parquet(path)).as[T]
    }
    

    我们会有错误

    Error:(11, 64) could not find implicit value for parameter as: frameless.ops.As[org.apache.spark.sql.Row,T]
        def read = TypedDataset.create(spark.read.parquet(path)).as[T]
    

    所以让我们再添加一个隐式参数

    import frameless.ops.As
    import frameless.{TypedDataset, TypedEncoder}
    import org.apache.spark.sql.{Row, SparkSession}
    
    class MyDataFrame[T <: Schema](path: String)(implicit spark: SparkSession, te: TypedEncoder[Row], as: As[Row, T]) {
      def read = TypedDataset.create(spark.read.parquet(path)).as[T]
    }
    

    或使用投影仪

    class MyDataFrame[T <: Schema : As[Row, ?]](path: String)(implicit spark: SparkSession, te: TypedEncoder[Row]) {
      def read = TypedDataset.create(spark.read.parquet(path)).as[T]
    }
    

    您可以创建自定义类型类

      trait Helper[T] {
        implicit def te: TypedEncoder[Row]
        implicit def as: As[Row, T]
      }
    
      object Helper {
        implicit def mkHelper[T](implicit te0: TypedEncoder[Row], as0: As[Row, T]): Helper[T] = new Helper[T] {
          override implicit def te: TypedEncoder[Row] = te0
          override implicit def as: As[Row, T] = as0
        }
      }
    
      class MyDataFrame[T <: Schema : Helper](path: String)(implicit spark: SparkSession) {
        val h = implicitly[Helper[T]]
        import h._
        def read = TypedDataset.create(spark.read.parquet(path)).as[T]
      }
    

      class MyDataFrame[T <: Schema](path: String)(implicit spark: SparkSession, h: Helper[T]) {
        import h._
        def read = TypedDataset.create(spark.read.parquet(path)).as[T]
      }
    

      trait Helper[T] {
        def create(dataFrame: DataFrame): TypedDataset[T]
      }
    
      object Helper {
        implicit def mkHelper[T](implicit te: TypedEncoder[Row], as: As[Row, T]): Helper[T] =
          (dataFrame: DataFrame) => TypedDataset.create(dataFrame).as[T]
      }
    
      class MyDataFrame[T <: Schema : Helper](path: String)(implicit spark: SparkSession) {
        def read = implicitly[Helper[T]].create(spark.read.parquet(path))
      }
    

      class MyDataFrame[T <: Schema](path: String)(implicit spark: SparkSession, h: Helper[T]) {
        def read = h.create(spark.read.parquet(path))
      }
    

    【讨论】:

    • 有趣,行得通。现在我只需要了解发生了什么。谢谢!
    • @DanielSevero 我用一些解释更新了我的答案。
    • 这不是强迫我为我需要创建的每个实例提供teas 吗?
    • @DanielSevero 我更新了我的答案。您可以创建自定义类型类。
    猜你喜欢
    • 2020-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-09
    • 2021-01-24
    • 2013-05-31
    • 2018-05-22
    相关资源
    最近更新 更多