Scala参数模式(喷涂路由示例)

Gre*_*reg 7 scala spray shapeless

抱歉模糊的标题...不知道如何表征这一点.

我已经在Scala中看到/使用过某种代码构造已有一段时间但我不知道它是如何工作的.它看起来像这样(来自Spray路由的示例):

path( "foo" / Segment / Segment ) { (a,b) => {  // <-- What's this style with a,b?
...
}}
Run Code Online (Sandbox Code Playgroud)

在此示例中,路径中的Segements分别绑定到关联块内的a和b.我知道如何使用这种模式,但它是如何工作的?为什么它没有绑定到"foo"?

我不太喜欢喷雾如何为我的目的而工作,但Scala的设施是什么,我怎么写自己的?

sen*_*nia 11

此代码来自扩展的类Directives.所以所有方法Directives都在范围内.

和PathMatcher

没有方法/String,因此隐式转换被用于转换StringPathMatcher0(PathMatcher[HNil]含)的方法/.

方法/取一个PathMatcher并返回一个PathMatcher.

SegmentPathMatcher1[String](PathMatcher[String :: HNil]).

方法/PathMatcher[HNil]使用PathMatcher[String :: HNil]参数返回PathMatcher[String :: HNil].

方法/PathMatcher[String :: HNil]使用PathMatcher[String :: HNil]参数返回PathMatcher[String :: String :: HNil].这是黑魔法shapeless.参见异类列表连接 ; 值得一读.

指示

所以,你调用方法pathPathMatcher[String :: String :: HNil]作为参数.它返回一个Directive[String :: String :: HNil].

然后你调用方法applyDirectiveFunction2[?, ?, ?]((a, b) => ..)作为参数.每个Directive[A :: B :: C ...]用方法创建对象的内容都有适当的隐式转换(参见黑魔法)apply((a: A, b: B, c: C ...) => Route).

解析

PathMatcher包含路径解析的规则.它返回结果为HList.

"foo"匹配器匹配 String并忽略它(返回HNil).

A / B匹配器结合了2个匹配器(AB由"/"分隔的字符串).它串接的结果AB采用HList级联.

Segment匹配匹配的路径段和返回它String :: HNil.

因此"foo" / Segment / Segment匹配3个段的路径,忽略第一个段并将剩余的段返回为String :: String :: HNil.

然后黑魔法允许你使用Function2[String, String, Route]((String, String) => Route)来处理String :: String :: HNil.如果没有这样的魔力,你将不得不使用这样的方法:{case a :: b :: HNil => ...}.

黑魔法

正如@AlexIv所说:

pimpApply每个Directive[A :: B :: C ...]用方法创建对象的隐式转换apply((a: A, b: B, c: C ...) => Route).

ApplyConverter含蓄地接受.型部件InApplyConverter表示适当的功能(A, B, C ...) => Route为每一个Directive[A :: B :: C ...].

没有宏或样板代码就无法创建这样的隐式值.所以sbt-boilerplate用于ApplyConverter生成.见ApplyConverterInstances.scala.


Ami*_*ico 5

Senia的回答有助于理解Spray-routing指令以及他们如何使用HLists来完成他们的工作.但我得到的印象是你真的只对使用的Scala结构感兴趣

path( "foo" / Segment / Segment ) { (a,b) => ... }
Run Code Online (Sandbox Code Playgroud)

听起来好像你将它解释为特殊的Scala语法,它以某种方式将这两个Segment实例连接到ab.事实并非如此.

path( "foo" / Segment / Segment )
Run Code Online (Sandbox Code Playgroud)

只是一个普通的调用,path只有一个参数,一个涉及两个/方法调用的表达式.没什么好看的,只是普通的方法调用.

该调用的结果是一个函数,它需要另一个函数 - 当匹配请求进入时你想要发生的事情 - 作为参数.这就是这部分:

{ (a,b) => ... }
Run Code Online (Sandbox Code Playgroud)

它只是一个带有两个参数的函数.第一部分(调用path)和第二部分(当收到匹配的消息时你想要做什么)在语法上没有任何关联.它们与Scala完全分开.但是,Spray的语义连接它们:第一部分创建一个函数,当收到匹配的消息时,它将调用第二部分.