Scala / Unfiltered中的模式匹配语法

时间:2013-09-28 23:18:08

标签: scala pattern-matching unfiltered

我是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服务器,所以我已经深入研究了几个小时。我知道它与提取器和unapplyGETPath对象上的Seg方法有关,我也知道当我调试它时{h} {在unapply之前的GETPath之前的Path {1}}。{/}

我不明白以下事项:

  1. 为什么我不能写Seg,但我可以写GET.unapply(req)GET(req),它会匹配任何HTTP GET?

  2. 编译器为什么或如何知道将哪些值传递给每个提取器的GET()方法?它似乎只是将它们链接在一起,除非其中一个返回unapply而不是None

  3. 它如何绑定变量p和q?它知道它们是字符串,它必须从Some的返回类型推断出来,但是我不明白指定p列表第一部分的值的机制和q的第二部分的值。清单。

  4. 有没有办法重写它以使其更清楚发生了什么?当我第一次看到这个例子时,我对这条线感到困惑 Seg.unapply,我挖了一遍并重新编写它,发现它隐含地创建了一个PartialFunction并将其传递给Planify.apply。

1 个答案:

答案 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)
                }
            }
        }
    }
}

希望这能让您了解匹配在幕后的工作原理。我不完全确定我的::位是否正确 - 欢迎提出意见。