加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

Scala – 如何“延迟”表达式的编译

发布时间:2020-12-16 09:55:11 所属栏目:安全 来源:网络整理
导读:我一直想为 Scala实现一个链式比较运算符,但经过几次尝试后,我认为没有办法实现它.这是它应该如何工作: val a = 31 a 5 //yields true3 a 5 //yields false 问题是,scala编译器在计算表达式时非常贪婪,因此上面的表达式计算如下: 1 a //yields truetrue 5
我一直想为 Scala实现一个链式比较运算符,但经过几次尝试后,我认为没有办法实现它.这是它应该如何工作:

val a = 3
1 < a < 5 //yields true
3 < a < 5 //yields false

问题是,scala编译器在计算表达式时非常贪婪,因此上面的表达式计算如下:

1 < a    //yields true
true < 5 //compilation error

我试图编写代码以某种方式实现它,这是我尝试过的:

>从Int类型到我的类型RichComparisonInt的隐式转换 – 由于上面的评估方式没有帮助,
>用我的类覆盖Int类 – 无法完成,因为Int既是抽象的又是最终的,
>我尝试创建名为<的case类,就像::,但后来我发现,这个类只是为了模式匹配而创建的,
>我想从=>创建隐式转换.布尔值,它可以在编译级别上工作,但是没有办法提取操作的参数,从而导致布尔结果.

有没有办法在Scala中做到这一点?也许宏可以完成这项工作?

解决方法

这是一个使用宏的解决方案.这里的一般方法是丰富布尔值,使其具有一个宏方法,该方法查看上下文的前缀以查找用于生成该布尔值的比较.

例如,假设我们有:

implicit class RichBooleanComparison(val x: Boolean) extends AnyVal {
  def <(rightConstant: Int): Boolean = macro Compare.ltImpl
}

和方法头的宏定义:

def ltImpl(c: Context)(rightConstant: c.Expr[Int]): c.Expr[Boolean]

现在假设编译器正在解析表达式1< 2<我们显然可以使用c.prefix来获得表达式1< 2在评估宏方法体时.但是,constant folding的概念阻止我们这样做.常量折叠是编译器在编译时计算预定常量的过程.因此,在评估宏的时候,在这种情况下,c.prefix已经被折叠为正确.我们失去了1< 2表达导致真实.您可以在this issue和this question上阅读有关常量折叠及其与Scala宏的交互的更多信息.

如果我们可以将讨论的范围限制为只有表格C1< x< C2,其中C1和C2是常量,x是变量,那么这变得可行,因为这种类型的表达式不会受到常量折叠的影响.这是一个实现:

object Compare {
  def ltImpl(c: Context)(rightConstant: c.Expr[Int]): c.Expr[Boolean] = {
    import c.universe._
    c.prefix.tree match {
      case Apply(_,Apply(Select(lhs@Literal(Constant(_)),_),(x@Select(_,TermName(_))) :: Nil) :: Nil) => 
          val leftConstant = c.Expr[Int](lhs)
          val variable = c.Expr[Int](x)
          reify((leftConstant.splice < variable.splice) && (variable.splice < rightConstant.splice))

      case _ => c.abort(c.enclosingPosition,s"Invalid format.  Must have format c1<x<c2,where c1 and c2 are constants,and x is variable.")
    }
  } 
}

在这里,我们将上下文前缀与预期类型匹配,提取相关部分(lhs和x),使用c.Expr [Int]构造新的子树,并使用reify和splice构造一个新的完整表达式树以制作所需的3-方式比较.如果与期望的类型不匹配,则无法编译.

这允许我们这样做:

val x = 5
1 < x < 5 //true
6 < x < 7 //false
3 < x < 4 //false

如预期的!

docs about macros,trees和this presentation是了解有关宏的更多信息的良好资源.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读