该pathInfo
函数返回 a 的所有路径段Request
。也许我们可以定义一个类型类,给定一个 Servant API,为段列表生成一个“解析器”,其结果将是列表的格式化版本。
解析器类型可能类似于:
import Data.Text
import Control.Monad.State.Strict
import Control.Applicative
type PathParser = StateT ([Text],[Text]) Maybe ()
其中[Text]
状态中的第一个是尚未解析的路径段,第二个是我们迄今为止积累的格式化路径段。
这种类型有一个Alternative
实例,其中失败丢弃状态(基本上是回溯)和一个在-blocks内的模式匹配失败MonadFail
时返回的实例。mzero
do
类型类:
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE OverloadedStrings #-}
import Data.Data ( Proxy )
import GHC.TypeLits
class HasPathParser (x :: k) where
pathParser :: Proxy x -> PathParser
的实例Symbol
将路径片段从待处理列表移动到已处理列表:
instance KnownSymbol piece => HasPathParser (piece :: Symbol) where
pathParser _ = do
(piece : rest, found) <- get -- we are using MonadFail here
guard (piece == Data.Text.pack (symbolVal (Proxy @piece)))
put (rest, piece : found)
for 的实例Capture
将路径变量的名称(而不是值)放在已处理列表中:
instance KnownSymbol name => HasPathParser (Capture name x) where
pathParser _ = do
(_ : rest, found) <- get -- we are using MonadFail here
put (rest, Data.Text.pack (symbolVal (Proxy @name)) : found)
当我们到达Verb
( GET
, POST
...) 时,我们要求不应保留任何待处理的路径片段:
instance HasPathParser (Verb method statusCode contextTypes a) where
pathParser _ = do
([], found) <- get -- we are using MonadFail here
put ([], found)
其他一些情况:
instance HasPathParser (ReqBody x y) where
pathParser _ = pure ()
instance (HasPathParser a, HasPathParser b) => HasPathParser (a :> b) where
pathParser _ = pathParser (Proxy @a) *> pathParser (Proxy @b)
instance (HasPathParser a, HasPathParser b) => HasPathParser (a :<|> b) where
pathParser _ = pathParser (Proxy @a) <|> pathParser (Proxy @b)
让它发挥作用:
main :: IO ()
main = do
do let Just ([], result) = execStateT (pathParser (Proxy @BookAPI)) (["books"],[])
print result
-- ["books"]
do let Just ([], result) = execStateT (pathParser (Proxy @BookAPI)) (["book", "somebookid"],[])
print result
-- ["Name","book"]