2013-09-28 135 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) 
       } 
      } 
     } 
    } 
} 

希望,這給你的匹配如何在幕後工作的想法。我不完全確定我是否得到了::一點 - 歡迎評論。