gpt4 book ai didi

scala - 使用宏制作案例类

转载 作者:行者123 更新时间:2023-12-02 21:11:05 26 4
gpt4 key购买 nike

给定以下宏(感谢@TravisBrown为此help):

JetDim.scala

case class JetDim(dimension: Int) {
require(dimension > 0)
}

object JetDim {
def validate(dimension: Int): Int = macro JetDimMacro.apply
def build(dimension: Int): JetDim = JetDim(validate(dimension))
}

JetDimMacro.scala

import reflect.macros.Context

object JetDimMacro {

sealed trait PosIntCheckResult
case class LteqZero(x: Int) extends PosIntCheckResult
case object NotConstant extends PosIntCheckResult

def apply(c: Context)(dimension: c.Expr[Int]): c.Expr[Int] = {

import c.universe._

getInt(c)(dimension) match {
case Right(_) => reify { dimension.splice }
case Left(LteqZero(x)) => c.abort(c.enclosingPosition, s"$x must be > 0.")
case Left(NotConstant) => reify { dimension.splice }
}
}

def getInt(c: Context)(dimension: c.Expr[Int]): Either[PosIntCheckResult, Int] = {

import c.universe._

dimension.tree match {
case Literal(Constant(x: Int)) => if (x > 0) Right(x) else Left(LteqZero(x))
case _ => Left(NotConstant)
}
}
}

它可以从 REPL 运行:

scala> import spire.math.JetDim
import spire.math.JetDim

scala> JetDim.validate(-55)
<console>:9: error: -55 must be > 0.
JetDim.validate(-55)
^

scala> JetDim.validate(100)
res1: Int = 100

但是,我想将此编译时检查(通过 JetDimMacro )构建到案例类的 apply 中。方法。

尝试 1

case class JetDim(dimension: Int) {
require(dimension > 0)
}

object JetDim {
private def validate(dimension: Int): Int = macro JetDimMacro.apply
def build(dimension: Int): JetDim = JetDim(validate(dimension))
}

但是失败了:

scala> import spire.math.JetDim
import spire.math.JetDim

scala> JetDim.build(-55)
java.lang.IllegalArgumentException: requirement failed
at scala.Predef$.require(Predef.scala:207)
at spire.math.JetDim.<init>(Jet.scala:21)
at spire.math.JetDim$.build(Jet.scala:26)
... 43 elided

尝试 2

class JetDim(dim: Int) {
require(dim > 0)

def dimension: Int = dim
}

object JetDim {
private def validate(dimension: Int): Int = macro JetDimMacro.apply
def apply(dimension: Int): JetDim = {
validate(dimension)
new JetDim(dimension)
}
}

但这也失败了:

scala> import spire.math.JetDim
import spire.math.JetDim

scala> JetDim(555)
res0: spire.math.JetDim = spire.math.JetDim@4b56f205

scala> JetDim(-555)
java.lang.IllegalArgumentException: requirement failed
at scala.Predef$.require(Predef.scala:207)
at spire.math.JetDim.<init>(Jet.scala:21)
at spire.math.JetDim$.apply(Jet.scala:30)
... 43 elided

我想修改JetDimMacro#apply返回 JetDim而不是Int 。然而,JetDim住在 core项目,据我所知,取决于 macros项目(JetDimMacro所在的地方)。

我该如何使用这个validate方法来自JetDim的伴生对象在编译时检查正整数?

最佳答案

问题是,当我们在 apply 中调用 validate 时,我们不再处理常量(单一类型)。因此,validate 得到一个非常量的 Int。

作为替代方案,您可以尝试对正整数使用隐式见证,然后 JetDim 将其作为构造函数。例如,类似:

package com.example

case class JetDim(n: PositiveInt)

case class PositiveInt(value: Int) {
require(value > 0)
}

然后,我们添加一个来自 Int => PositiveInt 的隐式(宏)转换来执行检查。

import scala.language.experimental.macros

import scala.reflect.macros.blackbox.Context

object PositiveInt {
implicit def wrapConstantInt(n: Int): PositiveInt = macro verifyPositiveInt

def verifyPositiveInt(c: Context)(n: c.Expr[Int]): c.Expr[PositiveInt] = {
import c.universe._

val tree = n.tree match {
case Literal(Constant(x: Int)) if x > 0 =>
q"_root_.com.example.PositiveInt($n)"
case Literal(Constant(x: Int)) =>
c.abort(c.enclosingPosition, s"$x <= 0")
case x =>
c.abort(c.enclosingPosition, s"cannot verify $x > 0")
}
c.Expr(tree)
}
}

然后,您可以使用 JetDim(12),这将通过,或者使用 JetDim(-12),这将失败(该宏将 Int 扩展为 PositiveInt) .

关于scala - 使用宏制作案例类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29949563/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com