当使用相同的谓词进行细化时,如何使用 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)
你不能让 UuidA
、UuidB
扩展 AnyVal
因为 Refined
已经扩展了 AnyVal
并且 Scala 不允许嵌套值 类.
如果您希望避免使用 UuidA
、UuidB
包装的运行时开销,您可以尝试 @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
我是 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)
你不能让 UuidA
、UuidB
扩展 AnyVal
因为 Refined
已经扩展了 AnyVal
并且 Scala 不允许嵌套值 类.
如果您希望避免使用 UuidA
、UuidB
包装的运行时开销,您可以尝试 @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