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
显然是没有意义的,反之亦然。有没有办法增强模型?
我个人不喜欢在 GcsFile
和 S3Config
等不真实的情况下抛出异常或将其保留为 MatchError
的想法。
您需要向编译器提供一些有关允许哪些对的信息。通过将 storageFile: StorageFile[T], storageConfig: StorageConfig[T]
对传递给 open
方法,您总是面临有人使用错误的 par 调用 open
方法的风险,您将不得不处理例外情况。为了使其以类型安全的方式工作,您需要传递 "knows" 允许的预定义类型。
例如像这样:
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 编译器抱怨,这是对的,您没有涵盖所有可能性。我认为你有 2 个选择。
1 基于泛型的模式匹配(如本题:Pattern matching on generic type in 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
logic.
因此,您可以只提供隐含的证据,例如:
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!
请注意,此方法仅在所有 StorageFiles
和 StorageConfigs
具有相同属性时才有效。
如果不是这种情况,您可以尝试以下操作:
但是请注意,这段代码不是完全类型安全的,可能会被欺骗
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!!!!!!!
我正在为远程存储设计一个模型,结果是:
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
显然是没有意义的,反之亦然。有没有办法增强模型?
我个人不喜欢在 GcsFile
和 S3Config
等不真实的情况下抛出异常或将其保留为 MatchError
的想法。
您需要向编译器提供一些有关允许哪些对的信息。通过将 storageFile: StorageFile[T], storageConfig: StorageConfig[T]
对传递给 open
方法,您总是面临有人使用错误的 par 调用 open
方法的风险,您将不得不处理例外情况。为了使其以类型安全的方式工作,您需要传递 "knows" 允许的预定义类型。
例如像这样:
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 编译器抱怨,这是对的,您没有涵盖所有可能性。我认为你有 2 个选择。
1 基于泛型的模式匹配(如本题:Pattern matching on generic type in 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
logic.
因此,您可以只提供隐含的证据,例如:
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!
请注意,此方法仅在所有 StorageFiles
和 StorageConfigs
具有相同属性时才有效。
如果不是这种情况,您可以尝试以下操作:
但是请注意,这段代码不是完全类型安全的,可能会被欺骗
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!!!!!!!