用于链接多个命令的Scala DSL“和”关键字

时间:2018-04-19 23:05:14

标签: scala dsl

假设我有两个相同类型的对象AB,方法doXdoY,两者都返回布尔值。

是否可以创建一个语言结构,例如andor,以执行以下操作?

  

doX和doY B

通常上述内容将通过

完成
  

doX B&&一个doY B

我想知道这是否可以缩短如上所述

我希望and能够针对多种方法进行推广,而不仅仅是doA,所以我不能单独使用隐式方法。

2 个答案:

答案 0 :(得分:2)

事实证明,语法可能并非完全不可能,但这仍然非常难看。特别需要对A课程进行一些重大修改。但这是一个概念证明:

case class Bar(s: String)

trait PredicateArgumentContext[-T] {
  type Result
  def apply(f: Foo, p: Bar => Boolean, arg: T): Result
}

implicit val ApplyPredicateArgumentContext = new PredicateArgumentContext[Bar] {
  type Result = Boolean
  def apply(f: Foo, p: Bar => Boolean, arg: Bar): Boolean = p(arg)
}

sealed trait PredicateOperator

object and extends PredicateOperator
object or extends PredicateOperator

implicit val OperatorPredicateArgumentContext = new PredicateArgumentContext[PredicateOperator] {
  type Result = Foo
  def apply(f: Foo, p: Bar => Boolean, arg: PredicateOperator): Foo = arg match {
    case `and` => f.copy(predTransform = (p1 => b => p(b) && p1(b) ))
    case `or` => f.copy(predTransform = (p1 => b => p(b) || p1(b) ))
  }
}

case class Foo(predTransform: (Bar => Boolean) => (Bar => Boolean) = identity) {

  def doX[A](a: A)(implicit ctx: PredicateArgumentContext[A]): ctx.Result = {
    ctx.apply(this, predTransform(doXInternal _), a)
  }
  def doY[A](a: A)(implicit ctx: PredicateArgumentContext[A]): ctx.Result = {
    ctx.apply(this, predTransform(doYInternal _), a)
  }

  def doXInternal(b: Bar): Boolean = {
    b.s.size % 2 == 0
  }

  def doYInternal(b: Bar): Boolean = {
    b.s.size > 0
  }
}

val A = Foo()
val B = new Bar("asdf")

A doX and doY B // returns true

他们的关键洞察力是让scala将其解析为A.doX(and).doY(B),并且如果传递了运算符doX或{{doYand方法,则返回不同的类型1}})或传递实际参数(or)。

答案 1 :(得分:1)

Scala解析器翻译

A doX and doY B

A.doX(and).doY(B)

在考虑任何定义,类型信息等之前,您不能以任何方式影响此翻译。

但是,如果您定义and,则A.doX(and)将返回Boolean(如果它已编译)并且不会有doY方法。您可以使用隐式转换进行类型检查

implicit def wrap(x: Boolean): A.type = A

但这仍然无法获得理想的结果。

我不认为让and宏也有帮助,但我可能会忽视那些东西。