我是Scala的新手并尝试理解模式匹配构造的语法,特别是Unfiltered(http://unfiltered.databinder.net/Try+Unfiltered.html)中的示例。
这是一个回传Hello World的简单HTTP服务器!如果路径长度为2个,则路径的2个部分:
package com.hello
import unfiltered.request.GET
import unfiltered.request.Path
import unfiltered.request.Seg
import unfiltered.response.ResponseString
object HelloWorld {
val sayhello = unfiltered.netty.cycle.Planify {
case GET(Path(Seg(p :: q :: Nil))) => {
ResponseString("Hello World! " + p + " " + q);
}
};
def main(args: Array[String]) {
unfiltered.netty.Http(10000).plan(sayhello).run();
}
}
还可以参考Path,Seg和GET / Method对象的源代码:
package unfiltered.request
object Path {
def unapply[T](req: HttpRequest[T]) = Some(req.uri.split('?')(0))
def apply[T](req: HttpRequest[T]) = req.uri.split('?')(0)
}
object Seg {
def unapply(path: String): Option[List[String]] = path.split("/").toList match {
case "" :: rest => Some(rest) // skip a leading slash
case all => Some(all)
}
}
class Method(method: String) {
def unapply[T](req: HttpRequest[T]) =
if (req.method.equalsIgnoreCase(method)) Some(req)
else None
}
object GET extends Method("GET")
我能够分解它的大部分工作方式,但这条线让我感到困惑:
case GET(Path(Seg(p :: q :: Nil))) => {
我理解代码的目的,但不了解它是如何应用的。我对学习Scala的细节非常感兴趣,而不是简单地用它来实现一个HTTP服务器,所以我已经深入研究了几个小时。我知道它与提取器和unapply
,GET
和Path
对象上的Seg
方法有关,我也知道当我调试它时{h} {在unapply
之前的GET
和Path
之前的Path
{1}}。{/}
我不明白以下事项:
为什么我不能写Seg
,但我可以写GET.unapply(req)
或GET(req)
,它会匹配任何HTTP GET?
编译器为什么或如何知道将哪些值传递给每个提取器的GET()
方法?它似乎只是将它们链接在一起,除非其中一个返回unapply
而不是None
?
它如何绑定变量p和q?它知道它们是字符串,它必须从Some
的返回类型推断出来,但是我不明白指定p列表第一部分的值的机制和q的第二部分的值。清单。
有没有办法重写它以使其更清楚发生了什么?当我第一次看到这个例子时,我对这条线感到困惑
Seg.unapply
,我挖了一遍并重新编写它,发现它隐含地创建了一个PartialFunction并将其传递给Planify.apply。
答案 0 :(得分:2)
理解它的一种方法是以Scala编译器重写的方式重写此表达式。
unfiltered.netty.cycle.Planify
期望PartialFunction[HttpRequest[ReceivedMessage], ResponseFunction[NHttpResponse]]
,即一个可能与参数匹配或不匹配的函数。如果case
语句中的任何一个都不匹配,则会忽略该请求。如果匹配 - 也必须通过所有提取器 - 将返回响应。
每个case
语句都会获得HttpRequest[ReceivedMessage]
的实例。然后,它通过一系列unapply
方法为每个匹配器应用左关联性:
// The request passed to us is HttpRequest[ReceivedMessage]
// GET.unapply only returns Some if the method is GET
GET.unapply(request) flatMap { getRequest =>
// this separates the path from the query
Path.unapply(getRequest) flatMap { path =>
// splits the path by "/"
Seg.unapply(path) flatMap { listOfParams =>
// Calls to unapply don't end here - now we build an
// instance of :: class, which
// since a :: b is the same as ::(a, b)
::.unapply(::(listOfParams.head, listOfParams.tail)) flatMap { case (p, restOfP) =>
::.unapply(::(restOfP.head, Nil)) map { case (q, _) =>
ResponseString("Hello World! " + p + " " + q)
}
}
}
}
}
希望这能让您了解匹配在幕后的工作原理。我不完全确定我的::
位是否正确 - 欢迎提出意见。