簡體   English   中英

Scala中的模式匹配與案例類

[英]Pattern matching in Scala with case classes

我正在為遠程存儲設計一個模型,結果是:

sealed trait StorageTag
case object Gcs extends StorageTag
case object S3 extends StorageTag

sealed trait StorageFile[T <: StorageTag]
final case class GcsFile(bucket: String, path: String) extends StorageFile[Gcs.type]
final case class S3File(bucket: String, path: String) extends StorageFile[S3.type]

sealed trait StorageConfig[T <: StorageTag]
final case class GcsConfig(keyPath: String) extends StorageConfig[Gcs.type]
final case class S3Config(keyPath: String) extends StorageConfig[S3.type]

def open[T <: StorageTag](storageFile: StorageFile[T], storageConfig: StorageConfig[T]): OutputStream =
  (storageFile, storageConfig) match {
    case (f: S3File, c: S3Config) => //
    case (f: GcsFile, c: GcsConfig) => //
  }

但Scala編譯器抱怨以下警告:

Warning:(39, 5) match may not be exhaustive.
It would fail on the following inputs: (GcsFile(_, _), S3Config(_)), (S3File(_, _), GcsConfig(_))
    (storageFile, storageConfig) match {

但在我的具體情況下,用GcsConfig打開S3File顯然是沒有意義的,反之亦然。 有沒有辦法增強模型?

我個人不喜歡拋出異常或將其作為MatchError在那些不真實的情況下,例如GcsFileS3Config

您需要向編譯器提供有關允許哪些對的一些信息。 通過將pair storageFile: StorageFile[T], storageConfig: StorageConfig[T]傳遞給open方法,你總是有風險,有人用錯誤的par調用open方法,你將不得不處理異常情況。 為了使其以類型安全的方式工作,您需要傳遞“知道”允許哪些對的預定義類型。

例如這樣:

sealed trait StorageTag
case object Gcs extends StorageTag
case object S3 extends StorageTag

sealed trait StorageFile[T <: StorageTag]
final case class GcsFile(bucket: String, path: String) extends StorageFile[Gcs.type]
final case class S3File(bucket: String, path: String) extends StorageFile[S3.type]

sealed trait StorageConfig[T <: StorageTag]
final case class GcsConfig(keyPath: String) extends StorageConfig[Gcs.type]
final case class S3Config(keyPath: String) extends StorageConfig[S3.type]

sealed trait FileConfPair
case class S3Conf(f: S3File, c: S3Config) extends FileConfPair
case class ScsConf(f: GcsFile, c: GcsConfig) extends FileConfPair

def open[T <: StorageTag](fp: FileConfPair): OutputStream =
  fp match {
    case S3Conf(f: S3File, c: S3Config) => ???
    case ScsConf(f: GcsFile, c: GcsConfig) => ???
  }

Scala編譯器抱怨並且它是正確的,您沒有涵蓋所有可能性。 我認為你有兩個選擇。

1基於泛型類型的模式匹配(如此問題: Scala中泛型類型的模式匹配

def open[T <: StorageTag](storageFile: StorageFile[T], storageConfig: StorageConfig[T]): OutputStream =
  (storageFile, storageConfig) match {
    case x if typeOf[T] <:< typeOf[Gcs]  => //
    case x if typeOf[T] <:< typeOf[S3]   => //
  }

2最簡單的方法是將邏輯提取為2個服務類,作為@Bogdan Vakulenko點

我想建議另一種解決這個問題的方法。
根據我對您的模型的理解,您真正需要的是確定您必須執行正確的open 邏輯的遠程存儲。
因此,您可以提供隱含的證據,例如:

sealed trait StorageTag extends Product with Serializable
implicit case object Gcs extends StorageTag
implicit case object S3 extends StorageTag

sealed trait StorageFile[T <: StorageTag] extends Product with Serializable { 
  def bucket: String
  def path: String
}
final case class GcsFile(bucket: String, path: String) extends StorageFile[Gcs.type]
final case class S3File(bucket: String, path: String) extends StorageFile[S3.type]

sealed trait StorageConfig[T <: StorageTag] extends Product with Serializable {
  def keyPath: String
}
final case class GcsConfig(keyPath: String) extends StorageConfig[Gcs.type]
final case class S3Config(keyPath: String) extends StorageConfig[S3.type]

def open[T <: StorageTag](storageFile: StorageFile[T], storageConfig: StorageConfig[T])
                         (implicit tag: T):String = tag match {
  case S3  =>
    s"S3 -> bucket: '${storageFile.bucket}', path: '${storageFile.path}' | config keyPath: '${storageConfig.keyPath}'"
  case Gcs =>
    s"Gcs -> bucket: '${storageFile.bucket}', path: '${storageFile.path}' | config keyPath: '${storageConfig.keyPath}'"
  }

現在,您可以通過這種方式調用方法

open(S3File(bucket = "bucket", path = "path"), S3Config(keyPath = "keyPath"))
// res0: String = "S3 -> bucket: 'bucket', path: 'path' | config keyPath: 'keyPath'"

open(GcsFile(bucket = "bucket", path = "path"), GcsConfig(keyPath = "keyPath"))
// res1: String = "Gcs -> bucket: 'bucket', path: 'path' | config keyPath: 'keyPath'"

open(S3File(bucket = "bucket", path = "path"), GcsConfig(keyPath = "keyPath"))
// Compile time error!

請注意,只有所有StorageFilesStorageConfigs具有相同的屬性時,此方法才有效。
如果不是這種情況,您可以嘗試以下內容:
但請注意,此代碼並非完全類型安全,可能會被欺騙

sealed trait StorageTag extends Product with Serializable
implicit case object Gcs extends StorageTag
implicit case object S3 extends StorageTag

sealed trait StorageFile[T <: StorageTag] extends Product with Serializable
final case class GcsFile(bucket: String, path: String, id: Int) extends StorageFile[Gcs.type]
final case class S3File(bucket: String, path: String) extends StorageFile[S3.type]

sealed trait StorageConfig[T <: StorageTag] extends Product with Serializable
final case class GcsConfig(keyPath: String, name: String) extends StorageConfig[Gcs.type]
final case class S3Config(keyPath: String) extends StorageConfig[S3.type]

def open[T <: StorageTag](storageFile: StorageFile[T], storageConfig: StorageConfig[T])
                         (implicit tag: T): String = tag match {
  case S3 =>
    // These lines are not checked in compile-time, you can put GcsFile instead, and it will compile and fail at run-time!!!
    val S3File(bucket, path) = storageFile
    val S3Config(keyPath) = storageConfig
    s"S3 -> bucket: '${bucket}', path: '${path}' | config keyPath: '${keyPath}'"
  case Gcs =>
    val GcsFile(bucket, path, id) = storageFile
    val GcsConfig(keyPath, name) = storageConfig
    s"Gcs -> bucket: '${bucket}', path: '${path}', id: $id | config keyPath: '${keyPath}', name: 'name'"
}

open(S3File(bucket = "bucket", path = "path"), S3Config(keyPath = "keyPath"))
// res0: String = "S3 -> bucket: 'bucket', path: 'path' | config keyPath: 'keyPath'"

open(GcsFile(bucket = "bucket", path = "path", id = 0), GcsConfig(keyPath = "keyPath", name = "name"))
// res1: String = "Gcs -> bucket: 'bucket', path: 'path', id: 0 | config keyPath: 'keyPath', name: 'name'"

open(S3File(bucket = "bucket", path = "path"), GcsConfig(keyPath = "keyPath", name = "name"))
// Compile time error!

open(
  GcsFile(bucket = "bucket", path = "path", id = 0).asInstanceOf[StorageFile[StorageTag]],
  GcsConfig(keyPath = "keyPath", name = "name").asInstanceOf[StorageConfig[StorageTag]]
)(S3.asInstanceOf[StorageTag])
// Runtime error!!!!!!!

暫無
暫無

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

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