Scala – 如何“延迟”表达式的编译
我一直想为
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的隐式转换 – 由于上面的评估方式没有帮助, 有没有办法在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是了解有关宏的更多信息的良好资源. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |