Scala中的函数/方法。这是如何运作的? [英] Functions / methods in Scala. How does this work?

查看:63
本文介绍了Scala中的函数/方法。这是如何运作的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是Scala的新手,很难理解所有声明和使用函数的方式。有人可以逐步解释一下这里发生了什么吗?

I am new to Scala and have a hard time understanding all the ways of declaring and using functions. Can someone please explain, step by step, what is going on here?

我正在学习介绍Akka HTTP的课程。该代码有效,但我不理解该路由方法:

I am following a course that introduces Akka HTTP. The code works, but I do not understand the route method:

import akka.http.scaladsl.server.Directives._

def route = path("hello") {
    get {
      complete("Hello, World!")
    }
  }

我们正在定义方法 route ,该方法被声明为值 path (从上面的行导入),但是在 path 函数内部,我们有一个叫做<$ c $的东西c>得到我不理解的。

We are defining a method route that is being declared the value of path (imported from the line above), but then inside the path function we have something called get that I don't understand.

当我声明路径作为一种方法,我是覆盖它,还是发生了什么?

And when I am declaring path as a method, am I overriding it, or what's going on?

如果有人可以逐行解释发生的事情,我会很乐意。而且不要介意这与Akka有关。我想了解Scala语法。

I would love if someone can explain what is going on, line by line. And don't mind that it is Akka involved. I want to know about the Scala syntax.

=========================== =====================================

=================================================================

感谢所有出色的回答。我想我明白了!

Thanks for all the great answers. I think I get it!

所以总结一下我的版本。

So to summarize my version of it.

path()是需要字符串的函数。它返回另一个需要 Directive 的函数。并且在Scala术语中,我们可以进行某种操作以直接将指令发送给返回的函数。

path() is a function that wants a string. It returns another function that wants a Directive. And in Scala lingo we can do some kind of currying to directly send a directive to the returned function.

因此,块{}中的所有内容都发送给了函数, code> path()返回。而且由于Scala中的某个块总是返回最后一行,因此我们返回的是 get ,根据我们称之为 complete 。

So everything in the block {} is sent to the function that path() returns. And since a block in Scala always returns the last line we are returning the get which by the same principles we call with complete.

get 也是一个函数,它带有一个参数并且可以写为一个块。这等效于只编写 get(complete( Hello,world))

get is also a function, that takes one parameter and that can be written as a block. This is equivalent to just writing get(complete("Hello, world")).

再次感谢!

推荐答案

为了有效地使用akka-http,您不一定需要了解此答案中的所有内容,但我保证您会有时(可能早些而不是晚些)您将与编译器进行斗争,并且只希望所有花哨的语法糖都消失了,好消息是有工具可以实现这一目标(坏消息是,一旦

You don't necessarily need to understand everything in this answer to use akka-http effectively, but I guarantee you there will be times—probably sooner rather than later—that you will be fighting with the compiler and will just want all of the fancy syntactic sugar to go away, and the good news is that there are tools that make this possible (the bad news is that once you get rid of the fancy syntax the reality can be a terrifying mess).

首先要注意的是,这里的花括号看起来很像范围或Java或其他语言的定义定界符,它们实际上只是将方法应用于参数。您可以使用括号来做同样的事情:

The first thing to note is that while the curly braces here may look a lot like scope or definition delimiters from Java or other languages, they're really just applying methods to arguments. You can do the same thing with parentheses:

scala> import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Directives._

scala> val route = path("hello")(get(complete("Hello, World!")))
route: akka.http.scaladsl.server.Route = ...

而这些 get 完成事物可能看起来像关键字之类的东西,它们实际上只是指令上的静态方法(大约-完整阅读全文),因此以下是也等效:

And while these get and complete things may look like keywords or something, they're really just static methods on Directives (approximately—read this whole thing for the full story), so the following is also equivalent:

scala> import akka.http.scaladsl.server.Directives
import akka.http.scaladsl.server.Directives

scala> val route = Directives.path("hello")(
     |   Directives.get(Directives.complete("Hello, World!"))
     | )
route: akka.http.scaladsl.server.Route = ...

这希望可以解释一些语法,但是仍然有很多看不见的东西在这里发生。如果您使用的是REPL,则可以使用scala-reflect的 reify 作为非常有用的工具来使这些内容可见。

That hopefully explains some of the syntax, but there's still a lot of invisible stuff going on here. If you're in a REPL, you can use scala-reflect's reify as an extremely helpful tool to help make this stuff visible.

从一个简单的(不相关的)示例开始,您可能想知道当看到诸如 a * 3 之类的Scala代码时发生了什么,特别是如果您知道Java字符串没有 * 运算符,因此您打开一个REPL:

To start with a simple (unrelated) example, you might wonder what's happening when you see Scala code like "a" * 3, especially if you know that Java strings don't have an * operator, so you open a REPL:

scala> import scala.reflect.runtime.universe.reify
import scala.reflect.runtime.universe.reify

scala> reify("a" * 3).tree
res6: reflect.runtime.universe.Tree = Predef.augmentString("a").$times(3)

其中有一个经过简化的版本,显示了应用于字符串的隐式方法,以使其为 *

And there's the desugared version, showing the implicit method that's being applied to the string to give it the * operator.

在您的情况下,您可以这样写:

In your case you could write something like this:

scala> import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Directives._

scala> import scala.reflect.runtime.universe.reify
import scala.reflect.runtime.universe.reify

scala> reify(path("hello")(get(complete("Hello, World!")))).tree
res0: reflect.runtime.universe.Tree = Directive.addByNameNullaryApply(Directives.path(Directives._segmentStringToPathMatcher("hello"))).apply(Directive.addByNameNullaryApply(Directives.get).apply(Directives.complete(ToResponseMarshallable.apply("Hello, World!")(Marshaller.liftMarshaller(Marshaller.StringMarshaller)))))

我们可以重新格式化化的表达式以提高可读性:

We can reformat the reified expression for readability:

Directive.addByNameNullaryApply(
  Directives.path(
    Directives._segmentStringToPathMatcher("hello")
  )
).apply(
  Directive.addByNameNullaryApply(Directives.get).apply(
    Directives.complete(
      ToResponseMarshallable.apply("Hello, World!")(
        Marshaller.liftMarshaller(Marshaller.StringMarshaller)
      )
    )
  )
)

如果添加几个进口商品,这也是完全合法的Scala鳕鱼e:

If you add a couple of imports, this is also perfectly legal Scala code:

scala> import akka.http.scaladsl.server.{ Directive, Directives }
import akka.http.scaladsl.server.{Directive, Directives}

scala> import akka.http.scaladsl.marshalling.{ Marshaller, ToResponseMarshaller }
import akka.http.scaladsl.marshalling.{Marshaller, ToResponseMarshaller}

scala> val route = Directive.addByNameNullaryApply(
     |   Directives.path(
     |     Directives._segmentStringToPathMatcher("hello")
     |   )
     | ).apply(
     |   Directive.addByNameNullaryApply(Directives.get).apply(
     |     Directives.complete(
     |       ToResponseMarshallable.apply("Hello, World!")(
     |         Marshaller.liftMarshaller(Marshaller.StringMarshaller)
     |       )
     |     )
     |   )
     | )
route: akka.http.scaladsl.server.Route = ...

要逐步解释,我们可以从 path( hello)。我们可以从 API文档 Directives.path 不需要字符串,而是 PathMatcher ,因此我们知道 String PathMatcher 的脚步,在我们完全去尾的版本中,我们可以在这里看到:

To explain this step by step, we can start with path("hello"). We can see from the API docs that Directives.path doesn't take a string, but rather a PathMatcher, so we know that an implicit conversion from String to PathMatcher is kicking in, and in our fully desugared version, we can see that here:

  Directives.path(
    Directives._segmentStringToPathMatcher("hello")
  )

当然,如果我们检查文档,则 _segmentStringToPathMatcher 是相应类型的隐式转换。

And sure enough if we check the docs, _segmentStringToPathMatcher is an implicit conversion of the appropriate type.

complete( Hello,World!)中发生了类似的事情。 Directives.complete 接受 ToMarshallableResponse ,而不是 String ,因此必须有一个隐式转换。在这种情况下,它是 ToResponseMarshallable.apply ,还需要一个隐式 Marshaller 实例,在这种情况下,它通过从 ToEntityMarshaller ToResponseMarshallable 的隐式转换获得,其中 ToEntityMarshaller 实例是 Marshaller.StringMarshaller ,而转换器是 Marshaller.liftMarshaller 部分:

A similar thing is happening in complete("Hello, World!"). Directives.complete takes a ToMarshallableResponse, not a String, so there must be an implicit conversion kicking in. In this case it's ToResponseMarshallable.apply, which also requires an implicit Marshaller instance, which in this case it gets via an implicit conversion from a ToEntityMarshaller to a ToResponseMarshallable, where the ToEntityMarshaller instance is Marshaller.StringMarshaller, and the converter is the Marshaller.liftMarshaller part:

    Directives.complete(
      ToResponseMarshallable.apply("Hello, World!")(
        Marshaller.liftMarshaller(Marshaller.StringMarshaller)
      )
    )

记住方式上面我说过 get 只是指令的静态方法?从某种意义上说,这是一个谎言,尽管这是指令上的静态方法,但在编写 get( ...)。相反,此 get 实际上是一个无参数方法,该方法返回 Directive0 Directive0 Directive [Unit] 的类型别名,而 Directive [Unit] 没有 apply 方法,可以通过 addByNameNullaryApply 指令上的c $ c>方法。因此,当您编写 get(...)时,Scala将其替换为 get.apply(...)和然后将 get 值转换为 Route => Route 函数,该函数具有适当的 apply 方法。 path( hello)(...)部分也发生了完全相同的事情。

Remember how above I said get was just a static method on Directives? That was kind of a lie, in the sense that while it is a static method on Directives, we're not calling it when we write get(...). Instead this get is actually a no-argument method that returns a Directive0. Directive0 is a type alias for Directive[Unit], and while Directive[Unit] doesn't have an apply method, it can be implicitly converted into a thing that does, via the addByNameNullaryApply method on Directive. So when you write get(...), Scala desugars that to get.apply(...) and then converts the get value into a Route => Route function, which has an appropriate apply method. And exactly the same thing is happening with the path("hello")(...) part.

这种事情似乎是一场噩梦,作为Scala的长期用户,我可以告诉您,这肯定是经常发生的。不过,像 reify 之类的工具和API文档可以使它的可怕程度降低一些。

This kind of thing may seem like a nightmare, and as a long-time Scala user I can tell you that it definitely often is. Tools like reify and API docs can make it a little less horrible, though.

这篇关于Scala中的函数/方法。这是如何运作的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆