使用与servant-client进行模式匹配的问题

时间:2017-05-16 16:41:11

标签: haskell servant

Servant docs中,我们有以下api:

type API = "position" :> Capture "x" Int :> Capture "y" Int :> Get '[JSON] Position
      :<|> "hello" :> QueryParam "name" String :> Get '[JSON] HelloMessage
      :<|> "marketing" :> ReqBody '[JSON] ClientInfo :> Post '[JSON] Email

我们可以像这样定义客户端函数:

api :: Proxy API
api = Proxy

position :<|> hello :<|> marketing = client api

如果我们的api类型看起来像:

type API = QueryParam "test" Int :> (
    "position" :> Capture "x" Int :> Capture "y" Int :> Get '[JSON] Position
      :<|> "hello" :> QueryParam "name" String :> Get '[JSON] HelloMessage
      :<|> "marketing" :> ReqBody '[JSON] ClientInfo :> Post '[JSON] Email)

与原始api相同但是对于所有端点都有一个额外的“test”查询参数,我们如何获得客户端函数?我尝试了几种模式匹配的变种,但无济于事。

如果一切都失败了,可以在每个端点的api类型中重复“test”查询参数,但这是Haskell,我们尽量避免重复。

1 个答案:

答案 0 :(得分:4)

Servant API定义存在于类型级别。如果我们想要操纵它们,我们需要像将函数(不是值,类型本身)转换为其他类型的函数。

Haskell中与此类型级函数最接近的是closed type family

{-# LANGUAGE TypeFamilies #-}

type family PrependParam api where
    PrependParam (a :<|> b) = PrependParam a :<|> PrependParam b
    PrependParam leaf = QueryParam "test" Int :> leaf

type API' = PrependParam API

:<|>分隔路由,而:>分隔路由中的组件。我们正在映射路由树并为每个路由添加前缀。

我们可以使用kind!命令从 ghci 检查它是否有效:

ghci> :kind! API'
API' :: *
= (QueryParam "test" Int
   :> ("position"
       :> (Capture "x" Int :> (Capture "y" Int :> Get '[JSON] Position))))
  :<|> ((QueryParam "test" Int
         :> ("hello"
             :> (QueryParam "name" String :> Get '[JSON] HelloMessage)))
        :<|> (QueryParam "test" Int
              :> ("marketing"
                  :> (ReqBody '[JSON] ClientInfo :> Post '[JSON] Email))))