如何从推断类型中不取出任何内容

velaa5lx  于 2021-05-27  发布在  Spark
关注(0)|答案(2)|浏览(371)

这个想法来自这个视频:https://www.youtube.com/watch?v=bfabet0pre0&t=526s,他们讨论了如何通过实现自定义类型来实现类型安全。
一个可能的琐碎实现是

trait Col[Self] { self: Self =>
}

trait Id extends Col[Id]
object IdCol extends Id

trait Val extends Col[Val]
object ValCol extends Val

trait Comment extends Col[Comment]
object CommentCol extends Comment

case class DataSet[Schema >: Nothing](df: DataFrame) {

  def validate[T1 <: Col[T1], T2 <: Col[T2]](
      col1: (Col[T1], String),
      col2: (Col[T2], String)
  ): Option[DataSet[Schema with T1 with T2]] =
    if (df.columns
          .map(e => e.toLowerCase)
          .filter(
            e =>
              e.toLowerCase() == col1._2.toLowerCase || e
                .toLowerCase() == col2._2.toLowerCase
          )
          .length >= 1)
      Some(DataSet[Schema with T1 with T2](df))
    else None
}

object SchemaTypes extends App {

  lazy val spark: SparkSession = SparkSession
    .builder()
    .config(
      new SparkConf()
        .setAppName(
          getClass()
            .getName()
        )
    )
    .getOrCreate()

  import spark.implicits._

  val df = Seq(
    (1, "a", "first value"),
    (2, "b", "second value"),
    (3, "c", "third value")
  ).toDF("Id", "Val", "Comment")

  val myData =
    DataSet/*[Id with Val with Comment]*/(df)
      .validate(IdCol -> "Id", ValCol -> "Val")

  myData match {
    case None => throw new java.lang.Exception("Required columns missing")
    case _    =>
  }
}

mydata的类型为 Option[DataSet[Nothing with T1 with T2]] . 这是有意义的,因为构造函数被称为w/o任何类型参数,但在视频中,它们显示的类型与 DataSet[T1 with T2] .
当然,通过传递显式类型来更改调用需要 Nothing out,但指定类型参数值是多余的,因为类型已经包含在arg列表中。

val myData =
  DataSet[Id with Val with Comment](df).validate(IdCol -> "Id", ValCol -> "Val")
lp0sw83n

lp0sw83n1#

类型 Id 以及 Val 因为有 IdCol 以及 ValCol 内部 .validate . 但是类型 Comment 无法推断。所以试试看

val myData =
  DataSet[Comment](df)
    .validate(IdCol -> "Id", ValCol -> "Val")

println(shapeless.test.showType(SchemaTypes.myData)) 
//Option[App.DataSet[App.Comment with App.Id with App.Val]]

https://scastie.scala-lang.org/yj0hnpkyqfcrekq8zv4d7a
实际上如果你指定 DataSet[Id with Val with Comment](df) 类型将为 Option[DataSet[Id with Val with Comment with Id with Val]] ,等于( =:= )至 Option[DataSet[Id with Val with Comment]] .
好吧,我一直看录像直到那个时候。我猜演讲者试图解释他们的想法(结合f-有界多态性) T <: Col[T] 具有交叉点类型 T with U )你不应该照字面理解他们的幻灯片,可能会有不准确的地方。
首先他们放映幻灯片

case class DataSet[Schema](df: DataFrame) {   
  def validate[T <: Col[T]](
    col: (Col[T], String)
  ): Option[DataSet[Schema with T]] = ??? 
}

这个代码可以用

val myDF: DataFrame = ???
val myData = DataSet[VideoId](myDF).validate(Country -> "country_code")
myData : Option[DataSet[VideoId with Country]]

然后放映幻灯片

val myData = DataSet(myDF).validate(
  VideoId -> "video_id",
  Country -> "country_code",
  ProfileId -> "profile_id",
  Score -> "score"
)

myData : DataSet[VideoId with Country with ProfileId with Score]

但是这个演示代码与上一张幻灯片不符。你应该定义

// actually we don't use Schema here
case class DataSet[Schema](df: DataFrame) {
  def validate[T1 <: Col[T1], T2 <: Col[T2], T3 <: Col[T3], T4 <: Col[T4]](
    col1: (Col[T1], String),
    col2: (Col[T2], String),
    col3: (Col[T3], String),
    col4: (Col[T4], String),
  ): DataSet[T1 with T2 with T3 with T4] = ???
}

所以把它当作一个想法,而不是字面意思。
你可以有类似的东西

case class DataSet[Schema](df: DataFrame) {
  def validate[T <: Col[T]](
    col: (Col[T], String)
  ): Option[DataSet[Schema with T]] = ???
}

val myDF: DataFrame = ???

val myData = DataSet[Any](myDF).validate(VideoId -> "video_id").flatMap(
  _.validate(Country -> "country_code")
).flatMap(
  _.validate(ProfileId -> "profile_id")
).flatMap(
  _.validate(Score -> "score")
)

myData: Option[DataSet[VideoId with Country with ProfileId with Score]]
o8x7eapl

o8x7eapl2#

dmytro mitin的回答很好,但我想提供更多的信息。
如果你写的是 DataSet(df).validate(...) ,的类型参数 DataSet(df) 首先推断。给你 Nothing 因为没有任何信息可以让它成为其他东西。所以 SchemaNothing ,和 Schema with T1 with T2 (出现在 validate )是 Nothing with Id with Val .

相关问题