在 Scala 中进行类型优化但不使用精化
Type refinements in Scala but without using refined
我正在尝试创建一个基于 String 的 HexString 类型,它应该满足条件 "that it contains only hexadecimal digits",如果可能的话,我想让编译器为我检查类型。
一个明显的解决方案是使用 refined 并编写如下内容:
type HexString = String Refined MatchesRegex[W.`"""^(([0-9a-f]+)|([0-9A-F]+))$"""`.T]
refineMV[MatchesRegex[W.`"""^(([0-9a-f]+)|([0-9A-F]+))$"""`.T]]("AF0")
现在,我并不反对精炼,只是我发现它对我正在尝试做的事情有点矫枉过正(并且根本不知道我是否会在其他地方使用它)而且我我不愿意导入一个我不确定总体上会使用超过一两次的库,并带来可能看起来像魔法的语法(如果不是对我来说,对团队中的其他开发人员来说)。
另一方面,我能用纯 Scala 代码编写的最好的是带有智能构造函数的值 class,这一切都很好,对我来说感觉很轻,只是我不能在编译时进行类型检查。目前看起来像这样:
final case class HexString private (str: String) extends AnyVal {
// ...
}
object HexString {
def fromStringLiteral(literal: String): HexString = {
def isValid(str: String): Boolean = "\p{XDigit}+".r.pattern.matcher(str).matches
if (isValid(literal)) HexString(literal)
else throw new IllegalArgumentException("Not a valid hexadecimal string")
}
}
对于大部分代码库,运行时检查就足够了;但是,我可能需要在某些时候进行编译时检查,而且除了使用精炼之外似乎没有办法实现它。
如果我能让代码尽可能本地化和易于理解,而不引入太多魔法,是否可以使用宏并指示编译器针对正则表达式测试赋值的 RHS,并取决于它是否匹配与否,它会创建 HexString 的实例或吐出编译器错误?
val ex1: HexString = "AF0" // HexString("AF0")
val ex2: HexString = "Hello World" // doesn't compile
除了我使用 Scala meta 编写的 ADT 遍历和转换程序外,我对 Scala 宏没有真正的经验。
如果你想让fromStringLiteral
在编译时工作,你可以把它变成macro (see sbt settings)
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
def fromStringLiteral(literal: String): HexString = macro fromStringLiteralImpl
def fromStringLiteralImpl(c: blackbox.Context)(literal: c.Tree): c.Tree = {
import c.universe._
val literalStr = literal match {
case q"${s: String}" => s
case _ => c.abort(c.enclosingPosition, s"$literal is not a string literal")
}
if (isValid(literalStr)) q"HexString($literal)"
else c.abort(c.enclosingPosition, s"$literalStr is not a valid hexadecimal string")
}
然后
val ex1: HexString = HexString.fromStringLiteral("AF0") // HexString("AF0")
//val ex2: HexString = HexString.fromStringLiteral("Hello World") // doesn't compile
如果你想让它像
那样工作
import HexString._
val ex1: HexString = "AF0" // HexString("AF0")
//val ex2: HexString = "Hello World" // doesn't compile
然后您还可以使 fromStringLiteral
成为隐式转换
implicit def fromStringLiteral(literal: String): HexString = macro fromStringLiteralImpl
我正在尝试创建一个基于 String 的 HexString 类型,它应该满足条件 "that it contains only hexadecimal digits",如果可能的话,我想让编译器为我检查类型。
一个明显的解决方案是使用 refined 并编写如下内容:
type HexString = String Refined MatchesRegex[W.`"""^(([0-9a-f]+)|([0-9A-F]+))$"""`.T]
refineMV[MatchesRegex[W.`"""^(([0-9a-f]+)|([0-9A-F]+))$"""`.T]]("AF0")
现在,我并不反对精炼,只是我发现它对我正在尝试做的事情有点矫枉过正(并且根本不知道我是否会在其他地方使用它)而且我我不愿意导入一个我不确定总体上会使用超过一两次的库,并带来可能看起来像魔法的语法(如果不是对我来说,对团队中的其他开发人员来说)。
另一方面,我能用纯 Scala 代码编写的最好的是带有智能构造函数的值 class,这一切都很好,对我来说感觉很轻,只是我不能在编译时进行类型检查。目前看起来像这样:
final case class HexString private (str: String) extends AnyVal {
// ...
}
object HexString {
def fromStringLiteral(literal: String): HexString = {
def isValid(str: String): Boolean = "\p{XDigit}+".r.pattern.matcher(str).matches
if (isValid(literal)) HexString(literal)
else throw new IllegalArgumentException("Not a valid hexadecimal string")
}
}
对于大部分代码库,运行时检查就足够了;但是,我可能需要在某些时候进行编译时检查,而且除了使用精炼之外似乎没有办法实现它。
如果我能让代码尽可能本地化和易于理解,而不引入太多魔法,是否可以使用宏并指示编译器针对正则表达式测试赋值的 RHS,并取决于它是否匹配与否,它会创建 HexString 的实例或吐出编译器错误?
val ex1: HexString = "AF0" // HexString("AF0")
val ex2: HexString = "Hello World" // doesn't compile
除了我使用 Scala meta 编写的 ADT 遍历和转换程序外,我对 Scala 宏没有真正的经验。
如果你想让fromStringLiteral
在编译时工作,你可以把它变成macro (see sbt settings)
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
def fromStringLiteral(literal: String): HexString = macro fromStringLiteralImpl
def fromStringLiteralImpl(c: blackbox.Context)(literal: c.Tree): c.Tree = {
import c.universe._
val literalStr = literal match {
case q"${s: String}" => s
case _ => c.abort(c.enclosingPosition, s"$literal is not a string literal")
}
if (isValid(literalStr)) q"HexString($literal)"
else c.abort(c.enclosingPosition, s"$literalStr is not a valid hexadecimal string")
}
然后
val ex1: HexString = HexString.fromStringLiteral("AF0") // HexString("AF0")
//val ex2: HexString = HexString.fromStringLiteral("Hello World") // doesn't compile
如果你想让它像
那样工作import HexString._
val ex1: HexString = "AF0" // HexString("AF0")
//val ex2: HexString = "Hello World" // doesn't compile
然后您还可以使 fromStringLiteral
成为隐式转换
implicit def fromStringLiteral(literal: String): HexString = macro fromStringLiteralImpl