【问题标题】:How should I read this piece of Scala (Play) code?我应该如何阅读这段 Scala (Play) 代码?
【发布时间】:2014-02-20 01:02:16
【问题描述】:

我是 Scala 的新手,正在通过查看一些 Play 代码来学习它。我已经很好地阅读了 Scala 的主要概念,并且在完成了一些 Haskell 和 ML 之后对函数式编程感到满意。

仅在语法和编程范例的层面上,我真的很难阅读这段代码。我了解代码应该做什么,但不了解它是如何做到的,因为我无法弄清楚语法。

  // -- Home page
def index(ref: Option[String]): Action[AnyContent] = Prismic.action(ref) { implicit request =>
for {
  someDocuments <- ctx.api.forms("everything").ref(ctx.ref).submit()
} yield {
    Ok(views.html.index(someDocuments))
  }
}

(Prismic 是一个独立于 Play 的 API,并不真正相关)。我将如何通过电话向其他开发人员描述此功能(或者它是一种方法??):换句话说,使用英语。例如在这段代码中:

def add(a: Int, b: Int): Int = a + b

我会说“add 是一个函数,它接受两个整数,将它们相加并将结果作为另一个整数返回”。

在上面的播放代码中,我什至不知道如何在“索引是一个函数,它接受一个字符串的选项并通过.....返回一个 AnyContent 类型的动作”之后如何描述它。

'=' 后面的位,然后是花括号和 '=>' 吓到我了!我如何阅读它们?是函数式还是OO?

感谢您的帮助

【问题讨论】:

    标签: scala playframework


    【解决方案1】:

    让我们把它简化为:

    def index(ref: Option[String]): Action[AnyContent] = Prismic.action(ref)(function)
    

    这样更好,不是吗? index 是从StringOptionAnyContentAction 的一个函数(一个字),它调用对象Prismicaction 方法传递两个咖喱参数:refindex 收到的参数,以及 function(待描述)。

    那么让我们分解一下匿名函数:

    { implicit request =>
      for {
        someDocuments <- ctx.api.forms("everything").ref(ctx.ref).submit()
      } yield {
        Ok(views.html.index(someDocuments))
      }
    }
    

    首先,它使用{} 而不是(),因为如果() 是单个参数(有两个参数列表,但每个列表都有一个参数),Scala 允许将其作为参数分隔符删除,并且该参数包含在{}中。

    那么,{} 呢?嗯,它是一个包含声明和语句的表达式,新行带有分号推断,其值是最后一条语句的值。也就是这两个表达式的值是一样的,3:

    { 1; 2; 3 }
    {
      1
      2
      3
    }
    

    当传递一个超过一行的函数时,使用{} 是一种语法约定,即使在本例中,该函数可以只用括号传递。

    接下来令人困惑的是implicit request =&gt;,让我们选择更简单的:

    x => x * 2
    

    这很容易,对吧?它接受一个参数x,并返回x * 2。在我们的例子中,它是相同的:函数接受一个参数,request,并返回:

      for (someDocuments <- somethingSomething())
      yield Ok(views.html.index(someDocuments))
    

    也就是说,它调用一些方法,遍历结果,并将这些结果映射到一个新值。这与 Haskell 的 do 表示法非常接近。您可以像下面这样重写它(为了便于阅读,我将其分成多行):

      ctx
        .api
        .forms("everything")
        .ref(ctx.ref)
        .submit()
        .map(someDocuments => Ok(views.html.index(someDocuments)))
    

    所以,回到我们的方法定义,我们有这个:

    def index(ref: Option[String]): Action[AnyContent] = Prismic.action(ref)(
      implicit request =>
      ctx
        .api
        .forms("everything")
        .ref(ctx.ref)
        .submit()
        .map(someDocuments => Ok(views.html.index(someDocuments)))
    )
    

    这里唯一剩下的问题是implicit 是关于什么的。基本上,它使该参数在函数范围内隐式可用。据推测,这些方法调用中至少有一个需要由request 正确分配的隐式参数。如果我知道这些方法中的哪一个需要它,我可以将implicit 明确地放在那里传递request,但由于我不知道,所以我将跳过它。

    另一种写法是:

    def index(ref: Option[String]): Action[AnyContent] = Prismic.action(ref)({
      request =>
      implicit val req = request
      ctx
        .api
        .forms("everything")
        .ref(ctx.ref)
        .submit()
        .map(someDocuments => Ok(views.html.index(someDocuments)))
    })
    

    我在这里添加了{},因为我在函数体中添加了一个声明,尽管我决定不删除括号,我可以这样做。

    【讨论】:

    • 哇,谢谢一百万,这是一个很好的解释。特别是关于'Haskell do notation'。我试图在 api 文档中找到这些方法但失败了。感谢您花时间输入所有内容!
    • @user3231690 顺便说一句,我怀疑submit() 的返回值是Option,它相当于Haskell 的Maybe。因此,for 理解将是您的 Some[Ok[???]]None。假设我是对的。
    【解决方案2】:

    类似这样的:

    index 是一个function,它接受StringOption 并返回AnyContent 类型的Action。它调用action 方法,该方法将Option 作为第一个参数,并将一个假定Request 类型的隐式值请求的方法作为第二个参数在作用域内。此方法使用一个 For-comprehension,它调用 submit 方法,该方法返回 OptionFuture,然后如果执行成功,它会产生结果 Ok(...),该结果将被包装在 ActionPrismicaction 方法返回。

    【讨论】:

    • 所以 Request 对象(它是一个对象还是一个函数还是什么?)在 Predef 范围内并作为参数注入到函数中?这看起来很奇怪和奇怪?我什至无法确定这是功能性的还是 OO....
    【解决方案3】:
    • Prismic.action 是一种采用 2 组参数的方法(也称为柯里化)。

    • 第一个是ref

    • 第二个是{ implicit request =&gt; ...},一个代码块中定义的函数

    more information on Action

    【讨论】:

    • 那就是柯里化,或者更准确地说是 schönfinkeling。
    猜你喜欢
    • 2018-05-02
    • 1970-01-01
    • 1970-01-01
    • 2017-11-24
    • 1970-01-01
    • 1970-01-01
    • 2010-12-02
    • 2023-03-26
    • 1970-01-01
    相关资源
    最近更新 更多