【问题标题】:Scala typeclass not working for parent typeScala类型类不适用于父类型
【发布时间】:2015-11-07 00:07:48
【问题描述】:

我有以下类型:

trait Loader[A, B, C] {
  //Any Spark loader requires
  // A -> Input Type
  // B -> Output Type
  // C -> some type of implicit context provided by the compiler from 
  //      the sourounding environment.
  def load(input: A)(implicit context: C): B
}

object Loader {
  implicit object HiveLoader extends Loader[HiveTableSource, DataFrame, HiveContext] {
    def load(source: HiveTableSource)(implicit hc: HiveContext): DataFrame = {
      val db = source.db
      val tbl = source.tbl
      val df =  hc.sql(s"select * from $db.$tbl")
      df
    }
  }

  def loadDataSource[A,B,C](d: A)(implicit ldr: Loader[A,B,C], context: C):B = 
    ldr.load(d)
}

sealed trait DataSource
case class HiveTableSource(db: String, tbl: String) extends DataSource

当我尝试以下操作时,代码无法通过“找不到隐式参数 ldr”进行编译

c // this is of type DataSource

import Loader._ 

loadDataSource(c) //This Fails 

但是,如果我明确地强制类型

LoadDataSource(c.asInstanceof[HiveTableSource]) The code compiles.

【问题讨论】:

  • 这有什么好惊讶的? cDataSource 类型;范围内没有Loader[DataSource, ?, ?] 的隐式实例;因此,loadDataSource(c) 无法编译。对我来说似乎合乎逻辑。
  • 在运行时 c 是一个 HiveTableSource,如果添加更多源,则键入为 DataSource。我正在尝试让类型类正确推断子类型,因此我不必使用 asInstanceOf 显式键入
  • c 的运行时类型在这里无关紧要。如果您想将它多态地视为DataSource,那么您必须在范围内拥有Load[DataSource, ?, ?]。首先,使用asInstanceOf 否定了将其键入为DataSource 的好处,并且通常是一种避免的方法。
  • DataSource 是通用的,没有具体的实现。我有子类型的类型类,但不知道如何让 scala 在 'c' 类型为 DataSource 时选择正确的子类型,以便它可以是多态的。
  • 这就是我要说的——你不能。隐式参数在编译时选择 - 因此它们必须与 c 的编译时类型匹配,而不是其运行时类型。

标签: scala typeclass


【解决方案1】:

使用asInstanceOf 是灾难的秘诀。请参阅"The Scalazzi Safe Scala Subset"

但是,如果您将 DataSource 视为 ADT(代数数据类型),那么您可以使用模式匹配来解决这个问题,但您必须自己选择类型类的实例。

为此,必须有一组限制性的可能数据源(就像Option[A] 仅限于Some[A]None)。我看到你已经密封了你的 DataSource 特征,所以你应该没问题。

sealed trait DataSource
final case class HiveTableSource(db: String, tbl: String) extends DataSource
final case class SomeOtherSource() extends DataSource

val c: DataSource = ???

c match {
  case s: HiveTableSource => loadDataSource(s)(HiveLoader)
  case s: SomeOtherSource => loadDataSource(s)(SomeOtherLoader)
}

【讨论】:

  • 谢谢,这正是我认为我需要做的。巨大的肝病。在这种情况下,使用类型类是正确的设计模式吗?
  • @user2726995 没问题。我不能确定这是否是最好的方法,我需要全局。但我看不出有什么明显的问题。
  • 实际上,模式匹配允许 s 成为类型化,并且无需显式传递即可正确计算出隐式。
猜你喜欢
  • 2020-07-20
  • 2021-04-14
  • 2011-11-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多