【问题标题】:Scala function body definition help wanted需要 Scala 函数体定义帮助
【发布时间】:2021-04-29 13:14:52
【问题描述】:

我正在尝试使用以下语法制作 scala dsl:

val a = Agent() setup { agent => // agent reference.
  agent add "Hello World!"
}
a add "Not allowed" // Atm this is allowed.

我可以在 setup 函数体之外调用方法 add 并调用 add 方法我必须在 setup 函数体的开头写 agent =>

我所做的atm是一个特质代理:

trait Agent {

  def setup(f: Agent => Unit): Agent

  def add(s: String): Agent
}

case class AgentImpl(strings: Seq[String]) extends Agent {
  override def setup(f: Agent => Unit): Agent = {
    f(this)
    this
  }

  override def add(s: String): Agent = copy(strings = strings:+s)
}

object Agent {
  def apply(): Agent = AgentImpl(Seq.empty)
}

我要做的是:

val a = Agent() setup { // No more references to agent
    add "Hello World!"
  }
  a add "Not allowed" // This mustn't be allowed. Compilation error.

我没有在设置函数主体的开头使用agent => 引用代理,如果我尝试在设置函数主体之外进行添加,则会出现错误。

这在 Scala 中可行吗?

我可以更改代码的每一部分,添加任意数量的特征/类/对象和其他内容,但不能更改我的 DSL 的语法。

【问题讨论】:

    标签: scala functional-programming higher-order-functions


    【解决方案1】:

    你可以这样做:

    sealed trait Agent {
      def getFoos: List[String]
      def getBars: List[Int]
    }
    
    object Agent {
      import scala.collection.mutable.ListBuffer
      
      private final class Builder {
        private[this] val foos: ListBuffer[String] = ListBuffer.empty
        private[this] val bars: ListBuffer[Int] = ListBuffer.empty
        
        private def addFoo(foo: String): Unit = {
          foos += foo
        }
        
        private def addBar(bar: Int): Unit = {
          bars += bar
        }
        
        private[Agent] def result(): Agent = new Agent {
          override final val getFoos: List[String] =
            foos.result()
          
          override final val getBars: List[Int] =
            bars.result()
        }
      }
      
      private[Agent] type BuilderStep = Builder => Unit
      
      object Builder {
        private[Agent] def create() = new Builder()
        
        object Steps {
          def addFoo(foo: String): BuilderStep =
            _.addFoo(foo)
          
          def addBar(bar: Int): BuilderStep =
            _.addBar(bar)
        }
      }
      
      def setup(steps: BuilderStep*): Agent = {
        val builder = Builder.create()
        steps.foreach(s => s(builder))
        builder.result()
      }
    }
    

    可以这样使用:

    import Agent.Builder.Steps._
    
    val agent = Agent.setup(
      addFoo("A"),
      addFoo("B"),
      addBar(1),
      addBar(2),
      addFoo("C"),
      addBar(3),
      addFoo("D")
    )
    

    但也许传统的不可变构建器会同样好且更简单。


    代码运行here

    【讨论】:

    • 嗨@Luis,谢谢。这对我有很大帮助。有一个技巧可以避免在 add 调用之间重复逗号或无法避免它们?
    • 抱歉@Luis 提出另一个请求:使用您的解决方案,我仍然可以在 setup 函数之外调用 addBar 和 addFoo ,并且通常在 setup 函数之外调用 BuilderStep 实例。有没有可能否认这一点?
    • 是的,你可以打电话给他们,但他们什么也没做。不是,如果不使用宏等极端功能,就不可能使某些东西仅在单个功能块中可用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-13
    • 2019-06-12
    相关资源
    最近更新 更多