scala - Scala 中的类型细化但不使用细化

标签 scala macros scala-macros refinement-type refined

我正在尝试创建一个基于 String 的 HexString 类型,它应该满足“它仅包含十六进制数字”的条件,并且如果可能的话,我想让编译器为我进行类型检查。

一个明显的解决方案是使用 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 代码编写的最好的东西是带有智能构造函数的值类,这一切都很好,对我来说感觉很轻量级,只是我无法进行编译时类型检查。目前看起来像这样:

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 元编写的 ADT 遍历和转换程序之外,我对 Scala 宏没有真正的经验。

最佳答案

如果您希望 fromStringLiteral 在编译时工作,您可以将其设置为 macro (参见 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

关于scala - Scala 中的类型细化但不使用细化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61886817/

相关文章:

scala - scala 隐式宏应该返回什么来告诉编译器 "forget my result, continue your search"

scala - 在 Scala 宏中使用 LabelDef (2.10)

scala - 如何在 Dockerfile 中为 sbt 插件添加自定义命令

scala - 无法理解 Scala 中的类型错误

macros - 如何在 ImageJ 中裁剪堆栈而不先复制

c++ - 如何在 C 或 C++ 宏中将函数用作文字字符串

斯卡拉宏 : Add function to class

scala - Scala是否有更好的表达“ self 递归泛型类型”的方法?

scala - akka流http速率限制

c - 在子宏中替换父标记