簡體   English   中英

如何以未來的方式用Scala“pimp my library”?

[英]How can I “pimp my library” with Scala in a future-proof way?

我使用Scala隱式類來擴展我經常使用的對象。 作為一個例子,我有一個類似於Spark DataFrame上定義的方法:

implicit class DataFrameExtensions(df: DataFrame) {
  def deduplicate: Boolean = 
    df.groupBy(df.columns.map(col): _*).count
}

但是如果類已經定義了相同的方法,則不會調用隱式defs。 如果我以后升級到定義DataFrame#deduplicate方法的Spark的新版本會發生什么? 客戶端代碼將靜默切換到新的實現,這可能會導致細微的錯誤(或明顯的錯誤,這些問題較少)。

使用反射,如果DataFrame在我的隱式定義之前已經定義了deduplicate ,我可以拋出運行時錯誤。 從理論上講,如果我的隱式方法與現有方法沖突,我可以檢測它並重命名我的隱式版本。 但是,一旦我升級Spark,運行應用程序並檢測問題,使用IDE重命名舊方法為時已晚,因為對df.deduplicate任何引用現在都引用了本機Spark版本。 我將不得不恢復我的Spark版本,通過IDE重命名該方法,然后再次升級。 不是世界末日,而是一個偉大的工作流程。

有沒有更好的方法來處理這種情況? 如何安全地使用“皮條客我的圖書館”模式?

您可以添加一個測試,確保某些代碼段不會編譯DataFrameExtension的測試套件中。 也許是這樣的:

"(???: DataFrame).deduplicate" shouldNot compile

如果它在沒有隱式轉換的情況下編譯,則意味着Spark庫引入了方法deduplicate 在這種情況下,測試失敗,您知道必須更新您的implicits。

如果導入啟用了擴展方法,請使用-Xlint顯示不再使用導入:

//class C
class C { def x = 17 }

trait T {
  import Extras._
  def f = new C().x
}

object Extras {
  implicit class X(val c: C) {
    def x = 42
  }
}

另一種觀點,必須在-Xlint -Xfatal-warnings下使用證據:

//class C[A]
class C[A] { def x = 17 }

trait T {
  import Mine.ev
  val c = new C[Mine]
  def f = c.x
}

trait Mine
object Mine {
  implicit class X[A](val c: C[A]) {
    def x(implicit @deprecated("unused","") ev: Mine) = 42
  }
  implicit val ev: Mine = null
}

object Test {
  def main(args: Array[String]): Unit = println {
    val t = new T {}
    t.f
  }
}

安全地執行此操作的解決方案是明確要求擴展數據框,以最小化影響,您可以使用隱式轉換語法(如toJava / toScala等):

implicit class DataFrameExtSyntax(df: DataFrame) { 
 def toExtended: DataFrameExtensions = DataFrameExtensions(df)
}

然后你的調用將看起來:

myDf.asExtended
  .deduplicate
  .someOtherExtensionMethod
  .andMore

這樣你就可以在沒有運行時檢查/ linting /單元測試技巧的情況下對未來的擴展方法進行驗證(你甚至可以使用myDf.ext因為myDf.toExtended太長了:))

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM