当使用相同的谓词进行细化时,如何使用 Scala 的 Refined 库确保类型安全

How to ensure type safety with Scala's Refined library when using the same predicate for refinement

我是 scala 和精炼库的新手,但我正在尝试创建两种基于 UUID 的精炼类型。

为了这样做,我这样做了(注:本例中的Uuid来自eu.timepit.refined.string.Uuid):

type UuidPredicate = Uuid

type UuidA = String Refined UuidPredicate
type UuidB = String Refined UuidPredicate

但是,这似乎只是创建了别名,因此没有类型安全。

所以如果我有一个像 Product(a UuidA, b UuidB) 这样的构造函数 并继续做这样的事情:

val myUuid: UuidA = "9f9ef0c6-b6f8-11ea-b3de-0242ac130004"
val Product = Product(myUuid, myUuid)

它会编译并且 运行 正确。无论如何要确保不是这种情况?如果一个变量被创建为一种类型,我怎样才能让它只用作特定的细化类型,即使这些类型基本相同?

最简单的就是引入不同的数据类型

case class UuidA(value: String Refined UuidPredicate)
case class UuidB(value: String Refined UuidPredicate)

你不能让 UuidAUuidB 扩展 AnyVal 因为 Refined 已经扩展了 AnyVal 并且 Scala 不允许嵌套值 类.

如果您希望避免使用 UuidAUuidB 包装的运行时开销,您可以尝试 @newtype 作为 @LuisMiguelMejíaSuárez 建议

import io.estatico.newtype.macros.newtype

@newtype case class UuidA(value: String Refined UuidPredicate)
@newtype case class UuidB(value: String Refined UuidPredicate)

或尝试添加更多标签

import eu.timepit.refined.api.Refined
import eu.timepit.refined.string.Uuid
import eu.timepit.refined.auto._
import shapeless.tag
import shapeless.tag.@@

type UuidPredicate = Uuid
type UuidString = Refined[String, UuidPredicate]
type TagA
type TagB
type UuidA = UuidString @@ TagA
type UuidB = UuidString @@ TagB

case class Product(a: UuidA, b: UuidB)
val myUuid: UuidA = tag[TagA][UuidString]("9f9ef0c6-b6f8-11ea-b3de-0242ac130004")
//  val product = Product(myUuid, myUuid) // doesn't compile
val myUuid1: UuidB = tag[TagB][UuidString]("9f9ef0c6-b6f8-11ea-b3de-0242ac130004")
val product1 = Product(myUuid, myUuid1) // compiles