你是对的,该语法通过了32
到函数test
。你缺少的是Directive
接受函数作为参数(记住,我们现在正在进行函数式编程,所以函数就是值)。如果你想写这个:
path(IntNumber) {
userId =>
complete(s"Hello user $userId")
}
以一种不太 DSL 的方式,你可以这样做:
val innerFunction: Int => Route = {userId => complete(s"Hello user $userId")}
(path(IntNumber))(innerFunction)
甚至这个:
def innerMethod(userId: Int): Route = complete(s"Hello user $userId")
(path(IntNumber))(innerMethod)
实际上实现这一点的机制是……复杂的;这个方法使得Directive
隐式转换为函数:
implicit def pimpApply[L <: HList](directive: Directive[L])(implicit hac: ApplyConverter[L]): hac.In ⇒ Route = f ⇒ directive.happly(hac(f))
这是使用“磁铁模式”来选择合适的hac
,这样如果指令提取参数,它可以采用内部路径中的函数(具有适当数量的参数),或者如果指令不提取参数,则可以采用内部路径(普通路由)中的值。代码看起来比实际更复杂,因为 scala 不直接支持完全依赖类型,所以我们必须通过隐式来模拟它。看ApplyConverterInstances
对于可怕的代码,这需要:/。
当我们获得实际路线时,实际的提取就会发生,在happly
具体指令的方法。 (如果一切都使用HList
在任何地方,我们基本上都可以避免/忽略前面的恐怖)。大多数提取指令(例如path
)最终调用hextract
:
def hextract[L <: HList](f: RequestContext ⇒ L): Directive[L] = new Directive[L] {
def happly(inner: L ⇒ Route) = ctx ⇒ inner(f(ctx))(ctx)
}
记住一个Route
真的只是一个RequestContext => Unit
,所以这会返回一个Route
那,当通过一个RequestContext
:
- Runs
f
在其上,提取需要提取的内容(例如 URL 路径组件)
- Runs
inner
on that; inner
是一个函数,例如内部路由的路径组件。
- 在上下文中运行该内部路由。
(以下内容是由模组从评论对话中编辑的):
从根本上来说,它非常优雅,而且很高兴您可以看到所有的喷雾代码,而且它是普通的 scala 代码(我真的建议您在感到困惑时阅读源代码)。但“桥接”部分ApplyConverter
很复杂,而且确实没有办法解决;它来自于尝试用一种并非真正为它们设计的语言来执行完全依赖类型。
你必须记住,喷射路由 DSL 是一种 DSL;它是一种 DSL。这是一种几乎任何其他语言都必须作为外部配置文件的东西。我想不出有哪个 Web 框架能够在路由定义方面提供与 Spray 相同的灵活性,并具有完整的编译时类型安全性。所以,是的,喷雾所做的一些事情很复杂 - 但正如引言所说,简单的事情应该是简单的,困难的事情应该是可能的。所有 scala 级别的事情都很简单; Spray 很复杂,但用另一种语言会更复杂(无法使用)。