2013-09-28 90 views
2

我是Scala的新手,试图理解模式匹配结构的语法,特别是来自Unfiltered(http://unfiltered.databinder.net/Try+Unfiltered.html)中的示例。Scala/Unfiltered中的模式匹配语法

这是一个简单的HTTP服务器,可以回显Hello World!和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(); 
    } 
} 

同样为了参考的路径,波段的源代码,和GET /方法对象:

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服务器,所以我一直在深入研究它几个小时。我明白,这事做对了GETPathSeg对象提取和unapply方法,我也知道,当我调试它击中在GETunapplySeg之前PathPath之前。

我不明白下面的事情:

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

  2. 为什么编译器知道什么值会传递给每个提取器的unapply方法?看起来它会将它们链接在一起,除非其中一个返回None而不是Some

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

  4. 有没有办法改写它,使其更清楚发生了什么?当我第一次看到这个例子时,我被 val sayhello = unfiltered.netty.cycle.Planify {弄糊涂了,我找到并重写了它,发现它隐式地创建了一个PartialFunction并将它传递给了Planify.apply。

回答

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

希望,这给你的匹配如何在幕后工作的想法。我不完全确定我是否得到了::一点 - 欢迎评论。