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

时间:2013-08-23 05:01:45

标签: scala spray shapeless

对于模糊的标题感到抱歉...不知道如何描述这一点。

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

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

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

我对喷雾如何为我的目的不感兴趣,但Scala的设施是什么,我将如何编写自己的?

4 个答案:

答案 0 :(得分:11)

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

的PathMatcher

/中没有方法String,因此使用an implicit conversionString转换为PathMatcher0PathMatcher[HNil]/ 3}}

方法PathMatcher需要PathMatcher并返回PathMatcher1[String]

method /PathMatcher[String :: HNil]/)。

具有PathMatcher[HNil]参数的PathMatcher[String :: HNil]的方法PathMatcher[String :: HNil]会返回/

具有PathMatcher[String :: HNil]参数的PathMatcher[String :: HNil]的方法PathMatcher[String :: String :: HNil]会返回PathMatcher[String :: String :: HNil]。这是来自Segment的黑魔法。请参阅异类列表shapeless;值得一读。

指令

因此,您使用Directive[String :: String :: HNil]作为参数调用concatenation。它返回apply

然后,您使用DirectiveFunction2[?, ?, ?])作为参数在(a, b) => ..上调用方法Directive[A :: B :: C ...]。对于使用方法apply((a: A, b: B, c: C ...) => Route)创建对象的每个PathMatcher,都有适当的隐式转换(请参阅黑魔法)。

解析

HList包含路径解析的规则。它将结果作为HNil返回。

“foo”匹配器method path一个字符串并忽略它(返回A / B)。

A匹配器将2个匹配器(BA)组合在一起,用“/”字符串分隔。它使用B连接来连接HListSegment的结果。

String :: HNil匹配器matches路径段并将其作为"foo" / Segment / Segment返回。

因此String :: String :: HNil匹配3个细分的路径,忽略第一个细分并将剩余的细分返回为Function2[String, String, Route]

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

黑魔法

正如@AlexIv所说:

对于使用方法apply((a: A, b: B, c: C ...) => Route)创建对象的每个ApplyConverter,都存在隐式转化matches

隐含地接受ApplyConverter(A, B, C ...) => Route的类型成员pimpApply代表每个Directive[A :: B :: C ...]的适当函数ApplyConverter

如果没有宏或样板代码,则无法创建此类隐式值。因此In用于生成{{1}}。请参阅sbt-boilerplate

答案 1 :(得分:5)

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

中使用的Scala结构感兴趣
path( "foo" / Segment / Segment ) { (a,b) => ... }

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

path( "foo" / Segment / Segment )

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

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

{ (a,b) => ... }

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

答案 2 :(得分:1)

senia 回答的一些补充说明,这真的很好。

当你写这样的东西时:

path("foo" / Segment / Segment) { (a,b) => {...} }

您正在调用Directive上的应用方法,例如 senia ,但指令中没有apply方法,因此使用隐式conversion来{ {3}}方法。正如您所能pimpApply使用类型类型模式happly实现的那样,默认情况下仅为Directive0定义。如您所见,它的伴随对象扩展ApplyConverterInstances,它是使用ApplyConverter插件生成的

答案 3 :(得分:0)

  1. 你为什么不看源?
  2. 至于我,它可以实现如下

    • 方法path采用任意类型参数,该类型的某个模式对象以及该类型的函数:

      def path[T](pattern:Pattern[T])(function:Function[T, `some other type like unit or any`])
      
    • 模式由两个技巧构成。

      • 字符串是“pimped”以使方法/或隐式转换为Pattern[Nothing]
      • Pattern[T]有方法/,它构造了一些新类型的模式。该方法采用单个参数(Segment的一些祖先)。我猜 - 模式[T2]:

        trait Pattern[T] {
            ///
            def `/`[T2](otherPattern:Pattern[T2]):Pattern[(T,T2)] 
        }
        
  3. 因此path的第一个参数允许将构造的模式类型确定为该对。因此我们得到第二个参数的正确类型。
  4. 实际匹配工作在path内完成。我认为这不属于问题范围。