0

如果我在仆人 DSL 中描述了一些仆人的 API,例如:

type API = "some" :> QueryParam "a" Int :> QueryParam "b" Double:> Get '[JSON] Int

它有 HasClient 实例,所以我有一个客户端:

λ> let theClient = client (Proxy :: Proxy API)
λ> :t theClient 
theClient
  :: Maybe Int
     -> Maybe Double
     -> http-client-0.4.31:Network.HTTP.Client.Types.Manager
     -> BaseUrl
     -> ClientM Int
λ> 

如果我要手动调用所有这些 API,那就太好了。但是,如果我需要从文本文件生成 HTTP 请求怎么办?

API我可以从使用(我自己的)类型类 生成简单的解析器,HasParser它解析来自文本文件的所有查询参数,如下所示:

get some a=1234 b=0.1234
...

但是如何将解析的结果与客户端的类型联系起来呢?

当然,我可以为每个API方法在值级别编写所有解析器,但是我想从API类型级别生成所有 thar 解析器,因此特别API是解析器应该生成一个函数:

Manager -> BaseUrl -> ClientM Int

甚至可能吗?

UPD。

好的,问题实际上包括两个:

1)如何生成生成通用客户端

2)如何生成解析器,实际上使用什么类型来保持解析结果。

(1)虽然很笨重,但很简单:

type API = QueryParam "a" Int :> Get '[JSON] Int

type family FinalType k where
  FinalType (a :> b) = FinalType b
  FinalType (Verb a b c d) =  Client (Verb a b c d)

class ConvertAPI api input where
  convert :: Proxy api -> input -> FinalType api

instance ( HasClient api
         , Client api ~ (Maybe a -> FinalType api) 
         , Read a 
         )
         => ConvertAPI api String where
  convert (Proxy :: Proxy api) i = (client (Proxy :: Proxy api)) (Just (read i))

instance ( HasClient api
         , Client api ~ (Maybe a -> Maybe b -> FinalType api) 
         , Read a 
         , Read b 
         )
         => ConvertAPI api (String,String) where
  convert (Proxy :: Proxy api) (i,ii) = (client (Proxy :: Proxy api)) (Just (read i)) (Just (read i))

单例和二元组有两个实例,但很容易扩展所有可能维度的元组。

第二个问题还不清楚,我还不知道如何生成会产生不同维度元组的解析器。

UPD2。

使用嵌套元组的最小实现在这里: https ://gist.github.com/voidlizard/ef67a7ae486834d591d7b45c493bec1d

it gives:
λ> parse (Proxy :: Proxy API) ["42"]
Just ("42",())
λ> parse (Proxy :: Proxy API2) ["some","42"]
Just ("42",())
λ> parse (Proxy :: Proxy API2) ["42"]
Nothing

所以基本上这就是我想要的,但实现非常难看。是否有可能让它变得更好 - 使用一些 HLists 或 Vinyl 或其他东西来摆脱嵌套的元组?

UPD3 和结论。

好吧,好消息是很有可能的。我们可以定义和实现 HasParser 类型类,它定义了所有解析函数和类型或数据族来解析结果,以及 ConvertAPI 类型类,它将把 Servant 的客户端函数和解析结果转换为具有 type 的东西[Token] -> Manager -> BaseUrl -> ClientM a应该将a的类型包装成存在类型有一些复杂性,但它仍然是可能的。实现这一点仍然不完美,但这是另一个问题,如何实现具有现代特性(如类型级编程)的 API。

另一种方法是使用从servant 的API 定义生成的解析器,在Servant.Common.Req 上在值级别上构造HTTP 请求。更容易,但需要大量代码,并且以某种方式复制了 Servant 的 HasClient 实例。

坦率地说,我们都需要某种手册,如何使用现代类型级功能设计 API。

4

0 回答 0