Kri*_*fer 10 scala function akka-http
我是Scala的新手,很难理解声明和使用函数的所有方法.有人可以一步一步解释这里发生了什么吗?
我正在学习介绍Akka HTTP的课程.代码有效,但我不明白路由方法:
import akka.http.scaladsl.server.Directives._
def route = path("hello") {
get {
complete("Hello, World!")
}
}
Run Code Online (Sandbox Code Playgroud)
我们正在定义一个route被声明为path(从上面的行导入)的值的方法,但是在path函数内部我们有一些get我不理解的东西.
当我宣布path作为一种方法时,我是否压倒它,或者发生了什么?
如果有人能够逐行解释发生了什么,我会很高兴.并且不介意它是Akka参与.我想知道Scala语法.
================================================== ===============
感谢所有的好答案.我想我明白了!
所以总结一下我的版本吧.
path()是一个想要字符串的函数.它返回另一个想要的函数Directive.在Scala lingo中我们可以做一些currying来直接向返回的函数发送指令.
所以块{}中的所有内容都被发送到path()返回的函数.由于Scala中的一个块总是返回最后一行,我们将get按照我们调用的相同原则返回该行complete.
get也是一个函数,它接受一个参数,可以写成一个块.这相当于写作get(complete("Hello, world")).
再次感谢!
Tra*_*own 28
你不一定需要理解这个答案中的所有内容,以便有效地使用akka-http,但我保证你会有时间 - 可能是早一点,而不是一点一点 - 你将与编译器斗争,并且只是想要所有的幻想语法糖消失了,好消息是有工具使这成为可能(坏消息是,一旦你摆脱了花哨的语法,现实可能是一个可怕的混乱).
首先要注意的是,虽然这里的花括号可能看起来很像Java或其他语言的范围或定义分隔符,但它们实际上只是将方法应用于参数.你可以用括号做同样的事情:
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 = ...
Run Code Online (Sandbox Code Playgroud)
虽然这些get和complete事物可能看起来像关键字或其他东西,但它们实际上只是静态方法Directives(大概阅读整篇文章的全部内容),因此以下内容也是等效的:
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 = ...
Run Code Online (Sandbox Code Playgroud)
希望能解释一些语法,但这里还有很多看不见的东西.如果你在REPL中,你可以使用scala-reflect reify作为一个非常有用的工具来帮助使这些东西可见.
从一个简单的(不相关的)示例开始,您可能想知道当您看到Scala代码时发生了什么"a" * 3,特别是如果您知道Java字符串没有*运算符,那么您打开一个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)
Run Code Online (Sandbox Code Playgroud)
还有desberared版本,显示了应用于字符串的隐式方法,使其成为*运算符.
在你的情况下你可以写这样的东西:
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)))))
Run Code Online (Sandbox Code Playgroud)
为了便于阅读,我们可以重新格式化已知的表达式:
Directive.addByNameNullaryApply(
Directives.path(
Directives._segmentStringToPathMatcher("hello")
)
).apply(
Directive.addByNameNullaryApply(Directives.get).apply(
Directives.complete(
ToResponseMarshallable.apply("Hello, World!")(
Marshaller.liftMarshaller(Marshaller.StringMarshaller)
)
)
)
)
Run Code Online (Sandbox Code Playgroud)
如果你添加了几个导入,这也是完全合法的Scala代码:
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 = ...
Run Code Online (Sandbox Code Playgroud)
为了逐步解释这一点,我们可以从一开始path("hello").我们可以从API文档中看到Directives.path不带字符串的文档,而是a PathMatcher,所以我们知道从中String开始的隐式转换PathMatcher正在进行中,在我们完全脱落的版本中,我们可以在这里看到:
Directives.path(
Directives._segmentStringToPathMatcher("hello")
)
Run Code Online (Sandbox Code Playgroud)
当然,如果我们检查文档,_segmentStringToPathMatcher则是对适当类型的隐式转换.
类似的事情正在发生complete("Hello, World!").Directives.complete取一个ToMarshallableResponse,而不是一个String,所以必须有一个隐式转换.在这种情况下ToResponseMarshallable.apply,它也需要一个隐式Marshaller实例,在这种情况下,它通过从a ToEntityMarshaller到a 的隐式转换得到ToResponseMarshallable,其中ToEntityMarshaller实例是Marshaller.StringMarshaller,和转换器是Marshaller.liftMarshaller部分:
Directives.complete(
ToResponseMarshallable.apply("Hello, World!")(
Marshaller.liftMarshaller(Marshaller.StringMarshaller)
)
)
Run Code Online (Sandbox Code Playgroud)
还记得上面我说的get只是一个静态方法Directives吗?这是一种谎言,在某种意义上说,虽然它是一个静态方法Directives,但我们在编写时并没有调用它get(...).相反,这get实际上是一个返回a的无参数方法Directive0.Directive0为类型别名Directive[Unit],虽然Directive[Unit]不具有apply的方法,它可以被隐式转换成做的事情,通过addByNameNullaryApply上方法Directive.因此,当您编写时get(...),Scala将该数据去掉,get.apply(...)然后将该get值转换为Route => Route函数,该函数具有适当的apply方法.这个path("hello")(...)部分正好发生了同样的事情.
这种事情看起来像是一场噩梦,作为一个长期的Scala用户,我可以告诉你,这绝对经常是.但是,类似工具reify和API文档可以让它变得不那么可怕.
这里发生了很多事情,这是一个理解 scala 的非常复杂的例子。但我会尝试。
\n\nroute\ 的类型是,Route它是一个类型别名,定义为type Route = RequestContext \xe2\x87\x92 Future[RouteResult]其中是一个消耗和生成 的RequestContext \xe2\x87\x92 Future[RouteResult]函数。RequestContextFuture[RouteResult]
path是一种创建Directive[Unit]. 有一个隐式转换可以转换Directive[Unit]为函数Route => Route(简化)。函数可以通过方法apply或使用编译器糖调用(???)或{???}。
get是一种创建太的方法Directive[Unit],并且类似的方法也适用于它。
completeStandardRoute是扩展的类型Route。
知道了这一切,我们可以丑化你的例子,将写成
\n\npath("hello").apply { ctx =>\n val inner: Route = { ctx =>\n ctx.complete("done")\n }\n get.apply(inner).apply(ctx)\n}\nRun Code Online (Sandbox Code Playgroud)\n